xref: /illumos-gate/usr/src/common/iscsit/iscsit_common.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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 }
1589