diff options
Diffstat (limited to 'usr/src/common/iscsit/iscsit_common.c')
-rw-r--r-- | usr/src/common/iscsit/iscsit_common.c | 1588 |
1 files changed, 1588 insertions, 0 deletions
diff --git a/usr/src/common/iscsit/iscsit_common.c b/usr/src/common/iscsit/iscsit_common.c new file mode 100644 index 0000000..7b2b18e --- /dev/null +++ b/usr/src/common/iscsit/iscsit_common.c @@ -0,0 +1,1588 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include <sys/time.h> + +#if defined(_KERNEL) +#include <sys/ddi.h> +#include <sys/types.h> +#include <sys/sunddi.h> +#include <sys/socket.h> +#include <inet/tcp.h> +#else +#include <stdio.h> +#include <strings.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#include <sys/iscsit/iscsit_common.h> +#include <sys/iscsi_protocol.h> +#include <sys/iscsit/isns_protocol.h> + +void * +iscsit_zalloc(size_t size) +{ +#if defined(_KERNEL) + return (kmem_zalloc(size, KM_SLEEP)); +#else + return (calloc(1, size)); +#endif +} + +void +iscsit_free(void *buf, size_t size) /* ARGSUSED */ +{ +#if defined(_KERNEL) + kmem_free(buf, size); +#else + free(buf); +#endif +} + +/* + * default_port should be the port to be used, if not specified + * as part of the supplied string 'arg'. + */ + +#define NI_MAXHOST 1025 +#define NI_MAXSERV 32 + + +struct sockaddr_storage * +it_common_convert_sa(char *arg, struct sockaddr_storage *buf, + uint32_t default_port) +{ + /* Why does addrbuf need to be this big!??! XXX */ + char addrbuf[NI_MAXHOST + NI_MAXSERV + 1]; + char *addr_str; + char *port_str; +#ifndef _KERNEL + char *errchr; +#endif + long tmp_port = 0; + sa_family_t af; + + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct sockaddr_storage *sa = buf; + + if (!arg || !buf) { + return (NULL); + } + + bzero(buf, sizeof (struct sockaddr_storage)); + + /* don't modify the passed-in string */ + (void) strlcpy(addrbuf, arg, sizeof (addrbuf)); + + addr_str = addrbuf; + + if (*addr_str == '[') { + /* + * An IPv6 address must be inside square brackets + */ + port_str = strchr(addr_str, ']'); + if (!port_str) { + /* No closing bracket */ + return (NULL); + } + + /* strip off the square brackets so we can convert */ + addr_str++; + *port_str = '\0'; + port_str++; + + if (*port_str == ':') { + /* TCP port to follow */ + port_str++; + } else if (*port_str == '\0') { + /* No port specified */ + port_str = NULL; + } else { + /* malformed */ + return (NULL); + } + af = AF_INET6; + } else { + port_str = strchr(addr_str, ':'); + if (port_str) { + *port_str = '\0'; + port_str++; + } + af = AF_INET; + } + + if (port_str) { +#if defined(_KERNEL) + if (ddi_strtol(port_str, NULL, 10, &tmp_port) != 0) { + return (NULL); + } +#else + tmp_port = strtol(port_str, &errchr, 10); +#endif + if (tmp_port < 0 || tmp_port > 65535) { + return (NULL); + } + } else { + tmp_port = default_port; + } + + sa->ss_family = af; + + sin = (struct sockaddr_in *)sa; + if (af == AF_INET) { + if (inet_pton(af, addr_str, + (void *)&(sin->sin_addr.s_addr)) != 1) { + return (NULL); + } + /* + * intet_pton does not seem to convert to network + * order in kernel. This is a workaround until the + * inet_pton works or we have our own inet_pton function. + */ +#ifdef _KERNEL + sin->sin_addr.s_addr = ntohl((uint32_t)sin->sin_addr.s_addr); +#endif + sin->sin_port = htons(tmp_port); + } else { + sin6 = (struct sockaddr_in6 *)sa; + if (inet_pton(af, addr_str, + (void *)&(sin6->sin6_addr.s6_addr)) != 1) { + return (NULL); + } + sin6->sin6_port = htons(tmp_port); + } + + /* successful */ + return (sa); +} + + +/* Functions to convert iSCSI target structures to/from nvlists. */ + +#ifndef _KERNEL +int +it_config_to_nv(it_config_t *cfg, nvlist_t **nvl) +{ + int ret; + nvlist_t *nv; + nvlist_t *lnv = NULL; + + if (!nvl) { + return (EINVAL); + } + + *nvl = NULL; + + ret = nvlist_alloc(&nv, NV_UNIQUE_NAME_TYPE, 0); + if (ret != 0) { + return (ret); + } + + /* if there's no config, store an empty list */ + if (!cfg) { + *nvl = nv; + return (0); + } + + ret = nvlist_add_uint32(nv, "cfgVersion", cfg->config_version); + if (ret == 0) { + ret = it_tgtlist_to_nv(cfg->config_tgt_list, &lnv); + } + + if ((ret == 0) && (lnv != NULL)) { + ret = nvlist_add_nvlist(nv, "targetList", lnv); + nvlist_free(lnv); + lnv = NULL; + } + + if (ret == 0) { + ret = it_tpglist_to_nv(cfg->config_tpg_list, &lnv); + } + + if ((ret == 0) && (lnv != NULL)) { + ret = nvlist_add_nvlist(nv, "tpgList", lnv); + nvlist_free(lnv); + lnv = NULL; + } + + if (ret == 0) { + ret = it_inilist_to_nv(cfg->config_ini_list, &lnv); + } + + if ((ret == 0) && (lnv != NULL)) { + ret = nvlist_add_nvlist(nv, "iniList", lnv); + nvlist_free(lnv); + lnv = NULL; + } + + if (ret == 0) { + ret = nvlist_add_nvlist(nv, "globalProperties", + cfg->config_global_properties); + } + + if (ret == 0) { + *nvl = nv; + } else { + nvlist_free(nv); + } + + return (ret); +} +#endif /* !_KERNEL */ + +/* + * nvlist version of config is 3 list-of-list, + 1 proplist. arrays + * are interesting, but lists-of-lists are more useful when doing + * individual lookups when we later add support for it. Also, no + * need to store name in individual struct representation. + */ +int +it_nv_to_config(nvlist_t *nvl, it_config_t **cfg) +{ + int ret; + uint32_t intval; + nvlist_t *listval; + it_config_t *tmpcfg; + + if (!cfg) { + return (EINVAL); + } + + /* initialize output */ + *cfg = NULL; + + tmpcfg = iscsit_zalloc(sizeof (it_config_t)); + if (tmpcfg == NULL) { + return (ENOMEM); + } + + if (!nvl) { + /* nothing to decode, but return the empty cfg struct */ + ret = nvlist_alloc(&tmpcfg->config_global_properties, + NV_UNIQUE_NAME, 0); + if (ret != 0) { + iscsit_free(tmpcfg, sizeof (it_config_t)); + return (ret); + } + *cfg = tmpcfg; + return (0); + } + + ret = nvlist_lookup_uint32(nvl, "cfgVersion", &intval); + if (ret != 0) { + iscsit_free(tmpcfg, sizeof (it_config_t)); + return (ret); + } + + tmpcfg->config_version = intval; + + ret = nvlist_lookup_nvlist(nvl, "targetList", &listval); + if (ret == 0) { + /* decode list of it_tgt_t */ + ret = it_nv_to_tgtlist(listval, &(tmpcfg->config_tgt_count), + &(tmpcfg->config_tgt_list)); + } + + ret = nvlist_lookup_nvlist(nvl, "tpgList", &listval); + if (ret == 0) { + /* decode list of it_tpg_t */ + ret = it_nv_to_tpglist(listval, &(tmpcfg->config_tpg_count), + &(tmpcfg->config_tpg_list)); + } + + ret = nvlist_lookup_nvlist(nvl, "iniList", &listval); + if (ret == 0) { + /* decode list of initiators */ + ret = it_nv_to_inilist(listval, &(tmpcfg->config_ini_count), + &(tmpcfg->config_ini_list)); + } + + ret = nvlist_lookup_nvlist(nvl, "globalProperties", &listval); + if (ret == 0) { + /* + * don't depend on the original nvlist staying in-scope, + * duplicate the nvlist + */ + ret = nvlist_dup(listval, &(tmpcfg->config_global_properties), + 0); + } else if (ret == ENOENT) { + /* + * No global properties defined, make an empty list + */ + ret = nvlist_alloc(&tmpcfg->config_global_properties, + NV_UNIQUE_NAME, 0); + } + + if (ret == 0) { + char **isnsArray = NULL; + uint32_t numisns = 0; + + /* + * decode the list of iSNS server information to make + * references from the kernel simpler. + */ + if (tmpcfg->config_global_properties) { + ret = nvlist_lookup_string_array( + tmpcfg->config_global_properties, + PROP_ISNS_SERVER, + &isnsArray, &numisns); + if (ret == 0) { + ret = it_array_to_portallist(isnsArray, + numisns, ISNS_DEFAULT_SERVER_PORT, + &tmpcfg->config_isns_svr_list, + &tmpcfg->config_isns_svr_count); + } else if (ret == ENOENT) { + /* It's OK if we don't have any iSNS servers */ + ret = 0; + } + } + } + + if (ret == 0) { + *cfg = tmpcfg; + } else { + it_config_free_cmn(tmpcfg); + } + + return (ret); +} + +it_tgt_t * +it_tgt_lookup(it_config_t *cfg, char *tgt_name) +{ + it_tgt_t *cfg_tgt = NULL; + + for (cfg_tgt = cfg->config_tgt_list; + cfg_tgt != NULL; + cfg_tgt = cfg_tgt->tgt_next) { + if (strncmp(cfg_tgt->tgt_name, tgt_name, + MAX_ISCSI_NODENAMELEN) == 0) { + return (cfg_tgt); + } + } + + return (NULL); +} + +int +it_nv_to_tgtlist(nvlist_t *nvl, uint32_t *count, it_tgt_t **tgtlist) +{ + int ret = 0; + it_tgt_t *tgt; + it_tgt_t *prev = NULL; + nvpair_t *nvp = NULL; + nvlist_t *nvt; + char *name; + + if (!tgtlist || !count) { + return (EINVAL); + } + + *tgtlist = NULL; + *count = 0; + + if (!nvl) { + /* nothing to do */ + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + + ret = nvpair_value_nvlist(nvp, &nvt); + if (ret != 0) { + /* invalid entry? */ + continue; + } + + ret = it_nv_to_tgt(nvt, name, &tgt); + if (ret != 0) { + break; + } + + (*count)++; + + if (*tgtlist == NULL) { + *tgtlist = tgt; + } else { + prev->tgt_next = tgt; + } + prev = tgt; + } + + if (ret != 0) { + it_tgt_free_cmn(*tgtlist); + *tgtlist = NULL; + } + + return (ret); +} + +int +it_tgtlist_to_nv(it_tgt_t *tgtlist, nvlist_t **nvl) +{ + int ret; + it_tgt_t *tgtp = tgtlist; + nvlist_t *pnv = NULL; + nvlist_t *tnv; + + if (!nvl) { + return (EINVAL); + } + + if (!tgtlist) { + /* nothing to do */ + return (0); + } + + /* create the target list if required */ + if (*nvl == NULL) { + ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + *nvl = pnv; + } + + while (tgtp) { + ret = it_tgt_to_nv(tgtp, &tnv); + + if (ret != 0) { + break; + } + + ret = nvlist_add_nvlist(*nvl, tgtp->tgt_name, tnv); + + if (ret != 0) { + break; + } + + nvlist_free(tnv); + + tgtp = tgtp->tgt_next; + } + + if (ret != 0) { + if (pnv) { + nvlist_free(pnv); + *nvl = NULL; + } + } + + return (ret); +} + +int +it_tgt_to_nv(it_tgt_t *tgt, nvlist_t **nvl) +{ + int ret; + nvlist_t *tnv = NULL; + + if (!nvl) { + return (EINVAL); + } + + if (!tgt) { + /* nothing to do */ + return (0); + } + + ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + + if (tgt->tgt_properties) { + ret = nvlist_add_nvlist(*nvl, "properties", + tgt->tgt_properties); + } + + if (ret == 0) { + ret = nvlist_add_uint64(*nvl, "generation", + tgt->tgt_generation); + } + + if (ret == 0) { + ret = it_tpgtlist_to_nv(tgt->tgt_tpgt_list, &tnv); + } + + if ((ret == 0) && tnv) { + ret = nvlist_add_nvlist(*nvl, "tpgtList", tnv); + nvlist_free(tnv); + } + + if (ret != 0) { + nvlist_free(*nvl); + *nvl = NULL; + } + + return (ret); +} + +int +it_nv_to_tgt(nvlist_t *nvl, char *name, it_tgt_t **tgt) +{ + int ret; + it_tgt_t *ttgt; + nvlist_t *listval; + uint32_t intval; + + if (!nvl || !tgt || !name) { + return (EINVAL); + } + + *tgt = NULL; + + ttgt = iscsit_zalloc(sizeof (it_tgt_t)); + if (!ttgt) { + return (ENOMEM); + } + + (void) strlcpy(ttgt->tgt_name, name, sizeof (ttgt->tgt_name)); + + ret = nvlist_lookup_nvlist(nvl, "properties", &listval); + if (ret == 0) { + /* duplicate list so it does not go out of context */ + ret = nvlist_dup(listval, &(ttgt->tgt_properties), 0); + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret == 0) { + ret = nvlist_lookup_uint64(nvl, "generation", + &(ttgt->tgt_generation)); + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret == 0) { + ret = nvlist_lookup_nvlist(nvl, "tpgtList", &listval); + } + + if (ret == 0) { + ret = it_nv_to_tpgtlist(listval, &intval, + &(ttgt->tgt_tpgt_list)); + ttgt->tgt_tpgt_count = intval; + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret == 0) { + *tgt = ttgt; + } else { + it_tgt_free_cmn(ttgt); + } + + return (ret); +} + +int +it_tpgt_to_nv(it_tpgt_t *tpgt, nvlist_t **nvl) +{ + int ret; + + if (!nvl) { + return (EINVAL); + } + + if (!tpgt) { + /* nothing to do */ + return (0); + } + + ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + + ret = nvlist_add_uint16(*nvl, "tag", tpgt->tpgt_tag); + if (ret == 0) { + ret = nvlist_add_uint64(*nvl, "generation", + tpgt->tpgt_generation); + } + + if (ret != 0) { + nvlist_free(*nvl); + *nvl = NULL; + } + + return (ret); +} + +int +it_nv_to_tpgt(nvlist_t *nvl, char *name, it_tpgt_t **tpgt) +{ + int ret; + it_tpgt_t *ptr; + + if (!tpgt || !name) { + return (EINVAL); + } + + *tpgt = NULL; + + if (!nvl) { + return (0); + } + + ptr = iscsit_zalloc(sizeof (it_tpgt_t)); + if (!ptr) { + return (ENOMEM); + } + + (void) strlcpy(ptr->tpgt_tpg_name, name, sizeof (ptr->tpgt_tpg_name)); + + ret = nvlist_lookup_uint16(nvl, "tag", &(ptr->tpgt_tag)); + if (ret == 0) { + ret = nvlist_lookup_uint64(nvl, "generation", + &(ptr->tpgt_generation)); + } + + if (ret == 0) { + *tpgt = ptr; + } else { + iscsit_free(ptr, sizeof (it_tpgt_t)); + } + + return (ret); +} + +int +it_tpgtlist_to_nv(it_tpgt_t *tpgtlist, nvlist_t **nvl) +{ + int ret; + nvlist_t *pnv = NULL; + nvlist_t *tnv; + it_tpgt_t *ptr = tpgtlist; + + if (!nvl) { + return (EINVAL); + } + + if (!tpgtlist) { + /* nothing to do */ + return (0); + } + + /* create the target list if required */ + if (*nvl == NULL) { + ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + *nvl = pnv; + } + + while (ptr) { + ret = it_tpgt_to_nv(ptr, &tnv); + + if (ret != 0) { + break; + } + + ret = nvlist_add_nvlist(*nvl, ptr->tpgt_tpg_name, tnv); + + if (ret != 0) { + break; + } + + nvlist_free(tnv); + + ptr = ptr->tpgt_next; + } + + if (ret != 0) { + if (pnv) { + nvlist_free(pnv); + *nvl = NULL; + } + } + + return (ret); +} + +int +it_nv_to_tpgtlist(nvlist_t *nvl, uint32_t *count, it_tpgt_t **tpgtlist) +{ + int ret = 0; + it_tpgt_t *tpgt; + it_tpgt_t *prev = NULL; + nvpair_t *nvp = NULL; + nvlist_t *nvt; + char *name; + + if (!tpgtlist || !count) { + return (EINVAL); + } + + *tpgtlist = NULL; + *count = 0; + + if (!nvl) { + /* nothing to do */ + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + + ret = nvpair_value_nvlist(nvp, &nvt); + if (ret != 0) { + /* invalid entry? */ + continue; + } + + ret = it_nv_to_tpgt(nvt, name, &tpgt); + if (ret != 0) { + break; + } + + (*count)++; + + if (*tpgtlist == NULL) { + *tpgtlist = tpgt; + } else { + prev->tpgt_next = tpgt; + } + + prev = tpgt; + } + + if (ret != 0) { + it_tpgt_free_cmn(*tpgtlist); + *tpgtlist = NULL; + } + + return (ret); +} + +#ifndef _KERNEL +int +it_tpg_to_nv(it_tpg_t *tpg, nvlist_t **nvl) +{ + int ret; + char **portalArray = NULL; + int i; + it_portal_t *ptr; + + if (!nvl) { + return (EINVAL); + } + + if (!tpg) { + /* nothing to do */ + return (0); + } + + ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + + ret = nvlist_add_uint64(*nvl, "generation", tpg->tpg_generation); + + if ((ret == 0) && tpg->tpg_portal_list) { + /* add the portals */ + portalArray = iscsit_zalloc(tpg->tpg_portal_count * + sizeof (it_portal_t)); + if (portalArray == NULL) { + nvlist_free(*nvl); + *nvl = NULL; + return (ENOMEM); + } + + i = 0; + ptr = tpg->tpg_portal_list; + + while (ptr && (i < tpg->tpg_portal_count)) { + ret = sockaddr_to_str(&(ptr->portal_addr), + &(portalArray[i])); + if (ret != 0) { + break; + } + ptr = ptr->portal_next; + i++; + } + } + + if ((ret == 0) && portalArray) { + ret = nvlist_add_string_array(*nvl, "portalList", + portalArray, i); + } + + + if (portalArray) { + while (--i >= 0) { + if (portalArray[i]) { + iscsit_free(portalArray[i], + strlen(portalArray[i] + 1)); + } + } + iscsit_free(portalArray, + tpg->tpg_portal_count * sizeof (it_portal_t)); + } + + if (ret != 0) { + nvlist_free(*nvl); + *nvl = NULL; + } + + return (ret); +} +#endif /* !_KERNEL */ + +int +it_nv_to_tpg(nvlist_t *nvl, char *name, it_tpg_t **tpg) +{ + int ret; + it_tpg_t *ptpg; + char **portalArray = NULL; + uint32_t count = 0; + + if (!name || !tpg) { + return (EINVAL); + } + + *tpg = NULL; + + ptpg = iscsit_zalloc(sizeof (it_tpg_t)); + if (ptpg == NULL) { + return (ENOMEM); + } + + (void) strlcpy(ptpg->tpg_name, name, sizeof (ptpg->tpg_name)); + + ret = nvlist_lookup_uint64(nvl, "generation", + &(ptpg->tpg_generation)); + + if (ret == 0) { + ret = nvlist_lookup_string_array(nvl, "portalList", + &portalArray, &count); + } + + if (ret == 0) { + /* set the portals */ + ret = it_array_to_portallist(portalArray, count, + ISCSI_LISTEN_PORT, &ptpg->tpg_portal_list, + &ptpg->tpg_portal_count); + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret == 0) { + *tpg = ptpg; + } else { + it_tpg_free_cmn(ptpg); + } + + return (ret); +} + + + + +#ifndef _KERNEL +int +it_tpglist_to_nv(it_tpg_t *tpglist, nvlist_t **nvl) +{ + int ret; + nvlist_t *pnv = NULL; + nvlist_t *tnv; + it_tpg_t *ptr = tpglist; + + if (!nvl) { + return (EINVAL); + } + + if (!tpglist) { + /* nothing to do */ + return (0); + } + + /* create the target portal group list if required */ + if (*nvl == NULL) { + ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + *nvl = pnv; + } + + while (ptr) { + ret = it_tpg_to_nv(ptr, &tnv); + + if (ret != 0) { + break; + } + + ret = nvlist_add_nvlist(*nvl, ptr->tpg_name, tnv); + + if (ret != 0) { + break; + } + + nvlist_free(tnv); + + ptr = ptr->tpg_next; + } + + if (ret != 0) { + if (pnv) { + nvlist_free(pnv); + *nvl = NULL; + } + } + + return (ret); +} +#endif /* !_KERNEL */ + +it_tpg_t * +it_tpg_lookup(it_config_t *cfg, char *tpg_name) +{ + it_tpg_t *cfg_tpg = NULL; + + for (cfg_tpg = cfg->config_tpg_list; + cfg_tpg != NULL; + cfg_tpg = cfg_tpg->tpg_next) { + if (strncmp(&cfg_tpg->tpg_name[0], tpg_name, + MAX_TPG_NAMELEN) == 0) { + return (cfg_tpg); + } + } + + return (NULL); +} + +int +it_sa_compare(struct sockaddr_storage *sa1, struct sockaddr_storage *sa2) +{ + struct sockaddr_in *sin1, *sin2; + struct sockaddr_in6 *sin6_1, *sin6_2; + + /* + * XXX - should we check here for IPv4 addrs mapped to v6? + * see also iscsit_is_v4_mapped in iscsit_login.c + */ + + if (sa1->ss_family != sa2->ss_family) { + return (1); + } + + /* + * sockaddr_in has padding which may not be initialized. + * be more specific in the comparison, and don't trust the + * caller has fully initialized the structure. + */ + if (sa1->ss_family == AF_INET) { + sin1 = (struct sockaddr_in *)sa1; + sin2 = (struct sockaddr_in *)sa2; + if ((bcmp(&sin1->sin_addr, &sin2->sin_addr, + sizeof (struct in_addr)) == 0) && + (sin1->sin_port == sin2->sin_port)) { + return (0); + } + } else if (sa1->ss_family == AF_INET6) { + sin6_1 = (struct sockaddr_in6 *)sa1; + sin6_2 = (struct sockaddr_in6 *)sa2; + if (bcmp(sin6_1, sin6_2, sizeof (struct sockaddr_in6)) == 0) { + return (0); + } + } + + return (1); +} + +it_portal_t * +it_portal_lookup(it_tpg_t *tpg, struct sockaddr_storage *sa) +{ + it_portal_t *cfg_portal; + + for (cfg_portal = tpg->tpg_portal_list; + cfg_portal != NULL; + cfg_portal = cfg_portal->portal_next) { + if (it_sa_compare(sa, &cfg_portal->portal_addr) == 0) + return (cfg_portal); + } + + return (NULL); +} + +it_portal_t * +it_sns_svr_lookup(it_config_t *cfg, struct sockaddr_storage *sa) +{ + it_portal_t *cfg_portal; + + for (cfg_portal = cfg->config_isns_svr_list; + cfg_portal != NULL; + cfg_portal = cfg_portal->portal_next) { + if (it_sa_compare(sa, &cfg_portal->portal_addr) == 0) + return (cfg_portal); + } + + return (NULL); +} + +int +it_nv_to_tpglist(nvlist_t *nvl, uint32_t *count, it_tpg_t **tpglist) +{ + int ret = 0; + it_tpg_t *tpg; + it_tpg_t *prev = NULL; + nvpair_t *nvp = NULL; + nvlist_t *nvt; + char *name; + + if (!tpglist || !count) { + return (EINVAL); + } + + *tpglist = NULL; + *count = 0; + + if (!nvl) { + /* nothing to do */ + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + + ret = nvpair_value_nvlist(nvp, &nvt); + if (ret != 0) { + /* invalid entry? */ + continue; + } + + ret = it_nv_to_tpg(nvt, name, &tpg); + if (ret != 0) { + break; + } + + (*count)++; + + if (*tpglist == NULL) { + *tpglist = tpg; + } else { + prev->tpg_next = tpg; + } + prev = tpg; + } + + if (ret != 0) { + it_tpg_free_cmn(*tpglist); + *tpglist = NULL; + } + + return (ret); +} + +int +it_ini_to_nv(it_ini_t *ini, nvlist_t **nvl) +{ + int ret; + + if (!nvl) { + return (EINVAL); + } + + if (!ini) { + return (0); + } + + ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + + if (ini->ini_properties) { + ret = nvlist_add_nvlist(*nvl, "properties", + ini->ini_properties); + } + + if (ret == 0) { + ret = nvlist_add_uint64(*nvl, "generation", + ini->ini_generation); + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret != 0) { + nvlist_free(*nvl); + *nvl = NULL; + } + + return (ret); +} + +int +it_nv_to_ini(nvlist_t *nvl, char *name, it_ini_t **ini) +{ + int ret; + it_ini_t *inip; + nvlist_t *listval; + + if (!name || !ini) { + return (EINVAL); + } + + *ini = NULL; + + if (!nvl) { + return (0); + } + + inip = iscsit_zalloc(sizeof (it_ini_t)); + if (!inip) { + return (ENOMEM); + } + + (void) strlcpy(inip->ini_name, name, sizeof (inip->ini_name)); + + ret = nvlist_lookup_nvlist(nvl, "properties", &listval); + if (ret == 0) { + ret = nvlist_dup(listval, &(inip->ini_properties), 0); + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret == 0) { + ret = nvlist_lookup_uint64(nvl, "generation", + &(inip->ini_generation)); + } + + if (ret == 0) { + *ini = inip; + } else { + it_ini_free_cmn(inip); + } + + return (ret); +} + +int +it_inilist_to_nv(it_ini_t *inilist, nvlist_t **nvl) +{ + int ret; + nvlist_t *pnv = NULL; + nvlist_t *tnv; + it_ini_t *ptr = inilist; + + if (!nvl) { + return (EINVAL); + } + + if (!inilist) { + return (0); + } + + /* create the target list if required */ + if (*nvl == NULL) { + ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + *nvl = pnv; + } + + while (ptr) { + ret = it_ini_to_nv(ptr, &tnv); + + if (ret != 0) { + break; + } + + ret = nvlist_add_nvlist(*nvl, ptr->ini_name, tnv); + + if (ret != 0) { + break; + } + + nvlist_free(tnv); + + ptr = ptr->ini_next; + } + + if (ret != 0) { + if (pnv) { + nvlist_free(pnv); + *nvl = NULL; + } + } + + return (ret); +} + +int +it_nv_to_inilist(nvlist_t *nvl, uint32_t *count, it_ini_t **inilist) +{ + int ret = 0; + it_ini_t *inip; + it_ini_t *prev = NULL; + nvpair_t *nvp = NULL; + nvlist_t *nvt; + char *name; + + if (!inilist || !count) { + return (EINVAL); + } + + *inilist = NULL; + *count = 0; + + if (!nvl) { + /* nothing to do */ + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + + ret = nvpair_value_nvlist(nvp, &nvt); + if (ret != 0) { + /* invalid entry? */ + continue; + } + + ret = it_nv_to_ini(nvt, name, &inip); + if (ret != 0) { + break; + } + + (*count)++; + + if (*inilist == NULL) { + *inilist = inip; + } else { + prev->ini_next = inip; + } + prev = inip; + } + + if (ret != 0) { + it_ini_free_cmn(*inilist); + *inilist = NULL; + } + + return (ret); +} + +/* + * Convert a sockaddr to the string representation, suitable for + * storing in an nvlist or printing out in a list. + */ +#ifndef _KERNEL +int +sockaddr_to_str(struct sockaddr_storage *sa, char **addr) +{ + int ret; + char buf[INET6_ADDRSTRLEN + 7]; /* addr : port */ + char pbuf[7]; + const char *bufp; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + uint16_t port; + + if (!sa || !addr) { + return (EINVAL); + } + + buf[0] = '\0'; + + if (sa->ss_family == AF_INET) { + sin = (struct sockaddr_in *)sa; + bufp = inet_ntop(AF_INET, + (const void *)&(sin->sin_addr.s_addr), + buf, sizeof (buf)); + if (bufp == NULL) { + ret = errno; + return (ret); + } + port = ntohs(sin->sin_port); + } else if (sa->ss_family == AF_INET6) { + (void) strlcat(buf, "[", sizeof (buf)); + sin6 = (struct sockaddr_in6 *)sa; + bufp = inet_ntop(AF_INET6, + (const void *)&sin6->sin6_addr.s6_addr, + &buf[1], (sizeof (buf) - 1)); + if (bufp == NULL) { + ret = errno; + return (ret); + } + (void) strlcat(buf, "]", sizeof (buf)); + port = ntohs(sin6->sin6_port); + } else { + return (EINVAL); + } + + + (void) snprintf(pbuf, sizeof (pbuf), ":%u", port); + (void) strlcat(buf, pbuf, sizeof (buf)); + + *addr = strdup(buf); + if (*addr == NULL) { + return (ENOMEM); + } + + return (0); +} +#endif /* !_KERNEL */ + +int +it_array_to_portallist(char **arr, uint32_t count, uint32_t default_port, + it_portal_t **portallist, uint32_t *list_count) +{ + int ret = 0; + int i; + it_portal_t *portal; + it_portal_t *prev = NULL; + it_portal_t *tmp; + + if (!arr || !portallist || !list_count) { + return (EINVAL); + } + + *list_count = 0; + *portallist = NULL; + + for (i = 0; i < count; i++) { + if (!arr[i]) { + /* should never happen */ + continue; + } + portal = iscsit_zalloc(sizeof (it_portal_t)); + if (!portal) { + ret = ENOMEM; + break; + } + if (it_common_convert_sa(arr[i], + &(portal->portal_addr), default_port) == NULL) { + iscsit_free(portal, sizeof (it_portal_t)); + ret = EINVAL; + break; + } + + /* make sure no duplicates */ + tmp = *portallist; + while (tmp) { + if (it_sa_compare(&(tmp->portal_addr), + &(portal->portal_addr)) == 0) { + iscsit_free(portal, sizeof (it_portal_t)); + portal = NULL; + break; + } + tmp = tmp->portal_next; + } + + if (!portal) { + continue; + } + + /* + * The first time through the loop, *portallist == NULL + * because we assigned it to NULL above. Subsequently + * prev will have been set. Therefor it's OK to put + * lint override before prev->portal_next assignment. + */ + if (*portallist == NULL) { + *portallist = portal; + } else { + prev->portal_next = portal; + } + + prev = portal; + (*list_count)++; + } + + return (ret); +} + +/* + * Function: it_config_free_cmn() + * + * Free any resources associated with the it_config_t structure. + * + * Parameters: + * cfg A C representation of the current iSCSI configuration + */ +void +it_config_free_cmn(it_config_t *cfg) +{ + if (!cfg) { + return; + } + + if (cfg->config_tgt_list) { + it_tgt_free_cmn(cfg->config_tgt_list); + } + + if (cfg->config_tpg_list) { + it_tpg_free_cmn(cfg->config_tpg_list); + } + + if (cfg->config_ini_list) { + it_ini_free_cmn(cfg->config_ini_list); + } + + if (cfg->config_global_properties) { + nvlist_free(cfg->config_global_properties); + } + + if (cfg->config_isns_svr_list) { + it_portal_t *pp = cfg->config_isns_svr_list; + it_portal_t *pp_next; + + while (pp) { + pp_next = pp->portal_next; + iscsit_free(pp, sizeof (it_portal_t)); + pp = pp_next; + } + } + + iscsit_free(cfg, sizeof (it_config_t)); +} + +/* + * Function: it_tgt_free_cmn() + * + * Frees an it_tgt_t structure. If tgt_next is not NULL, frees + * all structures in the list. + */ +void +it_tgt_free_cmn(it_tgt_t *tgt) +{ + it_tgt_t *tgtp = tgt; + it_tgt_t *next; + + if (!tgt) { + return; + } + + while (tgtp) { + next = tgtp->tgt_next; + + if (tgtp->tgt_tpgt_list) { + it_tpgt_free_cmn(tgtp->tgt_tpgt_list); + } + + if (tgtp->tgt_properties) { + nvlist_free(tgtp->tgt_properties); + } + + iscsit_free(tgtp, sizeof (it_tgt_t)); + + tgtp = next; + } +} + +/* + * Function: it_tpgt_free_cmn() + * + * Deallocates resources of an it_tpgt_t structure. If tpgt->next + * is not NULL, frees all members of the list. + */ +void +it_tpgt_free_cmn(it_tpgt_t *tpgt) +{ + it_tpgt_t *tpgtp = tpgt; + it_tpgt_t *next; + + if (!tpgt) { + return; + } + + while (tpgtp) { + next = tpgtp->tpgt_next; + + iscsit_free(tpgtp, sizeof (it_tpgt_t)); + + tpgtp = next; + } +} + +/* + * Function: it_tpg_free_cmn() + * + * 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_cmn(it_tpg_t *tpg) +{ + it_tpg_t *tpgp = tpg; + it_tpg_t *next; + it_portal_t *portalp; + it_portal_t *pnext; + + while (tpgp) { + next = tpgp->tpg_next; + + portalp = tpgp->tpg_portal_list; + + while (portalp) { + pnext = portalp->portal_next; + iscsit_free(portalp, sizeof (it_portal_t)); + portalp = pnext; + } + + iscsit_free(tpgp, sizeof (it_tpg_t)); + + tpgp = next; + } +} + +/* + * Function: it_ini_free_cmn() + * + * Deallocates resources of an it_ini_t structure. If ini->next is + * not NULL, frees all members of the list. + */ +void +it_ini_free_cmn(it_ini_t *ini) +{ + it_ini_t *inip = ini; + it_ini_t *next; + + if (!ini) { + return; + } + + while (inip) { + next = inip->ini_next; + + if (inip->ini_properties) { + nvlist_free(inip->ini_properties); + } + + iscsit_free(inip, sizeof (it_ini_t)); + + inip = next; + } +} |