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