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.
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.
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]
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
25 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
28 #include <sys/types.h>
32 #include <uuid/uuid.h>
41 #include <libiscsit.h>
42 #include <sys/iscsi_protocol.h>
43 #include <sys/iscsit/isns_protocol.h>
46 #define TARGET_NAME_VERS 2
48 /* this should be defined someplace central... */
49 #define ISCSI_NAME_LEN_MAX 223
51 /* max length of a base64 encoded secret */
52 #define MAX_BASE64_LEN 341
54 /* Default RADIUS server port */
55 #define DEFAULT_RADIUS_PORT 1812
57 /* The iscsit SMF service FMRI */
58 #define ISCSIT_FMRI "svc:/network/iscsi/target:default"
60 * The kernel reserves target portal group tag value 1 as the default.
62 #define ISCSIT_DEFAULT_TPGT 1
65 /* helper for property list validation */
66 #define PROPERR(lst, key, value) { \
68 (void) nvlist_add_string(lst, key, value); \
72 /* helper function declarations */
74 it_iqn_generate(char *iqn_buf
, int iqn_buf_len
, char *opt_iqn_suffix
);
77 it_val_pass(char *name
, char *val
, nvlist_t
*e
);
79 /* consider making validate funcs public */
81 it_validate_configprops(nvlist_t
*nvl
, nvlist_t
*errs
);
84 it_validate_tgtprops(nvlist_t
*nvl
, nvlist_t
*errs
);
87 it_validate_iniprops(nvlist_t
*nvl
, nvlist_t
*errs
);
90 is_iscsit_enabled(void);
99 free_empty_errlist(nvlist_t
**errlist
);
102 * Function: it_config_load()
104 * Allocate and create an it_config_t structure representing the
105 * current iSCSI configuration. This structure is compiled using
106 * the 'provider' data returned by stmfGetProviderData(). If there
107 * is no provider data associated with iscsit, the it_config_t
108 * structure will be set to a default configuration.
111 * cfg A C representation of the current iSCSI configuration
115 * ENOMEM Could not allocate resources
116 * EINVAL Invalid parameter
119 it_config_load(it_config_t
**cfg
)
122 nvlist_t
*cfg_nv
= NULL
;
123 it_config_t
*newcfg
= NULL
;
124 uint64_t stmf_token
= 0;
132 ret
= stmfGetProviderDataProt(ISCSIT_MODNAME
, &cfg_nv
,
133 STMF_PORT_PROVIDER_TYPE
, &stmf_token
);
135 if ((ret
== STMF_STATUS_SUCCESS
) ||
136 (ret
== STMF_ERROR_NOT_FOUND
)) {
138 * If not initialized yet, return empty it_config_t
139 * Else, convert nvlist to struct
141 ret
= it_nv_to_config(cfg_nv
, &newcfg
);
145 newcfg
->stmf_token
= stmf_token
;
157 * Function: it_config_commit()
159 * Informs the iscsit service that the configuration has changed and
160 * commits the new configuration to persistent store by calling
161 * stmfSetProviderData. This function can be called multiple times
162 * during a configuration sequence if necessary.
165 * cfg A C representation of the current iSCSI configuration
169 * ENOMEM Could not allocate resources
170 * EINVAL Invalid it_config_t structure
172 * TBD could not save config to STMF
175 it_config_commit(it_config_t
*cfg
)
178 nvlist_t
*cfgnv
= NULL
;
179 char *packednv
= NULL
;
182 iscsit_ioc_set_config_t iop
;
189 ret
= it_config_to_nv(cfg
, &cfgnv
);
191 ret
= nvlist_size(cfgnv
, &pnv_size
, NV_ENCODE_NATIVE
);
195 * If the iscsit service is enabled, send the changes to the
196 * kernel first. Kernel will be the final sanity check before
197 * the config is saved persistently.
199 * This somewhat leaves open the simultaneous-change hole
200 * that STMF was trying to solve, but is a better sanity
201 * check and allows for graceful handling of target renames.
203 if ((ret
== 0) && is_iscsit_enabled()) {
204 packednv
= malloc(pnv_size
);
208 ret
= nvlist_pack(cfgnv
, &packednv
, &pnv_size
,
209 NV_ENCODE_NATIVE
, 0);
213 iscsit_fd
= open(ISCSIT_NODE
, O_RDWR
|O_EXCL
);
214 if (iscsit_fd
!= -1) {
215 iop
.set_cfg_vers
= ISCSIT_API_VERS0
;
216 iop
.set_cfg_pnvlist
= packednv
;
217 iop
.set_cfg_pnvlist_len
= pnv_size
;
218 if ((ioctl(iscsit_fd
, ISCSIT_IOC_SET_CONFIG
,
223 (void) close(iscsit_fd
);
229 if (packednv
!= NULL
) {
235 * Before saving the config persistently, remove any
236 * PROP_OLD_TARGET_NAME entries. This is only interesting to
237 * the active service.
240 boolean_t changed
= B_FALSE
;
242 tgtp
= cfg
->config_tgt_list
;
243 for (; tgtp
!= NULL
; tgtp
= tgtp
->tgt_next
) {
244 if (!tgtp
->tgt_properties
) {
247 if (nvlist_exists(tgtp
->tgt_properties
,
248 PROP_OLD_TARGET_NAME
)) {
249 (void) nvlist_remove_all(tgtp
->tgt_properties
,
250 PROP_OLD_TARGET_NAME
);
256 /* rebuild the config nvlist */
259 ret
= it_config_to_nv(cfg
, &cfgnv
);
264 * stmfGetProviderDataProt() checks to ensure
265 * that the config data hasn't changed since we fetched it.
267 * The kernel now has a version we need to save persistently.
268 * CLI will 'do the right thing' and warn the user if it
269 * gets STMF_ERROR_PROV_DATA_STALE. We'll try once to revert
270 * the kernel to the persistently saved data, but ultimately,
271 * it's up to the administrator to validate things are as they
275 ret
= stmfSetProviderDataProt(ISCSIT_MODNAME
, cfgnv
,
276 STMF_PORT_PROVIDER_TYPE
, &(cfg
->stmf_token
));
278 if (ret
== STMF_STATUS_SUCCESS
) {
280 } else if (ret
== STMF_ERROR_NOMEM
) {
282 } else if (ret
== STMF_ERROR_PROV_DATA_STALE
) {
284 it_config_t
*rcfg
= NULL
;
286 st
= it_config_load(&rcfg
);
288 (void) it_config_commit(rcfg
);
289 it_config_free(rcfg
);
302 * Function: it_config_setprop()
304 * Validate the provided property list and set the global properties
305 * for iSCSI Target. If errlist is not NULL, returns detailed
306 * errors for each property that failed. The format for errorlist
307 * is key = property, value = error string.
311 * cfg The current iSCSI configuration obtained from
313 * proplist nvlist_t containing properties for this target.
314 * errlist (optional) nvlist_t of errors encountered when
315 * validating the properties.
319 * EINVAL Invalid property
323 it_config_setprop(it_config_t
*cfg
, nvlist_t
*proplist
, nvlist_t
**errlist
)
326 nvlist_t
*errs
= NULL
;
327 it_portal_t
*isns
= NULL
;
328 it_portal_t
*pnext
= NULL
;
329 it_portal_t
*newisnslist
= NULL
;
333 nvlist_t
*cprops
= NULL
;
336 if (!cfg
|| !proplist
) {
341 (void) nvlist_alloc(&errs
, 0, 0);
346 * copy the existing properties, merge, then validate
347 * the merged properties before committing them.
349 if (cfg
->config_global_properties
) {
350 ret
= nvlist_dup(cfg
->config_global_properties
, &cprops
, 0);
352 ret
= nvlist_alloc(&cprops
, NV_UNIQUE_NAME
, 0);
359 ret
= nvlist_merge(cprops
, proplist
, 0);
366 * base64 encode the radius secret, if it's changed.
369 (void) nvlist_lookup_string(proplist
, PROP_RADIUS_SECRET
, &val
);
371 char bsecret
[MAX_BASE64_LEN
];
373 ret
= it_val_pass(PROP_RADIUS_SECRET
, val
, errs
);
376 (void) memset(bsecret
, 0, MAX_BASE64_LEN
);
378 ret
= iscsi_binary_to_base64_str((uint8_t *)val
,
379 strlen(val
), bsecret
, MAX_BASE64_LEN
);
382 /* replace the value in the nvlist */
383 ret
= nvlist_add_string(cprops
,
384 PROP_RADIUS_SECRET
, bsecret
);
394 /* see if we need to remove the radius server setting */
396 (void) nvlist_lookup_string(cprops
, PROP_RADIUS_SERVER
, &val
);
397 if (val
&& (strcasecmp(val
, "none") == 0)) {
398 (void) nvlist_remove_all(cprops
, PROP_RADIUS_SERVER
);
401 /* and/or remove the alias */
403 (void) nvlist_lookup_string(cprops
, PROP_ALIAS
, &val
);
404 if (val
&& (strcasecmp(val
, "none") == 0)) {
405 (void) nvlist_remove_all(cprops
, PROP_ALIAS
);
408 ret
= it_validate_configprops(cprops
, errs
);
417 * Update iSNS server list, if exists in provided property list.
419 ret
= nvlist_lookup_string_array(proplist
, PROP_ISNS_SERVER
,
423 /* special case: if "none", remove all defined */
424 if (strcasecmp(arr
[0], "none") != 0) {
425 ret
= it_array_to_portallist(arr
, count
,
426 ISNS_DEFAULT_SERVER_PORT
, &newisnslist
, &newcount
);
430 (void) nvlist_remove_all(cprops
, PROP_ISNS_SERVER
);
434 isns
= cfg
->config_isns_svr_list
;
436 pnext
= isns
->portal_next
;
441 cfg
->config_isns_svr_list
= newisnslist
;
442 cfg
->config_isns_svr_count
= newcount
;
445 * Replace the array in the nvlist to ensure
446 * duplicates are properly removed & port numbers
453 newarray
= malloc(sizeof (char *) * newcount
);
454 if (newarray
== NULL
) {
457 for (isns
= newisnslist
; isns
!= NULL
;
458 isns
= isns
->portal_next
) {
459 (void) sockaddr_to_str(
460 &(isns
->portal_addr
),
463 (void) nvlist_add_string_array(cprops
,
464 PROP_ISNS_SERVER
, newarray
,
467 for (i
= 0; i
< newcount
; i
++) {
476 } else if (ret
== ENOENT
) {
482 /* replace the global properties list */
483 nvlist_free(cfg
->config_global_properties
);
484 cfg
->config_global_properties
= cprops
;
492 free_empty_errlist(errlist
);
498 * Function: it_config_free()
500 * Free any resources associated with the it_config_t structure.
503 * cfg A C representation of the current iSCSI configuration
506 it_config_free(it_config_t
*cfg
)
508 it_config_free_cmn(cfg
);
512 * Function: it_tgt_create()
514 * Allocate and create an it_tgt_t structure representing a new iSCSI
515 * target node. If tgt_name is NULL, then a unique target node name will
516 * be generated automatically. Otherwise, the value of tgt_name will be
517 * used as the target node name. The new it_tgt_t structure is added to
518 * the target list (cfg_tgt_list) in the configuration structure, and the
519 * new target will not be instantiated until the modified configuration
520 * is committed by calling it_config_commit().
523 * cfg The current iSCSI configuration obtained from
525 * tgt Pointer to an iSCSI target structure
526 * tgt_name The target node name for the target to be created.
527 * The name must be in either IQN or EUI format. If
528 * this value is NULL, a node name will be generated
529 * automatically in IQN format.
533 * ENOMEM Could not allocated resources
534 * EINVAL Invalid parameter
535 * EFAULT Invalid iSCSI name specified
536 * E2BIG Too many already exist
539 it_tgt_create(it_config_t
*cfg
, it_tgt_t
**tgt
, char *tgt_name
)
545 char buf
[ISCSI_NAME_LEN_MAX
+ 1];
552 /* generate a name */
553 ret
= it_iqn_generate(buf
, sizeof (buf
), NULL
);
558 /* validate the passed-in name */
559 if (!validate_iscsi_name(tgt_name
)) {
562 (void) strlcpy(buf
, tgt_name
, sizeof (buf
));
563 canonical_iscsi_name(buf
);
567 /* make sure this name isn't already on the list */
568 cfgtgt
= cfg
->config_tgt_list
;
569 while (cfgtgt
!= NULL
) {
570 if (strcasecmp(namep
, cfgtgt
->tgt_name
) == 0) {
573 cfgtgt
= cfgtgt
->tgt_next
;
576 /* Too many targets? */
577 if (cfg
->config_tgt_count
>= MAX_TARGETS
) {
581 ptr
= calloc(1, sizeof (it_tgt_t
));
586 (void) strlcpy(ptr
->tgt_name
, namep
, sizeof (ptr
->tgt_name
));
587 ptr
->tgt_generation
= 1;
588 ptr
->tgt_next
= cfg
->config_tgt_list
;
589 cfg
->config_tgt_list
= ptr
;
590 cfg
->config_tgt_count
++;
598 * Function: it_tgt_setprop()
600 * Validate the provided property list and set the properties for
601 * the specified target. If errlist is not NULL, returns detailed
602 * errors for each property that failed. The format for errorlist
603 * is key = property, value = error string.
607 * cfg The current iSCSI configuration obtained from
609 * tgt Pointer to an iSCSI target structure
610 * proplist nvlist_t containing properties for this target.
611 * errlist (optional) nvlist_t of errors encountered when
612 * validating the properties.
616 * EINVAL Invalid property
620 it_tgt_setprop(it_config_t
*cfg
, it_tgt_t
*tgt
, nvlist_t
*proplist
,
624 nvlist_t
*errs
= NULL
;
625 nvlist_t
*tprops
= NULL
;
628 if (!cfg
|| !tgt
|| !proplist
) {
632 /* verify the target name in case the target node is renamed */
633 if (!validate_iscsi_name(tgt
->tgt_name
)) {
636 canonical_iscsi_name(tgt
->tgt_name
);
639 (void) nvlist_alloc(&errs
, 0, 0);
644 * copy the existing properties, merge, then validate
645 * the merged properties before committing them.
647 if (tgt
->tgt_properties
) {
648 ret
= nvlist_dup(tgt
->tgt_properties
, &tprops
, 0);
650 ret
= nvlist_alloc(&tprops
, NV_UNIQUE_NAME
, 0);
657 ret
= nvlist_merge(tprops
, proplist
, 0);
663 /* unset chap username or alias if requested */
665 (void) nvlist_lookup_string(proplist
, PROP_TARGET_CHAP_USER
, &val
);
666 if (val
&& (strcasecmp(val
, "none") == 0)) {
667 (void) nvlist_remove_all(tprops
, PROP_TARGET_CHAP_USER
);
671 (void) nvlist_lookup_string(proplist
, PROP_ALIAS
, &val
);
672 if (val
&& (strcasecmp(val
, "none") == 0)) {
673 (void) nvlist_remove_all(tprops
, PROP_ALIAS
);
676 /* base64 encode the CHAP secret, if it's changed */
678 (void) nvlist_lookup_string(proplist
, PROP_TARGET_CHAP_SECRET
, &val
);
680 char bsecret
[MAX_BASE64_LEN
];
682 ret
= it_val_pass(PROP_TARGET_CHAP_SECRET
, val
, errs
);
685 (void) memset(bsecret
, 0, MAX_BASE64_LEN
);
687 ret
= iscsi_binary_to_base64_str((uint8_t *)val
,
688 strlen(val
), bsecret
, MAX_BASE64_LEN
);
691 /* replace the value in the nvlist */
692 ret
= nvlist_add_string(tprops
,
693 PROP_TARGET_CHAP_SECRET
, bsecret
);
699 ret
= it_validate_tgtprops(tprops
, errs
);
709 if (tgt
->tgt_properties
) {
710 nvlist_free(tgt
->tgt_properties
);
712 tgt
->tgt_properties
= tprops
;
714 free_empty_errlist(errlist
);
721 * Function: it_tgt_delete()
723 * Delete target represented by 'tgt', where 'tgt' is an existing
724 * it_tgt_structure within the configuration 'cfg'. The target removal
725 * will not take effect until the modified configuration is committed
726 * by calling it_config_commit().
729 * cfg The current iSCSI configuration obtained from
731 * tgt Pointer to an iSCSI target structure
733 * force Set the target to offline before removing it from
734 * the config. If not specified, the operation will
735 * fail if the target is determined to be online.
738 * EBUSY Target is online
741 it_tgt_delete(it_config_t
*cfg
, it_tgt_t
*tgt
, boolean_t force
)
745 it_tgt_t
*prev
= NULL
;
747 stmfTargetProperties props
;
753 ptgt
= cfg
->config_tgt_list
;
754 while (ptgt
!= NULL
) {
755 if (strcasecmp(tgt
->tgt_name
, ptgt
->tgt_name
) == 0) {
759 ptgt
= ptgt
->tgt_next
;
767 * check to see if this target is offline. If it is not,
768 * and the 'force' flag is TRUE, tell STMF to offline it
769 * before removing from the configuration.
771 ret
= stmfDevidFromIscsiName(ptgt
->tgt_name
, &devid
);
772 if (ret
!= STMF_STATUS_SUCCESS
) {
777 ret
= stmfGetTargetProperties(&devid
, &props
);
778 if (ret
== STMF_STATUS_SUCCESS
) {
780 * only other return is STMF_ERROR_NOT_FOUND, which
781 * means we don't have to offline it.
783 if (props
.status
== STMF_TARGET_PORT_ONLINE
) {
787 ret
= stmfOfflineTarget(&devid
);
795 prev
->tgt_next
= ptgt
->tgt_next
;
797 /* first one on the list */
798 cfg
->config_tgt_list
= ptgt
->tgt_next
;
801 ptgt
->tgt_next
= NULL
; /* Only free this target */
803 cfg
->config_tgt_count
--;
810 * Function: it_tgt_free()
812 * Frees an it_tgt_t structure. If tgt_next is not NULL, frees
813 * all structures in the list.
816 it_tgt_free(it_tgt_t
*tgt
)
818 it_tgt_free_cmn(tgt
);
822 * Function: it_tpgt_create()
824 * Allocate and create an it_tpgt_t structure representing a new iSCSI
825 * target portal group tag. The new it_tpgt_t structure is added to the
826 * target tpgt list (tgt_tpgt_list) in the it_tgt_t structure. The new
827 * target portal group tag will not be instantiated until the modified
828 * configuration is committed by calling it_config_commit().
831 * cfg The current iSCSI configuration obtained from
833 * tgt Pointer to the iSCSI target structure associated
834 * with the target portal group tag
835 * tpgt Pointer to a target portal group tag structure
836 * tpg_name The name of the TPG to be associated with this TPGT
837 * tpgt_tag 16-bit numerical identifier for this TPGT. If
838 * tpgt_tag is '0', this function will choose the
839 * tag number. If tpgt_tag is >0, and the requested
840 * tag is determined to be in use, another value
845 * ENOMEM Could not allocate resources
846 * EINVAL Invalid parameter
847 * EEXIST Specified tag name is already used.
848 * E2BIG No available tag numbers
851 it_tpgt_create(it_config_t
*cfg
, it_tgt_t
*tgt
, it_tpgt_t
**tpgt
,
852 char *tpg_name
, uint16_t tpgt_tag
)
854 it_tpgt_t
*ptr
= NULL
;
856 char tagid_used
[MAXTAG
+ 1];
857 uint16_t tagid
= ISCSIT_DEFAULT_TPGT
;
859 if (!cfg
|| !tgt
|| !tpgt
|| !tpg_name
) {
863 (void) memset(&(tagid_used
[0]), 0, sizeof (tagid_used
));
866 * Make sure this name and/or tag isn't already on the list
867 * At the same time, capture all tag ids in use for this target
869 * About tag numbering -- since tag numbers are used by
870 * the iSCSI protocol, we should be careful about reusing
871 * them too quickly. Start with a value greater than the
872 * highest one currently defined. If current == MAXTAG,
873 * just find an unused tag.
875 cfgt
= tgt
->tgt_tpgt_list
;
876 while (cfgt
!= NULL
) {
877 tagid_used
[cfgt
->tpgt_tag
] = 1;
879 if (strcmp(tpg_name
, cfgt
->tpgt_tpg_name
) == 0) {
883 if (cfgt
->tpgt_tag
> tagid
) {
884 tagid
= cfgt
->tpgt_tag
;
887 cfgt
= cfgt
->tpgt_next
;
890 if ((tpgt_tag
> ISCSIT_DEFAULT_TPGT
) && (tpgt_tag
< MAXTAG
) &&
891 (tagid_used
[tpgt_tag
] == 0)) {
892 /* ok to use requested */
894 } else if (tagid
== MAXTAG
) {
896 * The highest value is used, find an available id.
898 tagid
= ISCSIT_DEFAULT_TPGT
+ 1;
899 for (; tagid
< MAXTAG
; tagid
++) {
900 if (tagid_used
[tagid
] == 0) {
904 if (tagid
>= MAXTAG
) {
908 /* next available ID */
912 ptr
= calloc(1, sizeof (it_tpgt_t
));
917 (void) strlcpy(ptr
->tpgt_tpg_name
, tpg_name
,
918 sizeof (ptr
->tpgt_tpg_name
));
919 ptr
->tpgt_generation
= 1;
920 ptr
->tpgt_tag
= tagid
;
922 ptr
->tpgt_next
= tgt
->tgt_tpgt_list
;
923 tgt
->tgt_tpgt_list
= ptr
;
924 tgt
->tgt_tpgt_count
++;
925 tgt
->tgt_generation
++;
933 * Function: it_tpgt_delete()
935 * Delete the target portal group tag represented by 'tpgt', where
936 * 'tpgt' is an existing is_tpgt_t structure within the target 'tgt'.
937 * The target portal group tag removal will not take effect until the
938 * modified configuration is committed by calling it_config_commit().
941 * cfg The current iSCSI configuration obtained from
943 * tgt Pointer to the iSCSI target structure associated
944 * with the target portal group tag
945 * tpgt Pointer to a target portal group tag structure
948 it_tpgt_delete(it_config_t
*cfg
, it_tgt_t
*tgt
, it_tpgt_t
*tpgt
)
951 it_tpgt_t
*prev
= NULL
;
953 if (!cfg
|| !tgt
|| !tpgt
) {
957 ptr
= tgt
->tgt_tpgt_list
;
959 if (ptr
->tpgt_tag
== tpgt
->tpgt_tag
) {
963 ptr
= ptr
->tpgt_next
;
971 prev
->tpgt_next
= ptr
->tpgt_next
;
973 tgt
->tgt_tpgt_list
= ptr
->tpgt_next
;
975 ptr
->tpgt_next
= NULL
;
977 tgt
->tgt_tpgt_count
--;
978 tgt
->tgt_generation
++;
984 * Function: it_tpgt_free()
986 * Deallocates resources of an it_tpgt_t structure. If tpgt->next
987 * is not NULL, frees all members of the list.
990 it_tpgt_free(it_tpgt_t
*tpgt
)
992 it_tpgt_free_cmn(tpgt
);
996 * Function: it_tpg_create()
998 * Allocate and create an it_tpg_t structure representing a new iSCSI
999 * target portal group. The new it_tpg_t structure is added to the global
1000 * tpg list (cfg_tgt_list) in the it_config_t structure. The new target
1001 * portal group will not be instantiated until the modified configuration
1002 * is committed by calling it_config_commit().
1005 * cfg The current iSCSI configuration obtained from
1007 * tpg Pointer to the it_tpg_t structure representing
1008 * the target portal group
1009 * tpg_name Identifier for the target portal group
1010 * portal_ip_port A string containing an appropriatedly formatted
1011 * IP address:port. Both IPv4 and IPv6 addresses are
1012 * permitted. This value becomes the first portal in
1013 * the TPG -- applications can add additional values
1014 * using it_portal_create() before committing the TPG.
1017 * ENOMEM Cannot allocate resources
1018 * EINVAL Invalid parameter
1019 * EEXIST Requested portal in use by another target portal
1023 it_tpg_create(it_config_t
*cfg
, it_tpg_t
**tpg
, char *tpg_name
,
1024 char *portal_ip_port
)
1028 it_portal_t
*portal
= NULL
;
1030 if (!cfg
|| !tpg
|| !tpg_name
|| !portal_ip_port
) {
1036 ptr
= cfg
->config_tpg_list
;
1038 if (strcmp(tpg_name
, ptr
->tpg_name
) == 0) {
1041 ptr
= ptr
->tpg_next
;
1048 ptr
= calloc(1, sizeof (it_tpg_t
));
1053 ptr
->tpg_generation
= 1;
1054 (void) strlcpy(ptr
->tpg_name
, tpg_name
, sizeof (ptr
->tpg_name
));
1056 /* create the portal */
1057 ret
= it_portal_create(cfg
, ptr
, &portal
, portal_ip_port
);
1063 ptr
->tpg_next
= cfg
->config_tpg_list
;
1064 cfg
->config_tpg_list
= ptr
;
1065 cfg
->config_tpg_count
++;
1073 * Function: it_tpg_delete()
1075 * Delete target portal group represented by 'tpg', where 'tpg' is an
1076 * existing it_tpg_t structure within the global configuration 'cfg'.
1077 * The target portal group removal will not take effect until the
1078 * modified configuration is committed by calling it_config_commit().
1081 * cfg The current iSCSI configuration obtained from
1083 * tpg Pointer to the it_tpg_t structure representing
1084 * the target portal group
1085 * force Remove this target portal group even if it's
1086 * associated with one or more targets.
1090 * EINVAL Invalid parameter
1091 * EBUSY Portal group associated with one or more targets.
1094 it_tpg_delete(it_config_t
*cfg
, it_tpg_t
*tpg
, boolean_t force
)
1097 it_tpg_t
*prev
= NULL
;
1106 ptr
= cfg
->config_tpg_list
;
1108 if (strcmp(ptr
->tpg_name
, tpg
->tpg_name
) == 0) {
1112 ptr
= ptr
->tpg_next
;
1120 * See if any targets are using this portal group.
1121 * If there are, and the force flag is not set, fail.
1123 tgt
= cfg
->config_tgt_list
;
1125 tpgt
= tgt
->tgt_tpgt_list
;
1127 ntpgt
= tpgt
->tpgt_next
;
1129 if (strcmp(tpgt
->tpgt_tpg_name
, tpg
->tpg_name
)
1134 it_tpgt_delete(cfg
, tgt
, tpgt
);
1139 tgt
= tgt
->tgt_next
;
1142 /* Now that it's not in use anywhere, remove the TPG */
1144 prev
->tpg_next
= ptr
->tpg_next
;
1146 cfg
->config_tpg_list
= ptr
->tpg_next
;
1148 ptr
->tpg_next
= NULL
;
1150 cfg
->config_tpg_count
--;
1158 * Function: it_tpg_free()
1160 * Deallocates resources associated with an it_tpg_t structure.
1161 * If tpg->next is not NULL, frees all members of the list.
1164 it_tpg_free(it_tpg_t
*tpg
)
1166 it_tpg_free_cmn(tpg
);
1170 * Function: it_portal_create()
1172 * Add an it_portal_t structure presenting a new portal to the specified
1173 * target portal group. The change to the target portal group will not take
1174 * effect until the modified configuration is committed by calling
1175 * it_config_commit().
1178 * cfg The current iSCSI configration obtained from
1180 * tpg Pointer to the it_tpg_t structure representing the
1181 * target portal group
1182 * portal Pointer to the it_portal_t structure representing
1184 * portal_ip_port A string containing an appropriately formatted
1185 * IP address or IP address:port in either IPv4 or
1189 * ENOMEM Could not allocate resources
1190 * EINVAL Invalid parameter
1191 * EEXIST Portal already configured for another portal group
1194 it_portal_create(it_config_t
*cfg
, it_tpg_t
*tpg
, it_portal_t
**portal
,
1195 char *portal_ip_port
)
1197 struct sockaddr_storage sa
;
1199 it_tpg_t
*ctpg
= NULL
;
1201 if (!cfg
|| !tpg
|| !portal
|| !portal_ip_port
) {
1205 if ((it_common_convert_sa(portal_ip_port
, &sa
, ISCSI_LISTEN_PORT
))
1210 /* Check that this portal doesn't appear in any other tag */
1211 ctpg
= cfg
->config_tpg_list
;
1213 ptr
= ctpg
->tpg_portal_list
;
1214 for (; ptr
!= NULL
; ptr
= ptr
->portal_next
) {
1215 if (it_sa_compare(&(ptr
->portal_addr
), &sa
) != 0) {
1220 * Existing in the same group is not an error,
1221 * but don't add it again.
1223 if (strcmp(ctpg
->tpg_name
, tpg
->tpg_name
) == 0) {
1230 ctpg
= ctpg
->tpg_next
;
1233 ptr
= calloc(1, sizeof (it_portal_t
));
1238 (void) memcpy(&(ptr
->portal_addr
), &sa
,
1239 sizeof (struct sockaddr_storage
));
1240 ptr
->portal_next
= tpg
->tpg_portal_list
;
1241 tpg
->tpg_portal_list
= ptr
;
1242 tpg
->tpg_portal_count
++;
1243 tpg
->tpg_generation
++;
1249 * Function: it_portal_delete()
1251 * Remove the specified portal from the specified target portal group.
1252 * The portal removal will not take effect until the modified configuration
1253 * is committed by calling it_config_commit().
1256 * cfg The current iSCSI configration obtained from
1258 * tpg Pointer to the it_tpg_t structure representing the
1259 * target portal group
1260 * portal Pointer to the it_portal_t structure representing
1264 it_portal_delete(it_config_t
*cfg
, it_tpg_t
*tpg
, it_portal_t
*portal
)
1267 it_portal_t
*prev
= NULL
;
1269 if (!cfg
|| !tpg
|| !portal
) {
1273 ptr
= tpg
->tpg_portal_list
;
1275 if (memcmp(&(ptr
->portal_addr
), &(portal
->portal_addr
),
1276 sizeof (ptr
->portal_addr
)) == 0) {
1280 ptr
= ptr
->portal_next
;
1288 prev
->portal_next
= ptr
->portal_next
;
1290 tpg
->tpg_portal_list
= ptr
->portal_next
;
1292 tpg
->tpg_portal_count
--;
1293 tpg
->tpg_generation
++;
1299 * Function: it_ini_create()
1301 * Add an initiator context to the global configuration. The new
1302 * initiator context will not be instantiated until the modified
1303 * configuration is committed by calling it_config_commit().
1306 * cfg The current iSCSI configration obtained from
1308 * ini Pointer to the it_ini_t structure representing
1309 * the initiator context.
1310 * ini_node_name The iSCSI node name of the remote initiator.
1314 * ENOMEM Could not allocate resources
1315 * EINVAL Invalid parameter.
1316 * EFAULT Invalid initiator name
1319 it_ini_create(it_config_t
*cfg
, it_ini_t
**ini
, char *ini_node_name
)
1323 if (!cfg
|| !ini
|| !ini_node_name
) {
1328 * Ensure this is a valid ini name
1330 if (!validate_iscsi_name(ini_node_name
)) {
1334 ptr
= cfg
->config_ini_list
;
1336 if (strcasecmp(ptr
->ini_name
, ini_node_name
) == 0) {
1339 ptr
= ptr
->ini_next
;
1346 ptr
= calloc(1, sizeof (it_ini_t
));
1351 (void) strlcpy(ptr
->ini_name
, ini_node_name
, sizeof (ptr
->ini_name
));
1352 ptr
->ini_generation
= 1;
1353 /* nvlist for props? */
1355 ptr
->ini_next
= cfg
->config_ini_list
;
1356 cfg
->config_ini_list
= ptr
;
1357 cfg
->config_ini_count
++;
1365 * Function: it_ini_setprop()
1367 * Validate the provided property list and set the initiator properties.
1368 * If errlist is not NULL, returns detailed errors for each property
1369 * that failed. The format for errorlist is key = property,
1370 * value = error string.
1374 * ini The initiator being updated.
1375 * proplist nvlist_t containing properties for this target.
1376 * errlist (optional) nvlist_t of errors encountered when
1377 * validating the properties.
1381 * EINVAL Invalid property
1385 it_ini_setprop(it_ini_t
*ini
, nvlist_t
*proplist
, nvlist_t
**errlist
)
1388 nvlist_t
*errs
= NULL
;
1389 nvlist_t
*iprops
= NULL
;
1392 if (!ini
|| !proplist
) {
1397 (void) nvlist_alloc(&errs
, 0, 0);
1402 * copy the existing properties, merge, then validate
1403 * the merged properties before committing them.
1405 if (ini
->ini_properties
) {
1406 ret
= nvlist_dup(ini
->ini_properties
, &iprops
, 0);
1408 ret
= nvlist_alloc(&iprops
, NV_UNIQUE_NAME
, 0);
1415 ret
= nvlist_merge(iprops
, proplist
, 0);
1417 nvlist_free(iprops
);
1421 /* unset chap username if requested */
1422 if ((nvlist_lookup_string(proplist
, PROP_CHAP_USER
, &val
)) == 0) {
1423 if (strcasecmp(val
, "none") == 0) {
1424 (void) nvlist_remove_all(iprops
, PROP_CHAP_USER
);
1428 /* base64 encode the CHAP secret, if it's changed */
1429 if ((nvlist_lookup_string(proplist
, PROP_CHAP_SECRET
, &val
)) == 0) {
1430 char bsecret
[MAX_BASE64_LEN
];
1432 ret
= it_val_pass(PROP_CHAP_SECRET
, val
, errs
);
1434 (void) memset(bsecret
, 0, MAX_BASE64_LEN
);
1436 ret
= iscsi_binary_to_base64_str((uint8_t *)val
,
1437 strlen(val
), bsecret
, MAX_BASE64_LEN
);
1440 /* replace the value in the nvlist */
1441 ret
= nvlist_add_string(iprops
,
1442 PROP_CHAP_SECRET
, bsecret
);
1448 ret
= it_validate_iniprops(iprops
, errs
);
1453 nvlist_free(iprops
);
1458 if (ini
->ini_properties
) {
1459 nvlist_free(ini
->ini_properties
);
1461 ini
->ini_properties
= iprops
;
1463 free_empty_errlist(errlist
);
1469 * Function: it_ini_delete()
1471 * Remove the specified initiator context from the global configuration.
1472 * The removal will not take effect until the modified configuration is
1473 * committed by calling it_config_commit().
1476 * cfg The current iSCSI configration obtained from
1478 * ini Pointer to the it_ini_t structure representing
1479 * the initiator context.
1482 it_ini_delete(it_config_t
*cfg
, it_ini_t
*ini
)
1485 it_ini_t
*prev
= NULL
;
1491 ptr
= cfg
->config_ini_list
;
1493 if (strcasecmp(ptr
->ini_name
, ini
->ini_name
) == 0) {
1497 ptr
= ptr
->ini_next
;
1505 prev
->ini_next
= ptr
->ini_next
;
1507 cfg
->config_ini_list
= ptr
->ini_next
;
1510 ptr
->ini_next
= NULL
; /* Only free this initiator */
1512 cfg
->config_ini_count
--;
1518 * Function: it_ini_free()
1520 * Deallocates resources of an it_ini_t structure. If ini->next is
1521 * not NULL, frees all members of the list.
1524 it_ini_free(it_ini_t
*ini
)
1526 it_ini_free_cmn(ini
);
1530 * Goes through the target property list and validates
1531 * each entry. If errs is non-NULL, will return explicit errors
1532 * for each property that fails validation.
1535 it_validate_tgtprops(nvlist_t
*nvl
, nvlist_t
*errs
)
1538 nvpair_t
*nvp
= NULL
;
1548 while ((nvp
= nvlist_next_nvpair(nvl
, nvp
)) != NULL
) {
1549 name
= nvpair_name(nvp
);
1550 nvtype
= nvpair_type(nvp
);
1557 if (strcmp(name
, PROP_TARGET_CHAP_USER
) == 0) {
1558 if (nvtype
!= DATA_TYPE_STRING
) {
1560 gettext("must be a string value"));
1564 } else if (strcmp(name
, PROP_TARGET_CHAP_SECRET
) == 0) {
1566 * must be between 12 and 255 chars in cleartext.
1567 * will be base64 encoded when it's set.
1569 if (nvtype
== DATA_TYPE_STRING
) {
1570 (void) nvpair_value_string(nvp
, &val
);
1575 gettext("must be a string value"));
1579 } else if (strcmp(name
, PROP_ALIAS
) == 0) {
1580 if (nvtype
!= DATA_TYPE_STRING
) {
1582 gettext("must be a string value"));
1586 } else if (strcmp(name
, PROP_AUTH
) == 0) {
1587 if (nvtype
== DATA_TYPE_STRING
) {
1589 (void) nvpair_value_string(nvp
, &val
);
1594 gettext("must be a string value"));
1598 if ((strcmp(val
, PA_AUTH_NONE
) != 0) &&
1599 (strcmp(val
, PA_AUTH_CHAP
) != 0) &&
1600 (strcmp(val
, PA_AUTH_RADIUS
) != 0) &&
1601 (strcmp(val
, "default") != 0)) {
1602 PROPERR(errs
, val
, gettext(
1603 "must be none, chap, radius or default"));
1608 } else if (strcmp(name
, PROP_OLD_TARGET_NAME
) == 0) {
1611 /* unrecognized property */
1612 PROPERR(errs
, name
, gettext("unrecognized property"));
1621 /* if auth is being set to default, remove from this nvlist */
1622 if (auth
&& (strcmp(auth
, "default") == 0)) {
1623 (void) nvlist_remove_all(nvl
, PROP_AUTH
);
1630 * Goes through the config property list and validates
1631 * each entry. If errs is non-NULL, will return explicit errors
1632 * for each property that fails validation.
1635 it_validate_configprops(nvlist_t
*nvl
, nvlist_t
*errs
)
1638 nvpair_t
*nvp
= NULL
;
1642 struct sockaddr_storage sa
;
1643 boolean_t update_rad_server
= B_FALSE
;
1651 while ((nvp
= nvlist_next_nvpair(nvl
, nvp
)) != NULL
) {
1652 name
= nvpair_name(nvp
);
1653 nvtype
= nvpair_type(nvp
);
1661 /* prefetch string value as we mostly need it */
1662 if (nvtype
== DATA_TYPE_STRING
) {
1663 (void) nvpair_value_string(nvp
, &val
);
1666 if (strcmp(name
, PROP_ALIAS
) == 0) {
1669 gettext("must be a string value"));
1672 } else if (strcmp(name
, PROP_AUTH
) == 0) {
1675 gettext("must be a string value"));
1680 if ((strcmp(val
, PA_AUTH_NONE
) != 0) &&
1681 (strcmp(val
, PA_AUTH_CHAP
) != 0) &&
1682 (strcmp(val
, PA_AUTH_RADIUS
) != 0)) {
1683 PROPERR(errs
, PROP_AUTH
,
1684 gettext("must be none, chap or radius"));
1690 } else if (strcmp(name
, PROP_ISNS_ENABLED
) == 0) {
1691 if (nvtype
!= DATA_TYPE_BOOLEAN_VALUE
) {
1693 gettext("must be a boolean value"));
1696 } else if (strcmp(name
, PROP_ISNS_SERVER
) == 0) {
1698 uint32_t acount
= 0;
1700 (void) nvlist_lookup_string_array(nvl
, name
,
1703 while (acount
> 0) {
1704 if (strcasecmp(arr
[acount
- 1], "none") == 0) {
1707 if ((it_common_convert_sa(arr
[acount
- 1],
1709 PROPERR(errs
, arr
[acount
- 1],
1710 gettext("invalid address"));
1716 } else if (strcmp(name
, PROP_RADIUS_SECRET
) == 0) {
1719 gettext("must be a string value"));
1723 } else if (strcmp(name
, PROP_RADIUS_SERVER
) == 0) {
1724 struct sockaddr_storage sa
;
1727 gettext("must be a string value"));
1732 if ((it_common_convert_sa(val
, &sa
,
1733 DEFAULT_RADIUS_PORT
)) == NULL
) {
1735 gettext("invalid address"));
1739 * rewrite this property to ensure port
1743 if (sockaddr_to_str(&sa
, &rad_server
) == 0) {
1744 update_rad_server
= B_TRUE
;
1748 /* unrecognized property */
1749 PROPERR(errs
, name
, gettext("unrecognized property"));
1755 * If we successfully reformatted the radius server to add the port
1756 * number then update the nvlist
1758 if (update_rad_server
) {
1759 (void) nvlist_add_string(nvl
, PROP_RADIUS_SERVER
, rad_server
);
1764 * if auth = radius, ensure radius server & secret are set.
1767 if (strcmp(auth
, PA_AUTH_RADIUS
) == 0) {
1768 /* need server & secret for radius */
1769 if (!nvlist_exists(nvl
, PROP_RADIUS_SERVER
)) {
1770 PROPERR(errs
, PROP_RADIUS_SERVER
,
1771 gettext("missing required property"));
1774 if (!nvlist_exists(nvl
, PROP_RADIUS_SECRET
)) {
1775 PROPERR(errs
, PROP_RADIUS_SECRET
,
1776 gettext("missing required property"));
1790 * Goes through the ini property list and validates
1791 * each entry. If errs is non-NULL, will return explicit errors
1792 * for each property that fails validation.
1795 it_validate_iniprops(nvlist_t
*nvl
, nvlist_t
*errs
)
1798 nvpair_t
*nvp
= NULL
;
1807 while ((nvp
= nvlist_next_nvpair(nvl
, nvp
)) != NULL
) {
1808 name
= nvpair_name(nvp
);
1809 nvtype
= nvpair_type(nvp
);
1815 if (strcmp(name
, PROP_CHAP_USER
) == 0) {
1816 if (nvtype
!= DATA_TYPE_STRING
) {
1818 gettext("must be a string value"));
1822 } else if (strcmp(name
, PROP_CHAP_SECRET
) == 0) {
1824 * must be between 12 and 255 chars in cleartext.
1825 * will be base64 encoded when it's set.
1827 if (nvtype
== DATA_TYPE_STRING
) {
1829 (void) nvpair_value_string(nvp
, &val
);
1834 gettext("must be a string value"));
1839 /* unrecognized property */
1840 PROPERR(errs
, name
, gettext("unrecognized property"));
1853 it_iqn_generate(char *iqn_buf
, int iqn_buf_len
, char *opt_iqn_suffix
)
1857 char id_str
[UUID_PRINTABLE_STRING_LENGTH
];
1859 uuid_generate_random(id
);
1860 uuid_unparse(id
, id_str
);
1862 if (opt_iqn_suffix
) {
1863 ret
= snprintf(iqn_buf
, iqn_buf_len
, DEFAULT_IQN
1864 "%02d:%s.%s", TARGET_NAME_VERS
, id_str
, opt_iqn_suffix
);
1866 ret
= snprintf(iqn_buf
, iqn_buf_len
, DEFAULT_IQN
1867 "%02d:%s", TARGET_NAME_VERS
, id_str
);
1870 if (ret
> iqn_buf_len
) {
1878 it_val_pass(char *name
, char *val
, nvlist_t
*e
)
1882 if (!name
|| !val
) {
1887 * must be at least 12 chars and less than 256 chars cleartext.
1892 * Since we will be automatically encoding secrets we don't really
1893 * need the prefix anymore.
1896 PROPERR(e
, name
, gettext("secret too short"));
1897 } else if (sz
> 255) {
1898 PROPERR(e
, name
, gettext("secret too long"));
1908 * Function: validate_iscsi_name()
1910 * Ensures the passed-in string is a valid IQN or EUI iSCSI name
1914 validate_iscsi_name(char *in_name
)
1920 if (in_name
== NULL
) {
1924 in_len
= strlen(in_name
);
1929 if (IS_IQN_NAME(in_name
)) {
1931 * IQN names are iqn.yyyy-mm.<xxx>
1933 if ((!isdigit(in_name
[4])) ||
1934 (!isdigit(in_name
[5])) ||
1935 (!isdigit(in_name
[6])) ||
1936 (!isdigit(in_name
[7])) ||
1937 (in_name
[8] != '-') ||
1938 (!isdigit(in_name
[9])) ||
1939 (!isdigit(in_name
[10])) ||
1940 (in_name
[11] != '.')) {
1944 (void) strncpy(month
, &(in_name
[9]), 2);
1948 if ((i
< 0) || (i
> 12)) {
1953 * RFC 3722: if using only ASCII chars, only the following
1954 * chars are allowed: dash, dot, colon, lower case a-z, 0-9.
1955 * We allow upper case names, which should be folded
1956 * to lower case names later.
1958 for (i
= 12; i
< in_len
; i
++) {
1959 char c
= in_name
[i
];
1961 if ((c
!= '-') && (c
!= '.') && (c
!= ':') &&
1962 !isalpha(c
) && !isdigit(c
)) {
1967 /* Finally, validate the overall length, in wide chars */
1968 in_len
= mbstowcs(NULL
, in_name
, 0);
1969 if (in_len
> ISCSI_NAME_LEN_MAX
) {
1972 } else if (IS_EUI_NAME(in_name
)) {
1974 * EUI names are "eui." + 16 hex chars
1980 for (i
= 4; i
< in_len
; i
++) {
1981 if (!isxdigit(in_name
[i
])) {
1993 is_iscsit_enabled(void)
1997 state
= smf_get_state(ISCSIT_FMRI
);
1998 if (state
!= NULL
) {
1999 if (strcmp(state
, SCF_STATE_STRING_ONLINE
) == 0) {
2010 * Function: canonical_iscsi_name()
2012 * Fold the iqn iscsi name to lower-case and the EUI-64 identifier of
2013 * the eui iscsi name to upper-case.
2014 * Ensures the passed-in string is a valid IQN or EUI iSCSI name
2017 canonical_iscsi_name(char *tgt
)
2019 if (IS_IQN_NAME(tgt
)) {
2020 /* lowercase iqn names */
2023 /* uppercase EUI-64 identifier */
2029 * Fold an iqn name to lower-case.
2043 * Fold the EUI-64 identifier of a eui name to upper-case.
2058 free_empty_errlist(nvlist_t
**errlist
)
2060 if (errlist
!= NULL
&& *errlist
!= NULL
) {
2061 assert(fnvlist_num_pairs(*errlist
) == 0);
2062 nvlist_free(*errlist
);