summaryrefslogtreecommitdiff
path: root/usr/src/lib/libiscsit/common
diff options
context:
space:
mode:
authorPeter Dunlap <Peter.Dunlap@Sun.COM>2008-10-30 15:49:07 -0600
committerPeter Dunlap <Peter.Dunlap@Sun.COM>2008-10-30 15:49:07 -0600
commita6d42e7d71324c5193c3b94d57d96ba2925d52e1 (patch)
treeb99a1d06464c8dcb9ae0462623f708a380a27d2c /usr/src/lib/libiscsit/common
parent2d6aa547f868d751d00f1f2e70171d9dc9317af1 (diff)
downloadillumos-joyent-a6d42e7d71324c5193c3b94d57d96ba2925d52e1.tar.gz
PSARC 2008/587 iSCSI Port Provider for COMSTAR
6702584 Need iSCSI port provider for COMSTAR
Diffstat (limited to 'usr/src/lib/libiscsit/common')
-rw-r--r--usr/src/lib/libiscsit/common/libiscsit.c1889
-rw-r--r--usr/src/lib/libiscsit/common/libiscsit.h747
-rw-r--r--usr/src/lib/libiscsit/common/llib-liscsit30
-rw-r--r--usr/src/lib/libiscsit/common/mapfile-vers79
4 files changed, 2745 insertions, 0 deletions
diff --git a/usr/src/lib/libiscsit/common/libiscsit.c b/usr/src/lib/libiscsit/common/libiscsit.c
new file mode 100644
index 0000000000..a9b2edeae4
--- /dev/null
+++ b/usr/src/lib/libiscsit/common/libiscsit.c
@@ -0,0 +1,1889 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <uuid/uuid.h>
+#include <errno.h>
+#include <unistd.h>
+#include <strings.h>
+#include <libintl.h>
+
+#include <libstmf.h>
+#include <libiscsit.h>
+#include <sys/iscsit/iscsit_common.h>
+#include <sys/iscsi_protocol.h>
+#include <sys/iscsit/isns_protocol.h>
+
+/* From iscsitgtd */
+#define TARGET_NAME_VERS 2
+
+/* this should be defined someplace central... */
+#define ISCSI_NAME_LEN_MAX 223
+
+/* max length of a base64 encoded secret */
+#define MAX_BASE64_LEN 341
+
+/* Default RADIUS server port */
+#define DEFAULT_RADIUS_PORT 1812
+
+/*
+ * The kernel reserves target portal group tag value 1 as the default.
+ */
+#define ISCSIT_DEFAULT_TPGT 1
+#define MAXTAG 0xffff
+
+/* helper for property list validation */
+#define PROPERR(lst, key, value) { \
+ if (lst) { \
+ (void) nvlist_add_string(lst, key, value); \
+ } \
+}
+
+/* helper function declarations */
+static int
+it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix);
+
+static int
+it_val_pass(char *name, char *val, nvlist_t *e);
+
+/* consider making validate funcs public */
+static int
+it_validate_configprops(nvlist_t *nvl, nvlist_t *errs);
+
+static int
+it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs);
+
+static int
+it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs);
+
+/*
+ * Function: it_config_load()
+ *
+ * Allocate and create an it_config_t structure representing the
+ * current iSCSI configuration. This structure is compiled using
+ * the 'provider' data returned by stmfGetProviderData(). If there
+ * is no provider data associated with iscsit, the it_config_t
+ * structure will be set to a default configuration.
+ *
+ * Parameters:
+ * cfg A C representation of the current iSCSI configuration
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid parameter
+ */
+int
+it_config_load(it_config_t **cfg)
+{
+ int ret = 0;
+ nvlist_t *cfg_nv = NULL;
+ it_config_t *newcfg = NULL;
+ uint64_t stmf_token = 0;
+
+ if (!cfg) {
+ return (EINVAL);
+ }
+
+ *cfg = NULL;
+
+ ret = stmfGetProviderDataProt(ISCSIT_MODNAME, &cfg_nv,
+ STMF_PORT_PROVIDER_TYPE, &stmf_token);
+
+ if ((ret == STMF_STATUS_SUCCESS) ||
+ (ret == STMF_ERROR_NOT_FOUND)) {
+ /*
+ * If not initialized yet, return empty it_config_t
+ * Else, convert nvlist to struct
+ */
+ ret = it_nv_to_config(cfg_nv, &newcfg);
+ }
+
+ if (ret == 0) {
+ newcfg->stmf_token = stmf_token;
+ *cfg = newcfg;
+ }
+
+ return (ret);
+}
+
+/*
+ * Function: it_config_commit()
+ *
+ * Informs the iscsit service that the configuration has changed and
+ * commits the new configuration to persistent store by calling
+ * stmfSetProviderData. This function can be called multiple times
+ * during a configuration sequence if necessary.
+ *
+ * Parameters:
+ * cfg A C representation of the current iSCSI configuration
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid it_config_t structure
+ * TBD ioctl() failed
+ * TBD could not save config to STMF
+ */
+int
+it_config_commit(it_config_t *cfg)
+{
+ int ret;
+ nvlist_t *cfgnv = NULL;
+ char *packednv = NULL;
+ int iscsit_fd = -1;
+ size_t pnv_size;
+ iscsit_ioc_set_config_t iop;
+ it_tgt_t *tgtp;
+
+ if (!cfg) {
+ return (EINVAL);
+ }
+
+ iscsit_fd = open(ISCSIT_NODE, O_RDWR|O_EXCL);
+ if (iscsit_fd == -1) {
+ ret = errno;
+ return (ret);
+ }
+
+ ret = it_config_to_nv(cfg, &cfgnv);
+ if (ret == 0) {
+ ret = nvlist_size(cfgnv, &pnv_size, NV_ENCODE_NATIVE);
+ }
+
+ if (ret == 0) {
+ packednv = malloc(pnv_size);
+ if (!packednv) {
+ ret = ENOMEM;
+ } else {
+ ret = nvlist_pack(cfgnv, &packednv, &pnv_size,
+ NV_ENCODE_NATIVE, 0);
+ }
+ }
+
+ /*
+ * Send the changes to the kernel first, for now. Kernel
+ * will be the final sanity check before config is saved
+ * persistently.
+ *
+ * XXX - this leaves open the simultaneous-change hole
+ * that STMF was trying to solve, but is a better sanity
+ * check. Final decision on save order/config generation
+ * number TBD.
+ */
+ if (ret == 0) {
+ iop.set_cfg_vers = ISCSIT_API_VERS0;
+ iop.set_cfg_pnvlist = packednv;
+ iop.set_cfg_pnvlist_len = pnv_size;
+ if ((ioctl(iscsit_fd, ISCSIT_IOC_SET_CONFIG, &iop)) != 0) {
+ ret = errno;
+ }
+ }
+
+ /*
+ * Before saving the config persistently, remove any
+ * PROP_OLD_TARGET_NAME entries. This is only interesting to
+ * the active service.
+ */
+ if (ret == 0) {
+ tgtp = cfg->config_tgt_list;
+ for (; tgtp != NULL; tgtp = tgtp->tgt_next) {
+ if (!tgtp->tgt_properties) {
+ continue;
+ }
+ if (nvlist_exists(tgtp->tgt_properties,
+ PROP_OLD_TARGET_NAME)) {
+ (void) nvlist_remove_all(tgtp->tgt_properties,
+ PROP_OLD_TARGET_NAME);
+ }
+ }
+ }
+
+ /*
+ * stmfGetProviderDataProt() checks to ensure
+ * that the config data hasn't changed since we fetched it.
+ *
+ * The kernel now has a version we need to save persistently.
+ * CLI will 'do the right thing' and warn the user if it
+ * gets STMF_ERROR_PROV_DATA_STALE. We'll try once to revert
+ * the kernel to the persistently saved data, but ultimately,
+ * it's up to the administrator to validate things are as they
+ * want them to be.
+ */
+ if (ret == 0) {
+ ret = stmfSetProviderDataProt(ISCSIT_MODNAME, cfgnv,
+ STMF_PORT_PROVIDER_TYPE, &(cfg->stmf_token));
+
+ if (ret == STMF_STATUS_SUCCESS) {
+ ret = 0;
+ } else if (ret == STMF_ERROR_NOMEM) {
+ ret = ENOMEM;
+ } else if (ret == STMF_ERROR_PROV_DATA_STALE) {
+ int st;
+ it_config_t *rcfg = NULL;
+
+ st = it_config_load(&rcfg);
+ if (st == 0) {
+ (void) it_config_commit(rcfg);
+ it_config_free(rcfg);
+ }
+ }
+ }
+
+ (void) close(iscsit_fd);
+
+ if (packednv) {
+ free(packednv);
+ }
+
+ if (cfgnv) {
+ nvlist_free(cfgnv);
+ }
+
+ return (ret);
+}
+
+/*
+ * Function: it_config_setprop()
+ *
+ * Validate the provided property list and set the global properties
+ * for iSCSI Target. If errlist is not NULL, returns detailed
+ * errors for each property that failed. The format for errorlist
+ * is key = property, value = error string.
+ *
+ * Parameters:
+ *
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * proplist nvlist_t containing properties for this target.
+ * errlist (optional) nvlist_t of errors encountered when
+ * validating the properties.
+ *
+ * Return Values:
+ * 0 Success
+ * EINVAL Invalid property
+ *
+ */
+int
+it_config_setprop(it_config_t *cfg, nvlist_t *proplist, nvlist_t **errlist)
+{
+ int ret;
+ it_portal_t *isns = NULL;
+ it_portal_t *pnext = NULL;
+ it_portal_t *newisnslist = NULL;
+ char **arr;
+ uint32_t count;
+ uint32_t newcount;
+ nvlist_t *cprops = NULL;
+ char *val = NULL;
+
+ if (!cfg || !proplist) {
+ return (EINVAL);
+ }
+
+ if (errlist) {
+ (void) nvlist_alloc(errlist, 0, 0);
+ }
+
+ /*
+ * copy the existing properties, merge, then validate
+ * the merged properties before committing them.
+ */
+ if (cfg->config_global_properties) {
+ ret = nvlist_dup(cfg->config_global_properties, &cprops, 0);
+ } else {
+ ret = nvlist_alloc(&cprops, NV_UNIQUE_NAME, 0);
+ }
+
+ /* base64 encode the radius secret, if it's changed */
+ val = NULL;
+ (void) nvlist_lookup_string(proplist, PROP_RADIUS_SECRET, &val);
+ if (val) {
+ char bsecret[MAX_BASE64_LEN];
+
+ ret = it_val_pass(PROP_RADIUS_SECRET, val, *errlist);
+
+ if (ret == 0) {
+ (void) memset(bsecret, 0, MAX_BASE64_LEN);
+
+ ret = iscsi_binary_to_base64_str((uint8_t *)val,
+ strlen(val), bsecret, MAX_BASE64_LEN);
+
+ if (ret == 0) {
+ /* replace the value in the nvlist */
+ ret = nvlist_add_string(proplist,
+ PROP_RADIUS_SECRET, bsecret);
+ }
+ }
+ }
+
+ if (ret == 0) {
+ ret = nvlist_merge(cprops, proplist, 0);
+ }
+
+ /* see if we need to remove the radius server setting */
+ val = NULL;
+ (void) nvlist_lookup_string(cprops, PROP_RADIUS_SERVER, &val);
+ if (val && (strcasecmp(val, "none") == 0)) {
+ (void) nvlist_remove_all(cprops, PROP_RADIUS_SERVER);
+ }
+
+ /* and/or remove the alias */
+ val = NULL;
+ (void) nvlist_lookup_string(cprops, PROP_ALIAS, &val);
+ if (val && (strcasecmp(val, "none") == 0)) {
+ (void) nvlist_remove_all(cprops, PROP_ALIAS);
+ }
+
+ if (ret == 0) {
+ ret = it_validate_configprops(cprops, *errlist);
+ }
+
+ if (ret != 0) {
+ if (cprops) {
+ nvlist_free(cprops);
+ }
+ return (ret);
+ }
+
+ /*
+ * Update iSNS server list, if exists in provided property list.
+ */
+ ret = nvlist_lookup_string_array(proplist, PROP_ISNS_SERVER,
+ &arr, &count);
+
+ if (ret == 0) {
+ /* special case: if "none", remove all defined */
+ if (strcasecmp(arr[0], "none") != 0) {
+ ret = it_array_to_portallist(arr, count,
+ ISNS_DEFAULT_SERVER_PORT, &newisnslist, &newcount);
+ } else {
+ newisnslist = NULL;
+ newcount = 0;
+ (void) nvlist_remove_all(cprops, PROP_ISNS_SERVER);
+ }
+
+ if (ret == 0) {
+ isns = cfg->config_isns_svr_list;
+ while (isns) {
+ pnext = isns->next;
+ free(isns);
+ isns = pnext;
+ }
+
+ cfg->config_isns_svr_list = newisnslist;
+ cfg->config_isns_svr_count = newcount;
+
+ /*
+ * Replace the array in the nvlist to ensure
+ * duplicates are properly removed & port numbers
+ * are added.
+ */
+ if (newcount > 0) {
+ int i = 0;
+ char **newarray;
+
+ newarray = malloc(sizeof (char *) * newcount);
+ if (newarray == NULL) {
+ ret = ENOMEM;
+ } else {
+ for (isns = newisnslist; isns != NULL;
+ isns = isns->next) {
+ (void) sockaddr_to_str(
+ &(isns->portal_addr),
+ &(newarray[i++]));
+ }
+ (void) nvlist_add_string_array(cprops,
+ PROP_ISNS_SERVER, newarray,
+ newcount);
+
+ for (i = 0; i < newcount; i++) {
+ if (newarray[i]) {
+ free(newarray[i]);
+ }
+ }
+ free(newarray);
+ }
+ }
+ }
+ } else if (ret == ENOENT) {
+ /* not an error */
+ ret = 0;
+ }
+
+ if (ret == 0) {
+ /* replace the global properties list */
+ nvlist_free(cfg->config_global_properties);
+ cfg->config_global_properties = cprops;
+ } else {
+ if (cprops) {
+ nvlist_free(cprops);
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * Function: it_config_free()
+ *
+ * Free any resources associated with the it_config_t structure.
+ *
+ * Parameters:
+ * cfg A C representation of the current iSCSI configuration
+ */
+void
+it_config_free(it_config_t *cfg)
+{
+ it_config_free_cmn(cfg);
+}
+
+/*
+ * Function: it_tgt_create()
+ *
+ * Allocate and create an it_tgt_t structure representing a new iSCSI
+ * target node. If tgt_name is NULL, then a unique target node name will
+ * be generated automatically. Otherwise, the value of tgt_name will be
+ * used as the target node name. The new it_tgt_t structure is added to
+ * the target list (cfg_tgt_list) in the configuration structure, and the
+ * new target will not be instantiated until the modified configuration
+ * is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tgt Pointer to an iSCSI target structure
+ * tgt_name The target node name for the target to be created.
+ * The name must be in either IQN or EUI format. If
+ * this value is NULL, a node name will be generated
+ * automatically in IQN format.
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocated resources
+ * EINVAL Invalid parameter
+ * EFAULT Invalid iSCSI name specified
+ */
+int
+it_tgt_create(it_config_t *cfg, it_tgt_t **tgt, char *tgt_name)
+{
+ int ret = 0;
+ it_tgt_t *ptr;
+ it_tgt_t *cfgtgt;
+ char *namep = tgt_name;
+ char buf[ISCSI_NAME_LEN_MAX + 1];
+
+ if (!cfg || !tgt) {
+ return (EINVAL);
+ }
+
+ if (!namep) {
+ /* generate a name */
+
+ ret = it_iqn_generate(buf, sizeof (buf), NULL);
+ if (ret != 0) {
+ return (ret);
+ }
+ namep = buf;
+ } else {
+ /* validate the passed-in name */
+ if (!validate_iscsi_name(namep)) {
+ return (EFAULT);
+ }
+ }
+
+ /* make sure this name isn't already on the list */
+ cfgtgt = cfg->config_tgt_list;
+ while (cfgtgt != NULL) {
+ if (strcmp(namep, cfgtgt->tgt_name) == 0) {
+ return (EEXIST);
+ }
+ cfgtgt = cfgtgt->tgt_next;
+ }
+
+ ptr = calloc(1, sizeof (it_tgt_t));
+ if (ptr == NULL) {
+ return (ENOMEM);
+ }
+
+ (void) strlcpy(ptr->tgt_name, namep, sizeof (ptr->tgt_name));
+ ptr->tgt_generation = 1;
+ ptr->tgt_next = cfg->config_tgt_list;
+ cfg->config_tgt_list = ptr;
+ cfg->config_tgt_count++;
+
+ *tgt = ptr;
+
+ return (0);
+}
+
+/*
+ * Function: it_tgt_setprop()
+ *
+ * Validate the provided property list and set the properties for
+ * the specified target. If errlist is not NULL, returns detailed
+ * errors for each property that failed. The format for errorlist
+ * is key = property, value = error string.
+ *
+ * Parameters:
+ *
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tgt Pointer to an iSCSI target structure
+ * proplist nvlist_t containing properties for this target.
+ * errlist (optional) nvlist_t of errors encountered when
+ * validating the properties.
+ *
+ * Return Values:
+ * 0 Success
+ * EINVAL Invalid property
+ *
+ */
+int
+it_tgt_setprop(it_config_t *cfg, it_tgt_t *tgt, nvlist_t *proplist,
+ nvlist_t **errlist)
+{
+ int ret;
+ nvlist_t *tprops = NULL;
+ char *val = NULL;
+
+ if (!cfg || !tgt || !proplist) {
+ return (EINVAL);
+ }
+
+ if (errlist) {
+ (void) nvlist_alloc(errlist, 0, 0);
+ }
+
+ /*
+ * copy the existing properties, merge, then validate
+ * the merged properties before committing them.
+ */
+ if (tgt->tgt_properties) {
+ ret = nvlist_dup(tgt->tgt_properties, &tprops, 0);
+ } else {
+ ret = nvlist_alloc(&tprops, NV_UNIQUE_NAME, 0);
+ }
+
+ if (ret == 0) {
+ ret = nvlist_merge(tprops, proplist, 0);
+ }
+
+ /* unset chap username or alias if requested */
+ val = NULL;
+ (void) nvlist_lookup_string(proplist, PROP_TARGET_CHAP_USER, &val);
+ if (val && (strcasecmp(val, "none") == 0)) {
+ (void) nvlist_remove_all(tprops, PROP_TARGET_CHAP_USER);
+ }
+
+ val = NULL;
+ (void) nvlist_lookup_string(proplist, PROP_ALIAS, &val);
+ if (val && (strcasecmp(val, "none") == 0)) {
+ (void) nvlist_remove_all(tprops, PROP_ALIAS);
+ }
+
+ /* base64 encode the CHAP secret, if it's changed */
+ val = NULL;
+ (void) nvlist_lookup_string(proplist, PROP_TARGET_CHAP_SECRET, &val);
+ if (val) {
+ char bsecret[MAX_BASE64_LEN];
+
+ ret = it_val_pass(PROP_TARGET_CHAP_SECRET, val, *errlist);
+
+ if (ret == 0) {
+ (void) memset(bsecret, 0, MAX_BASE64_LEN);
+
+ ret = iscsi_binary_to_base64_str((uint8_t *)val,
+ strlen(val), bsecret, MAX_BASE64_LEN);
+
+ if (ret == 0) {
+ /* replace the value in the nvlist */
+ ret = nvlist_add_string(tprops,
+ PROP_TARGET_CHAP_SECRET, bsecret);
+ }
+ }
+ }
+
+ if (ret == 0) {
+ ret = it_validate_tgtprops(tprops, *errlist);
+ }
+
+ if (ret != 0) {
+ if (tprops) {
+ nvlist_free(tprops);
+ }
+ return (ret);
+ }
+
+ if (tgt->tgt_properties) {
+ nvlist_free(tgt->tgt_properties);
+ }
+ tgt->tgt_properties = tprops;
+
+ return (0);
+}
+
+
+/*
+ * Function: it_tgt_delete()
+ *
+ * Delete target represented by 'tgt', where 'tgt' is an existing
+ * it_tgt_structure within the configuration 'cfg'. The target removal
+ * will not take effect until the modified configuration is committed
+ * by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tgt Pointer to an iSCSI target structure
+ *
+ * force Set the target to offline before removing it from
+ * the config. If not specified, the operation will
+ * fail if the target is determined to be online.
+ * Return Values:
+ * 0 Success
+ * EBUSY Target is online
+ */
+int
+it_tgt_delete(it_config_t *cfg, it_tgt_t *tgt, boolean_t force)
+{
+ int ret;
+ it_tgt_t *ptgt;
+ it_tgt_t *prev = NULL;
+ stmfDevid devid;
+ stmfTargetProperties props;
+
+ if (!cfg || !tgt) {
+ return (0);
+ }
+
+ ptgt = cfg->config_tgt_list;
+ while (ptgt != NULL) {
+ if (strcmp(tgt->tgt_name, ptgt->tgt_name) == 0) {
+ break;
+ }
+ prev = ptgt;
+ ptgt = ptgt->tgt_next;
+ }
+
+ if (!ptgt) {
+ return (0);
+ }
+
+ /*
+ * check to see if this target is offline. If it is not,
+ * and the 'force' flag is TRUE, tell STMF to offline it
+ * before removing from the configuration.
+ */
+ ret = stmfDevidFromIscsiName(ptgt->tgt_name, &devid);
+ if (ret != STMF_STATUS_SUCCESS) {
+ /* can't happen? */
+ return (EINVAL);
+ }
+
+ ret = stmfGetTargetProperties(&devid, &props);
+ if (ret == STMF_STATUS_SUCCESS) {
+ /*
+ * only other return is STMF_ERROR_NOT_FOUND, which
+ * means we don't have to offline it.
+ */
+ if (props.status == STMF_TARGET_PORT_ONLINE) {
+ if (!force) {
+ return (EBUSY);
+ }
+ ret = stmfOfflineTarget(&devid);
+ if (ret != 0) {
+ return (EBUSY);
+ }
+ }
+ }
+
+ if (prev) {
+ prev->tgt_next = ptgt->tgt_next;
+ } else {
+ /* first one on the list */
+ cfg->config_tgt_list = ptgt->tgt_next;
+ }
+
+ ptgt->tgt_next = NULL; /* Only free this target */
+
+ cfg->config_tgt_count--;
+ it_tgt_free(ptgt);
+
+ return (0);
+}
+
+/*
+ * Function: it_tgt_free()
+ *
+ * Frees an it_tgt_t structure. If tgt_next is not NULL, frees
+ * all structures in the list.
+ */
+void
+it_tgt_free(it_tgt_t *tgt)
+{
+ it_tgt_free_cmn(tgt);
+}
+
+/*
+ * Function: it_tpgt_create()
+ *
+ * Allocate and create an it_tpgt_t structure representing a new iSCSI
+ * target portal group tag. The new it_tpgt_t structure is added to the
+ * target tpgt list (tgt_tpgt_list) in the it_tgt_t structure. The new
+ * target portal group tag will not be instantiated until the modified
+ * configuration is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tgt Pointer to the iSCSI target structure associated
+ * with the target portal group tag
+ * tpgt Pointer to a target portal group tag structure
+ * tpg_name The name of the TPG to be associated with this TPGT
+ * tpgt_tag 16-bit numerical identifier for this TPGT. If
+ * tpgt_tag is '0', this function will choose the
+ * tag number. If tpgt_tag is >0, and the requested
+ * tag is determined to be in use, another value
+ * will be chosen.
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid parameter
+ * EEXIST Specified tag name is already used.
+ * E2BIG No available tag numbers
+ */
+int
+it_tpgt_create(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t **tpgt,
+ char *tpg_name, uint16_t tpgt_tag)
+{
+ it_tpgt_t *ptr = NULL;
+ it_tpgt_t *cfgt;
+ char tagid_used[MAXTAG + 1];
+ uint16_t tagid = ISCSIT_DEFAULT_TPGT;
+
+ if (!cfg || !tgt || !tpgt || !tpg_name) {
+ return (EINVAL);
+ }
+
+ (void) memset(&(tagid_used[0]), 0, sizeof (tagid_used));
+
+ /*
+ * Make sure this name and/or tag isn't already on the list
+ * At the same time, capture all tag ids in use for this target
+ *
+ * About tag numbering -- since tag numbers are used by
+ * the iSCSI protocol, we should be careful about reusing
+ * them too quickly. Start with a value greater than the
+ * highest one currently defined. If current == MAXTAG,
+ * just find an unused tag.
+ */
+ cfgt = tgt->tgt_tpgt_list;
+ while (cfgt != NULL) {
+ tagid_used[cfgt->tpgt_tag] = 1;
+
+ if (strcmp(tpg_name, cfgt->tpgt_tpg_name) == 0) {
+ return (EEXIST);
+ }
+
+ if (cfgt->tpgt_tag > tagid) {
+ tagid = cfgt->tpgt_tag;
+ }
+
+ cfgt = cfgt->tpgt_next;
+ }
+
+ if ((tpgt_tag > ISCSIT_DEFAULT_TPGT) && (tpgt_tag < MAXTAG) &&
+ (tagid_used[tpgt_tag] == 0)) {
+ /* ok to use requested */
+ tagid = tpgt_tag;
+ } else if (tagid == MAXTAG) {
+ /*
+ * The highest value is used, find an available id.
+ */
+ tagid = ISCSIT_DEFAULT_TPGT + 1;
+ for (; tagid < MAXTAG; tagid++) {
+ if (tagid_used[tagid] == 0) {
+ break;
+ }
+ }
+ if (tagid >= MAXTAG) {
+ return (E2BIG);
+ }
+ } else {
+ /* next available ID */
+ tagid++;
+ }
+
+ ptr = calloc(1, sizeof (it_tpgt_t));
+ if (!ptr) {
+ return (ENOMEM);
+ }
+
+ (void) strlcpy(ptr->tpgt_tpg_name, tpg_name,
+ sizeof (ptr->tpgt_tpg_name));
+ ptr->tpgt_generation = 1;
+ ptr->tpgt_tag = tagid;
+
+ ptr->tpgt_next = tgt->tgt_tpgt_list;
+ tgt->tgt_tpgt_list = ptr;
+ tgt->tgt_tpgt_count++;
+ tgt->tgt_generation++;
+
+ *tpgt = ptr;
+
+ return (0);
+}
+
+/*
+ * Function: it_tpgt_delete()
+ *
+ * Delete the target portal group tag represented by 'tpgt', where
+ * 'tpgt' is an existing is_tpgt_t structure within the target 'tgt'.
+ * The target portal group tag removal will not take effect until the
+ * modified configuration is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tgt Pointer to the iSCSI target structure associated
+ * with the target portal group tag
+ * tpgt Pointer to a target portal group tag structure
+ */
+void
+it_tpgt_delete(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t *tpgt)
+{
+ it_tpgt_t *ptr;
+ it_tpgt_t *prev = NULL;
+
+ if (!cfg || !tgt || !tpgt) {
+ return;
+ }
+
+ ptr = tgt->tgt_tpgt_list;
+ while (ptr) {
+ if (ptr->tpgt_tag == tpgt->tpgt_tag) {
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->tpgt_next;
+ }
+
+ if (!ptr) {
+ return;
+ }
+
+ if (prev) {
+ prev->tpgt_next = ptr->tpgt_next;
+ } else {
+ tgt->tgt_tpgt_list = ptr->tpgt_next;
+ }
+ ptr->tpgt_next = NULL;
+
+ tgt->tgt_tpgt_count--;
+ tgt->tgt_generation++;
+
+ it_tpgt_free(ptr);
+}
+
+/*
+ * Function: it_tpgt_free()
+ *
+ * Deallocates resources of an it_tpgt_t structure. If tpgt->next
+ * is not NULL, frees all members of the list.
+ */
+void
+it_tpgt_free(it_tpgt_t *tpgt)
+{
+ it_tpgt_free_cmn(tpgt);
+}
+
+/*
+ * Function: it_tpg_create()
+ *
+ * Allocate and create an it_tpg_t structure representing a new iSCSI
+ * target portal group. The new it_tpg_t structure is added to the global
+ * tpg list (cfg_tgt_list) in the it_config_t structure. The new target
+ * portal group will not be instantiated until the modified configuration
+ * is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tpg Pointer to the it_tpg_t structure representing
+ * the target portal group
+ * tpg_name Identifier for the target portal group
+ * portal_ip_port A string containing an appropriatedly formatted
+ * IP address:port. Both IPv4 and IPv6 addresses are
+ * permitted. This value becomes the first portal in
+ * the TPG -- applications can add additional values
+ * using it_portal_create() before committing the TPG.
+ * Return Values:
+ * 0 Success
+ * ENOMEM Cannot allocate resources
+ * EINVAL Invalid parameter
+ * EEXIST Requested portal in use by another target portal
+ * group
+ */
+int
+it_tpg_create(it_config_t *cfg, it_tpg_t **tpg, char *tpg_name,
+ char *portal_ip_port)
+{
+ int ret;
+ it_tpg_t *ptr;
+ it_portal_t *portal = NULL;
+
+ if (!cfg || !tpg || !tpg_name || !portal_ip_port) {
+ return (EINVAL);
+ }
+
+ *tpg = NULL;
+
+ ptr = cfg->config_tpg_list;
+ while (ptr) {
+ if (strcmp(tpg_name, ptr->tpg_name) == 0) {
+ break;
+ }
+ ptr = ptr->tpg_next;
+ }
+
+ if (ptr) {
+ return (EEXIST);
+ }
+
+ ptr = calloc(1, sizeof (it_tpg_t));
+ if (!ptr) {
+ return (ENOMEM);
+ }
+
+ ptr->tpg_generation = 1;
+ (void) strlcpy(ptr->tpg_name, tpg_name, sizeof (ptr->tpg_name));
+
+ /* create the portal */
+ ret = it_portal_create(cfg, ptr, &portal, portal_ip_port);
+ if (ret != 0) {
+ free(ptr);
+ return (ret);
+ }
+
+ ptr->tpg_next = cfg->config_tpg_list;
+ cfg->config_tpg_list = ptr;
+ cfg->config_tpg_count++;
+
+ *tpg = ptr;
+
+ return (0);
+}
+
+/*
+ * Function: it_tpg_delete()
+ *
+ * Delete target portal group represented by 'tpg', where 'tpg' is an
+ * existing it_tpg_t structure within the global configuration 'cfg'.
+ * The target portal group removal will not take effect until the
+ * modified configuration is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tpg Pointer to the it_tpg_t structure representing
+ * the target portal group
+ * force Remove this target portal group even if it's
+ * associated with one or more targets.
+ *
+ * Return Values:
+ * 0 Success
+ * EINVAL Invalid parameter
+ * EBUSY Portal group associated with one or more targets.
+ */
+int
+it_tpg_delete(it_config_t *cfg, it_tpg_t *tpg, boolean_t force)
+{
+ it_tpg_t *ptr;
+ it_tpg_t *prev = NULL;
+ it_tgt_t *tgt;
+ it_tpgt_t *tpgt;
+ it_tpgt_t *ntpgt;
+
+ if (!cfg || !tpg) {
+ return (EINVAL);
+ }
+
+ ptr = cfg->config_tpg_list;
+ while (ptr) {
+ if (strcmp(ptr->tpg_name, tpg->tpg_name) == 0) {
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->tpg_next;
+ }
+
+ if (!ptr) {
+ return (0);
+ }
+
+ /*
+ * See if any targets are using this portal group.
+ * If there are, and the force flag is not set, fail.
+ */
+ tgt = cfg->config_tgt_list;
+ while (tgt) {
+ tpgt = tgt->tgt_tpgt_list;
+ while (tpgt) {
+ ntpgt = tpgt->tpgt_next;
+
+ if (strcmp(tpgt->tpgt_tpg_name, tpg->tpg_name)
+ == 0) {
+ if (!force) {
+ return (EBUSY);
+ }
+ it_tpgt_delete(cfg, tgt, tpgt);
+ }
+
+ tpgt = ntpgt;
+ }
+ tgt = tgt->tgt_next;
+ }
+
+ /* Now that it's not in use anywhere, remove the TPG */
+ if (prev) {
+ prev->tpg_next = ptr->tpg_next;
+ } else {
+ cfg->config_tpg_list = ptr->tpg_next;
+ }
+ ptr->tpg_next = NULL;
+
+ cfg->config_tpg_count--;
+
+ it_tpg_free(ptr);
+
+ return (0);
+}
+
+/*
+ * Function: it_tpg_free()
+ *
+ * Deallocates resources associated with an it_tpg_t structure.
+ * If tpg->next is not NULL, frees all members of the list.
+ */
+void
+it_tpg_free(it_tpg_t *tpg)
+{
+ it_tpg_free_cmn(tpg);
+}
+
+/*
+ * Function: it_portal_create()
+ *
+ * Add an it_portal_t structure presenting a new portal to the specified
+ * target portal group. The change to the target portal group will not take
+ * effect until the modified configuration is committed by calling
+ * it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configration obtained from
+ * it_config_load()
+ * tpg Pointer to the it_tpg_t structure representing the
+ * target portal group
+ * portal Pointer to the it_portal_t structure representing
+ * the portal
+ * portal_ip_port A string containing an appropriately formatted
+ * IP address or IP address:port in either IPv4 or
+ * IPv6 format.
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid parameter
+ * EEXIST Portal already configured for another portal group
+ */
+int
+it_portal_create(it_config_t *cfg, it_tpg_t *tpg, it_portal_t **portal,
+ char *portal_ip_port)
+{
+ struct sockaddr_storage sa;
+ it_portal_t *ptr;
+ it_tpg_t *ctpg = NULL;
+
+ if (!cfg || !tpg || !portal || !portal_ip_port) {
+ return (EINVAL);
+ }
+
+ if ((it_common_convert_sa(portal_ip_port, &sa, ISCSI_LISTEN_PORT))
+ == NULL) {
+ return (EINVAL);
+ }
+
+ /* Check that this portal doesn't appear in any other tag */
+ ctpg = cfg->config_tpg_list;
+ while (ctpg) {
+ ptr = ctpg->tpg_portal_list;
+ for (; ptr != NULL; ptr = ptr->next) {
+ if (it_sa_compare(&(ptr->portal_addr), &sa) != 0) {
+ continue;
+ }
+
+ /*
+ * Existing in the same group is not an error,
+ * but don't add it again.
+ */
+ if (strcmp(ctpg->tpg_name, tpg->tpg_name) == 0) {
+ return (0);
+ } else {
+ /* Not allowed */
+ return (EEXIST);
+ }
+ }
+ ctpg = ctpg->tpg_next;
+ }
+
+ ptr = calloc(1, sizeof (it_portal_t));
+ if (!ptr) {
+ return (ENOMEM);
+ }
+
+ (void) memcpy(&(ptr->portal_addr), &sa,
+ sizeof (struct sockaddr_storage));
+ ptr->next = tpg->tpg_portal_list;
+ tpg->tpg_portal_list = ptr;
+ tpg->tpg_portal_count++;
+ tpg->tpg_generation++;
+
+ return (0);
+}
+
+/*
+ * Function: it_portal_delete()
+ *
+ * Remove the specified portal from the specified target portal group.
+ * The portal removal will not take effect until the modified configuration
+ * is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configration obtained from
+ * it_config_load()
+ * tpg Pointer to the it_tpg_t structure representing the
+ * target portal group
+ * portal Pointer to the it_portal_t structure representing
+ * the portal
+ */
+void
+it_portal_delete(it_config_t *cfg, it_tpg_t *tpg, it_portal_t *portal)
+{
+ it_portal_t *ptr;
+ it_portal_t *prev;
+
+ if (!cfg || !tpg || !portal) {
+ return;
+ }
+
+ ptr = tpg->tpg_portal_list;
+ while (ptr) {
+ if (memcmp(&(ptr->portal_addr), &(portal->portal_addr),
+ sizeof (ptr->portal_addr)) == 0) {
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+
+ if (!ptr) {
+ return;
+ }
+
+ if (prev) {
+ prev->next = ptr->next;
+ } else {
+ tpg->tpg_portal_list = ptr->next;
+ }
+ tpg->tpg_portal_count--;
+ tpg->tpg_generation++;
+
+ free(ptr);
+}
+
+/*
+ * Function: it_ini_create()
+ *
+ * Add an initiator context to the global configuration. The new
+ * initiator context will not be instantiated until the modified
+ * configuration is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configration obtained from
+ * it_config_load()
+ * ini Pointer to the it_ini_t structure representing
+ * the initiator context.
+ * ini_node_name The iSCSI node name of the remote initiator.
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid parameter.
+ * EFAULT Invalid initiator name
+ */
+int
+it_ini_create(it_config_t *cfg, it_ini_t **ini, char *ini_node_name)
+{
+ it_ini_t *ptr;
+
+ if (!cfg || !ini || !ini_node_name) {
+ return (EINVAL);
+ }
+
+ /*
+ * Ensure this is a valid ini name
+ */
+ if (!validate_iscsi_name(ini_node_name)) {
+ return (EFAULT);
+ }
+
+ ptr = cfg->config_ini_list;
+ while (ptr) {
+ if (strcmp(ptr->ini_name, ini_node_name) == 0) {
+ break;
+ }
+ ptr = ptr->ini_next;
+ }
+
+ if (ptr) {
+ return (EEXIST);
+ }
+
+ ptr = calloc(1, sizeof (it_ini_t));
+ if (!ptr) {
+ return (ENOMEM);
+ }
+
+ (void) strlcpy(ptr->ini_name, ini_node_name, sizeof (ptr->ini_name));
+ ptr->ini_generation = 1;
+ /* nvlist for props? */
+
+ ptr->ini_next = cfg->config_ini_list;
+ cfg->config_ini_list = ptr;
+ cfg->config_ini_count++;
+
+ *ini = ptr;
+
+ return (0);
+}
+
+/*
+ * Function: it_ini_setprop()
+ *
+ * Validate the provided property list and set the initiator properties.
+ * If errlist is not NULL, returns detailed errors for each property
+ * that failed. The format for errorlist is key = property,
+ * value = error string.
+ *
+ * Parameters:
+ *
+ * ini The initiator being updated.
+ * proplist nvlist_t containing properties for this target.
+ * errlist (optional) nvlist_t of errors encountered when
+ * validating the properties.
+ *
+ * Return Values:
+ * 0 Success
+ * EINVAL Invalid property
+ *
+ */
+int
+it_ini_setprop(it_ini_t *ini, nvlist_t *proplist, nvlist_t **errlist)
+{
+ int ret;
+ nvlist_t *iprops = NULL;
+ char *val = NULL;
+
+ if (!ini || !proplist) {
+ return (EINVAL);
+ }
+
+ if (errlist) {
+ (void) nvlist_alloc(errlist, 0, 0);
+ }
+
+ /*
+ * copy the existing properties, merge, then validate
+ * the merged properties before committing them.
+ */
+ if (ini->ini_properties) {
+ ret = nvlist_dup(ini->ini_properties, &iprops, 0);
+ } else {
+ ret = nvlist_alloc(&iprops, NV_UNIQUE_NAME, 0);
+ }
+
+ if (ret == 0) {
+ ret = nvlist_merge(iprops, proplist, 0);
+ }
+
+ /* unset chap username if requested */
+ if ((nvlist_lookup_string(proplist, PROP_CHAP_USER, &val)) == 0) {
+ if (strcasecmp(val, "none") == 0) {
+ (void) nvlist_remove_all(iprops, PROP_CHAP_USER);
+ }
+ }
+
+ /* base64 encode the CHAP secret, if it's changed */
+ if ((nvlist_lookup_string(proplist, PROP_CHAP_SECRET, &val)) == 0) {
+ char bsecret[MAX_BASE64_LEN];
+
+ ret = it_val_pass(PROP_CHAP_SECRET, val, *errlist);
+ if (ret == 0) {
+ (void) memset(bsecret, 0, MAX_BASE64_LEN);
+
+ ret = iscsi_binary_to_base64_str((uint8_t *)val,
+ strlen(val), bsecret, MAX_BASE64_LEN);
+
+ if (ret == 0) {
+ /* replace the value in the nvlist */
+ ret = nvlist_add_string(iprops,
+ PROP_CHAP_SECRET, bsecret);
+ }
+ }
+ }
+
+ if (ret == 0) {
+ ret = it_validate_iniprops(iprops, *errlist);
+ }
+
+ if (ret != 0) {
+ if (iprops) {
+ nvlist_free(iprops);
+ }
+ return (ret);
+ }
+
+ if (ini->ini_properties) {
+ nvlist_free(ini->ini_properties);
+ }
+ ini->ini_properties = iprops;
+
+ return (0);
+}
+
+/*
+ * Function: it_ini_delete()
+ *
+ * Remove the specified initiator context from the global configuration.
+ * The removal will not take effect until the modified configuration is
+ * committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configration obtained from
+ * it_config_load()
+ * ini Pointer to the it_ini_t structure representing
+ * the initiator context.
+ */
+void
+it_ini_delete(it_config_t *cfg, it_ini_t *ini)
+{
+ it_ini_t *ptr;
+ it_ini_t *prev = NULL;
+
+ if (!cfg || !ini) {
+ return;
+ }
+
+ ptr = cfg->config_ini_list;
+ while (ptr) {
+ if (strcmp(ptr->ini_name, ini->ini_name) == 0) {
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->ini_next;
+ }
+
+ if (!ptr) {
+ return;
+ }
+
+ if (prev) {
+ prev->ini_next = ptr->ini_next;
+ } else {
+ cfg->config_ini_list = ptr->ini_next;
+ }
+
+ ptr->ini_next = NULL; /* Only free this initiator */
+
+ cfg->config_ini_count--;
+
+ it_ini_free(ptr);
+}
+
+/*
+ * Function: it_ini_free()
+ *
+ * Deallocates resources of an it_ini_t structure. If ini->next is
+ * not NULL, frees all members of the list.
+ */
+void
+it_ini_free(it_ini_t *ini)
+{
+ it_ini_free_cmn(ini);
+}
+
+/*
+ * Goes through the target property list and validates
+ * each entry. If errs is non-NULL, will return explicit errors
+ * for each property that fails validation.
+ */
+static int
+it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs)
+{
+ int errcnt = 0;
+ nvpair_t *nvp = NULL;
+ data_type_t nvtype;
+ char *name;
+ char *val;
+ char *auth = NULL;
+
+ if (!nvl) {
+ return (0);
+ }
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ name = nvpair_name(nvp);
+ nvtype = nvpair_type(nvp);
+
+ if (!name) {
+ continue;
+ }
+
+ val = NULL;
+ if (strcmp(name, PROP_TARGET_CHAP_USER) == 0) {
+ if (nvtype != DATA_TYPE_STRING) {
+ PROPERR(errs, name,
+ gettext("must be a string value"));
+ errcnt++;
+ continue;
+ }
+ } else if (strcmp(name, PROP_TARGET_CHAP_SECRET) == 0) {
+ /*
+ * must be between 12 and 255 chars in cleartext.
+ * will be base64 encoded when it's set.
+ */
+ if (nvtype == DATA_TYPE_STRING) {
+ (void) nvpair_value_string(nvp, &val);
+ }
+
+ if (!val) {
+ PROPERR(errs, name,
+ gettext("must be a string value"));
+ errcnt++;
+ continue;
+ }
+ } else if (strcmp(name, PROP_ALIAS) == 0) {
+ if (nvtype != DATA_TYPE_STRING) {
+ PROPERR(errs, name,
+ gettext("must be a string value"));
+ errcnt++;
+ continue;
+ }
+ } else if (strcmp(name, PROP_AUTH) == 0) {
+ if (nvtype == DATA_TYPE_STRING) {
+ val = NULL;
+ (void) nvpair_value_string(nvp, &val);
+ }
+
+ if (!val) {
+ PROPERR(errs, name,
+ gettext("must be a string value"));
+ errcnt++;
+ continue;
+ }
+ if ((strcmp(val, PA_AUTH_NONE) != 0) &&
+ (strcmp(val, PA_AUTH_CHAP) != 0) &&
+ (strcmp(val, PA_AUTH_RADIUS) != 0) &&
+ (strcmp(val, "default") != 0)) {
+ PROPERR(errs, val, gettext(
+ "must be none, chap, radius or default"));
+ errcnt++;
+ }
+ auth = val;
+ continue;
+ } else if (strcmp(name, PROP_OLD_TARGET_NAME) == 0) {
+ continue;
+ } else {
+ /* unrecognized property */
+ PROPERR(errs, name, gettext("unrecognized property"));
+ errcnt++;
+ }
+ }
+
+ if (errcnt) {
+ return (EINVAL);
+ }
+
+ /* if auth is being set to default, remove from this nvlist */
+ if (auth && (strcmp(auth, "default") == 0)) {
+ (void) nvlist_remove_all(nvl, PROP_AUTH);
+ }
+
+ return (0);
+}
+
+/*
+ * Goes through the config property list and validates
+ * each entry. If errs is non-NULL, will return explicit errors
+ * for each property that fails validation.
+ */
+static int
+it_validate_configprops(nvlist_t *nvl, nvlist_t *errs)
+{
+ int errcnt = 0;
+ nvpair_t *nvp = NULL;
+ data_type_t nvtype;
+ char *name;
+ char *val;
+ struct sockaddr_storage sa;
+ char *auth = NULL;
+
+ if (!nvl) {
+ return (0);
+ }
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ name = nvpair_name(nvp);
+ nvtype = nvpair_type(nvp);
+
+ if (!name) {
+ continue;
+ }
+
+ val = NULL;
+
+ /* prefetch string value as we mostly need it */
+ if (nvtype == DATA_TYPE_STRING) {
+ (void) nvpair_value_string(nvp, &val);
+ }
+
+ if (strcmp(name, PROP_ALIAS) == 0) {
+ if (!val) {
+ PROPERR(errs, name,
+ gettext("must be a string value"));
+ errcnt++;
+ }
+ } else if (strcmp(name, PROP_AUTH) == 0) {
+ if (!val) {
+ PROPERR(errs, name,
+ gettext("must be a string value"));
+ errcnt++;
+ continue;
+ }
+
+ if ((strcmp(val, PA_AUTH_NONE) != 0) &&
+ (strcmp(val, PA_AUTH_CHAP) != 0) &&
+ (strcmp(val, PA_AUTH_RADIUS) != 0)) {
+ PROPERR(errs, PROP_AUTH,
+ gettext("must be none, chap or radius"));
+ errcnt++;
+ }
+
+ auth = val;
+
+ } else if (strcmp(name, PROP_ISNS_ENABLED) == 0) {
+ if (nvtype != DATA_TYPE_BOOLEAN_VALUE) {
+ PROPERR(errs, name,
+ gettext("must be a boolean value"));
+ errcnt++;
+ }
+ } else if (strcmp(name, PROP_ISNS_SERVER) == 0) {
+ char **arr = NULL;
+ uint32_t acount = 0;
+
+ (void) nvlist_lookup_string_array(nvl, name,
+ &arr, &acount);
+
+ while (acount > 0) {
+ if (strcasecmp(arr[acount - 1], "none") == 0) {
+ break;
+ }
+ if ((it_common_convert_sa(arr[acount - 1],
+ &sa, 0)) == NULL) {
+ PROPERR(errs, arr[acount - 1],
+ gettext("invalid address"));
+ errcnt++;
+ }
+ acount--;
+ }
+
+ } else if (strcmp(name, PROP_RADIUS_SECRET) == 0) {
+ if (!val) {
+ PROPERR(errs, name,
+ gettext("must be a string value"));
+ errcnt++;
+ continue;
+ }
+ } else if (strcmp(name, PROP_RADIUS_SERVER) == 0) {
+ struct sockaddr_storage sa;
+
+ if (!val) {
+ PROPERR(errs, name,
+ gettext("must be a string value"));
+ errcnt++;
+ continue;
+ }
+
+ if ((it_common_convert_sa(val, &sa,
+ DEFAULT_RADIUS_PORT)) == NULL) {
+ PROPERR(errs, name,
+ gettext("invalid address"));
+ errcnt++;
+ } else {
+ /*
+ * rewrite this property to ensure port
+ * number is added.
+ */
+ char *rad = NULL;
+
+ if (sockaddr_to_str(&sa, &rad) == 0) {
+ (void) nvlist_add_string(nvl,
+ name, rad);
+ }
+ }
+ } else {
+ /* unrecognized property */
+ PROPERR(errs, name, gettext("unrecognized property"));
+ errcnt++;
+ }
+ }
+
+ /*
+ * if auth = radius, ensure radius server & secret are set.
+ */
+ if (auth) {
+ if (strcmp(auth, PA_AUTH_RADIUS) == 0) {
+ /* need server & secret for radius */
+ if (!nvlist_exists(nvl, PROP_RADIUS_SERVER)) {
+ PROPERR(errs, PROP_RADIUS_SERVER,
+ gettext("missing required property"));
+ errcnt++;
+ }
+ if (!nvlist_exists(nvl, PROP_RADIUS_SECRET)) {
+ PROPERR(errs, PROP_RADIUS_SECRET,
+ gettext("missing required property"));
+ errcnt++;
+ }
+ }
+ }
+
+ if (errcnt) {
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+/*
+ * Goes through the ini property list and validates
+ * each entry. If errs is non-NULL, will return explicit errors
+ * for each property that fails validation.
+ */
+static int
+it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs)
+{
+ int errcnt = 0;
+ nvpair_t *nvp = NULL;
+ data_type_t nvtype;
+ char *name;
+ char *val;
+
+ if (!nvl) {
+ return (0);
+ }
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ name = nvpair_name(nvp);
+ nvtype = nvpair_type(nvp);
+
+ if (!name) {
+ continue;
+ }
+
+ if (strcmp(name, PROP_CHAP_USER) == 0) {
+ if (nvtype != DATA_TYPE_STRING) {
+ PROPERR(errs, name,
+ gettext("must be a string value"));
+ errcnt++;
+ continue;
+ }
+ } else if (strcmp(name, PROP_CHAP_SECRET) == 0) {
+ /*
+ * must be between 12 and 255 chars in cleartext.
+ * will be base64 encoded when it's set.
+ */
+ if (nvtype == DATA_TYPE_STRING) {
+ val = NULL;
+ (void) nvpair_value_string(nvp, &val);
+ }
+
+ if (!val) {
+ PROPERR(errs, name,
+ gettext("must be a string value"));
+ errcnt++;
+ continue;
+ }
+ } else {
+ /* unrecognized property */
+ PROPERR(errs, name, gettext("unrecognized property"));
+ errcnt++;
+ }
+ }
+
+ if (errcnt) {
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix)
+{
+ int ret;
+ uuid_t id;
+ char id_str[UUID_PRINTABLE_STRING_LENGTH];
+
+ uuid_generate_random(id);
+ uuid_unparse(id, id_str);
+
+ if (opt_iqn_suffix) {
+ ret = snprintf(iqn_buf, iqn_buf_len, "iqn.1986-03.com.sun:"
+ "%02d:%s.%s", TARGET_NAME_VERS, id_str, opt_iqn_suffix);
+ } else {
+ ret = snprintf(iqn_buf, iqn_buf_len, "iqn.1986-03.com.sun:"
+ "%02d:%s", TARGET_NAME_VERS, id_str);
+ }
+
+ if (ret > iqn_buf_len) {
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+it_val_pass(char *name, char *val, nvlist_t *e)
+{
+ size_t sz;
+
+ if (!name || !val) {
+ return (EINVAL);
+ }
+
+ /*
+ * must be at least 12 chars and less than 256 chars cleartext.
+ */
+ sz = strlen(val);
+
+ /*
+ * Since we will be automatically encoding secrets we don't really
+ * need the prefix anymore.
+ */
+ if (sz < 12) {
+ PROPERR(e, name, gettext("secret too short"));
+ } else if (sz > 255) {
+ PROPERR(e, name, gettext("secret too long"));
+ } else {
+ /* all is well */
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * Function: validate_iscsi_name()
+ *
+ * Ensures the passed-in string is a valid IQN or EUI iSCSI name
+ *
+ */
+boolean_t
+validate_iscsi_name(char *in_name)
+{
+ size_t in_len;
+ int i;
+ char month[3];
+
+ if (in_name == NULL) {
+ return (B_FALSE);
+ }
+
+ in_len = strlen(in_name);
+ if (in_len < 12) {
+ return (B_FALSE);
+ }
+
+ if (strncasecmp(in_name, "iqn.", 4) == 0) {
+ /*
+ * IQN names are iqn.yyyy-mm.<xxx>
+ */
+ if ((!isdigit(in_name[4])) ||
+ (!isdigit(in_name[5])) ||
+ (!isdigit(in_name[6])) ||
+ (!isdigit(in_name[7])) ||
+ (in_name[8] != '-') ||
+ (!isdigit(in_name[9])) ||
+ (!isdigit(in_name[10])) ||
+ (in_name[11] != '.')) {
+ return (B_FALSE);
+ }
+
+ (void) strncpy(month, &(in_name[9]), 2);
+ month[2] = '\0';
+
+ i = atoi(month);
+ if ((i < 0) || (i > 12)) {
+ return (B_FALSE);
+ }
+
+ /* Finally, validate the overall length, in wide chars */
+ in_len = mbstowcs(NULL, in_name, 0);
+ if (in_len > ISCSI_NAME_LEN_MAX) {
+ return (B_FALSE);
+ }
+ } else if (strncasecmp(in_name, "eui.", 4) == 0) {
+ /*
+ * EUI names are "eui." + 16 hex chars
+ */
+ if (in_len != 20) {
+ return (B_FALSE);
+ }
+
+ for (i = 4; i < in_len; i++) {
+ if (!isxdigit(in_name[i])) {
+ return (B_FALSE);
+ }
+ }
+ } else {
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
diff --git a/usr/src/lib/libiscsit/common/libiscsit.h b/usr/src/lib/libiscsit/common/libiscsit.h
new file mode 100644
index 0000000000..598a46a09c
--- /dev/null
+++ b/usr/src/lib/libiscsit/common/libiscsit.h
@@ -0,0 +1,747 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBISCSIT_H
+#define _LIBISCSIT_H
+
+#ifndef _KERNEL
+#include <libnvpair.h>
+#include <sys/socket.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ISCSIT_MODNAME "iscsit"
+#define ISCSIT_NODE "/devices/pseudo/iscsit@0:iscsit"
+
+#define MAX_TPGT 256
+#define CFG_TPGTLIST "tpgt-list"
+
+/*
+ * Object Hierarchy
+ *
+ * _______________________
+ * | |
+ * | iSCSI Target Config |
+ * | it_config_t |
+ * |_______________________|
+ * | |
+ * | |
+ * | | ________ ________ ________
+ * | | | | | | | |
+ * | | | Target |-->| Target |-- - - -->| Target |
+ * | | |________| |________| |________|
+ * | | |
+ * | | |
+ * | | |
+ * | | | ______ ______
+ * | | | | | | |
+ * | | +----->| TPGT |-- - - -->| TPGT |
+ * | | |______| |______|
+ * | | | |
+ * | +--+ | |
+ * | | _______ _______ | ______ |
+ * | | | | | |<--+ | |<--+
+ * | +->| TPG |-->| TPG |-- - - -->| TPG |
+ * | |_______| |_______| |______|
+ * |
+ * | ___________ ___________ ___________
+ * | | | | | | |
+ * +---->| Initiator |-->| Initiator |-- - - -->| Initiator |
+ * | Context | | Context | | Context |
+ * |___________| |___________| |___________|
+ *
+ *
+ * it_config_t includes a list of global properties
+ *
+ * Targets include a list of properties which override the global properties
+ * if set
+ *
+ * Initiators also include a list of properties but never inherit properties
+ * from the global config.
+ */
+
+/* Maximum size of a Target Portal Group name */
+#define MAX_TPG_NAMELEN 256 /* XXX */
+
+/* Maximum size of an iSCSI Target Node name */
+#define MAX_ISCSI_NODENAMELEN 256 /* XXX */
+
+/*
+ * A target portal group tag is a binding between a target and a target
+ * portal group along with a numerical value associated with that binding.
+ * The numerical identifier is used as the 'target portal group tag' defined
+ * in RFC3720.
+ *
+ * tpgt_tpg_name The name of the target portal group associated with
+ * this target portal group tag.
+ * tpgt_generation Generation number which is incremented each time the
+ * structure changes.
+ * tpgt_next Next target portal group tag in th list of target portal
+ * group tags. If tpgt_next is NUL, then this is the last
+ * target portal group in the list.
+ * tpgt_tag A numerical identifier that uniquely identifies a
+ * target portal group within the associated target node.
+ */
+typedef struct it_tpgt_s {
+ char tpgt_tpg_name[MAX_TPG_NAMELEN];
+ uint64_t tpgt_generation;
+ struct it_tpgt_s *tpgt_next;
+ uint16_t tpgt_tag;
+} it_tpgt_t;
+
+/*
+ * An iSCSI target node is represented by an it_tgt_structure. Each
+ * target node includes a list of associated target portal group tags
+ * and a list of properties.
+ *
+ * tgt_name The iSCSI target node name in either IQN or EUI
+ * format (see RFC3720).
+ * tgt_generation Generation number which is incremented each time
+ * the structure changes.
+ * tgt_next Next target in the list of targets. If tgt_next
+ * is NULL, then this is the last target in the list.
+ * tgt_tpgt_list A linked list representing the current target
+ * portal group tags associated with this target.
+ * tgt_tpgt_count The number of currently defined target portal
+ * group tags.
+ * tgt_properties An nvlist representation of the properties
+ * associated with this target. This list can be
+ * manipulated using libnvpair(3lib), and should be
+ * validated and stored using it_tgt_setprop().
+ *
+ * Target nvlist Properties:
+ *
+ * nvlist Key Type Valid Values
+ * --------------------------------------------------------------------
+ * targetchapuser string any string or "none" to remove
+ * targetchapsecret string string of at least 12 characters
+ * but not more than 255 characters.
+ * secret will be base64 encoded when
+ * stored.
+ * alias string any string or "none" to remove
+ * auth string "radius", "chap", or "none"
+ *
+ */
+typedef struct it_tgt_s {
+ char tgt_name[MAX_ISCSI_NODENAMELEN];
+ uint64_t tgt_generation;
+ struct it_tgt_s *tgt_next;
+ it_tpgt_t *tgt_tpgt_list;
+ uint32_t tgt_tpgt_count;
+ nvlist_t *tgt_properties;
+} it_tgt_t;
+
+/*
+ * A target portal is represented by an IP address and a listening
+ * TCP port.
+ *
+ * portal_addr sockaddr_storage structure representing the
+ * IPv4 or IPv6 address and TCP port associated
+ * with the portal.
+ * portal_next Next portal in the list of portals. If
+ * portal_next is NULL, this is the last portal
+ * in the list.
+ */
+typedef struct it_portal_s {
+ struct sockaddr_storage portal_addr;
+ struct it_portal_s *next;
+} it_portal_t;
+
+/*
+ * A portal is an IP address and TCP port and a portal group is a set
+ * of portals. Each defined portal belongs to exactly one portal group.
+ * Applications can associate a target portal group with a particular
+ * target using a target portal group name. Initiators can only connect
+ * to targets through the portals associated with the target's target
+ * portal group tags.
+ *
+ * tpg_name Identifier for the target portal group.
+ * tpg_generation Generation number which is incremented each
+ * time this structure changes.
+ * tpg_next Next target portal group in the list of target
+ * portal groups. If tpg_next is NULL, this is the
+ * last target portal group in the list.
+ * tpg_portal_count Number of it_portal_t structures in the list.
+ * tpg_portal_list Linked list of it_portal_t structures.
+ */
+typedef struct it_tpg_s {
+ char tpg_name[MAX_TPG_NAMELEN];
+ uint64_t tpg_generation;
+ struct it_tpg_s *tpg_next;
+ uint32_t tpg_portal_count;
+ it_portal_t *tpg_portal_list;
+} it_tpg_t;
+
+/*
+ * A context representing a remote iSCSI initiator node. The purpose
+ * of this structure is to maintain information specific to a remote
+ * initiator such as the CHAP username and CHAP secret.
+ *
+ * ini_name the iSCSI node name of the remote initiator.
+ * ini_generation Generation number which is incremented each
+ * time this structure changes.
+ * ini_next Next initiator in the list of initiators.
+ * If ini_next is NULL, this is the last initiator
+ * in the list.
+ * ini_properties Name/Value list containing the properties
+ * associated with the initiator context. This list
+ * can be manipulated using libnvpair(3lib), and should
+ * be validated and stored using it_ini_setprop().
+ *
+ * Initiator nvlist Properties:
+ *
+ * nvlist Key Type Valid Values
+ * --------------------------------------------------------------------
+ * chapuser string any string
+ * chapsecret string string of at least 12 characters
+ * but not more than 255 characters.
+ * secret will be base64 encoded when
+ * stored.
+ */
+typedef struct it_ini_s {
+ char ini_name[MAX_ISCSI_NODENAMELEN];
+ uint64_t ini_generation;
+ struct it_ini_s *ini_next;
+ nvlist_t *ini_properties;
+} it_ini_t;
+
+
+/*
+ * This structure represents a complete configuration for the iscsit
+ * port provider. In addition to the global configuration, it_config_t
+ * includes lists of child objects including targets, target portal
+ * groups and initiator contexts. Each object includes a "generation"
+ * value which is used by the iscsit kernel driver to identify changes
+ * from one configuration update to the next.
+ *
+ * stmf_token A uint64_t that contains the value returned from a
+ * successful call to stmfGetProviderDataProt(3STMF).
+ * This token is used to verify that the configuration
+ * data persistently stored in COMSTAR has not been
+ * modified since this version was loaded.
+ * config_version Version number for this configuration structure
+ * config_tgt_list Linked list of target contexts representing the
+ * currently defined targets. Applications can add
+ * targets to or remove targets from this list using
+ * the it_tgt_create and it_tgt_delete functions.
+ * config_tgt_count The number of currently defined targets.
+ * config_tpg_list Linked list of target portal group contexts.
+ * Applications can add or remove target portal groups
+ * to/from this list using the it_tpg_create and
+ * it_tpg_delete functions.
+ * config_tpg_count The number of currently defined target portal groups
+ * config_ini_list Linked list of initiator contexts. Applications
+ * can add initiator contexts or remove initiator
+ * contexts from this list using the it_ini_create
+ * and it_ini_delete functions.
+ * config_ini_count The number of currently defined initiator contexts.
+ * config_global_properties
+ * Name/Value list representing the current global
+ * property settings. This list can be manipulated
+ * using libnvpair(3lib), and should be validated
+ * and stored using it_config_setprop().
+ * config_isns_svr_list
+ * Linked list of currently defined iSNS servers.
+ * Applications can add or remove iSNS servers by
+ * using the it_config_setprop() function and changing
+ * the array of iSNS servers stored in the "isnsserver"
+ * property.
+ * config_isns_svr_count
+ * The number of currently defined iSNS servers.
+ *
+ * Global nvlist Properties:
+ *
+ * nvlist Key Type Valid Values
+ * --------------------------------------------------------------------
+ * alias string any string
+ * auth string "radius", "chap", or "none"
+ * isns boolean B_TRUE, B_FALSE
+ * isnsserver string array Array of portal specifications of
+ * the form IPaddress:port. Port
+ * is optional; if not specified, the
+ * default iSNS port number of 3205 will
+ * be used. IPv6 addresses should
+ * be enclosed in square brackets '[' ']'.
+ * If "none" is specified, all defined
+ * iSNS servers will be removed from the
+ * configuration.
+ * radiusserver string IPaddress:port specification as
+ * described for 'isnsserver'.
+ * radiussecret string string of at least 12 characters
+ * but not more than 255 characters.
+ * secret will be base64 encoded when
+ * stored.
+ */
+typedef struct it_config_s {
+ uint64_t stmf_token;
+ uint32_t config_version;
+ it_tgt_t *config_tgt_list;
+ uint32_t config_tgt_count;
+ it_tpg_t *config_tpg_list;
+ uint32_t config_tpg_count;
+ it_ini_t *config_ini_list;
+ uint32_t config_ini_count;
+ it_portal_t *config_isns_svr_list;
+ uint32_t config_isns_svr_count;
+ nvlist_t *config_global_properties;
+} it_config_t;
+
+/*
+ * Function: it_config_load()
+ *
+ * Allocate and create an it_config_t structure representing the
+ * current iSCSI configuration. This structure is compiled using
+ * the 'provider' data returned by stmfGetProviderData(). If there
+ * is no provider data associated with iscsit, the it_config_t
+ * structure will be set to a default configuration.
+ *
+ * Parameters:
+ * cfg A C representation of the current iSCSI configuration
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid parameter
+ */
+int
+it_config_load(it_config_t **cfg);
+
+/*
+ * Function: it_config_commit()
+ *
+ * Informs the iscsit service that the configuration has changed and
+ * commits the new configuration to persistent store by calling
+ * stmfSetProviderData. This function can be called multiple times
+ * during a configuration sequence if necessary.
+ *
+ * Parameters:
+ * cfg A C representation of the current iSCSI configuration
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid it_config_t structure
+ * STMF_ERROR_SERVICE_DATA_VERSION Configuration was updated by another
+ * client. See stmfSetProviderDataProt().
+ */
+int
+it_config_commit(it_config_t *cfg);
+
+/*
+ * Function: it_config_setprop()
+ *
+ * Validate the provided property list and set the global properties
+ * for iSCSI Target. If errlist is not NULL, returns detailed
+ * errors for each property that failed. The format for errorlist
+ * is key = property, value = error string.
+ *
+ * Parameters:
+ *
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * proplist nvlist_t containing properties for this target.
+ * errlist (optional) nvlist_t of errors encountered when
+ * validating the properties.
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid property
+ *
+ */
+int
+it_config_setprop(it_config_t *cfg, nvlist_t *proplist, nvlist_t **errlist);
+
+/*
+ * Function: it_config_free()
+ *
+ * Free any resources associated with the it_config_t structure.
+ *
+ * Parameters:
+ * cfg A C representation of the current iSCSI configuration
+ */
+void
+it_config_free(it_config_t *cfg);
+
+/*
+ * Function: it_tgt_create()
+ *
+ * Allocate and create an it_tgt_t structure representing a new iSCSI
+ * target node. If tgt_name is NULL, then a unique target node name will
+ * be generated automatically. Otherwise, the value of tgt_name will be
+ * used as the target node name. The new it_tgt_t structure is added to
+ * the target list (cfg_tgt_list) in the configuration structure, and the
+ * new target will not be instantiated until the modified configuration
+ * is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tgt Pointer to an iSCSI target structure
+ * tgt_name The target node name for the target to be created.
+ * The name must be in either IQN or EUI format. If
+ * this value is NULL, a node name will be generated
+ * automatically in IQN format.
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid parameter
+ * EEXIST The requested target node name is already configured
+ * EFAULT Invalid iSCSI target name
+ */
+int
+it_tgt_create(it_config_t *cfg, it_tgt_t **tgt, char *tgt_name);
+
+/*
+ * Function: it_tgt_setprop()
+ *
+ * Validate the provided property list and set the properties for
+ * the specified target. If errlist is not NULL, returns detailed
+ * errors for each property that failed. The format for errorlist
+ * is key = property, value = error string.
+ *
+ * Parameters:
+ *
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tgt Pointer to an iSCSI target structure
+ * proplist nvlist_t containing properties for this target.
+ * errlist (optional) nvlist_t of errors encountered when
+ * validating the properties.
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid property
+ *
+ */
+int
+it_tgt_setprop(it_config_t *cfg, it_tgt_t *tgt, nvlist_t *proplist,
+ nvlist_t **errlist);
+
+
+/*
+ * Function: it_tgt_delete()
+ *
+ * Delete target represented by 'tgt', where 'tgt' is an existing
+ * it_tgt_t structure within the configuration 'cfg'. The target removal
+ * will not take effect until the modified configuration is committed
+ * by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tgt Pointer to an iSCSI target structure
+ * force Set the target to offline before removing it from
+ * the config. If not specified, the operation will
+ * fail if the target is determined to be online.
+ *
+ * Return Values:
+ * 0 Success
+ * EBUSY Target is online
+ */
+int
+it_tgt_delete(it_config_t *cfg, it_tgt_t *tgt, boolean_t force);
+
+/*
+ * Function: it_tpgt_create()
+ *
+ * Allocate and create an it_tpgt_t structure representing a new iSCSI
+ * target portal group tag. The new it_tpgt_t structure is added to the
+ * target tpgt list (tgt_tpgt_list) in the it_tgt_t structure. The new
+ * target portal group tag will not be instantiated until the modified
+ * configuration is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tgt Pointer to the iSCSI target structure associated
+ * with the target portal group tag
+ * tpgt Pointer to a target portal group tag structure
+ * tpg_name The name of the TPG to be associated with this TPGT
+ * tpgt_tag 16-bit numerical identifier for this TPGT. Valid
+ * values are 2 through 65535. If tpgt_tag is '0',
+ * this function will assign an appropriate tag number.
+ * If tpgt_tag is != 0, and the requested number is
+ * unavailable, another value will be chosen.
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid parameter
+ * EEXIST Specified TPG is already associated with the target
+ * E2BIG All tag numbers already in use
+ */
+int
+it_tpgt_create(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t **tpgt,
+ char *tpg_name, uint16_t tpgt_tag);
+
+/*
+ * Function: it_tpgt_delete()
+ *
+ * Delete the target portal group tag represented by 'tpgt', where
+ * 'tpgt' is an existing is_tpgt_t structure within the target 'tgt'.
+ * The target portal group tag removal will not take effect until the
+ * modified configuation is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tgt Pointer to the iSCSI target structure associated
+ * with the target portal group tag
+ * tpgt Pointer to a target portal group tag structure
+ */
+void
+it_tpgt_delete(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t *tpgt);
+
+/*
+ * Function: it_tpg_create()
+ *
+ * Allocate and create an it_tpg_t structure representing a new iSCSI
+ * target portal group. The new it_tpg_t structure is added to the global
+ * tpg list (cfg_tgt_list) in the it_config_t structure. The new target
+ * portal group will not be instantiated until the modified configuration
+ * is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tpg Pointer to the it_tpg_t structure representing
+ * the target portal group
+ * tpg_name Identifier for the target portal group
+ * portal_ip_port A string containing an appropriatedly formatted
+ * IP address:port. Both IPv4 and IPv6 addresses are
+ * permitted. This value becomes the first portal in
+ * the TPG -- applications can add additional values
+ * using it_portal_create() before committing the TPG.
+ * Return Values:
+ * 0 Success
+ * ENOMEM Cannot allocate resources
+ * EINVAL Invalid parameter
+ * EEXIST Portal already configured for another portal group
+ * associated with this target.
+ */
+int
+it_tpg_create(it_config_t *cfg, it_tpg_t **tpg, char *tpg_name,
+ char *portal_ip_port);
+
+/*
+ * Function: it_tpg_delete()
+ *
+ * Delete target portal group represented by 'tpg', where 'tpg' is an
+ * existing it_tpg_t structure within the global configuration 'cfg'.
+ * The target portal group removal will not take effect until the
+ * modified configuration is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configuration obtained from
+ * it_config_load()
+ * tpg Pointer to the it_tpg_t structure representing
+ * the target portal group
+ * force Remove this target portal group even if it's
+ * associated with one or more targets.
+ *
+ * Return Values:
+ * 0 Success
+ * EINVAL Invalid parameter
+ * EBUSY Portal group associated with one or more targets.
+ */
+int
+it_tpg_delete(it_config_t *cfg, it_tpg_t *tpg, boolean_t force);
+
+/*
+ * Function: it_portal_create()
+ *
+ * Add an it_portal_t structure representing a new portal to the specified
+ * target portal group. The change to the target portal group will not take
+ * effect until the modified configuration is committed by calling
+ * it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configration obtained from
+ * it_config_load()
+ * tpg Pointer to the it_tpg_t structure representing the
+ * target portal group or "none" to remove
+ * portal Pointer to the it_portal_t structure representing
+ * the portal
+ * portal_ip_port A string containing an appropriately formatted
+ * IP address or IP address:port in either IPv4 or
+ * IPv6 format.
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid parameter
+ * EEXIST Portal already configured for another portal group
+ */
+int
+it_portal_create(it_config_t *cfg, it_tpg_t *tpg, it_portal_t **portal,
+ char *portal_ip_port);
+
+/*
+ * Function: it_portal_delete()
+ *
+ * Remove the specified portal from the specified target portal group.
+ * The portal removal will not take effect until the modified configuration
+ * is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configration obtained from
+ * it_config_load()
+ * tpg Pointer to the it_tpg_t structure representing the
+ * target portal group
+ * portal Pointer to the it_portal_t structure representing
+ * the portal
+ */
+void
+it_portal_delete(it_config_t *cfg, it_tpg_t *tpg, it_portal_t *portal);
+
+/*
+ * Function: it_ini_create()
+ *
+ * Add an initiator context to the global configuration. The new
+ * initiator context will not be instantiated until the modified
+ * configuration is committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configration obtained from
+ * it_config_load()
+ * ini Pointer to the it_ini_t structure representing
+ * the initiator context.
+ * ini_node_name The iSCSI node name of the remote initiator.
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid parameter.
+ * EEXIST Initiator already configured
+ * EFAULT Invalid initiator name
+ */
+int
+it_ini_create(it_config_t *cfg, it_ini_t **ini, char *ini_node_name);
+
+/*
+ * Function: it_ini_setprop()
+ *
+ * Validate the provided property list and set the initiator properties.
+ * If errlist is not NULL, returns detailed errors for each property
+ * that failed. The format for errorlist is
+ * key = property, value = error string.
+ *
+ * Parameters:
+ *
+ * ini The initiator being updated.
+ * proplist nvlist_t containing properties for this target.
+ * errlist (optional) nvlist_t of errors encountered when
+ * validating the properties.
+ *
+ * Return Values:
+ * 0 Success
+ * ENOMEM Could not allocate resources
+ * EINVAL Invalid property
+ *
+ */
+int
+it_ini_setprop(it_ini_t *ini, nvlist_t *proplist, nvlist_t **errlist);
+
+/*
+ * Function: it_ini_delete()
+ *
+ * Remove the specified initiator context from the global configuration.
+ * The removal will not take effect until the modified configuration is
+ * committed by calling it_config_commit().
+ *
+ * Parameters:
+ * cfg The current iSCSI configration obtained from
+ * it_config_load()
+ * ini Pointer to the it_ini_t structure representing
+ * the initiator context.
+ */
+void
+it_ini_delete(it_config_t *cfg, it_ini_t *ini);
+
+/*
+ * Function: it_config_free()
+ *
+ * Free any resources associated with the it_config_t structure.
+ *
+ * Parameters:
+ * cfg A C representation of the current iSCSI configuration
+ */
+void
+it_config_free(it_config_t *cfg);
+
+/*
+ * Function: it_tgt_free()
+ *
+ * Frees an it_tgt_t structure. If tgt_next is not NULL, frees
+ * all structures in the list.
+ */
+void
+it_tgt_free(it_tgt_t *tgt);
+
+/*
+ * Function: it_tpgt_free()
+ *
+ * Deallocates resources of an it_tpgt_t structure. If tpgt->next
+ * is not NULL, frees all members of the list.
+ */
+void
+it_tpgt_free(it_tpgt_t *tpgt);
+
+/*
+ * Function: it_tpg_free()
+ *
+ * Deallocates resources associated with an it_tpg_t structure.
+ * If tpg->next is not NULL, frees all members of the list.
+ */
+void
+it_tpg_free(it_tpg_t *tpg);
+
+/*
+ * Function: it_ini_free()
+ *
+ * Deallocates resources of an it_ini_t structure. If ini->next is
+ * not NULL, frees all members of the list.
+ */
+void
+it_ini_free(it_ini_t *ini);
+
+/*
+ * Function: validate_iscsi_name()
+ *
+ * Ensures the passed-in string is a valid IQN or EUI iSCSI name
+ */
+boolean_t
+validate_iscsi_name(char *in_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBISCSIT_H */
diff --git a/usr/src/lib/libiscsit/common/llib-liscsit b/usr/src/lib/libiscsit/common/llib-liscsit
new file mode 100644
index 0000000000..8d9fc33604
--- /dev/null
+++ b/usr/src/lib/libiscsit/common/llib-liscsit
@@ -0,0 +1,30 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <libiscsit.h>
+#include <sys/iscsit/iscsit_common.h>
diff --git a/usr/src/lib/libiscsit/common/mapfile-vers b/usr/src/lib/libiscsit/common/mapfile-vers
new file mode 100644
index 0000000000..cceac0978d
--- /dev/null
+++ b/usr/src/lib/libiscsit/common/mapfile-vers
@@ -0,0 +1,79 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+SUNW_1.1 {
+ global:
+ it_config_load;
+ it_config_commit;
+ it_config_setprop;
+ it_config_free;
+ it_tgt_create;
+ it_tgt_setprop;
+ it_tgt_delete;
+ it_tgt_free;
+ it_tpg_create;
+ it_tpg_delete;
+ it_tpg_free;
+ it_ini_create;
+ it_ini_delete;
+ it_ini_setprop;
+ it_ini_free;
+ it_tpgt_create;
+ it_tpgt_delete;
+ it_tpgt_free;
+ it_portal_create;
+};
+
+SUNWprivate {
+ global:
+ it_config_to_nv;
+ it_nv_to_config;
+ it_nv_to_tgtlist;
+ it_tgtlist_to_nv;
+ it_tgt_to_nv;
+ it_nv_to_tgt;
+ it_tpgt_to_nv;
+ it_nv_to_tpgt;
+ it_tpgtlist_to_nv;
+ it_nv_to_tpgtlist;
+ it_tpg_to_nv;
+ it_nv_to_tpg;
+ it_tpglist_to_nv;
+ it_nv_to_tpglist;
+ it_ini_to_nv;
+ it_nv_to_ini;
+ it_inilist_to_nv;
+ it_nv_to_inilist;
+ it_common_convert_sa;
+ it_config_free_cmn;
+ it_tgt_free_cmn;
+ it_tpg_free_cmn;
+ it_ini_free_cmn;
+ it_tpgt_free_cmn;
+ sockaddr_to_str;
+ validate_iscsi_name;
+ local:
+ *;
+};
+