1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 /*
  25  * Copyright (c) 2012, Nexenta Systems, Inc. All rights reserved.
  26  */
  27 
  28 #include <sys/time.h>
  29 
  30 #if defined(_KERNEL)
  31 #include <sys/ddi.h>
  32 #include <sys/types.h>
  33 #include <sys/sunddi.h>
  34 #include <sys/socket.h>
  35 #include <inet/tcp.h>
  36 #include <inet/ip.h>
  37 #else
  38 #include <stdio.h>
  39 #include <strings.h>
  40 #include <stdlib.h>
  41 #include <errno.h>
  42 #include <sys/types.h>
  43 #include <sys/socket.h>
  44 #include <netinet/in.h>
  45 #include <arpa/inet.h>
  46 #endif
  47 
  48 #include <sys/iscsit/iscsit_common.h>
  49 #include <sys/iscsi_protocol.h>
  50 #include <sys/iscsit/isns_protocol.h>
  51 
  52 void *
  53 iscsit_zalloc(size_t size)
  54 {
  55 #if defined(_KERNEL)
  56         return (kmem_zalloc(size, KM_SLEEP));
  57 #else
  58         return (calloc(1, size));
  59 #endif
  60 }
  61 
  62 void
  63 iscsit_free(void *buf, size_t size)     /* ARGSUSED */
  64 {
  65 #if defined(_KERNEL)
  66         kmem_free(buf, size);
  67 #else
  68         free(buf);
  69 #endif
  70 }
  71 
  72 /*
  73  * default_port should be the port to be used, if not specified
  74  * as part of the supplied string 'arg'.
  75  */
  76 
  77 #define NI_MAXHOST      1025
  78 #define NI_MAXSERV      32
  79 
  80 
  81 struct sockaddr_storage *
  82 it_common_convert_sa(char *arg, struct sockaddr_storage *buf,
  83     uint32_t default_port)
  84 {
  85         /* Why does addrbuf need to be this big!??! XXX */
  86         char            addrbuf[NI_MAXHOST + NI_MAXSERV + 1];
  87         char            *addr_str;
  88         char            *port_str;
  89 #ifndef _KERNEL
  90         char            *errchr;
  91 #endif
  92         long            tmp_port = 0;
  93         sa_family_t     af;
  94 
  95         struct sockaddr_in      *sin;
  96         struct sockaddr_in6     *sin6;
  97         struct sockaddr_storage *sa = buf;
  98 
  99         if (!arg || !buf) {
 100                 return (NULL);
 101         }
 102 
 103         bzero(buf, sizeof (struct sockaddr_storage));
 104 
 105         /* don't modify the passed-in string */
 106         (void) strlcpy(addrbuf, arg, sizeof (addrbuf));
 107 
 108         addr_str = addrbuf;
 109 
 110         if (*addr_str == '[') {
 111                 /*
 112                  * An IPv6 address must be inside square brackets
 113                  */
 114                 port_str = strchr(addr_str, ']');
 115                 if (!port_str) {
 116                         /* No closing bracket */
 117                         return (NULL);
 118                 }
 119 
 120                 /* strip off the square brackets so we can convert */
 121                 addr_str++;
 122                 *port_str = '\0';
 123                 port_str++;
 124 
 125                 if (*port_str == ':') {
 126                         /* TCP port to follow */
 127                         port_str++;
 128                 } else if (*port_str == '\0') {
 129                         /* No port specified */
 130                         port_str = NULL;
 131                 } else {
 132                         /* malformed */
 133                         return (NULL);
 134                 }
 135                 af = AF_INET6;
 136         } else {
 137                 port_str = strchr(addr_str, ':');
 138                 if (port_str) {
 139                         *port_str = '\0';
 140                         port_str++;
 141                 }
 142                 af = AF_INET;
 143         }
 144 
 145         if (port_str) {
 146 #if defined(_KERNEL)
 147                 if (ddi_strtol(port_str, NULL, 10, &tmp_port) != 0) {
 148                         return (NULL);
 149                 }
 150 #else
 151                 tmp_port = strtol(port_str, &errchr, 10);
 152 #endif
 153                 if (tmp_port < 0 || tmp_port > 65535) {
 154                         return (NULL);
 155                 }
 156         } else {
 157                 tmp_port = default_port;
 158         }
 159 
 160         sa->ss_family = af;
 161 
 162         sin = (struct sockaddr_in *)sa;
 163         if (af == AF_INET) {
 164                 if (inet_pton(af, addr_str,
 165                     (void *)&(sin->sin_addr.s_addr)) != 1) {
 166                         return (NULL);
 167                 }
 168                 sin->sin_port = htons(tmp_port);
 169         } else {
 170                 sin6 = (struct sockaddr_in6 *)sa;
 171                 if (inet_pton(af, addr_str,
 172                     (void *)&(sin6->sin6_addr.s6_addr)) != 1) {
 173                         return (NULL);
 174                 }
 175                 sin6->sin6_port = htons(tmp_port);
 176         }
 177 
 178         /* successful */
 179         return (sa);
 180 }
 181 
 182 
 183 /*  Functions to convert iSCSI target structures to/from nvlists. */
 184 
 185 #ifndef _KERNEL
 186 int
 187 it_config_to_nv(it_config_t *cfg, nvlist_t **nvl)
 188 {
 189         int             ret;
 190         nvlist_t        *nv;
 191         nvlist_t        *lnv = NULL;
 192 
 193         if (!nvl) {
 194                 return (EINVAL);
 195         }
 196 
 197         *nvl = NULL;
 198 
 199         ret = nvlist_alloc(&nv, NV_UNIQUE_NAME_TYPE, 0);
 200         if (ret != 0) {
 201                 return (ret);
 202         }
 203 
 204         /* if there's no config, store an empty list */
 205         if (!cfg) {
 206                 *nvl = nv;
 207                 return (0);
 208         }
 209 
 210         ret = nvlist_add_uint32(nv, "cfgVersion", cfg->config_version);
 211         if (ret == 0) {
 212                 ret = it_tgtlist_to_nv(cfg->config_tgt_list, &lnv);
 213         }
 214 
 215         if ((ret == 0) && (lnv != NULL)) {
 216                 ret = nvlist_add_nvlist(nv, "targetList", lnv);
 217                 nvlist_free(lnv);
 218                 lnv = NULL;
 219         }
 220 
 221         if (ret == 0) {
 222                 ret = it_tpglist_to_nv(cfg->config_tpg_list, &lnv);
 223         }
 224 
 225         if ((ret == 0) && (lnv != NULL)) {
 226                 ret = nvlist_add_nvlist(nv, "tpgList", lnv);
 227                 nvlist_free(lnv);
 228                 lnv = NULL;
 229         }
 230 
 231         if (ret == 0) {
 232                 ret = it_inilist_to_nv(cfg->config_ini_list, &lnv);
 233         }
 234 
 235         if ((ret == 0) && (lnv != NULL)) {
 236                 ret = nvlist_add_nvlist(nv, "iniList", lnv);
 237                 nvlist_free(lnv);
 238                 lnv = NULL;
 239         }
 240 
 241         if (ret == 0) {
 242                 ret = nvlist_add_nvlist(nv, "globalProperties",
 243                     cfg->config_global_properties);
 244         }
 245 
 246         if (ret == 0) {
 247                 *nvl = nv;
 248         } else {
 249                 nvlist_free(nv);
 250         }
 251 
 252         return (ret);
 253 }
 254 #endif /* !_KERNEL */
 255 
 256 /*
 257  * nvlist version of config is 3 list-of-list, + 1 proplist.  arrays
 258  * are interesting, but lists-of-lists are more useful when doing
 259  * individual lookups when we later add support for it.  Also, no
 260  * need to store name in individual struct representation.
 261  */
 262 int
 263 it_nv_to_config(nvlist_t *nvl, it_config_t **cfg)
 264 {
 265         int             ret;
 266         uint32_t        intval;
 267         nvlist_t        *listval;
 268         it_config_t     *tmpcfg;
 269 
 270         if (!cfg) {
 271                 return (EINVAL);
 272         }
 273 
 274         /* initialize output */
 275         *cfg = NULL;
 276 
 277         tmpcfg = iscsit_zalloc(sizeof (it_config_t));
 278         if (tmpcfg == NULL) {
 279                 return (ENOMEM);
 280         }
 281 
 282         if (!nvl) {
 283                 /* nothing to decode, but return the empty cfg struct */
 284                 ret = nvlist_alloc(&tmpcfg->config_global_properties,
 285                     NV_UNIQUE_NAME, 0);
 286                 if (ret != 0) {
 287                         iscsit_free(tmpcfg, sizeof (it_config_t));
 288                         return (ret);
 289                 }
 290                 *cfg = tmpcfg;
 291                 return (0);
 292         }
 293 
 294         ret = nvlist_lookup_uint32(nvl, "cfgVersion", &intval);
 295         if (ret != 0) {
 296                 iscsit_free(tmpcfg, sizeof (it_config_t));
 297                 return (ret);
 298         }
 299 
 300         tmpcfg->config_version = intval;
 301 
 302         ret = nvlist_lookup_nvlist(nvl, "targetList", &listval);
 303         if (ret == 0) {
 304                 /* decode list of it_tgt_t */
 305                 ret = it_nv_to_tgtlist(listval, &(tmpcfg->config_tgt_count),
 306                     &(tmpcfg->config_tgt_list));
 307         }
 308 
 309         ret = nvlist_lookup_nvlist(nvl, "tpgList", &listval);
 310         if (ret == 0) {
 311                 /* decode list of it_tpg_t */
 312                 ret = it_nv_to_tpglist(listval, &(tmpcfg->config_tpg_count),
 313                     &(tmpcfg->config_tpg_list));
 314         }
 315 
 316         ret = nvlist_lookup_nvlist(nvl, "iniList", &listval);
 317         if (ret == 0) {
 318                 /* decode list of initiators */
 319                 ret = it_nv_to_inilist(listval, &(tmpcfg->config_ini_count),
 320                     &(tmpcfg->config_ini_list));
 321         }
 322 
 323         ret = nvlist_lookup_nvlist(nvl, "globalProperties", &listval);
 324         if (ret == 0) {
 325                 /*
 326                  * don't depend on the original nvlist staying in-scope,
 327                  * duplicate the nvlist
 328                  */
 329                 ret = nvlist_dup(listval, &(tmpcfg->config_global_properties),
 330                     0);
 331         } else if (ret == ENOENT) {
 332                 /*
 333                  * No global properties defined, make an empty list
 334                  */
 335                 ret = nvlist_alloc(&tmpcfg->config_global_properties,
 336                     NV_UNIQUE_NAME, 0);
 337         }
 338 
 339         if (ret == 0) {
 340                 char            **isnsArray = NULL;
 341                 uint32_t        numisns = 0;
 342 
 343                 /*
 344                  * decode the list of iSNS server information to make
 345                  * references from the kernel simpler.
 346                  */
 347                 if (tmpcfg->config_global_properties) {
 348                         ret = nvlist_lookup_string_array(
 349                             tmpcfg->config_global_properties,
 350                             PROP_ISNS_SERVER,
 351                             &isnsArray, &numisns);
 352                         if (ret == 0) {
 353                                 ret = it_array_to_portallist(isnsArray,
 354                                     numisns, ISNS_DEFAULT_SERVER_PORT,
 355                                     &tmpcfg->config_isns_svr_list,
 356                                     &tmpcfg->config_isns_svr_count);
 357                         } else if (ret == ENOENT) {
 358                                 /* It's OK if we don't have any iSNS servers */
 359                                 ret = 0;
 360                         }
 361                 }
 362         }
 363 
 364         if (ret == 0) {
 365                 *cfg = tmpcfg;
 366         } else {
 367                 it_config_free_cmn(tmpcfg);
 368         }
 369 
 370         return (ret);
 371 }
 372 
 373 it_tgt_t *
 374 it_tgt_lookup(it_config_t *cfg, char *tgt_name)
 375 {
 376         it_tgt_t *cfg_tgt = NULL;
 377 
 378         for (cfg_tgt = cfg->config_tgt_list;
 379             cfg_tgt != NULL;
 380             cfg_tgt = cfg_tgt->tgt_next) {
 381                 if (strncmp(cfg_tgt->tgt_name, tgt_name,
 382                     MAX_ISCSI_NODENAMELEN) == 0) {
 383                         return (cfg_tgt);
 384                 }
 385         }
 386 
 387         return (NULL);
 388 }
 389 
 390 int
 391 it_nv_to_tgtlist(nvlist_t *nvl, uint32_t *count, it_tgt_t **tgtlist)
 392 {
 393         int             ret = 0;
 394         it_tgt_t        *tgt;
 395         it_tgt_t        *prev = NULL;
 396         nvpair_t        *nvp = NULL;
 397         nvlist_t        *nvt;
 398         char            *name;
 399 
 400         if (!tgtlist || !count) {
 401                 return (EINVAL);
 402         }
 403 
 404         *tgtlist = NULL;
 405         *count = 0;
 406 
 407         if (!nvl) {
 408                 /* nothing to do */
 409                 return (0);
 410         }
 411 
 412         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
 413                 name = nvpair_name(nvp);
 414 
 415                 ret = nvpair_value_nvlist(nvp, &nvt);
 416                 if (ret != 0) {
 417                         /* invalid entry? */
 418                         continue;
 419                 }
 420 
 421                 ret = it_nv_to_tgt(nvt, name, &tgt);
 422                 if (ret != 0) {
 423                         break;
 424                 }
 425 
 426                 (*count)++;
 427 
 428                 if (*tgtlist == NULL) {
 429                         *tgtlist = tgt;
 430                 } else {
 431                         prev->tgt_next = tgt;
 432                 }
 433                 prev = tgt;
 434         }
 435 
 436         if (ret != 0) {
 437                 it_tgt_free_cmn(*tgtlist);
 438                 *tgtlist = NULL;
 439         }
 440 
 441         return (ret);
 442 }
 443 
 444 int
 445 it_tgtlist_to_nv(it_tgt_t *tgtlist, nvlist_t **nvl)
 446 {
 447         int             ret;
 448         it_tgt_t        *tgtp = tgtlist;
 449         nvlist_t        *pnv = NULL;
 450         nvlist_t        *tnv;
 451 
 452         if (!nvl) {
 453                 return (EINVAL);
 454         }
 455 
 456         if (!tgtlist) {
 457                 /* nothing to do */
 458                 return (0);
 459         }
 460 
 461         /* create the target list if required */
 462         if (*nvl == NULL) {
 463                 ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0);
 464                 if (ret != 0) {
 465                         return (ret);
 466                 }
 467                 *nvl = pnv;
 468         }
 469 
 470         while (tgtp) {
 471                 ret = it_tgt_to_nv(tgtp, &tnv);
 472 
 473                 if (ret != 0) {
 474                         break;
 475                 }
 476 
 477                 ret = nvlist_add_nvlist(*nvl, tgtp->tgt_name, tnv);
 478 
 479                 if (ret != 0) {
 480                         break;
 481                 }
 482 
 483                 nvlist_free(tnv);
 484 
 485                 tgtp = tgtp->tgt_next;
 486         }
 487 
 488         if (ret != 0) {
 489                 if (pnv) {
 490                         nvlist_free(pnv);
 491                         *nvl = NULL;
 492                 }
 493         }
 494 
 495         return (ret);
 496 }
 497 
 498 int
 499 it_tgt_to_nv(it_tgt_t *tgt, nvlist_t **nvl)
 500 {
 501         int             ret;
 502         nvlist_t        *tnv = NULL;
 503 
 504         if (!nvl) {
 505                 return (EINVAL);
 506         }
 507 
 508         if (!tgt) {
 509                 /* nothing to do */
 510                 return (0);
 511         }
 512 
 513         ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
 514         if (ret != 0) {
 515                 return (ret);
 516         }
 517 
 518         if (tgt->tgt_properties) {
 519                 ret = nvlist_add_nvlist(*nvl, "properties",
 520                     tgt->tgt_properties);
 521         }
 522 
 523         if (ret == 0) {
 524                 ret = nvlist_add_uint64(*nvl, "generation",
 525                     tgt->tgt_generation);
 526         }
 527 
 528         if (ret == 0) {
 529                 ret = it_tpgtlist_to_nv(tgt->tgt_tpgt_list, &tnv);
 530         }
 531 
 532         if ((ret == 0) && tnv) {
 533                 ret = nvlist_add_nvlist(*nvl, "tpgtList", tnv);
 534                 nvlist_free(tnv);
 535         }
 536 
 537         if (ret != 0) {
 538                 nvlist_free(*nvl);
 539                 *nvl = NULL;
 540         }
 541 
 542         return (ret);
 543 }
 544 
 545 int
 546 it_nv_to_tgt(nvlist_t *nvl, char *name, it_tgt_t **tgt)
 547 {
 548         int             ret;
 549         it_tgt_t        *ttgt;
 550         nvlist_t        *listval;
 551         uint32_t        intval;
 552 
 553         if (!nvl || !tgt || !name) {
 554                 return (EINVAL);
 555         }
 556 
 557         *tgt = NULL;
 558 
 559         ttgt = iscsit_zalloc(sizeof (it_tgt_t));
 560         if (!ttgt) {
 561                 return (ENOMEM);
 562         }
 563 
 564         (void) strlcpy(ttgt->tgt_name, name, sizeof (ttgt->tgt_name));
 565 
 566         ret = nvlist_lookup_nvlist(nvl, "properties", &listval);
 567         if (ret == 0) {
 568                 /* duplicate list so it does not go out of context */
 569                 ret = nvlist_dup(listval, &(ttgt->tgt_properties), 0);
 570         } else if (ret == ENOENT) {
 571                 ret = 0;
 572         }
 573 
 574         if (ret == 0) {
 575                 ret = nvlist_lookup_uint64(nvl, "generation",
 576                     &(ttgt->tgt_generation));
 577         } else if (ret == ENOENT) {
 578                 ret = 0;
 579         }
 580 
 581         if (ret == 0) {
 582                 ret = nvlist_lookup_nvlist(nvl, "tpgtList", &listval);
 583         }
 584 
 585         if (ret == 0) {
 586                 ret = it_nv_to_tpgtlist(listval, &intval,
 587                     &(ttgt->tgt_tpgt_list));
 588                 ttgt->tgt_tpgt_count = intval;
 589         } else if (ret == ENOENT) {
 590                 ret = 0;
 591         }
 592 
 593         if (ret == 0) {
 594                 *tgt = ttgt;
 595         } else {
 596                 it_tgt_free_cmn(ttgt);
 597         }
 598 
 599         return (ret);
 600 }
 601 
 602 int
 603 it_tpgt_to_nv(it_tpgt_t *tpgt, nvlist_t **nvl)
 604 {
 605         int             ret;
 606 
 607         if (!nvl) {
 608                 return (EINVAL);
 609         }
 610 
 611         if (!tpgt) {
 612                 /* nothing to do */
 613                 return (0);
 614         }
 615 
 616         ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
 617         if (ret != 0) {
 618                 return (ret);
 619         }
 620 
 621         ret = nvlist_add_uint16(*nvl, "tag", tpgt->tpgt_tag);
 622         if (ret == 0) {
 623                 ret = nvlist_add_uint64(*nvl, "generation",
 624                     tpgt->tpgt_generation);
 625         }
 626 
 627         if (ret != 0) {
 628                 nvlist_free(*nvl);
 629                 *nvl = NULL;
 630         }
 631 
 632         return (ret);
 633 }
 634 
 635 int
 636 it_nv_to_tpgt(nvlist_t *nvl, char *name, it_tpgt_t **tpgt)
 637 {
 638         int             ret;
 639         it_tpgt_t       *ptr;
 640 
 641         if (!tpgt || !name) {
 642                 return (EINVAL);
 643         }
 644 
 645         *tpgt = NULL;
 646 
 647         if (!nvl) {
 648                 return (0);
 649         }
 650 
 651         ptr = iscsit_zalloc(sizeof (it_tpgt_t));
 652         if (!ptr) {
 653                 return (ENOMEM);
 654         }
 655 
 656         (void) strlcpy(ptr->tpgt_tpg_name, name, sizeof (ptr->tpgt_tpg_name));
 657 
 658         ret = nvlist_lookup_uint16(nvl, "tag", &(ptr->tpgt_tag));
 659         if (ret == 0) {
 660                 ret = nvlist_lookup_uint64(nvl, "generation",
 661                     &(ptr->tpgt_generation));
 662         }
 663 
 664         if (ret == 0) {
 665                 *tpgt = ptr;
 666         } else {
 667                 iscsit_free(ptr, sizeof (it_tpgt_t));
 668         }
 669 
 670         return (ret);
 671 }
 672 
 673 int
 674 it_tpgtlist_to_nv(it_tpgt_t *tpgtlist, nvlist_t **nvl)
 675 {
 676         int             ret;
 677         nvlist_t        *pnv = NULL;
 678         nvlist_t        *tnv;
 679         it_tpgt_t       *ptr = tpgtlist;
 680 
 681         if (!nvl) {
 682                 return (EINVAL);
 683         }
 684 
 685         if (!tpgtlist) {
 686                 /* nothing to do */
 687                 return (0);
 688         }
 689 
 690         /* create the target list if required */
 691         if (*nvl == NULL) {
 692                 ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0);
 693                 if (ret != 0) {
 694                         return (ret);
 695                 }
 696                 *nvl = pnv;
 697         }
 698 
 699         while (ptr) {
 700                 ret = it_tpgt_to_nv(ptr, &tnv);
 701 
 702                 if (ret != 0) {
 703                         break;
 704                 }
 705 
 706                 ret = nvlist_add_nvlist(*nvl, ptr->tpgt_tpg_name, tnv);
 707 
 708                 if (ret != 0) {
 709                         break;
 710                 }
 711 
 712                 nvlist_free(tnv);
 713 
 714                 ptr = ptr->tpgt_next;
 715         }
 716 
 717         if (ret != 0) {
 718                 if (pnv) {
 719                         nvlist_free(pnv);
 720                         *nvl = NULL;
 721                 }
 722         }
 723 
 724         return (ret);
 725 }
 726 
 727 int
 728 it_nv_to_tpgtlist(nvlist_t *nvl, uint32_t *count, it_tpgt_t **tpgtlist)
 729 {
 730         int             ret = 0;
 731         it_tpgt_t       *tpgt;
 732         it_tpgt_t       *prev = NULL;
 733         nvpair_t        *nvp = NULL;
 734         nvlist_t        *nvt;
 735         char            *name;
 736 
 737         if (!tpgtlist || !count) {
 738                 return (EINVAL);
 739         }
 740 
 741         *tpgtlist = NULL;
 742         *count = 0;
 743 
 744         if (!nvl) {
 745                 /* nothing to do */
 746                 return (0);
 747         }
 748 
 749         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
 750                 name = nvpair_name(nvp);
 751 
 752                 ret = nvpair_value_nvlist(nvp, &nvt);
 753                 if (ret != 0) {
 754                         /* invalid entry? */
 755                         continue;
 756                 }
 757 
 758                 ret = it_nv_to_tpgt(nvt, name, &tpgt);
 759                 if (ret != 0) {
 760                         break;
 761                 }
 762 
 763                 (*count)++;
 764 
 765                 if (*tpgtlist == NULL) {
 766                         *tpgtlist = tpgt;
 767                 } else {
 768                         prev->tpgt_next = tpgt;
 769                 }
 770 
 771                 prev = tpgt;
 772         }
 773 
 774         if (ret != 0) {
 775                 it_tpgt_free_cmn(*tpgtlist);
 776                 *tpgtlist = NULL;
 777         }
 778 
 779         return (ret);
 780 }
 781 
 782 #ifndef _KERNEL
 783 int
 784 it_tpg_to_nv(it_tpg_t *tpg, nvlist_t **nvl)
 785 {
 786         int             ret;
 787         char            **portalArray = NULL;
 788         int             i;
 789         it_portal_t     *ptr;
 790 
 791         if (!nvl) {
 792                 return (EINVAL);
 793         }
 794 
 795         if (!tpg) {
 796                 /* nothing to do */
 797                 return (0);
 798         }
 799 
 800         ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
 801         if (ret != 0) {
 802                 return (ret);
 803         }
 804 
 805         ret = nvlist_add_uint64(*nvl, "generation", tpg->tpg_generation);
 806 
 807         if ((ret == 0) && tpg->tpg_portal_list) {
 808                 /* add the portals */
 809                 portalArray = iscsit_zalloc(tpg->tpg_portal_count *
 810                     sizeof (it_portal_t));
 811                 if (portalArray == NULL) {
 812                         nvlist_free(*nvl);
 813                         *nvl = NULL;
 814                         return (ENOMEM);
 815                 }
 816 
 817                 i = 0;
 818                 ptr = tpg->tpg_portal_list;
 819 
 820                 while (ptr && (i < tpg->tpg_portal_count)) {
 821                         ret = sockaddr_to_str(&(ptr->portal_addr),
 822                             &(portalArray[i]));
 823                         if (ret != 0) {
 824                                 break;
 825                         }
 826                         ptr = ptr->portal_next;
 827                         i++;
 828                 }
 829         }
 830 
 831         if ((ret == 0) && portalArray) {
 832                 ret = nvlist_add_string_array(*nvl, "portalList",
 833                     portalArray, i);
 834         }
 835 
 836 
 837         if (portalArray) {
 838                 while (--i >= 0) {
 839                         if (portalArray[i]) {
 840                                 iscsit_free(portalArray[i],
 841                                     strlen(portalArray[i] + 1));
 842                         }
 843                 }
 844                 iscsit_free(portalArray,
 845                     tpg->tpg_portal_count * sizeof (it_portal_t));
 846         }
 847 
 848         if (ret != 0) {
 849                 nvlist_free(*nvl);
 850                 *nvl = NULL;
 851         }
 852 
 853         return (ret);
 854 }
 855 #endif /* !_KERNEL */
 856 
 857 int
 858 it_nv_to_tpg(nvlist_t *nvl, char *name, it_tpg_t **tpg)
 859 {
 860         int             ret;
 861         it_tpg_t        *ptpg;
 862         char            **portalArray = NULL;
 863         uint32_t        count = 0;
 864 
 865         if (!name || !tpg) {
 866                 return (EINVAL);
 867         }
 868 
 869         *tpg = NULL;
 870 
 871         ptpg = iscsit_zalloc(sizeof (it_tpg_t));
 872         if (ptpg == NULL) {
 873                 return (ENOMEM);
 874         }
 875 
 876         (void) strlcpy(ptpg->tpg_name, name, sizeof (ptpg->tpg_name));
 877 
 878         ret = nvlist_lookup_uint64(nvl, "generation",
 879             &(ptpg->tpg_generation));
 880 
 881         if (ret == 0) {
 882                 ret = nvlist_lookup_string_array(nvl, "portalList",
 883                     &portalArray, &count);
 884         }
 885 
 886         if (ret == 0) {
 887                 /* set the portals */
 888                 ret = it_array_to_portallist(portalArray, count,
 889                     ISCSI_LISTEN_PORT, &ptpg->tpg_portal_list,
 890                     &ptpg->tpg_portal_count);
 891         } else if (ret == ENOENT) {
 892                 ret = 0;
 893         }
 894 
 895         if (ret == 0) {
 896                 *tpg = ptpg;
 897         } else {
 898                 it_tpg_free_cmn(ptpg);
 899         }
 900 
 901         return (ret);
 902 }
 903 
 904 
 905 
 906 
 907 #ifndef _KERNEL
 908 int
 909 it_tpglist_to_nv(it_tpg_t *tpglist, nvlist_t **nvl)
 910 {
 911         int             ret;
 912         nvlist_t        *pnv = NULL;
 913         nvlist_t        *tnv;
 914         it_tpg_t        *ptr = tpglist;
 915 
 916         if (!nvl) {
 917                 return (EINVAL);
 918         }
 919 
 920         if (!tpglist) {
 921                 /* nothing to do */
 922                 return (0);
 923         }
 924 
 925         /* create the target portal group list if required */
 926         if (*nvl == NULL) {
 927                 ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0);
 928                 if (ret != 0) {
 929                         return (ret);
 930                 }
 931                 *nvl = pnv;
 932         }
 933 
 934         while (ptr) {
 935                 ret = it_tpg_to_nv(ptr, &tnv);
 936 
 937                 if (ret != 0) {
 938                         break;
 939                 }
 940 
 941                 ret = nvlist_add_nvlist(*nvl, ptr->tpg_name, tnv);
 942 
 943                 if (ret != 0) {
 944                         break;
 945                 }
 946 
 947                 nvlist_free(tnv);
 948 
 949                 ptr = ptr->tpg_next;
 950         }
 951 
 952         if (ret != 0) {
 953                 if (pnv) {
 954                         nvlist_free(pnv);
 955                         *nvl = NULL;
 956                 }
 957         }
 958 
 959         return (ret);
 960 }
 961 #endif /* !_KERNEL */
 962 
 963 it_tpg_t *
 964 it_tpg_lookup(it_config_t *cfg, char *tpg_name)
 965 {
 966         it_tpg_t *cfg_tpg = NULL;
 967 
 968         for (cfg_tpg = cfg->config_tpg_list;
 969             cfg_tpg != NULL;
 970             cfg_tpg = cfg_tpg->tpg_next) {
 971                 if (strncmp(&cfg_tpg->tpg_name[0], tpg_name,
 972                     MAX_TPG_NAMELEN) == 0) {
 973                         return (cfg_tpg);
 974                 }
 975         }
 976 
 977         return (NULL);
 978 }
 979 
 980 int
 981 it_sa_compare(struct sockaddr_storage *sa1, struct sockaddr_storage *sa2)
 982 {
 983         struct sockaddr_in      *sin1, *sin2;
 984         struct sockaddr_in6     *sin6_1, *sin6_2;
 985 
 986         /*
 987          * XXX - should we check here for IPv4 addrs mapped to v6?
 988          * see also iscsit_is_v4_mapped in iscsit_login.c
 989          */
 990 
 991         if (sa1->ss_family != sa2->ss_family) {
 992                 return (1);
 993         }
 994 
 995         /*
 996          * sockaddr_in has padding which may not be initialized.
 997          * be more specific in the comparison, and don't trust the
 998          * caller has fully initialized the structure.
 999          */
1000         if (sa1->ss_family == AF_INET) {
1001                 sin1 = (struct sockaddr_in *)sa1;
1002                 sin2 = (struct sockaddr_in *)sa2;
1003                 if ((bcmp(&sin1->sin_addr, &sin2->sin_addr,
1004                     sizeof (struct in_addr)) == 0) &&
1005                     (sin1->sin_port == sin2->sin_port)) {
1006                         return (0);
1007                 }
1008         } else if (sa1->ss_family == AF_INET6) {
1009                 sin6_1 = (struct sockaddr_in6 *)sa1;
1010                 sin6_2 = (struct sockaddr_in6 *)sa2;
1011                 if (bcmp(sin6_1, sin6_2, sizeof (struct sockaddr_in6)) == 0) {
1012                         return (0);
1013                 }
1014         }
1015 
1016         return (1);
1017 }
1018 
1019 it_portal_t *
1020 it_portal_lookup(it_tpg_t *tpg, struct sockaddr_storage *sa)
1021 {
1022         it_portal_t *cfg_portal;
1023 
1024         for (cfg_portal = tpg->tpg_portal_list;
1025             cfg_portal != NULL;
1026             cfg_portal = cfg_portal->portal_next) {
1027                 if (it_sa_compare(sa, &cfg_portal->portal_addr) == 0)
1028                         return (cfg_portal);
1029         }
1030 
1031         return (NULL);
1032 }
1033 
1034 it_portal_t *
1035 it_sns_svr_lookup(it_config_t *cfg, struct sockaddr_storage *sa)
1036 {
1037         it_portal_t *cfg_portal;
1038 
1039         for (cfg_portal = cfg->config_isns_svr_list;
1040             cfg_portal != NULL;
1041             cfg_portal = cfg_portal->portal_next) {
1042                 if (it_sa_compare(sa, &cfg_portal->portal_addr) == 0)
1043                         return (cfg_portal);
1044         }
1045 
1046         return (NULL);
1047 }
1048 
1049 int
1050 it_nv_to_tpglist(nvlist_t *nvl, uint32_t *count, it_tpg_t **tpglist)
1051 {
1052         int             ret = 0;
1053         it_tpg_t        *tpg;
1054         it_tpg_t        *prev = NULL;
1055         nvpair_t        *nvp = NULL;
1056         nvlist_t        *nvt;
1057         char            *name;
1058 
1059         if (!tpglist || !count) {
1060                 return (EINVAL);
1061         }
1062 
1063         *tpglist = NULL;
1064         *count = 0;
1065 
1066         if (!nvl) {
1067                 /* nothing to do */
1068                 return (0);
1069         }
1070 
1071         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1072                 name = nvpair_name(nvp);
1073 
1074                 ret = nvpair_value_nvlist(nvp, &nvt);
1075                 if (ret != 0) {
1076                         /* invalid entry? */
1077                         continue;
1078                 }
1079 
1080                 ret = it_nv_to_tpg(nvt, name, &tpg);
1081                 if (ret != 0) {
1082                         break;
1083                 }
1084 
1085                 (*count)++;
1086 
1087                 if (*tpglist == NULL) {
1088                         *tpglist = tpg;
1089                 } else {
1090                         prev->tpg_next = tpg;
1091                 }
1092                 prev = tpg;
1093         }
1094 
1095         if (ret != 0) {
1096                 it_tpg_free_cmn(*tpglist);
1097                 *tpglist = NULL;
1098         }
1099 
1100         return (ret);
1101 }
1102 
1103 int
1104 it_ini_to_nv(it_ini_t *ini, nvlist_t **nvl)
1105 {
1106         int             ret;
1107 
1108         if (!nvl) {
1109                 return (EINVAL);
1110         }
1111 
1112         if (!ini) {
1113                 return (0);
1114         }
1115 
1116         ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
1117         if (ret != 0) {
1118                 return (ret);
1119         }
1120 
1121         if (ini->ini_properties) {
1122                 ret = nvlist_add_nvlist(*nvl, "properties",
1123                     ini->ini_properties);
1124         }
1125 
1126         if (ret == 0) {
1127                 ret = nvlist_add_uint64(*nvl, "generation",
1128                     ini->ini_generation);
1129         } else if (ret == ENOENT) {
1130                 ret = 0;
1131         }
1132 
1133         if (ret != 0) {
1134                 nvlist_free(*nvl);
1135                 *nvl = NULL;
1136         }
1137 
1138         return (ret);
1139 }
1140 
1141 int
1142 it_nv_to_ini(nvlist_t *nvl, char *name, it_ini_t **ini)
1143 {
1144         int             ret;
1145         it_ini_t        *inip;
1146         nvlist_t        *listval;
1147 
1148         if (!name || !ini) {
1149                 return (EINVAL);
1150         }
1151 
1152         *ini = NULL;
1153 
1154         if (!nvl) {
1155                 return (0);
1156         }
1157 
1158         inip = iscsit_zalloc(sizeof (it_ini_t));
1159         if (!inip) {
1160                 return (ENOMEM);
1161         }
1162 
1163         (void) strlcpy(inip->ini_name, name, sizeof (inip->ini_name));
1164 
1165         ret = nvlist_lookup_nvlist(nvl, "properties", &listval);
1166         if (ret == 0) {
1167                 ret = nvlist_dup(listval, &(inip->ini_properties), 0);
1168         } else if (ret == ENOENT) {
1169                 ret = 0;
1170         }
1171 
1172         if (ret == 0) {
1173                 ret = nvlist_lookup_uint64(nvl, "generation",
1174                     &(inip->ini_generation));
1175         }
1176 
1177         if (ret == 0) {
1178                 *ini = inip;
1179         } else {
1180                 it_ini_free_cmn(inip);
1181         }
1182 
1183         return (ret);
1184 }
1185 
1186 int
1187 it_inilist_to_nv(it_ini_t *inilist, nvlist_t **nvl)
1188 {
1189         int             ret;
1190         nvlist_t        *pnv = NULL;
1191         nvlist_t        *tnv;
1192         it_ini_t        *ptr = inilist;
1193 
1194         if (!nvl) {
1195                 return (EINVAL);
1196         }
1197 
1198         if (!inilist) {
1199                 return (0);
1200         }
1201 
1202         /* create the target list if required */
1203         if (*nvl == NULL) {
1204                 ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0);
1205                 if (ret != 0) {
1206                         return (ret);
1207                 }
1208                 *nvl = pnv;
1209         }
1210 
1211         while (ptr) {
1212                 ret = it_ini_to_nv(ptr, &tnv);
1213 
1214                 if (ret != 0) {
1215                         break;
1216                 }
1217 
1218                 ret = nvlist_add_nvlist(*nvl, ptr->ini_name, tnv);
1219 
1220                 if (ret != 0) {
1221                         break;
1222                 }
1223 
1224                 nvlist_free(tnv);
1225 
1226                 ptr = ptr->ini_next;
1227         }
1228 
1229         if (ret != 0) {
1230                 if (pnv) {
1231                         nvlist_free(pnv);
1232                         *nvl = NULL;
1233                 }
1234         }
1235 
1236         return (ret);
1237 }
1238 
1239 int
1240 it_nv_to_inilist(nvlist_t *nvl, uint32_t *count, it_ini_t **inilist)
1241 {
1242         int             ret = 0;
1243         it_ini_t        *inip;
1244         it_ini_t        *prev = NULL;
1245         nvpair_t        *nvp = NULL;
1246         nvlist_t        *nvt;
1247         char            *name;
1248 
1249         if (!inilist || !count) {
1250                 return (EINVAL);
1251         }
1252 
1253         *inilist = NULL;
1254         *count = 0;
1255 
1256         if (!nvl) {
1257                 /* nothing to do */
1258                 return (0);
1259         }
1260 
1261         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1262                 name = nvpair_name(nvp);
1263 
1264                 ret = nvpair_value_nvlist(nvp, &nvt);
1265                 if (ret != 0) {
1266                         /* invalid entry? */
1267                         continue;
1268                 }
1269 
1270                 ret = it_nv_to_ini(nvt, name, &inip);
1271                 if (ret != 0) {
1272                         break;
1273                 }
1274 
1275                 (*count)++;
1276 
1277                 if (*inilist == NULL) {
1278                         *inilist = inip;
1279                 } else {
1280                         prev->ini_next = inip;
1281                 }
1282                 prev = inip;
1283         }
1284 
1285         if (ret != 0) {
1286                 it_ini_free_cmn(*inilist);
1287                 *inilist = NULL;
1288         }
1289 
1290         return (ret);
1291 }
1292 
1293 /*
1294  * Convert a sockaddr to the string representation, suitable for
1295  * storing in an nvlist or printing out in a list.
1296  */
1297 #ifndef _KERNEL
1298 int
1299 sockaddr_to_str(struct sockaddr_storage *sa, char **addr)
1300 {
1301         int                     ret;
1302         char                    buf[INET6_ADDRSTRLEN + 7]; /* addr : port */
1303         char                    pbuf[7];
1304         const char              *bufp;
1305         struct sockaddr_in      *sin;
1306         struct sockaddr_in6     *sin6;
1307         uint16_t                port;
1308 
1309         if (!sa || !addr) {
1310                 return (EINVAL);
1311         }
1312 
1313         buf[0] = '\0';
1314 
1315         if (sa->ss_family == AF_INET) {
1316                 sin = (struct sockaddr_in *)sa;
1317                 bufp = inet_ntop(AF_INET,
1318                     (const void *)&(sin->sin_addr.s_addr),
1319                     buf, sizeof (buf));
1320                 if (bufp == NULL) {
1321                         ret = errno;
1322                         return (ret);
1323                 }
1324                 port = ntohs(sin->sin_port);
1325         } else if (sa->ss_family == AF_INET6) {
1326                 (void) strlcat(buf, "[", sizeof (buf));
1327                 sin6 = (struct sockaddr_in6 *)sa;
1328                 bufp = inet_ntop(AF_INET6,
1329                     (const void *)&sin6->sin6_addr.s6_addr,
1330                     &buf[1], (sizeof (buf) - 1));
1331                 if (bufp == NULL) {
1332                         ret = errno;
1333                         return (ret);
1334                 }
1335                 (void) strlcat(buf, "]", sizeof (buf));
1336                 port = ntohs(sin6->sin6_port);
1337         } else {
1338                 return (EINVAL);
1339         }
1340 
1341 
1342         (void) snprintf(pbuf, sizeof (pbuf), ":%u", port);
1343         (void) strlcat(buf, pbuf, sizeof (buf));
1344 
1345         *addr = strdup(buf);
1346         if (*addr == NULL) {
1347                 return (ENOMEM);
1348         }
1349 
1350         return (0);
1351 }
1352 #endif /* !_KERNEL */
1353 
1354 int
1355 it_array_to_portallist(char **arr, uint32_t count, uint32_t default_port,
1356     it_portal_t **portallist, uint32_t *list_count)
1357 {
1358         int             ret = 0;
1359         int             i;
1360         it_portal_t     *portal;
1361         it_portal_t     *prev = NULL;
1362         it_portal_t     *tmp;
1363 
1364         if (!arr || !portallist || !list_count) {
1365                 return (EINVAL);
1366         }
1367 
1368         *list_count = 0;
1369         *portallist = NULL;
1370 
1371         for (i = 0; i < count; i++) {
1372                 if (!arr[i]) {
1373                         /* should never happen */
1374                         continue;
1375                 }
1376                 portal = iscsit_zalloc(sizeof (it_portal_t));
1377                 if (!portal) {
1378                         ret = ENOMEM;
1379                         break;
1380                 }
1381                 if (it_common_convert_sa(arr[i],
1382                     &(portal->portal_addr), default_port) == NULL) {
1383                         iscsit_free(portal, sizeof (it_portal_t));
1384                         ret = EINVAL;
1385                         break;
1386                 }
1387 
1388                 /* make sure no duplicates */
1389                 tmp = *portallist;
1390                 while (tmp) {
1391                         if (it_sa_compare(&(tmp->portal_addr),
1392                             &(portal->portal_addr)) == 0) {
1393                                 iscsit_free(portal, sizeof (it_portal_t));
1394                                 portal = NULL;
1395                                 break;
1396                         }
1397                         tmp = tmp->portal_next;
1398                 }
1399 
1400                 if (!portal) {
1401                         continue;
1402                 }
1403 
1404                 /*
1405                  * The first time through the loop, *portallist == NULL
1406                  * because we assigned it to NULL above.  Subsequently
1407                  * prev will have been set.  Therefor it's OK to put
1408                  * lint override before prev->portal_next assignment.
1409                  */
1410                 if (*portallist == NULL) {
1411                         *portallist = portal;
1412                 } else {
1413                         prev->portal_next = portal;
1414                 }
1415 
1416                 prev = portal;
1417                 (*list_count)++;
1418         }
1419 
1420         return (ret);
1421 }
1422 
1423 /*
1424  * Function:  it_config_free_cmn()
1425  *
1426  * Free any resources associated with the it_config_t structure.
1427  *
1428  * Parameters:
1429  *    cfg       A C representation of the current iSCSI configuration
1430  */
1431 void
1432 it_config_free_cmn(it_config_t *cfg)
1433 {
1434         if (!cfg) {
1435                 return;
1436         }
1437 
1438         if (cfg->config_tgt_list) {
1439                 it_tgt_free_cmn(cfg->config_tgt_list);
1440         }
1441 
1442         if (cfg->config_tpg_list) {
1443                 it_tpg_free_cmn(cfg->config_tpg_list);
1444         }
1445 
1446         if (cfg->config_ini_list) {
1447                 it_ini_free_cmn(cfg->config_ini_list);
1448         }
1449 
1450         if (cfg->config_global_properties) {
1451                 nvlist_free(cfg->config_global_properties);
1452         }
1453 
1454         if (cfg->config_isns_svr_list) {
1455                 it_portal_t     *pp = cfg->config_isns_svr_list;
1456                 it_portal_t     *pp_next;
1457 
1458                 while (pp) {
1459                         pp_next = pp->portal_next;
1460                         iscsit_free(pp, sizeof (it_portal_t));
1461                         pp = pp_next;
1462                 }
1463         }
1464 
1465         iscsit_free(cfg, sizeof (it_config_t));
1466 }
1467 
1468 /*
1469  * Function:  it_tgt_free_cmn()
1470  *
1471  * Frees an it_tgt_t structure.  If tgt_next is not NULL, frees
1472  * all structures in the list.
1473  */
1474 void
1475 it_tgt_free_cmn(it_tgt_t *tgt)
1476 {
1477         it_tgt_t        *tgtp = tgt;
1478         it_tgt_t        *next;
1479 
1480         if (!tgt) {
1481                 return;
1482         }
1483 
1484         while (tgtp) {
1485                 next = tgtp->tgt_next;
1486 
1487                 if (tgtp->tgt_tpgt_list) {
1488                         it_tpgt_free_cmn(tgtp->tgt_tpgt_list);
1489                 }
1490 
1491                 if (tgtp->tgt_properties) {
1492                         nvlist_free(tgtp->tgt_properties);
1493                 }
1494 
1495                 iscsit_free(tgtp, sizeof (it_tgt_t));
1496 
1497                 tgtp = next;
1498         }
1499 }
1500 
1501 /*
1502  * Function:  it_tpgt_free_cmn()
1503  *
1504  * Deallocates resources of an it_tpgt_t structure.  If tpgt->next
1505  * is not NULL, frees all members of the list.
1506  */
1507 void
1508 it_tpgt_free_cmn(it_tpgt_t *tpgt)
1509 {
1510         it_tpgt_t       *tpgtp = tpgt;
1511         it_tpgt_t       *next;
1512 
1513         if (!tpgt) {
1514                 return;
1515         }
1516 
1517         while (tpgtp) {
1518                 next = tpgtp->tpgt_next;
1519 
1520                 iscsit_free(tpgtp, sizeof (it_tpgt_t));
1521 
1522                 tpgtp = next;
1523         }
1524 }
1525 
1526 /*
1527  * Function:  it_tpg_free_cmn()
1528  *
1529  * Deallocates resources associated with an it_tpg_t structure.
1530  * If tpg->next is not NULL, frees all members of the list.
1531  */
1532 void
1533 it_tpg_free_cmn(it_tpg_t *tpg)
1534 {
1535         it_tpg_t        *tpgp = tpg;
1536         it_tpg_t        *next;
1537         it_portal_t     *portalp;
1538         it_portal_t     *pnext;
1539 
1540         while (tpgp) {
1541                 next = tpgp->tpg_next;
1542 
1543                 portalp = tpgp->tpg_portal_list;
1544 
1545                 while (portalp) {
1546                         pnext = portalp->portal_next;
1547                         iscsit_free(portalp, sizeof (it_portal_t));
1548                         portalp = pnext;
1549                 }
1550 
1551                 iscsit_free(tpgp, sizeof (it_tpg_t));
1552 
1553                 tpgp = next;
1554         }
1555 }
1556 
1557 /*
1558  * Function:  it_ini_free_cmn()
1559  *
1560  * Deallocates resources of an it_ini_t structure. If ini->next is
1561  * not NULL, frees all members of the list.
1562  */
1563 void
1564 it_ini_free_cmn(it_ini_t *ini)
1565 {
1566         it_ini_t        *inip = ini;
1567         it_ini_t        *next;
1568 
1569         if (!ini) {
1570                 return;
1571         }
1572 
1573         while (inip) {
1574                 next = inip->ini_next;
1575 
1576                 if (inip->ini_properties) {
1577                         nvlist_free(inip->ini_properties);
1578                 }
1579 
1580                 iscsit_free(inip, sizeof (it_ini_t));
1581 
1582                 inip = next;
1583         }
1584 }