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