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
;
155 * Function: it_config_commit()
157 * Informs the iscsit service that the configuration has changed and
158 * commits the new configuration to persistent store by calling
159 * stmfSetProviderData. This function can be called multiple times
160 * during a configuration sequence if necessary.
163 * cfg A C representation of the current iSCSI configuration
167 * ENOMEM Could not allocate resources
168 * EINVAL Invalid it_config_t structure
170 * TBD could not save config to STMF
173 it_config_commit(it_config_t
*cfg
)
176 nvlist_t
*cfgnv
= NULL
;
177 char *packednv
= NULL
;
180 iscsit_ioc_set_config_t iop
;
187 ret
= it_config_to_nv(cfg
, &cfgnv
);
189 ret
= nvlist_size(cfgnv
, &pnv_size
, NV_ENCODE_NATIVE
);
193 * If the iscsit service is enabled, send the changes to the
194 * kernel first. Kernel will be the final sanity check before
195 * the config is saved persistently.
197 * This somewhat leaves open the simultaneous-change hole
198 * that STMF was trying to solve, but is a better sanity
199 * check and allows for graceful handling of target renames.
201 if ((ret
== 0) && is_iscsit_enabled()) {
202 packednv
= malloc(pnv_size
);
206 ret
= nvlist_pack(cfgnv
, &packednv
, &pnv_size
,
207 NV_ENCODE_NATIVE
, 0);
211 iscsit_fd
= open(ISCSIT_NODE
, O_RDWR
|O_EXCL
);
212 if (iscsit_fd
!= -1) {
213 iop
.set_cfg_vers
= ISCSIT_API_VERS0
;
214 iop
.set_cfg_pnvlist
= packednv
;
215 iop
.set_cfg_pnvlist_len
= pnv_size
;
216 if ((ioctl(iscsit_fd
, ISCSIT_IOC_SET_CONFIG
,
221 (void) close(iscsit_fd
);
227 if (packednv
!= NULL
) {
233 * Before saving the config persistently, remove any
234 * PROP_OLD_TARGET_NAME entries. This is only interesting to
235 * the active service.
238 boolean_t changed
= B_FALSE
;
240 tgtp
= cfg
->config_tgt_list
;
241 for (; tgtp
!= NULL
; tgtp
= tgtp
->tgt_next
) {
242 if (!tgtp
->tgt_properties
) {
245 if (nvlist_exists(tgtp
->tgt_properties
,
246 PROP_OLD_TARGET_NAME
)) {
247 (void) nvlist_remove_all(tgtp
->tgt_properties
,
248 PROP_OLD_TARGET_NAME
);
254 /* rebuild the config nvlist */
257 ret
= it_config_to_nv(cfg
, &cfgnv
);
262 * stmfGetProviderDataProt() checks to ensure
263 * that the config data hasn't changed since we fetched it.
265 * The kernel now has a version we need to save persistently.
266 * CLI will 'do the right thing' and warn the user if it
267 * gets STMF_ERROR_PROV_DATA_STALE. We'll try once to revert
268 * the kernel to the persistently saved data, but ultimately,
269 * it's up to the administrator to validate things are as they
273 ret
= stmfSetProviderDataProt(ISCSIT_MODNAME
, cfgnv
,
274 STMF_PORT_PROVIDER_TYPE
, &(cfg
->stmf_token
));
276 if (ret
== STMF_STATUS_SUCCESS
) {
278 } else if (ret
== STMF_ERROR_NOMEM
) {
280 } else if (ret
== STMF_ERROR_PROV_DATA_STALE
) {
282 it_config_t
*rcfg
= NULL
;
284 st
= it_config_load(&rcfg
);
286 (void) it_config_commit(rcfg
);
287 it_config_free(rcfg
);
298 * Function: it_config_setprop()
300 * Validate the provided property list and set the global properties
301 * for iSCSI Target. If errlist is not NULL, returns detailed
302 * errors for each property that failed. The format for errorlist
303 * is key = property, value = error string.
307 * cfg The current iSCSI configuration obtained from
309 * proplist nvlist_t containing properties for this target.
310 * errlist (optional) nvlist_t of errors encountered when
311 * validating the properties.
315 * EINVAL Invalid property
319 it_config_setprop(it_config_t
*cfg
, nvlist_t
*proplist
, nvlist_t
**errlist
)
322 nvlist_t
*errs
= NULL
;
323 it_portal_t
*isns
= NULL
;
324 it_portal_t
*pnext
= NULL
;
325 it_portal_t
*newisnslist
= NULL
;
329 nvlist_t
*cprops
= NULL
;
332 if (!cfg
|| !proplist
) {
337 (void) nvlist_alloc(&errs
, 0, 0);
342 * copy the existing properties, merge, then validate
343 * the merged properties before committing them.
345 if (cfg
->config_global_properties
) {
346 ret
= nvlist_dup(cfg
->config_global_properties
, &cprops
, 0);
348 ret
= nvlist_alloc(&cprops
, NV_UNIQUE_NAME
, 0);
355 ret
= nvlist_merge(cprops
, proplist
, 0);
362 * base64 encode the radius secret, if it's changed.
365 (void) nvlist_lookup_string(proplist
, PROP_RADIUS_SECRET
, &val
);
367 char bsecret
[MAX_BASE64_LEN
];
369 ret
= it_val_pass(PROP_RADIUS_SECRET
, val
, errs
);
372 (void) memset(bsecret
, 0, MAX_BASE64_LEN
);
374 ret
= iscsi_binary_to_base64_str((uint8_t *)val
,
375 strlen(val
), bsecret
, MAX_BASE64_LEN
);
378 /* replace the value in the nvlist */
379 ret
= nvlist_add_string(cprops
,
380 PROP_RADIUS_SECRET
, bsecret
);
390 /* see if we need to remove the radius server setting */
392 (void) nvlist_lookup_string(cprops
, PROP_RADIUS_SERVER
, &val
);
393 if (val
&& (strcasecmp(val
, "none") == 0)) {
394 (void) nvlist_remove_all(cprops
, PROP_RADIUS_SERVER
);
397 /* and/or remove the alias */
399 (void) nvlist_lookup_string(cprops
, PROP_ALIAS
, &val
);
400 if (val
&& (strcasecmp(val
, "none") == 0)) {
401 (void) nvlist_remove_all(cprops
, PROP_ALIAS
);
404 ret
= it_validate_configprops(cprops
, errs
);
411 * Update iSNS server list, if exists in provided property list.
413 ret
= nvlist_lookup_string_array(proplist
, PROP_ISNS_SERVER
,
417 /* special case: if "none", remove all defined */
418 if (strcasecmp(arr
[0], "none") != 0) {
419 ret
= it_array_to_portallist(arr
, count
,
420 ISNS_DEFAULT_SERVER_PORT
, &newisnslist
, &newcount
);
424 (void) nvlist_remove_all(cprops
, PROP_ISNS_SERVER
);
428 isns
= cfg
->config_isns_svr_list
;
430 pnext
= isns
->portal_next
;
435 cfg
->config_isns_svr_list
= newisnslist
;
436 cfg
->config_isns_svr_count
= newcount
;
439 * Replace the array in the nvlist to ensure
440 * duplicates are properly removed & port numbers
447 newarray
= malloc(sizeof (char *) * newcount
);
448 if (newarray
== NULL
) {
451 for (isns
= newisnslist
; isns
!= NULL
;
452 isns
= isns
->portal_next
) {
453 (void) sockaddr_to_str(
454 &(isns
->portal_addr
),
457 (void) nvlist_add_string_array(cprops
,
458 PROP_ISNS_SERVER
, newarray
,
461 for (i
= 0; i
< newcount
; i
++) {
470 } else if (ret
== ENOENT
) {
476 /* replace the global properties list */
477 nvlist_free(cfg
->config_global_properties
);
478 cfg
->config_global_properties
= cprops
;
484 free_empty_errlist(errlist
);
490 * Function: it_config_free()
492 * Free any resources associated with the it_config_t structure.
495 * cfg A C representation of the current iSCSI configuration
498 it_config_free(it_config_t
*cfg
)
500 it_config_free_cmn(cfg
);
504 * Function: it_tgt_create()
506 * Allocate and create an it_tgt_t structure representing a new iSCSI
507 * target node. If tgt_name is NULL, then a unique target node name will
508 * be generated automatically. Otherwise, the value of tgt_name will be
509 * used as the target node name. The new it_tgt_t structure is added to
510 * the target list (cfg_tgt_list) in the configuration structure, and the
511 * new target will not be instantiated until the modified configuration
512 * is committed by calling it_config_commit().
515 * cfg The current iSCSI configuration obtained from
517 * tgt Pointer to an iSCSI target structure
518 * tgt_name The target node name for the target to be created.
519 * The name must be in either IQN or EUI format. If
520 * this value is NULL, a node name will be generated
521 * automatically in IQN format.
525 * ENOMEM Could not allocated resources
526 * EINVAL Invalid parameter
527 * EFAULT Invalid iSCSI name specified
528 * E2BIG Too many already exist
531 it_tgt_create(it_config_t
*cfg
, it_tgt_t
**tgt
, char *tgt_name
)
537 char buf
[ISCSI_NAME_LEN_MAX
+ 1];
544 /* generate a name */
545 ret
= it_iqn_generate(buf
, sizeof (buf
), NULL
);
550 /* validate the passed-in name */
551 if (!validate_iscsi_name(tgt_name
)) {
554 (void) strlcpy(buf
, tgt_name
, sizeof (buf
));
555 canonical_iscsi_name(buf
);
559 /* make sure this name isn't already on the list */
560 cfgtgt
= cfg
->config_tgt_list
;
561 while (cfgtgt
!= NULL
) {
562 if (strcasecmp(namep
, cfgtgt
->tgt_name
) == 0) {
565 cfgtgt
= cfgtgt
->tgt_next
;
568 /* Too many targets? */
569 if (cfg
->config_tgt_count
>= MAX_TARGETS
) {
573 ptr
= calloc(1, sizeof (it_tgt_t
));
578 (void) strlcpy(ptr
->tgt_name
, namep
, sizeof (ptr
->tgt_name
));
579 ptr
->tgt_generation
= 1;
580 ptr
->tgt_next
= cfg
->config_tgt_list
;
581 cfg
->config_tgt_list
= ptr
;
582 cfg
->config_tgt_count
++;
590 * Function: it_tgt_setprop()
592 * Validate the provided property list and set the properties for
593 * the specified target. If errlist is not NULL, returns detailed
594 * errors for each property that failed. The format for errorlist
595 * is key = property, value = error string.
599 * cfg The current iSCSI configuration obtained from
601 * tgt Pointer to an iSCSI target structure
602 * proplist nvlist_t containing properties for this target.
603 * errlist (optional) nvlist_t of errors encountered when
604 * validating the properties.
608 * EINVAL Invalid property
612 it_tgt_setprop(it_config_t
*cfg
, it_tgt_t
*tgt
, nvlist_t
*proplist
,
616 nvlist_t
*errs
= NULL
;
617 nvlist_t
*tprops
= NULL
;
620 if (!cfg
|| !tgt
|| !proplist
) {
624 /* verify the target name in case the target node is renamed */
625 if (!validate_iscsi_name(tgt
->tgt_name
)) {
628 canonical_iscsi_name(tgt
->tgt_name
);
631 (void) nvlist_alloc(&errs
, 0, 0);
636 * copy the existing properties, merge, then validate
637 * the merged properties before committing them.
639 if (tgt
->tgt_properties
) {
640 ret
= nvlist_dup(tgt
->tgt_properties
, &tprops
, 0);
642 ret
= nvlist_alloc(&tprops
, NV_UNIQUE_NAME
, 0);
649 ret
= nvlist_merge(tprops
, proplist
, 0);
655 /* unset chap username or alias if requested */
657 (void) nvlist_lookup_string(proplist
, PROP_TARGET_CHAP_USER
, &val
);
658 if (val
&& (strcasecmp(val
, "none") == 0)) {
659 (void) nvlist_remove_all(tprops
, PROP_TARGET_CHAP_USER
);
663 (void) nvlist_lookup_string(proplist
, PROP_ALIAS
, &val
);
664 if (val
&& (strcasecmp(val
, "none") == 0)) {
665 (void) nvlist_remove_all(tprops
, PROP_ALIAS
);
668 /* base64 encode the CHAP secret, if it's changed */
670 (void) nvlist_lookup_string(proplist
, PROP_TARGET_CHAP_SECRET
, &val
);
672 char bsecret
[MAX_BASE64_LEN
];
674 ret
= it_val_pass(PROP_TARGET_CHAP_SECRET
, val
, errs
);
677 (void) memset(bsecret
, 0, MAX_BASE64_LEN
);
679 ret
= iscsi_binary_to_base64_str((uint8_t *)val
,
680 strlen(val
), bsecret
, MAX_BASE64_LEN
);
683 /* replace the value in the nvlist */
684 ret
= nvlist_add_string(tprops
,
685 PROP_TARGET_CHAP_SECRET
, bsecret
);
691 ret
= it_validate_tgtprops(tprops
, errs
);
699 nvlist_free(tgt
->tgt_properties
);
700 tgt
->tgt_properties
= tprops
;
702 free_empty_errlist(errlist
);
709 * Function: it_tgt_delete()
711 * Delete target represented by 'tgt', where 'tgt' is an existing
712 * it_tgt_structure within the configuration 'cfg'. The target removal
713 * will not take effect until the modified configuration is committed
714 * by calling it_config_commit().
717 * cfg The current iSCSI configuration obtained from
719 * tgt Pointer to an iSCSI target structure
721 * force Set the target to offline before removing it from
722 * the config. If not specified, the operation will
723 * fail if the target is determined to be online.
726 * EBUSY Target is online
729 it_tgt_delete(it_config_t
*cfg
, it_tgt_t
*tgt
, boolean_t force
)
733 it_tgt_t
*prev
= NULL
;
735 stmfTargetProperties props
;
741 ptgt
= cfg
->config_tgt_list
;
742 while (ptgt
!= NULL
) {
743 if (strcasecmp(tgt
->tgt_name
, ptgt
->tgt_name
) == 0) {
747 ptgt
= ptgt
->tgt_next
;
755 * check to see if this target is offline. If it is not,
756 * and the 'force' flag is TRUE, tell STMF to offline it
757 * before removing from the configuration.
759 ret
= stmfDevidFromIscsiName(ptgt
->tgt_name
, &devid
);
760 if (ret
!= STMF_STATUS_SUCCESS
) {
765 ret
= stmfGetTargetProperties(&devid
, &props
);
766 if (ret
== STMF_STATUS_SUCCESS
) {
768 * only other return is STMF_ERROR_NOT_FOUND, which
769 * means we don't have to offline it.
771 if (props
.status
== STMF_TARGET_PORT_ONLINE
) {
775 ret
= stmfOfflineTarget(&devid
);
783 prev
->tgt_next
= ptgt
->tgt_next
;
785 /* first one on the list */
786 cfg
->config_tgt_list
= ptgt
->tgt_next
;
789 ptgt
->tgt_next
= NULL
; /* Only free this target */
791 cfg
->config_tgt_count
--;
798 * Function: it_tgt_free()
800 * Frees an it_tgt_t structure. If tgt_next is not NULL, frees
801 * all structures in the list.
804 it_tgt_free(it_tgt_t
*tgt
)
806 it_tgt_free_cmn(tgt
);
810 * Function: it_tpgt_create()
812 * Allocate and create an it_tpgt_t structure representing a new iSCSI
813 * target portal group tag. The new it_tpgt_t structure is added to the
814 * target tpgt list (tgt_tpgt_list) in the it_tgt_t structure. The new
815 * target portal group tag will not be instantiated until the modified
816 * configuration is committed by calling it_config_commit().
819 * cfg The current iSCSI configuration obtained from
821 * tgt Pointer to the iSCSI target structure associated
822 * with the target portal group tag
823 * tpgt Pointer to a target portal group tag structure
824 * tpg_name The name of the TPG to be associated with this TPGT
825 * tpgt_tag 16-bit numerical identifier for this TPGT. If
826 * tpgt_tag is '0', this function will choose the
827 * tag number. If tpgt_tag is >0, and the requested
828 * tag is determined to be in use, another value
833 * ENOMEM Could not allocate resources
834 * EINVAL Invalid parameter
835 * EEXIST Specified tag name is already used.
836 * E2BIG No available tag numbers
839 it_tpgt_create(it_config_t
*cfg
, it_tgt_t
*tgt
, it_tpgt_t
**tpgt
,
840 char *tpg_name
, uint16_t tpgt_tag
)
842 it_tpgt_t
*ptr
= NULL
;
844 char tagid_used
[MAXTAG
+ 1];
845 uint16_t tagid
= ISCSIT_DEFAULT_TPGT
;
847 if (!cfg
|| !tgt
|| !tpgt
|| !tpg_name
) {
851 (void) memset(&(tagid_used
[0]), 0, sizeof (tagid_used
));
854 * Make sure this name and/or tag isn't already on the list
855 * At the same time, capture all tag ids in use for this target
857 * About tag numbering -- since tag numbers are used by
858 * the iSCSI protocol, we should be careful about reusing
859 * them too quickly. Start with a value greater than the
860 * highest one currently defined. If current == MAXTAG,
861 * just find an unused tag.
863 cfgt
= tgt
->tgt_tpgt_list
;
864 while (cfgt
!= NULL
) {
865 tagid_used
[cfgt
->tpgt_tag
] = 1;
867 if (strcmp(tpg_name
, cfgt
->tpgt_tpg_name
) == 0) {
871 if (cfgt
->tpgt_tag
> tagid
) {
872 tagid
= cfgt
->tpgt_tag
;
875 cfgt
= cfgt
->tpgt_next
;
878 if ((tpgt_tag
> ISCSIT_DEFAULT_TPGT
) && (tpgt_tag
< MAXTAG
) &&
879 (tagid_used
[tpgt_tag
] == 0)) {
880 /* ok to use requested */
882 } else if (tagid
== MAXTAG
) {
884 * The highest value is used, find an available id.
886 tagid
= ISCSIT_DEFAULT_TPGT
+ 1;
887 for (; tagid
< MAXTAG
; tagid
++) {
888 if (tagid_used
[tagid
] == 0) {
892 if (tagid
>= MAXTAG
) {
896 /* next available ID */
900 ptr
= calloc(1, sizeof (it_tpgt_t
));
905 (void) strlcpy(ptr
->tpgt_tpg_name
, tpg_name
,
906 sizeof (ptr
->tpgt_tpg_name
));
907 ptr
->tpgt_generation
= 1;
908 ptr
->tpgt_tag
= tagid
;
910 ptr
->tpgt_next
= tgt
->tgt_tpgt_list
;
911 tgt
->tgt_tpgt_list
= ptr
;
912 tgt
->tgt_tpgt_count
++;
913 tgt
->tgt_generation
++;
921 * Function: it_tpgt_delete()
923 * Delete the target portal group tag represented by 'tpgt', where
924 * 'tpgt' is an existing is_tpgt_t structure within the target 'tgt'.
925 * The target portal group tag removal will not take effect until the
926 * modified configuration is committed by calling it_config_commit().
929 * cfg The current iSCSI configuration obtained from
931 * tgt Pointer to the iSCSI target structure associated
932 * with the target portal group tag
933 * tpgt Pointer to a target portal group tag structure
936 it_tpgt_delete(it_config_t
*cfg
, it_tgt_t
*tgt
, it_tpgt_t
*tpgt
)
939 it_tpgt_t
*prev
= NULL
;
941 if (!cfg
|| !tgt
|| !tpgt
) {
945 ptr
= tgt
->tgt_tpgt_list
;
947 if (ptr
->tpgt_tag
== tpgt
->tpgt_tag
) {
951 ptr
= ptr
->tpgt_next
;
959 prev
->tpgt_next
= ptr
->tpgt_next
;
961 tgt
->tgt_tpgt_list
= ptr
->tpgt_next
;
963 ptr
->tpgt_next
= NULL
;
965 tgt
->tgt_tpgt_count
--;
966 tgt
->tgt_generation
++;
972 * Function: it_tpgt_free()
974 * Deallocates resources of an it_tpgt_t structure. If tpgt->next
975 * is not NULL, frees all members of the list.
978 it_tpgt_free(it_tpgt_t
*tpgt
)
980 it_tpgt_free_cmn(tpgt
);
984 * Function: it_tpg_create()
986 * Allocate and create an it_tpg_t structure representing a new iSCSI
987 * target portal group. The new it_tpg_t structure is added to the global
988 * tpg list (cfg_tgt_list) in the it_config_t structure. The new target
989 * portal group will not be instantiated until the modified configuration
990 * is committed by calling it_config_commit().
993 * cfg The current iSCSI configuration obtained from
995 * tpg Pointer to the it_tpg_t structure representing
996 * the target portal group
997 * tpg_name Identifier for the target portal group
998 * portal_ip_port A string containing an appropriatedly formatted
999 * IP address:port. Both IPv4 and IPv6 addresses are
1000 * permitted. This value becomes the first portal in
1001 * the TPG -- applications can add additional values
1002 * using it_portal_create() before committing the TPG.
1005 * ENOMEM Cannot allocate resources
1006 * EINVAL Invalid parameter
1007 * EEXIST Requested portal in use by another target portal
1011 it_tpg_create(it_config_t
*cfg
, it_tpg_t
**tpg
, char *tpg_name
,
1012 char *portal_ip_port
)
1016 it_portal_t
*portal
= NULL
;
1018 if (!cfg
|| !tpg
|| !tpg_name
|| !portal_ip_port
) {
1024 ptr
= cfg
->config_tpg_list
;
1026 if (strcmp(tpg_name
, ptr
->tpg_name
) == 0) {
1029 ptr
= ptr
->tpg_next
;
1036 ptr
= calloc(1, sizeof (it_tpg_t
));
1041 ptr
->tpg_generation
= 1;
1042 (void) strlcpy(ptr
->tpg_name
, tpg_name
, sizeof (ptr
->tpg_name
));
1044 /* create the portal */
1045 ret
= it_portal_create(cfg
, ptr
, &portal
, portal_ip_port
);
1051 ptr
->tpg_next
= cfg
->config_tpg_list
;
1052 cfg
->config_tpg_list
= ptr
;
1053 cfg
->config_tpg_count
++;
1061 * Function: it_tpg_delete()
1063 * Delete target portal group represented by 'tpg', where 'tpg' is an
1064 * existing it_tpg_t structure within the global configuration 'cfg'.
1065 * The target portal group removal will not take effect until the
1066 * modified configuration is committed by calling it_config_commit().
1069 * cfg The current iSCSI configuration obtained from
1071 * tpg Pointer to the it_tpg_t structure representing
1072 * the target portal group
1073 * force Remove this target portal group even if it's
1074 * associated with one or more targets.
1078 * EINVAL Invalid parameter
1079 * EBUSY Portal group associated with one or more targets.
1082 it_tpg_delete(it_config_t
*cfg
, it_tpg_t
*tpg
, boolean_t force
)
1085 it_tpg_t
*prev
= NULL
;
1094 ptr
= cfg
->config_tpg_list
;
1096 if (strcmp(ptr
->tpg_name
, tpg
->tpg_name
) == 0) {
1100 ptr
= ptr
->tpg_next
;
1108 * See if any targets are using this portal group.
1109 * If there are, and the force flag is not set, fail.
1111 tgt
= cfg
->config_tgt_list
;
1113 tpgt
= tgt
->tgt_tpgt_list
;
1115 ntpgt
= tpgt
->tpgt_next
;
1117 if (strcmp(tpgt
->tpgt_tpg_name
, tpg
->tpg_name
)
1122 it_tpgt_delete(cfg
, tgt
, tpgt
);
1127 tgt
= tgt
->tgt_next
;
1130 /* Now that it's not in use anywhere, remove the TPG */
1132 prev
->tpg_next
= ptr
->tpg_next
;
1134 cfg
->config_tpg_list
= ptr
->tpg_next
;
1136 ptr
->tpg_next
= NULL
;
1138 cfg
->config_tpg_count
--;
1146 * Function: it_tpg_free()
1148 * Deallocates resources associated with an it_tpg_t structure.
1149 * If tpg->next is not NULL, frees all members of the list.
1152 it_tpg_free(it_tpg_t
*tpg
)
1154 it_tpg_free_cmn(tpg
);
1158 * Function: it_portal_create()
1160 * Add an it_portal_t structure presenting a new portal to the specified
1161 * target portal group. The change to the target portal group will not take
1162 * effect until the modified configuration is committed by calling
1163 * it_config_commit().
1166 * cfg The current iSCSI configration obtained from
1168 * tpg Pointer to the it_tpg_t structure representing the
1169 * target portal group
1170 * portal Pointer to the it_portal_t structure representing
1172 * portal_ip_port A string containing an appropriately formatted
1173 * IP address or IP address:port in either IPv4 or
1177 * ENOMEM Could not allocate resources
1178 * EINVAL Invalid parameter
1179 * EEXIST Portal already configured for another portal group
1182 it_portal_create(it_config_t
*cfg
, it_tpg_t
*tpg
, it_portal_t
**portal
,
1183 char *portal_ip_port
)
1185 struct sockaddr_storage sa
;
1187 it_tpg_t
*ctpg
= NULL
;
1189 if (!cfg
|| !tpg
|| !portal
|| !portal_ip_port
) {
1193 if ((it_common_convert_sa(portal_ip_port
, &sa
, ISCSI_LISTEN_PORT
))
1198 /* Check that this portal doesn't appear in any other tag */
1199 ctpg
= cfg
->config_tpg_list
;
1201 ptr
= ctpg
->tpg_portal_list
;
1202 for (; ptr
!= NULL
; ptr
= ptr
->portal_next
) {
1203 if (it_sa_compare(&(ptr
->portal_addr
), &sa
) != 0) {
1208 * Existing in the same group is not an error,
1209 * but don't add it again.
1211 if (strcmp(ctpg
->tpg_name
, tpg
->tpg_name
) == 0) {
1218 ctpg
= ctpg
->tpg_next
;
1221 ptr
= calloc(1, sizeof (it_portal_t
));
1226 (void) memcpy(&(ptr
->portal_addr
), &sa
,
1227 sizeof (struct sockaddr_storage
));
1228 ptr
->portal_next
= tpg
->tpg_portal_list
;
1229 tpg
->tpg_portal_list
= ptr
;
1230 tpg
->tpg_portal_count
++;
1231 tpg
->tpg_generation
++;
1237 * Function: it_portal_delete()
1239 * Remove the specified portal from the specified target portal group.
1240 * The portal removal will not take effect until the modified configuration
1241 * is committed by calling it_config_commit().
1244 * cfg The current iSCSI configration obtained from
1246 * tpg Pointer to the it_tpg_t structure representing the
1247 * target portal group
1248 * portal Pointer to the it_portal_t structure representing
1252 it_portal_delete(it_config_t
*cfg
, it_tpg_t
*tpg
, it_portal_t
*portal
)
1255 it_portal_t
*prev
= NULL
;
1257 if (!cfg
|| !tpg
|| !portal
) {
1261 ptr
= tpg
->tpg_portal_list
;
1263 if (memcmp(&(ptr
->portal_addr
), &(portal
->portal_addr
),
1264 sizeof (ptr
->portal_addr
)) == 0) {
1268 ptr
= ptr
->portal_next
;
1276 prev
->portal_next
= ptr
->portal_next
;
1278 tpg
->tpg_portal_list
= ptr
->portal_next
;
1280 tpg
->tpg_portal_count
--;
1281 tpg
->tpg_generation
++;
1287 * Function: it_ini_create()
1289 * Add an initiator context to the global configuration. The new
1290 * initiator context will not be instantiated until the modified
1291 * configuration is committed by calling it_config_commit().
1294 * cfg The current iSCSI configration obtained from
1296 * ini Pointer to the it_ini_t structure representing
1297 * the initiator context.
1298 * ini_node_name The iSCSI node name of the remote initiator.
1302 * ENOMEM Could not allocate resources
1303 * EINVAL Invalid parameter.
1304 * EFAULT Invalid initiator name
1307 it_ini_create(it_config_t
*cfg
, it_ini_t
**ini
, char *ini_node_name
)
1311 if (!cfg
|| !ini
|| !ini_node_name
) {
1316 * Ensure this is a valid ini name
1318 if (!validate_iscsi_name(ini_node_name
)) {
1322 ptr
= cfg
->config_ini_list
;
1324 if (strcasecmp(ptr
->ini_name
, ini_node_name
) == 0) {
1327 ptr
= ptr
->ini_next
;
1334 ptr
= calloc(1, sizeof (it_ini_t
));
1339 (void) strlcpy(ptr
->ini_name
, ini_node_name
, sizeof (ptr
->ini_name
));
1340 ptr
->ini_generation
= 1;
1341 /* nvlist for props? */
1343 ptr
->ini_next
= cfg
->config_ini_list
;
1344 cfg
->config_ini_list
= ptr
;
1345 cfg
->config_ini_count
++;
1353 * Function: it_ini_setprop()
1355 * Validate the provided property list and set the initiator properties.
1356 * If errlist is not NULL, returns detailed errors for each property
1357 * that failed. The format for errorlist is key = property,
1358 * value = error string.
1362 * ini The initiator being updated.
1363 * proplist nvlist_t containing properties for this target.
1364 * errlist (optional) nvlist_t of errors encountered when
1365 * validating the properties.
1369 * EINVAL Invalid property
1373 it_ini_setprop(it_ini_t
*ini
, nvlist_t
*proplist
, nvlist_t
**errlist
)
1376 nvlist_t
*errs
= NULL
;
1377 nvlist_t
*iprops
= NULL
;
1380 if (!ini
|| !proplist
) {
1385 (void) nvlist_alloc(&errs
, 0, 0);
1390 * copy the existing properties, merge, then validate
1391 * the merged properties before committing them.
1393 if (ini
->ini_properties
) {
1394 ret
= nvlist_dup(ini
->ini_properties
, &iprops
, 0);
1396 ret
= nvlist_alloc(&iprops
, NV_UNIQUE_NAME
, 0);
1403 ret
= nvlist_merge(iprops
, proplist
, 0);
1405 nvlist_free(iprops
);
1409 /* unset chap username if requested */
1410 if ((nvlist_lookup_string(proplist
, PROP_CHAP_USER
, &val
)) == 0) {
1411 if (strcasecmp(val
, "none") == 0) {
1412 (void) nvlist_remove_all(iprops
, PROP_CHAP_USER
);
1416 /* base64 encode the CHAP secret, if it's changed */
1417 if ((nvlist_lookup_string(proplist
, PROP_CHAP_SECRET
, &val
)) == 0) {
1418 char bsecret
[MAX_BASE64_LEN
];
1420 ret
= it_val_pass(PROP_CHAP_SECRET
, val
, errs
);
1422 (void) memset(bsecret
, 0, MAX_BASE64_LEN
);
1424 ret
= iscsi_binary_to_base64_str((uint8_t *)val
,
1425 strlen(val
), bsecret
, MAX_BASE64_LEN
);
1428 /* replace the value in the nvlist */
1429 ret
= nvlist_add_string(iprops
,
1430 PROP_CHAP_SECRET
, bsecret
);
1436 ret
= it_validate_iniprops(iprops
, errs
);
1440 nvlist_free(iprops
);
1444 nvlist_free(ini
->ini_properties
);
1445 ini
->ini_properties
= iprops
;
1447 free_empty_errlist(errlist
);
1453 * Function: it_ini_delete()
1455 * Remove the specified initiator context from the global configuration.
1456 * The removal will not take effect until the modified configuration is
1457 * committed by calling it_config_commit().
1460 * cfg The current iSCSI configration obtained from
1462 * ini Pointer to the it_ini_t structure representing
1463 * the initiator context.
1466 it_ini_delete(it_config_t
*cfg
, it_ini_t
*ini
)
1469 it_ini_t
*prev
= NULL
;
1475 ptr
= cfg
->config_ini_list
;
1477 if (strcasecmp(ptr
->ini_name
, ini
->ini_name
) == 0) {
1481 ptr
= ptr
->ini_next
;
1489 prev
->ini_next
= ptr
->ini_next
;
1491 cfg
->config_ini_list
= ptr
->ini_next
;
1494 ptr
->ini_next
= NULL
; /* Only free this initiator */
1496 cfg
->config_ini_count
--;
1502 * Function: it_ini_free()
1504 * Deallocates resources of an it_ini_t structure. If ini->next is
1505 * not NULL, frees all members of the list.
1508 it_ini_free(it_ini_t
*ini
)
1510 it_ini_free_cmn(ini
);
1514 * Goes through the target property list and validates
1515 * each entry. If errs is non-NULL, will return explicit errors
1516 * for each property that fails validation.
1519 it_validate_tgtprops(nvlist_t
*nvl
, nvlist_t
*errs
)
1522 nvpair_t
*nvp
= NULL
;
1532 while ((nvp
= nvlist_next_nvpair(nvl
, nvp
)) != NULL
) {
1533 name
= nvpair_name(nvp
);
1534 nvtype
= nvpair_type(nvp
);
1541 if (strcmp(name
, PROP_TARGET_CHAP_USER
) == 0) {
1542 if (nvtype
!= DATA_TYPE_STRING
) {
1544 gettext("must be a string value"));
1548 } else if (strcmp(name
, PROP_TARGET_CHAP_SECRET
) == 0) {
1550 * must be between 12 and 255 chars in cleartext.
1551 * will be base64 encoded when it's set.
1553 if (nvtype
== DATA_TYPE_STRING
) {
1554 (void) nvpair_value_string(nvp
, &val
);
1559 gettext("must be a string value"));
1563 } else if (strcmp(name
, PROP_ALIAS
) == 0) {
1564 if (nvtype
!= DATA_TYPE_STRING
) {
1566 gettext("must be a string value"));
1570 } else if (strcmp(name
, PROP_AUTH
) == 0) {
1571 if (nvtype
== DATA_TYPE_STRING
) {
1573 (void) nvpair_value_string(nvp
, &val
);
1578 gettext("must be a string value"));
1582 if ((strcmp(val
, PA_AUTH_NONE
) != 0) &&
1583 (strcmp(val
, PA_AUTH_CHAP
) != 0) &&
1584 (strcmp(val
, PA_AUTH_RADIUS
) != 0) &&
1585 (strcmp(val
, "default") != 0)) {
1586 PROPERR(errs
, val
, gettext(
1587 "must be none, chap, radius or default"));
1592 } else if (strcmp(name
, PROP_OLD_TARGET_NAME
) == 0) {
1595 /* unrecognized property */
1596 PROPERR(errs
, name
, gettext("unrecognized property"));
1605 /* if auth is being set to default, remove from this nvlist */
1606 if (auth
&& (strcmp(auth
, "default") == 0)) {
1607 (void) nvlist_remove_all(nvl
, PROP_AUTH
);
1614 * Goes through the config property list and validates
1615 * each entry. If errs is non-NULL, will return explicit errors
1616 * for each property that fails validation.
1619 it_validate_configprops(nvlist_t
*nvl
, nvlist_t
*errs
)
1622 nvpair_t
*nvp
= NULL
;
1626 struct sockaddr_storage sa
;
1627 boolean_t update_rad_server
= B_FALSE
;
1635 while ((nvp
= nvlist_next_nvpair(nvl
, nvp
)) != NULL
) {
1636 name
= nvpair_name(nvp
);
1637 nvtype
= nvpair_type(nvp
);
1645 /* prefetch string value as we mostly need it */
1646 if (nvtype
== DATA_TYPE_STRING
) {
1647 (void) nvpair_value_string(nvp
, &val
);
1650 if (strcmp(name
, PROP_ALIAS
) == 0) {
1653 gettext("must be a string value"));
1656 } else if (strcmp(name
, PROP_AUTH
) == 0) {
1659 gettext("must be a string value"));
1664 if ((strcmp(val
, PA_AUTH_NONE
) != 0) &&
1665 (strcmp(val
, PA_AUTH_CHAP
) != 0) &&
1666 (strcmp(val
, PA_AUTH_RADIUS
) != 0)) {
1667 PROPERR(errs
, PROP_AUTH
,
1668 gettext("must be none, chap or radius"));
1674 } else if (strcmp(name
, PROP_ISNS_ENABLED
) == 0) {
1675 if (nvtype
!= DATA_TYPE_BOOLEAN_VALUE
) {
1677 gettext("must be a boolean value"));
1680 } else if (strcmp(name
, PROP_ISNS_SERVER
) == 0) {
1682 uint32_t acount
= 0;
1684 (void) nvlist_lookup_string_array(nvl
, name
,
1687 while (acount
> 0) {
1688 if (strcasecmp(arr
[acount
- 1], "none") == 0) {
1691 if ((it_common_convert_sa(arr
[acount
- 1],
1693 PROPERR(errs
, arr
[acount
- 1],
1694 gettext("invalid address"));
1700 } else if (strcmp(name
, PROP_RADIUS_SECRET
) == 0) {
1703 gettext("must be a string value"));
1707 } else if (strcmp(name
, PROP_RADIUS_SERVER
) == 0) {
1708 struct sockaddr_storage sa
;
1711 gettext("must be a string value"));
1716 if ((it_common_convert_sa(val
, &sa
,
1717 DEFAULT_RADIUS_PORT
)) == NULL
) {
1719 gettext("invalid address"));
1723 * rewrite this property to ensure port
1727 if (sockaddr_to_str(&sa
, &rad_server
) == 0) {
1728 update_rad_server
= B_TRUE
;
1732 /* unrecognized property */
1733 PROPERR(errs
, name
, gettext("unrecognized property"));
1739 * If we successfully reformatted the radius server to add the port
1740 * number then update the nvlist
1742 if (update_rad_server
) {
1743 (void) nvlist_add_string(nvl
, PROP_RADIUS_SERVER
, rad_server
);
1748 * if auth = radius, ensure radius server & secret are set.
1751 if (strcmp(auth
, PA_AUTH_RADIUS
) == 0) {
1752 /* need server & secret for radius */
1753 if (!nvlist_exists(nvl
, PROP_RADIUS_SERVER
)) {
1754 PROPERR(errs
, PROP_RADIUS_SERVER
,
1755 gettext("missing required property"));
1758 if (!nvlist_exists(nvl
, PROP_RADIUS_SECRET
)) {
1759 PROPERR(errs
, PROP_RADIUS_SECRET
,
1760 gettext("missing required property"));
1774 * Goes through the ini property list and validates
1775 * each entry. If errs is non-NULL, will return explicit errors
1776 * for each property that fails validation.
1779 it_validate_iniprops(nvlist_t
*nvl
, nvlist_t
*errs
)
1782 nvpair_t
*nvp
= NULL
;
1791 while ((nvp
= nvlist_next_nvpair(nvl
, nvp
)) != NULL
) {
1792 name
= nvpair_name(nvp
);
1793 nvtype
= nvpair_type(nvp
);
1799 if (strcmp(name
, PROP_CHAP_USER
) == 0) {
1800 if (nvtype
!= DATA_TYPE_STRING
) {
1802 gettext("must be a string value"));
1806 } else if (strcmp(name
, PROP_CHAP_SECRET
) == 0) {
1808 * must be between 12 and 255 chars in cleartext.
1809 * will be base64 encoded when it's set.
1811 if (nvtype
== DATA_TYPE_STRING
) {
1813 (void) nvpair_value_string(nvp
, &val
);
1818 gettext("must be a string value"));
1823 /* unrecognized property */
1824 PROPERR(errs
, name
, gettext("unrecognized property"));
1837 it_iqn_generate(char *iqn_buf
, int iqn_buf_len
, char *opt_iqn_suffix
)
1841 char id_str
[UUID_PRINTABLE_STRING_LENGTH
];
1843 uuid_generate_random(id
);
1844 uuid_unparse(id
, id_str
);
1846 if (opt_iqn_suffix
) {
1847 ret
= snprintf(iqn_buf
, iqn_buf_len
, DEFAULT_IQN
1848 "%02d:%s.%s", TARGET_NAME_VERS
, id_str
, opt_iqn_suffix
);
1850 ret
= snprintf(iqn_buf
, iqn_buf_len
, DEFAULT_IQN
1851 "%02d:%s", TARGET_NAME_VERS
, id_str
);
1854 if (ret
> iqn_buf_len
) {
1862 it_val_pass(char *name
, char *val
, nvlist_t
*e
)
1866 if (!name
|| !val
) {
1871 * must be at least 12 chars and less than 256 chars cleartext.
1876 * Since we will be automatically encoding secrets we don't really
1877 * need the prefix anymore.
1880 PROPERR(e
, name
, gettext("secret too short"));
1881 } else if (sz
> 255) {
1882 PROPERR(e
, name
, gettext("secret too long"));
1892 * Function: validate_iscsi_name()
1894 * Ensures the passed-in string is a valid IQN or EUI iSCSI name
1898 validate_iscsi_name(char *in_name
)
1904 if (in_name
== NULL
) {
1908 in_len
= strlen(in_name
);
1913 if (IS_IQN_NAME(in_name
)) {
1915 * IQN names are iqn.yyyy-mm.<xxx>
1917 if ((!isdigit(in_name
[4])) ||
1918 (!isdigit(in_name
[5])) ||
1919 (!isdigit(in_name
[6])) ||
1920 (!isdigit(in_name
[7])) ||
1921 (in_name
[8] != '-') ||
1922 (!isdigit(in_name
[9])) ||
1923 (!isdigit(in_name
[10])) ||
1924 (in_name
[11] != '.')) {
1928 (void) strncpy(month
, &(in_name
[9]), 2);
1932 if ((i
< 0) || (i
> 12)) {
1937 * RFC 3722: if using only ASCII chars, only the following
1938 * chars are allowed: dash, dot, colon, lower case a-z, 0-9.
1939 * We allow upper case names, which should be folded
1940 * to lower case names later.
1942 for (i
= 12; i
< in_len
; i
++) {
1943 char c
= in_name
[i
];
1945 if ((c
!= '-') && (c
!= '.') && (c
!= ':') &&
1946 !isalpha(c
) && !isdigit(c
)) {
1951 /* Finally, validate the overall length, in wide chars */
1952 in_len
= mbstowcs(NULL
, in_name
, 0);
1953 if (in_len
> ISCSI_NAME_LEN_MAX
) {
1956 } else if (IS_EUI_NAME(in_name
)) {
1958 * EUI names are "eui." + 16 hex chars
1964 for (i
= 4; i
< in_len
; i
++) {
1965 if (!isxdigit(in_name
[i
])) {
1977 is_iscsit_enabled(void)
1981 state
= smf_get_state(ISCSIT_FMRI
);
1982 if (state
!= NULL
) {
1983 if (strcmp(state
, SCF_STATE_STRING_ONLINE
) == 0) {
1994 * Function: canonical_iscsi_name()
1996 * Fold the iqn iscsi name to lower-case and the EUI-64 identifier of
1997 * the eui iscsi name to upper-case.
1998 * Ensures the passed-in string is a valid IQN or EUI iSCSI name
2001 canonical_iscsi_name(char *tgt
)
2003 if (IS_IQN_NAME(tgt
)) {
2004 /* lowercase iqn names */
2007 /* uppercase EUI-64 identifier */
2013 * Fold an iqn name to lower-case.
2027 * Fold the EUI-64 identifier of a eui name to upper-case.
2042 free_empty_errlist(nvlist_t
**errlist
)
2044 if (errlist
!= NULL
&& *errlist
!= NULL
) {
2045 assert(fnvlist_num_pairs(*errlist
) == 0);
2046 nvlist_free(*errlist
);