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