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