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