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                 if (ret == 0) {
 830                         ret = nvlist_add_string_array(*nvl, "portalList",
 831                             portalArray, i);
 832                 }
 833 
 834 
 835                 while (--i >= 0) {
 836                         if (portalArray[i]) {
 837                                 iscsit_free(portalArray[i],
 838                                     strlen(portalArray[i] + 1));
 839                         }
 840                 }
 841                 iscsit_free(portalArray,
 842                     tpg->tpg_portal_count * sizeof (it_portal_t));
 843         }
 844 
 845         if (ret != 0) {
 846                 nvlist_free(*nvl);
 847                 *nvl = NULL;
 848         }
 849 
 850         return (ret);
 851 }
 852 #endif /* !_KERNEL */
 853 
 854 int
 855 it_nv_to_tpg(nvlist_t *nvl, char *name, it_tpg_t **tpg)
 856 {
 857         int             ret;
 858         it_tpg_t        *ptpg;
 859         char            **portalArray = NULL;
 860         uint32_t        count = 0;
 861 
 862         if (!name || !tpg) {
 863                 return (EINVAL);
 864         }
 865 
 866         *tpg = NULL;
 867 
 868         ptpg = iscsit_zalloc(sizeof (it_tpg_t));
 869         if (ptpg == NULL) {
 870                 return (ENOMEM);
 871         }
 872 
 873         (void) strlcpy(ptpg->tpg_name, name, sizeof (ptpg->tpg_name));
 874 
 875         ret = nvlist_lookup_uint64(nvl, "generation",
 876             &(ptpg->tpg_generation));
 877 
 878         if (ret == 0) {
 879                 ret = nvlist_lookup_string_array(nvl, "portalList",
 880                     &portalArray, &count);
 881         }
 882 
 883         if (ret == 0) {
 884                 /* set the portals */
 885                 ret = it_array_to_portallist(portalArray, count,
 886                     ISCSI_LISTEN_PORT, &ptpg->tpg_portal_list,
 887                     &ptpg->tpg_portal_count);
 888         } else if (ret == ENOENT) {
 889                 ret = 0;
 890         }
 891 
 892         if (ret == 0) {
 893                 *tpg = ptpg;
 894         } else {
 895                 it_tpg_free_cmn(ptpg);
 896         }
 897 
 898         return (ret);
 899 }
 900 
 901 
 902 
 903 
 904 #ifndef _KERNEL
 905 int
 906 it_tpglist_to_nv(it_tpg_t *tpglist, nvlist_t **nvl)
 907 {
 908         int             ret;
 909         nvlist_t        *pnv = NULL;
 910         nvlist_t        *tnv;
 911         it_tpg_t        *ptr = tpglist;
 912 
 913         if (!nvl) {
 914                 return (EINVAL);
 915         }
 916 
 917         if (!tpglist) {
 918                 /* nothing to do */
 919                 return (0);
 920         }
 921 
 922         /* create the target portal group list if required */
 923         if (*nvl == NULL) {
 924                 ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0);
 925                 if (ret != 0) {
 926                         return (ret);
 927                 }
 928                 *nvl = pnv;
 929         }
 930 
 931         while (ptr) {
 932                 ret = it_tpg_to_nv(ptr, &tnv);
 933 
 934                 if (ret != 0) {
 935                         break;
 936                 }
 937 
 938                 ret = nvlist_add_nvlist(*nvl, ptr->tpg_name, tnv);
 939 
 940                 if (ret != 0) {
 941                         break;
 942                 }
 943 
 944                 nvlist_free(tnv);
 945 
 946                 ptr = ptr->tpg_next;
 947         }
 948 
 949         if (ret != 0) {
 950                 if (pnv) {
 951                         nvlist_free(pnv);
 952                         *nvl = NULL;
 953                 }
 954         }
 955 
 956         return (ret);
 957 }
 958 #endif /* !_KERNEL */
 959 
 960 it_tpg_t *
 961 it_tpg_lookup(it_config_t *cfg, char *tpg_name)
 962 {
 963         it_tpg_t *cfg_tpg = NULL;
 964 
 965         for (cfg_tpg = cfg->config_tpg_list;
 966             cfg_tpg != NULL;
 967             cfg_tpg = cfg_tpg->tpg_next) {
 968                 if (strncmp(&cfg_tpg->tpg_name[0], tpg_name,
 969                     MAX_TPG_NAMELEN) == 0) {
 970                         return (cfg_tpg);
 971                 }
 972         }
 973 
 974         return (NULL);
 975 }
 976 
 977 int
 978 it_sa_compare(struct sockaddr_storage *sa1, struct sockaddr_storage *sa2)
 979 {
 980         struct sockaddr_in      *sin1, *sin2;
 981         struct sockaddr_in6     *sin6_1, *sin6_2;
 982 
 983         /*
 984          * XXX - should we check here for IPv4 addrs mapped to v6?
 985          * see also iscsit_is_v4_mapped in iscsit_login.c
 986          */
 987 
 988         if (sa1->ss_family != sa2->ss_family) {
 989                 return (1);
 990         }
 991 
 992         /*
 993          * sockaddr_in has padding which may not be initialized.
 994          * be more specific in the comparison, and don't trust the
 995          * caller has fully initialized the structure.
 996          */
 997         if (sa1->ss_family == AF_INET) {
 998                 sin1 = (struct sockaddr_in *)sa1;
 999                 sin2 = (struct sockaddr_in *)sa2;
1000                 if ((bcmp(&sin1->sin_addr, &sin2->sin_addr,
1001                     sizeof (struct in_addr)) == 0) &&
1002                     (sin1->sin_port == sin2->sin_port)) {
1003                         return (0);
1004                 }
1005         } else if (sa1->ss_family == AF_INET6) {
1006                 sin6_1 = (struct sockaddr_in6 *)sa1;
1007                 sin6_2 = (struct sockaddr_in6 *)sa2;
1008                 if (bcmp(sin6_1, sin6_2, sizeof (struct sockaddr_in6)) == 0) {
1009                         return (0);
1010                 }
1011         }
1012 
1013         return (1);
1014 }
1015 
1016 it_portal_t *
1017 it_portal_lookup(it_tpg_t *tpg, struct sockaddr_storage *sa)
1018 {
1019         it_portal_t *cfg_portal;
1020 
1021         for (cfg_portal = tpg->tpg_portal_list;
1022             cfg_portal != NULL;
1023             cfg_portal = cfg_portal->portal_next) {
1024                 if (it_sa_compare(sa, &cfg_portal->portal_addr) == 0)
1025                         return (cfg_portal);
1026         }
1027 
1028         return (NULL);
1029 }
1030 
1031 it_portal_t *
1032 it_sns_svr_lookup(it_config_t *cfg, struct sockaddr_storage *sa)
1033 {
1034         it_portal_t *cfg_portal;
1035 
1036         for (cfg_portal = cfg->config_isns_svr_list;
1037             cfg_portal != NULL;
1038             cfg_portal = cfg_portal->portal_next) {
1039                 if (it_sa_compare(sa, &cfg_portal->portal_addr) == 0)
1040                         return (cfg_portal);
1041         }
1042 
1043         return (NULL);
1044 }
1045 
1046 int
1047 it_nv_to_tpglist(nvlist_t *nvl, uint32_t *count, it_tpg_t **tpglist)
1048 {
1049         int             ret = 0;
1050         it_tpg_t        *tpg;
1051         it_tpg_t        *prev = NULL;
1052         nvpair_t        *nvp = NULL;
1053         nvlist_t        *nvt;
1054         char            *name;
1055 
1056         if (!tpglist || !count) {
1057                 return (EINVAL);
1058         }
1059 
1060         *tpglist = NULL;
1061         *count = 0;
1062 
1063         if (!nvl) {
1064                 /* nothing to do */
1065                 return (0);
1066         }
1067 
1068         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1069                 name = nvpair_name(nvp);
1070 
1071                 ret = nvpair_value_nvlist(nvp, &nvt);
1072                 if (ret != 0) {
1073                         /* invalid entry? */
1074                         continue;
1075                 }
1076 
1077                 ret = it_nv_to_tpg(nvt, name, &tpg);
1078                 if (ret != 0) {
1079                         break;
1080                 }
1081 
1082                 (*count)++;
1083 
1084                 if (*tpglist == NULL) {
1085                         *tpglist = tpg;
1086                 } else {
1087                         prev->tpg_next = tpg;
1088                 }
1089                 prev = tpg;
1090         }
1091 
1092         if (ret != 0) {
1093                 it_tpg_free_cmn(*tpglist);
1094                 *tpglist = NULL;
1095         }
1096 
1097         return (ret);
1098 }
1099 
1100 int
1101 it_ini_to_nv(it_ini_t *ini, nvlist_t **nvl)
1102 {
1103         int             ret;
1104 
1105         if (!nvl) {
1106                 return (EINVAL);
1107         }
1108 
1109         if (!ini) {
1110                 return (0);
1111         }
1112 
1113         ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
1114         if (ret != 0) {
1115                 return (ret);
1116         }
1117 
1118         if (ini->ini_properties) {
1119                 ret = nvlist_add_nvlist(*nvl, "properties",
1120                     ini->ini_properties);
1121         }
1122 
1123         if (ret == 0) {
1124                 ret = nvlist_add_uint64(*nvl, "generation",
1125                     ini->ini_generation);
1126         } else if (ret == ENOENT) {
1127                 ret = 0;
1128         }
1129 
1130         if (ret != 0) {
1131                 nvlist_free(*nvl);
1132                 *nvl = NULL;
1133         }
1134 
1135         return (ret);
1136 }
1137 
1138 int
1139 it_nv_to_ini(nvlist_t *nvl, char *name, it_ini_t **ini)
1140 {
1141         int             ret;
1142         it_ini_t        *inip;
1143         nvlist_t        *listval;
1144 
1145         if (!name || !ini) {
1146                 return (EINVAL);
1147         }
1148 
1149         *ini = NULL;
1150 
1151         if (!nvl) {
1152                 return (0);
1153         }
1154 
1155         inip = iscsit_zalloc(sizeof (it_ini_t));
1156         if (!inip) {
1157                 return (ENOMEM);
1158         }
1159 
1160         (void) strlcpy(inip->ini_name, name, sizeof (inip->ini_name));
1161 
1162         ret = nvlist_lookup_nvlist(nvl, "properties", &listval);
1163         if (ret == 0) {
1164                 ret = nvlist_dup(listval, &(inip->ini_properties), 0);
1165         } else if (ret == ENOENT) {
1166                 ret = 0;
1167         }
1168 
1169         if (ret == 0) {
1170                 ret = nvlist_lookup_uint64(nvl, "generation",
1171                     &(inip->ini_generation));
1172         }
1173 
1174         if (ret == 0) {
1175                 *ini = inip;
1176         } else {
1177                 it_ini_free_cmn(inip);
1178         }
1179 
1180         return (ret);
1181 }
1182 
1183 int
1184 it_inilist_to_nv(it_ini_t *inilist, nvlist_t **nvl)
1185 {
1186         int             ret;
1187         nvlist_t        *pnv = NULL;
1188         nvlist_t        *tnv;
1189         it_ini_t        *ptr = inilist;
1190 
1191         if (!nvl) {
1192                 return (EINVAL);
1193         }
1194 
1195         if (!inilist) {
1196                 return (0);
1197         }
1198 
1199         /* create the target list if required */
1200         if (*nvl == NULL) {
1201                 ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0);
1202                 if (ret != 0) {
1203                         return (ret);
1204                 }
1205                 *nvl = pnv;
1206         }
1207 
1208         while (ptr) {
1209                 ret = it_ini_to_nv(ptr, &tnv);
1210 
1211                 if (ret != 0) {
1212                         break;
1213                 }
1214 
1215                 ret = nvlist_add_nvlist(*nvl, ptr->ini_name, tnv);
1216 
1217                 if (ret != 0) {
1218                         break;
1219                 }
1220 
1221                 nvlist_free(tnv);
1222 
1223                 ptr = ptr->ini_next;
1224         }
1225 
1226         if (ret != 0) {
1227                 if (pnv) {
1228                         nvlist_free(pnv);
1229                         *nvl = NULL;
1230                 }
1231         }
1232 
1233         return (ret);
1234 }
1235 
1236 int
1237 it_nv_to_inilist(nvlist_t *nvl, uint32_t *count, it_ini_t **inilist)
1238 {
1239         int             ret = 0;
1240         it_ini_t        *inip;
1241         it_ini_t        *prev = NULL;
1242         nvpair_t        *nvp = NULL;
1243         nvlist_t        *nvt;
1244         char            *name;
1245 
1246         if (!inilist || !count) {
1247                 return (EINVAL);
1248         }
1249 
1250         *inilist = NULL;
1251         *count = 0;
1252 
1253         if (!nvl) {
1254                 /* nothing to do */
1255                 return (0);
1256         }
1257 
1258         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1259                 name = nvpair_name(nvp);
1260 
1261                 ret = nvpair_value_nvlist(nvp, &nvt);
1262                 if (ret != 0) {
1263                         /* invalid entry? */
1264                         continue;
1265                 }
1266 
1267                 ret = it_nv_to_ini(nvt, name, &inip);
1268                 if (ret != 0) {
1269                         break;
1270                 }
1271 
1272                 (*count)++;
1273 
1274                 if (*inilist == NULL) {
1275                         *inilist = inip;
1276                 } else {
1277                         prev->ini_next = inip;
1278                 }
1279                 prev = inip;
1280         }
1281 
1282         if (ret != 0) {
1283                 it_ini_free_cmn(*inilist);
1284                 *inilist = NULL;
1285         }
1286 
1287         return (ret);
1288 }
1289 
1290 /*
1291  * Convert a sockaddr to the string representation, suitable for
1292  * storing in an nvlist or printing out in a list.
1293  */
1294 #ifndef _KERNEL
1295 int
1296 sockaddr_to_str(struct sockaddr_storage *sa, char **addr)
1297 {
1298         int                     ret;
1299         char                    buf[INET6_ADDRSTRLEN + 7]; /* addr : port */
1300         char                    pbuf[7];
1301         const char              *bufp;
1302         struct sockaddr_in      *sin;
1303         struct sockaddr_in6     *sin6;
1304         uint16_t                port;
1305 
1306         if (!sa || !addr) {
1307                 return (EINVAL);
1308         }
1309 
1310         buf[0] = '\0';
1311 
1312         if (sa->ss_family == AF_INET) {
1313                 sin = (struct sockaddr_in *)sa;
1314                 bufp = inet_ntop(AF_INET,
1315                     (const void *)&(sin->sin_addr.s_addr),
1316                     buf, sizeof (buf));
1317                 if (bufp == NULL) {
1318                         ret = errno;
1319                         return (ret);
1320                 }
1321                 port = ntohs(sin->sin_port);
1322         } else if (sa->ss_family == AF_INET6) {
1323                 (void) strlcat(buf, "[", sizeof (buf));
1324                 sin6 = (struct sockaddr_in6 *)sa;
1325                 bufp = inet_ntop(AF_INET6,
1326                     (const void *)&sin6->sin6_addr.s6_addr,
1327                     &buf[1], (sizeof (buf) - 1));
1328                 if (bufp == NULL) {
1329                         ret = errno;
1330                         return (ret);
1331                 }
1332                 (void) strlcat(buf, "]", sizeof (buf));
1333                 port = ntohs(sin6->sin6_port);
1334         } else {
1335                 return (EINVAL);
1336         }
1337 
1338 
1339         (void) snprintf(pbuf, sizeof (pbuf), ":%u", port);
1340         (void) strlcat(buf, pbuf, sizeof (buf));
1341 
1342         *addr = strdup(buf);
1343         if (*addr == NULL) {
1344                 return (ENOMEM);
1345         }
1346 
1347         return (0);
1348 }
1349 #endif /* !_KERNEL */
1350 
1351 int
1352 it_array_to_portallist(char **arr, uint32_t count, uint32_t default_port,
1353     it_portal_t **portallist, uint32_t *list_count)
1354 {
1355         int             ret = 0;
1356         int             i;
1357         it_portal_t     *portal;
1358         it_portal_t     *prev = NULL;
1359         it_portal_t     *tmp;
1360 
1361         if (!arr || !portallist || !list_count) {
1362                 return (EINVAL);
1363         }
1364 
1365         *list_count = 0;
1366         *portallist = NULL;
1367 
1368         for (i = 0; i < count; i++) {
1369                 if (!arr[i]) {
1370                         /* should never happen */
1371                         continue;
1372                 }
1373                 portal = iscsit_zalloc(sizeof (it_portal_t));
1374                 if (!portal) {
1375                         ret = ENOMEM;
1376                         break;
1377                 }
1378                 if (it_common_convert_sa(arr[i],
1379                     &(portal->portal_addr), default_port) == NULL) {
1380                         iscsit_free(portal, sizeof (it_portal_t));
1381                         ret = EINVAL;
1382                         break;
1383                 }
1384 
1385                 /* make sure no duplicates */
1386                 tmp = *portallist;
1387                 while (tmp) {
1388                         if (it_sa_compare(&(tmp->portal_addr),
1389                             &(portal->portal_addr)) == 0) {
1390                                 iscsit_free(portal, sizeof (it_portal_t));
1391                                 portal = NULL;
1392                                 break;
1393                         }
1394                         tmp = tmp->portal_next;
1395                 }
1396 
1397                 if (!portal) {
1398                         continue;
1399                 }
1400 
1401                 /*
1402                  * The first time through the loop, *portallist == NULL
1403                  * because we assigned it to NULL above.  Subsequently
1404                  * prev will have been set.  Therefor it's OK to put
1405                  * lint override before prev->portal_next assignment.
1406                  */
1407                 if (*portallist == NULL) {
1408                         *portallist = portal;
1409                 } else {
1410                         prev->portal_next = portal;
1411                 }
1412 
1413                 prev = portal;
1414                 (*list_count)++;
1415         }
1416 
1417         return (ret);
1418 }
1419 
1420 /*
1421  * Function:  it_config_free_cmn()
1422  *
1423  * Free any resources associated with the it_config_t structure.
1424  *
1425  * Parameters:
1426  *    cfg       A C representation of the current iSCSI configuration
1427  */
1428 void
1429 it_config_free_cmn(it_config_t *cfg)
1430 {
1431         if (!cfg) {
1432                 return;
1433         }
1434 
1435         if (cfg->config_tgt_list) {
1436                 it_tgt_free_cmn(cfg->config_tgt_list);
1437         }
1438 
1439         if (cfg->config_tpg_list) {
1440                 it_tpg_free_cmn(cfg->config_tpg_list);
1441         }
1442 
1443         if (cfg->config_ini_list) {
1444                 it_ini_free_cmn(cfg->config_ini_list);
1445         }
1446 
1447         if (cfg->config_global_properties) {
1448                 nvlist_free(cfg->config_global_properties);
1449         }
1450 
1451         if (cfg->config_isns_svr_list) {
1452                 it_portal_t     *pp = cfg->config_isns_svr_list;
1453                 it_portal_t     *pp_next;
1454 
1455                 while (pp) {
1456                         pp_next = pp->portal_next;
1457                         iscsit_free(pp, sizeof (it_portal_t));
1458                         pp = pp_next;
1459                 }
1460         }
1461 
1462         iscsit_free(cfg, sizeof (it_config_t));
1463 }
1464 
1465 /*
1466  * Function:  it_tgt_free_cmn()
1467  *
1468  * Frees an it_tgt_t structure.  If tgt_next is not NULL, frees
1469  * all structures in the list.
1470  */
1471 void
1472 it_tgt_free_cmn(it_tgt_t *tgt)
1473 {
1474         it_tgt_t        *tgtp = tgt;
1475         it_tgt_t        *next;
1476 
1477         if (!tgt) {
1478                 return;
1479         }
1480 
1481         while (tgtp) {
1482                 next = tgtp->tgt_next;
1483 
1484                 if (tgtp->tgt_tpgt_list) {
1485                         it_tpgt_free_cmn(tgtp->tgt_tpgt_list);
1486                 }
1487 
1488                 if (tgtp->tgt_properties) {
1489                         nvlist_free(tgtp->tgt_properties);
1490                 }
1491 
1492                 iscsit_free(tgtp, sizeof (it_tgt_t));
1493 
1494                 tgtp = next;
1495         }
1496 }
1497 
1498 /*
1499  * Function:  it_tpgt_free_cmn()
1500  *
1501  * Deallocates resources of an it_tpgt_t structure.  If tpgt->next
1502  * is not NULL, frees all members of the list.
1503  */
1504 void
1505 it_tpgt_free_cmn(it_tpgt_t *tpgt)
1506 {
1507         it_tpgt_t       *tpgtp = tpgt;
1508         it_tpgt_t       *next;
1509 
1510         if (!tpgt) {
1511                 return;
1512         }
1513 
1514         while (tpgtp) {
1515                 next = tpgtp->tpgt_next;
1516 
1517                 iscsit_free(tpgtp, sizeof (it_tpgt_t));
1518 
1519                 tpgtp = next;
1520         }
1521 }
1522 
1523 /*
1524  * Function:  it_tpg_free_cmn()
1525  *
1526  * Deallocates resources associated with an it_tpg_t structure.
1527  * If tpg->next is not NULL, frees all members of the list.
1528  */
1529 void
1530 it_tpg_free_cmn(it_tpg_t *tpg)
1531 {
1532         it_tpg_t        *tpgp = tpg;
1533         it_tpg_t        *next;
1534         it_portal_t     *portalp;
1535         it_portal_t     *pnext;
1536 
1537         while (tpgp) {
1538                 next = tpgp->tpg_next;
1539 
1540                 portalp = tpgp->tpg_portal_list;
1541 
1542                 while (portalp) {
1543                         pnext = portalp->portal_next;
1544                         iscsit_free(portalp, sizeof (it_portal_t));
1545                         portalp = pnext;
1546                 }
1547 
1548                 iscsit_free(tpgp, sizeof (it_tpg_t));
1549 
1550                 tpgp = next;
1551         }
1552 }
1553 
1554 /*
1555  * Function:  it_ini_free_cmn()
1556  *
1557  * Deallocates resources of an it_ini_t structure. If ini->next is
1558  * not NULL, frees all members of the list.
1559  */
1560 void
1561 it_ini_free_cmn(it_ini_t *ini)
1562 {
1563         it_ini_t        *inip = ini;
1564         it_ini_t        *next;
1565 
1566         if (!ini) {
1567                 return;
1568         }
1569 
1570         while (inip) {
1571                 next = inip->ini_next;
1572 
1573                 if (inip->ini_properties) {
1574                         nvlist_free(inip->ini_properties);
1575                 }
1576 
1577                 iscsit_free(inip, sizeof (it_ini_t));
1578 
1579                 inip = next;
1580         }
1581 }