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 *
iscsit_zalloc(size_t size)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
iscsit_free(void * buf,size_t size)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 *
it_common_convert_sa(char * arg,struct sockaddr_storage * buf,uint32_t default_port)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
it_config_to_nv(it_config_t * cfg,nvlist_t ** nvl)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
it_nv_to_config(nvlist_t * nvl,it_config_t ** cfg)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 *
it_tgt_lookup(it_config_t * cfg,char * tgt_name)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
it_nv_to_tgtlist(nvlist_t * nvl,uint32_t * count,it_tgt_t ** tgtlist)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
it_tgtlist_to_nv(it_tgt_t * tgtlist,nvlist_t ** nvl)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
it_tgt_to_nv(it_tgt_t * tgt,nvlist_t ** nvl)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
it_nv_to_tgt(nvlist_t * nvl,char * name,it_tgt_t ** tgt)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
it_tpgt_to_nv(it_tpgt_t * tpgt,nvlist_t ** nvl)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
it_nv_to_tpgt(nvlist_t * nvl,char * name,it_tpgt_t ** tpgt)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
it_tpgtlist_to_nv(it_tpgt_t * tpgtlist,nvlist_t ** nvl)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
it_nv_to_tpgtlist(nvlist_t * nvl,uint32_t * count,it_tpgt_t ** tpgtlist)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
it_tpg_to_nv(it_tpg_t * tpg,nvlist_t ** nvl)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
it_nv_to_tpg(nvlist_t * nvl,char * name,it_tpg_t ** tpg)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
it_tpglist_to_nv(it_tpg_t * tpglist,nvlist_t ** nvl)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 *
it_tpg_lookup(it_config_t * cfg,char * tpg_name)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
it_sa_compare(struct sockaddr_storage * sa1,struct sockaddr_storage * sa2)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 *
it_portal_lookup(it_tpg_t * tpg,struct sockaddr_storage * sa)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 *
it_sns_svr_lookup(it_config_t * cfg,struct sockaddr_storage * sa)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
it_nv_to_tpglist(nvlist_t * nvl,uint32_t * count,it_tpg_t ** tpglist)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
it_ini_to_nv(it_ini_t * ini,nvlist_t ** nvl)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
it_nv_to_ini(nvlist_t * nvl,char * name,it_ini_t ** ini)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
it_inilist_to_nv(it_ini_t * inilist,nvlist_t ** nvl)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
it_nv_to_inilist(nvlist_t * nvl,uint32_t * count,it_ini_t ** inilist)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
sockaddr_to_str(struct sockaddr_storage * sa,char ** addr)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
it_array_to_portallist(char ** arr,uint32_t count,uint32_t default_port,it_portal_t ** portallist,uint32_t * list_count)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
it_config_free_cmn(it_config_t * cfg)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
it_tgt_free_cmn(it_tgt_t * tgt)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
it_tpgt_free_cmn(it_tpgt_t * tpgt)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
it_tpg_free_cmn(it_tpg_t * tpg)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
it_ini_free_cmn(it_ini_t * ini)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