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