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