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