sd: remove 'ssd' driver support
[unleashed/tickless.git] / usr / src / lib / libiscsit / common / libiscsit.c
blob3055a3edf88055ed5847ed0739d77b80056ad044
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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>
29 #include <sys/stat.h>
30 #include <ctype.h>
31 #include <fcntl.h>
32 #include <uuid/uuid.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <strings.h>
36 #include <libintl.h>
37 #include <libscf.h>
38 #include <assert.h>
40 #include <libstmf.h>
41 #include <libiscsit.h>
42 #include <sys/iscsi_protocol.h>
43 #include <sys/iscsit/isns_protocol.h>
45 /* From iscsitgtd */
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
63 #define MAXTAG 0xffff
65 /* helper for property list validation */
66 #define PROPERR(lst, key, value) { \
67 if (lst) { \
68 (void) nvlist_add_string(lst, key, value); \
69 } \
72 /* helper function declarations */
73 static int
74 it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix);
76 static int
77 it_val_pass(char *name, char *val, nvlist_t *e);
79 /* consider making validate funcs public */
80 static int
81 it_validate_configprops(nvlist_t *nvl, nvlist_t *errs);
83 static int
84 it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs);
86 static int
87 it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs);
89 static boolean_t
90 is_iscsit_enabled(void);
92 static void
93 iqnstr(char *s);
95 static void
96 euistr(char *s);
98 static 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.
110 * Parameters:
111 * cfg A C representation of the current iSCSI configuration
113 * Return Values:
114 * 0 Success
115 * ENOMEM Could not allocate resources
116 * EINVAL Invalid parameter
119 it_config_load(it_config_t **cfg)
121 int ret = 0;
122 nvlist_t *cfg_nv = NULL;
123 it_config_t *newcfg = NULL;
124 uint64_t stmf_token = 0;
126 if (!cfg) {
127 return (EINVAL);
130 *cfg = NULL;
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);
144 if (ret == 0) {
145 newcfg->stmf_token = stmf_token;
146 *cfg = newcfg;
149 nvlist_free(cfg_nv);
151 return (ret);
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.
162 * Parameters:
163 * cfg A C representation of the current iSCSI configuration
165 * Return Values:
166 * 0 Success
167 * ENOMEM Could not allocate resources
168 * EINVAL Invalid it_config_t structure
169 * TBD ioctl() failed
170 * TBD could not save config to STMF
173 it_config_commit(it_config_t *cfg)
175 int ret;
176 nvlist_t *cfgnv = NULL;
177 char *packednv = NULL;
178 int iscsit_fd = -1;
179 size_t pnv_size;
180 iscsit_ioc_set_config_t iop;
181 it_tgt_t *tgtp;
183 if (!cfg) {
184 return (EINVAL);
187 ret = it_config_to_nv(cfg, &cfgnv);
188 if (ret == 0) {
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);
203 if (!packednv) {
204 ret = ENOMEM;
205 } else {
206 ret = nvlist_pack(cfgnv, &packednv, &pnv_size,
207 NV_ENCODE_NATIVE, 0);
210 if (ret == 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,
217 &iop)) != 0) {
218 ret = errno;
221 (void) close(iscsit_fd);
222 } else {
223 ret = errno;
227 if (packednv != NULL) {
228 free(packednv);
233 * Before saving the config persistently, remove any
234 * PROP_OLD_TARGET_NAME entries. This is only interesting to
235 * the active service.
237 if (ret == 0) {
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) {
243 continue;
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);
249 changed = B_TRUE;
253 if (changed) {
254 /* rebuild the config nvlist */
255 nvlist_free(cfgnv);
256 cfgnv = NULL;
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
270 * want them to be.
272 if (ret == 0) {
273 ret = stmfSetProviderDataProt(ISCSIT_MODNAME, cfgnv,
274 STMF_PORT_PROVIDER_TYPE, &(cfg->stmf_token));
276 if (ret == STMF_STATUS_SUCCESS) {
277 ret = 0;
278 } else if (ret == STMF_ERROR_NOMEM) {
279 ret = ENOMEM;
280 } else if (ret == STMF_ERROR_PROV_DATA_STALE) {
281 int st;
282 it_config_t *rcfg = NULL;
284 st = it_config_load(&rcfg);
285 if (st == 0) {
286 (void) it_config_commit(rcfg);
287 it_config_free(rcfg);
292 nvlist_free(cfgnv);
294 return (ret);
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.
305 * Parameters:
307 * cfg The current iSCSI configuration obtained from
308 * it_config_load()
309 * proplist nvlist_t containing properties for this target.
310 * errlist (optional) nvlist_t of errors encountered when
311 * validating the properties.
313 * Return Values:
314 * 0 Success
315 * EINVAL Invalid property
319 it_config_setprop(it_config_t *cfg, nvlist_t *proplist, nvlist_t **errlist)
321 int ret;
322 nvlist_t *errs = NULL;
323 it_portal_t *isns = NULL;
324 it_portal_t *pnext = NULL;
325 it_portal_t *newisnslist = NULL;
326 char **arr;
327 uint32_t count;
328 uint32_t newcount;
329 nvlist_t *cprops = NULL;
330 char *val = NULL;
332 if (!cfg || !proplist) {
333 return (EINVAL);
336 if (errlist) {
337 (void) nvlist_alloc(&errs, 0, 0);
338 *errlist = errs;
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);
347 } else {
348 ret = nvlist_alloc(&cprops, NV_UNIQUE_NAME, 0);
351 if (ret != 0) {
352 return (ret);
355 ret = nvlist_merge(cprops, proplist, 0);
356 if (ret != 0) {
357 nvlist_free(cprops);
358 return (ret);
362 * base64 encode the radius secret, if it's changed.
364 val = NULL;
365 (void) nvlist_lookup_string(proplist, PROP_RADIUS_SECRET, &val);
366 if (val) {
367 char bsecret[MAX_BASE64_LEN];
369 ret = it_val_pass(PROP_RADIUS_SECRET, val, errs);
371 if (ret == 0) {
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);
377 if (ret == 0) {
378 /* replace the value in the nvlist */
379 ret = nvlist_add_string(cprops,
380 PROP_RADIUS_SECRET, bsecret);
385 if (ret != 0) {
386 nvlist_free(cprops);
387 return (ret);
390 /* see if we need to remove the radius server setting */
391 val = NULL;
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 */
398 val = NULL;
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);
405 if (ret != 0) {
406 nvlist_free(cprops);
407 return (ret);
411 * Update iSNS server list, if exists in provided property list.
413 ret = nvlist_lookup_string_array(proplist, PROP_ISNS_SERVER,
414 &arr, &count);
416 if (ret == 0) {
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);
421 } else {
422 newisnslist = NULL;
423 newcount = 0;
424 (void) nvlist_remove_all(cprops, PROP_ISNS_SERVER);
427 if (ret == 0) {
428 isns = cfg->config_isns_svr_list;
429 while (isns) {
430 pnext = isns->portal_next;
431 free(isns);
432 isns = pnext;
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
441 * are added.
443 if (newcount > 0) {
444 int i = 0;
445 char **newarray;
447 newarray = malloc(sizeof (char *) * newcount);
448 if (newarray == NULL) {
449 ret = ENOMEM;
450 } else {
451 for (isns = newisnslist; isns != NULL;
452 isns = isns->portal_next) {
453 (void) sockaddr_to_str(
454 &(isns->portal_addr),
455 &(newarray[i++]));
457 (void) nvlist_add_string_array(cprops,
458 PROP_ISNS_SERVER, newarray,
459 newcount);
461 for (i = 0; i < newcount; i++) {
462 if (newarray[i]) {
463 free(newarray[i]);
466 free(newarray);
470 } else if (ret == ENOENT) {
471 /* not an error */
472 ret = 0;
475 if (ret == 0) {
476 /* replace the global properties list */
477 nvlist_free(cfg->config_global_properties);
478 cfg->config_global_properties = cprops;
479 } else {
480 nvlist_free(cprops);
483 if (ret == 0)
484 free_empty_errlist(errlist);
486 return (ret);
490 * Function: it_config_free()
492 * Free any resources associated with the it_config_t structure.
494 * Parameters:
495 * cfg A C representation of the current iSCSI configuration
497 void
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().
514 * Parameters:
515 * cfg The current iSCSI configuration obtained from
516 * it_config_load()
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.
523 * Return Values:
524 * 0 Success
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)
533 int ret = 0;
534 it_tgt_t *ptr;
535 it_tgt_t *cfgtgt;
536 char *namep;
537 char buf[ISCSI_NAME_LEN_MAX + 1];
539 if (!cfg || !tgt) {
540 return (EINVAL);
543 if (!tgt_name) {
544 /* generate a name */
545 ret = it_iqn_generate(buf, sizeof (buf), NULL);
546 if (ret != 0) {
547 return (ret);
549 } else {
550 /* validate the passed-in name */
551 if (!validate_iscsi_name(tgt_name)) {
552 return (EFAULT);
554 (void) strlcpy(buf, tgt_name, sizeof (buf));
555 canonical_iscsi_name(buf);
557 namep = 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) {
563 return (EEXIST);
565 cfgtgt = cfgtgt->tgt_next;
568 /* Too many targets? */
569 if (cfg->config_tgt_count >= MAX_TARGETS) {
570 return (E2BIG);
573 ptr = calloc(1, sizeof (it_tgt_t));
574 if (ptr == NULL) {
575 return (ENOMEM);
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++;
584 *tgt = ptr;
586 return (0);
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.
597 * Parameters:
599 * cfg The current iSCSI configuration obtained from
600 * it_config_load()
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.
606 * Return Values:
607 * 0 Success
608 * EINVAL Invalid property
612 it_tgt_setprop(it_config_t *cfg, it_tgt_t *tgt, nvlist_t *proplist,
613 nvlist_t **errlist)
615 int ret;
616 nvlist_t *errs = NULL;
617 nvlist_t *tprops = NULL;
618 char *val = NULL;
620 if (!cfg || !tgt || !proplist) {
621 return (EINVAL);
624 /* verify the target name in case the target node is renamed */
625 if (!validate_iscsi_name(tgt->tgt_name)) {
626 return (EINVAL);
628 canonical_iscsi_name(tgt->tgt_name);
630 if (errlist) {
631 (void) nvlist_alloc(&errs, 0, 0);
632 *errlist = errs;
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);
641 } else {
642 ret = nvlist_alloc(&tprops, NV_UNIQUE_NAME, 0);
645 if (ret != 0) {
646 return (ret);
649 ret = nvlist_merge(tprops, proplist, 0);
650 if (ret != 0) {
651 nvlist_free(tprops);
652 return (ret);
655 /* unset chap username or alias if requested */
656 val = NULL;
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);
662 val = NULL;
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 */
669 val = NULL;
670 (void) nvlist_lookup_string(proplist, PROP_TARGET_CHAP_SECRET, &val);
671 if (val) {
672 char bsecret[MAX_BASE64_LEN];
674 ret = it_val_pass(PROP_TARGET_CHAP_SECRET, val, errs);
676 if (ret == 0) {
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);
682 if (ret == 0) {
683 /* replace the value in the nvlist */
684 ret = nvlist_add_string(tprops,
685 PROP_TARGET_CHAP_SECRET, bsecret);
690 if (ret == 0) {
691 ret = it_validate_tgtprops(tprops, errs);
694 if (ret != 0) {
695 nvlist_free(tprops);
696 return (ret);
699 nvlist_free(tgt->tgt_properties);
700 tgt->tgt_properties = tprops;
702 free_empty_errlist(errlist);
704 return (0);
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().
716 * Parameters:
717 * cfg The current iSCSI configuration obtained from
718 * it_config_load()
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.
724 * Return Values:
725 * 0 Success
726 * EBUSY Target is online
729 it_tgt_delete(it_config_t *cfg, it_tgt_t *tgt, boolean_t force)
731 int ret;
732 it_tgt_t *ptgt;
733 it_tgt_t *prev = NULL;
734 stmfDevid devid;
735 stmfTargetProperties props;
737 if (!cfg || !tgt) {
738 return (0);
741 ptgt = cfg->config_tgt_list;
742 while (ptgt != NULL) {
743 if (strcasecmp(tgt->tgt_name, ptgt->tgt_name) == 0) {
744 break;
746 prev = ptgt;
747 ptgt = ptgt->tgt_next;
750 if (!ptgt) {
751 return (0);
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) {
761 /* can't happen? */
762 return (EINVAL);
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) {
772 if (!force) {
773 return (EBUSY);
775 ret = stmfOfflineTarget(&devid);
776 if (ret != 0) {
777 return (EBUSY);
782 if (prev) {
783 prev->tgt_next = ptgt->tgt_next;
784 } else {
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--;
792 it_tgt_free(ptgt);
794 return (0);
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.
803 void
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().
818 * Parameters:
819 * cfg The current iSCSI configuration obtained from
820 * it_config_load()
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
829 * will be chosen.
831 * Return Values:
832 * 0 Success
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;
843 it_tpgt_t *cfgt;
844 char tagid_used[MAXTAG + 1];
845 uint16_t tagid = ISCSIT_DEFAULT_TPGT;
847 if (!cfg || !tgt || !tpgt || !tpg_name) {
848 return (EINVAL);
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) {
868 return (EEXIST);
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 */
881 tagid = tpgt_tag;
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) {
889 break;
892 if (tagid >= MAXTAG) {
893 return (E2BIG);
895 } else {
896 /* next available ID */
897 tagid++;
900 ptr = calloc(1, sizeof (it_tpgt_t));
901 if (!ptr) {
902 return (ENOMEM);
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++;
915 *tpgt = ptr;
917 return (0);
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().
928 * Parameters:
929 * cfg The current iSCSI configuration obtained from
930 * it_config_load()
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
935 void
936 it_tpgt_delete(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t *tpgt)
938 it_tpgt_t *ptr;
939 it_tpgt_t *prev = NULL;
941 if (!cfg || !tgt || !tpgt) {
942 return;
945 ptr = tgt->tgt_tpgt_list;
946 while (ptr) {
947 if (ptr->tpgt_tag == tpgt->tpgt_tag) {
948 break;
950 prev = ptr;
951 ptr = ptr->tpgt_next;
954 if (!ptr) {
955 return;
958 if (prev) {
959 prev->tpgt_next = ptr->tpgt_next;
960 } else {
961 tgt->tgt_tpgt_list = ptr->tpgt_next;
963 ptr->tpgt_next = NULL;
965 tgt->tgt_tpgt_count--;
966 tgt->tgt_generation++;
968 it_tpgt_free(ptr);
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.
977 void
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().
992 * Parameters:
993 * cfg The current iSCSI configuration obtained from
994 * it_config_load()
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.
1003 * Return Values:
1004 * 0 Success
1005 * ENOMEM Cannot allocate resources
1006 * EINVAL Invalid parameter
1007 * EEXIST Requested portal in use by another target portal
1008 * group
1011 it_tpg_create(it_config_t *cfg, it_tpg_t **tpg, char *tpg_name,
1012 char *portal_ip_port)
1014 int ret;
1015 it_tpg_t *ptr;
1016 it_portal_t *portal = NULL;
1018 if (!cfg || !tpg || !tpg_name || !portal_ip_port) {
1019 return (EINVAL);
1022 *tpg = NULL;
1024 ptr = cfg->config_tpg_list;
1025 while (ptr) {
1026 if (strcmp(tpg_name, ptr->tpg_name) == 0) {
1027 break;
1029 ptr = ptr->tpg_next;
1032 if (ptr) {
1033 return (EEXIST);
1036 ptr = calloc(1, sizeof (it_tpg_t));
1037 if (!ptr) {
1038 return (ENOMEM);
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);
1046 if (ret != 0) {
1047 free(ptr);
1048 return (ret);
1051 ptr->tpg_next = cfg->config_tpg_list;
1052 cfg->config_tpg_list = ptr;
1053 cfg->config_tpg_count++;
1055 *tpg = ptr;
1057 return (0);
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().
1068 * Parameters:
1069 * cfg The current iSCSI configuration obtained from
1070 * it_config_load()
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.
1076 * Return Values:
1077 * 0 Success
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)
1084 it_tpg_t *ptr;
1085 it_tpg_t *prev = NULL;
1086 it_tgt_t *tgt;
1087 it_tpgt_t *tpgt;
1088 it_tpgt_t *ntpgt;
1090 if (!cfg || !tpg) {
1091 return (EINVAL);
1094 ptr = cfg->config_tpg_list;
1095 while (ptr) {
1096 if (strcmp(ptr->tpg_name, tpg->tpg_name) == 0) {
1097 break;
1099 prev = ptr;
1100 ptr = ptr->tpg_next;
1103 if (!ptr) {
1104 return (0);
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;
1112 while (tgt) {
1113 tpgt = tgt->tgt_tpgt_list;
1114 while (tpgt) {
1115 ntpgt = tpgt->tpgt_next;
1117 if (strcmp(tpgt->tpgt_tpg_name, tpg->tpg_name)
1118 == 0) {
1119 if (!force) {
1120 return (EBUSY);
1122 it_tpgt_delete(cfg, tgt, tpgt);
1125 tpgt = ntpgt;
1127 tgt = tgt->tgt_next;
1130 /* Now that it's not in use anywhere, remove the TPG */
1131 if (prev) {
1132 prev->tpg_next = ptr->tpg_next;
1133 } else {
1134 cfg->config_tpg_list = ptr->tpg_next;
1136 ptr->tpg_next = NULL;
1138 cfg->config_tpg_count--;
1140 it_tpg_free(ptr);
1142 return (0);
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.
1151 void
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().
1165 * Parameters:
1166 * cfg The current iSCSI configration obtained from
1167 * it_config_load()
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
1171 * the portal
1172 * portal_ip_port A string containing an appropriately formatted
1173 * IP address or IP address:port in either IPv4 or
1174 * IPv6 format.
1175 * Return Values:
1176 * 0 Success
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;
1186 it_portal_t *ptr;
1187 it_tpg_t *ctpg = NULL;
1189 if (!cfg || !tpg || !portal || !portal_ip_port) {
1190 return (EINVAL);
1193 if ((it_common_convert_sa(portal_ip_port, &sa, ISCSI_LISTEN_PORT))
1194 == NULL) {
1195 return (EINVAL);
1198 /* Check that this portal doesn't appear in any other tag */
1199 ctpg = cfg->config_tpg_list;
1200 while (ctpg) {
1201 ptr = ctpg->tpg_portal_list;
1202 for (; ptr != NULL; ptr = ptr->portal_next) {
1203 if (it_sa_compare(&(ptr->portal_addr), &sa) != 0) {
1204 continue;
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) {
1212 return (0);
1213 } else {
1214 /* Not allowed */
1215 return (EEXIST);
1218 ctpg = ctpg->tpg_next;
1221 ptr = calloc(1, sizeof (it_portal_t));
1222 if (!ptr) {
1223 return (ENOMEM);
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++;
1233 return (0);
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().
1243 * Parameters:
1244 * cfg The current iSCSI configration obtained from
1245 * it_config_load()
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
1249 * the portal
1251 void
1252 it_portal_delete(it_config_t *cfg, it_tpg_t *tpg, it_portal_t *portal)
1254 it_portal_t *ptr;
1255 it_portal_t *prev = NULL;
1257 if (!cfg || !tpg || !portal) {
1258 return;
1261 ptr = tpg->tpg_portal_list;
1262 while (ptr) {
1263 if (memcmp(&(ptr->portal_addr), &(portal->portal_addr),
1264 sizeof (ptr->portal_addr)) == 0) {
1265 break;
1267 prev = ptr;
1268 ptr = ptr->portal_next;
1271 if (!ptr) {
1272 return;
1275 if (prev) {
1276 prev->portal_next = ptr->portal_next;
1277 } else {
1278 tpg->tpg_portal_list = ptr->portal_next;
1280 tpg->tpg_portal_count--;
1281 tpg->tpg_generation++;
1283 free(ptr);
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().
1293 * Parameters:
1294 * cfg The current iSCSI configration obtained from
1295 * it_config_load()
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.
1300 * Return Values:
1301 * 0 Success
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)
1309 it_ini_t *ptr;
1311 if (!cfg || !ini || !ini_node_name) {
1312 return (EINVAL);
1316 * Ensure this is a valid ini name
1318 if (!validate_iscsi_name(ini_node_name)) {
1319 return (EFAULT);
1322 ptr = cfg->config_ini_list;
1323 while (ptr) {
1324 if (strcasecmp(ptr->ini_name, ini_node_name) == 0) {
1325 break;
1327 ptr = ptr->ini_next;
1330 if (ptr) {
1331 return (EEXIST);
1334 ptr = calloc(1, sizeof (it_ini_t));
1335 if (!ptr) {
1336 return (ENOMEM);
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++;
1347 *ini = ptr;
1349 return (0);
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.
1360 * Parameters:
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.
1367 * Return Values:
1368 * 0 Success
1369 * EINVAL Invalid property
1373 it_ini_setprop(it_ini_t *ini, nvlist_t *proplist, nvlist_t **errlist)
1375 int ret;
1376 nvlist_t *errs = NULL;
1377 nvlist_t *iprops = NULL;
1378 char *val = NULL;
1380 if (!ini || !proplist) {
1381 return (EINVAL);
1384 if (errlist) {
1385 (void) nvlist_alloc(&errs, 0, 0);
1386 *errlist = errs;
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);
1395 } else {
1396 ret = nvlist_alloc(&iprops, NV_UNIQUE_NAME, 0);
1399 if (ret != 0) {
1400 return (ret);
1403 ret = nvlist_merge(iprops, proplist, 0);
1404 if (ret != 0) {
1405 nvlist_free(iprops);
1406 return (ret);
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);
1421 if (ret == 0) {
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);
1427 if (ret == 0) {
1428 /* replace the value in the nvlist */
1429 ret = nvlist_add_string(iprops,
1430 PROP_CHAP_SECRET, bsecret);
1435 if (ret == 0) {
1436 ret = it_validate_iniprops(iprops, errs);
1439 if (ret != 0) {
1440 nvlist_free(iprops);
1441 return (ret);
1444 nvlist_free(ini->ini_properties);
1445 ini->ini_properties = iprops;
1447 free_empty_errlist(errlist);
1449 return (0);
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().
1459 * Parameters:
1460 * cfg The current iSCSI configration obtained from
1461 * it_config_load()
1462 * ini Pointer to the it_ini_t structure representing
1463 * the initiator context.
1465 void
1466 it_ini_delete(it_config_t *cfg, it_ini_t *ini)
1468 it_ini_t *ptr;
1469 it_ini_t *prev = NULL;
1471 if (!cfg || !ini) {
1472 return;
1475 ptr = cfg->config_ini_list;
1476 while (ptr) {
1477 if (strcasecmp(ptr->ini_name, ini->ini_name) == 0) {
1478 break;
1480 prev = ptr;
1481 ptr = ptr->ini_next;
1484 if (!ptr) {
1485 return;
1488 if (prev) {
1489 prev->ini_next = ptr->ini_next;
1490 } else {
1491 cfg->config_ini_list = ptr->ini_next;
1494 ptr->ini_next = NULL; /* Only free this initiator */
1496 cfg->config_ini_count--;
1498 it_ini_free(ptr);
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.
1507 void
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.
1518 static int
1519 it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs)
1521 int errcnt = 0;
1522 nvpair_t *nvp = NULL;
1523 data_type_t nvtype;
1524 char *name;
1525 char *val;
1526 char *auth = NULL;
1528 if (!nvl) {
1529 return (0);
1532 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1533 name = nvpair_name(nvp);
1534 nvtype = nvpair_type(nvp);
1536 if (!name) {
1537 continue;
1540 val = NULL;
1541 if (strcmp(name, PROP_TARGET_CHAP_USER) == 0) {
1542 if (nvtype != DATA_TYPE_STRING) {
1543 PROPERR(errs, name,
1544 gettext("must be a string value"));
1545 errcnt++;
1546 continue;
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);
1557 if (!val) {
1558 PROPERR(errs, name,
1559 gettext("must be a string value"));
1560 errcnt++;
1561 continue;
1563 } else if (strcmp(name, PROP_ALIAS) == 0) {
1564 if (nvtype != DATA_TYPE_STRING) {
1565 PROPERR(errs, name,
1566 gettext("must be a string value"));
1567 errcnt++;
1568 continue;
1570 } else if (strcmp(name, PROP_AUTH) == 0) {
1571 if (nvtype == DATA_TYPE_STRING) {
1572 val = NULL;
1573 (void) nvpair_value_string(nvp, &val);
1576 if (!val) {
1577 PROPERR(errs, name,
1578 gettext("must be a string value"));
1579 errcnt++;
1580 continue;
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"));
1588 errcnt++;
1590 auth = val;
1591 continue;
1592 } else if (strcmp(name, PROP_OLD_TARGET_NAME) == 0) {
1593 continue;
1594 } else {
1595 /* unrecognized property */
1596 PROPERR(errs, name, gettext("unrecognized property"));
1597 errcnt++;
1601 if (errcnt) {
1602 return (EINVAL);
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);
1610 return (0);
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.
1618 static int
1619 it_validate_configprops(nvlist_t *nvl, nvlist_t *errs)
1621 int errcnt = 0;
1622 nvpair_t *nvp = NULL;
1623 data_type_t nvtype;
1624 char *name;
1625 char *val;
1626 struct sockaddr_storage sa;
1627 boolean_t update_rad_server = B_FALSE;
1628 char *rad_server;
1629 char *auth = NULL;
1631 if (!nvl) {
1632 return (0);
1635 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1636 name = nvpair_name(nvp);
1637 nvtype = nvpair_type(nvp);
1639 if (!name) {
1640 continue;
1643 val = NULL;
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) {
1651 if (!val) {
1652 PROPERR(errs, name,
1653 gettext("must be a string value"));
1654 errcnt++;
1656 } else if (strcmp(name, PROP_AUTH) == 0) {
1657 if (!val) {
1658 PROPERR(errs, name,
1659 gettext("must be a string value"));
1660 errcnt++;
1661 continue;
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"));
1669 errcnt++;
1672 auth = val;
1674 } else if (strcmp(name, PROP_ISNS_ENABLED) == 0) {
1675 if (nvtype != DATA_TYPE_BOOLEAN_VALUE) {
1676 PROPERR(errs, name,
1677 gettext("must be a boolean value"));
1678 errcnt++;
1680 } else if (strcmp(name, PROP_ISNS_SERVER) == 0) {
1681 char **arr = NULL;
1682 uint32_t acount = 0;
1684 (void) nvlist_lookup_string_array(nvl, name,
1685 &arr, &acount);
1687 while (acount > 0) {
1688 if (strcasecmp(arr[acount - 1], "none") == 0) {
1689 break;
1691 if ((it_common_convert_sa(arr[acount - 1],
1692 &sa, 0)) == NULL) {
1693 PROPERR(errs, arr[acount - 1],
1694 gettext("invalid address"));
1695 errcnt++;
1697 acount--;
1700 } else if (strcmp(name, PROP_RADIUS_SECRET) == 0) {
1701 if (!val) {
1702 PROPERR(errs, name,
1703 gettext("must be a string value"));
1704 errcnt++;
1705 continue;
1707 } else if (strcmp(name, PROP_RADIUS_SERVER) == 0) {
1708 struct sockaddr_storage sa;
1709 if (!val) {
1710 PROPERR(errs, name,
1711 gettext("must be a string value"));
1712 errcnt++;
1713 continue;
1716 if ((it_common_convert_sa(val, &sa,
1717 DEFAULT_RADIUS_PORT)) == NULL) {
1718 PROPERR(errs, name,
1719 gettext("invalid address"));
1720 errcnt++;
1721 } else {
1723 * rewrite this property to ensure port
1724 * number is added.
1727 if (sockaddr_to_str(&sa, &rad_server) == 0) {
1728 update_rad_server = B_TRUE;
1731 } else {
1732 /* unrecognized property */
1733 PROPERR(errs, name, gettext("unrecognized property"));
1734 errcnt++;
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);
1744 free(rad_server);
1748 * if auth = radius, ensure radius server & secret are set.
1750 if (auth) {
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"));
1756 errcnt++;
1758 if (!nvlist_exists(nvl, PROP_RADIUS_SECRET)) {
1759 PROPERR(errs, PROP_RADIUS_SECRET,
1760 gettext("missing required property"));
1761 errcnt++;
1766 if (errcnt) {
1767 return (EINVAL);
1770 return (0);
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.
1778 static int
1779 it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs)
1781 int errcnt = 0;
1782 nvpair_t *nvp = NULL;
1783 data_type_t nvtype;
1784 char *name;
1785 char *val;
1787 if (!nvl) {
1788 return (0);
1791 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1792 name = nvpair_name(nvp);
1793 nvtype = nvpair_type(nvp);
1795 if (!name) {
1796 continue;
1799 if (strcmp(name, PROP_CHAP_USER) == 0) {
1800 if (nvtype != DATA_TYPE_STRING) {
1801 PROPERR(errs, name,
1802 gettext("must be a string value"));
1803 errcnt++;
1804 continue;
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) {
1812 val = NULL;
1813 (void) nvpair_value_string(nvp, &val);
1816 if (!val) {
1817 PROPERR(errs, name,
1818 gettext("must be a string value"));
1819 errcnt++;
1820 continue;
1822 } else {
1823 /* unrecognized property */
1824 PROPERR(errs, name, gettext("unrecognized property"));
1825 errcnt++;
1829 if (errcnt) {
1830 return (EINVAL);
1833 return (0);
1836 static int
1837 it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix)
1839 int ret;
1840 uuid_t id;
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);
1849 } else {
1850 ret = snprintf(iqn_buf, iqn_buf_len, DEFAULT_IQN
1851 "%02d:%s", TARGET_NAME_VERS, id_str);
1854 if (ret > iqn_buf_len) {
1855 return (1);
1858 return (0);
1861 static int
1862 it_val_pass(char *name, char *val, nvlist_t *e)
1864 size_t sz;
1866 if (!name || !val) {
1867 return (EINVAL);
1871 * must be at least 12 chars and less than 256 chars cleartext.
1873 sz = strlen(val);
1876 * Since we will be automatically encoding secrets we don't really
1877 * need the prefix anymore.
1879 if (sz < 12) {
1880 PROPERR(e, name, gettext("secret too short"));
1881 } else if (sz > 255) {
1882 PROPERR(e, name, gettext("secret too long"));
1883 } else {
1884 /* all is well */
1885 return (0);
1888 return (1);
1892 * Function: validate_iscsi_name()
1894 * Ensures the passed-in string is a valid IQN or EUI iSCSI name
1897 boolean_t
1898 validate_iscsi_name(char *in_name)
1900 size_t in_len;
1901 int i;
1902 char month[3];
1904 if (in_name == NULL) {
1905 return (B_FALSE);
1908 in_len = strlen(in_name);
1909 if (in_len < 12) {
1910 return (B_FALSE);
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] != '.')) {
1925 return (B_FALSE);
1928 (void) strncpy(month, &(in_name[9]), 2);
1929 month[2] = '\0';
1931 i = atoi(month);
1932 if ((i < 0) || (i > 12)) {
1933 return (B_FALSE);
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)) {
1947 return (B_FALSE);
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) {
1954 return (B_FALSE);
1956 } else if (IS_EUI_NAME(in_name)) {
1958 * EUI names are "eui." + 16 hex chars
1960 if (in_len != 20) {
1961 return (B_FALSE);
1964 for (i = 4; i < in_len; i++) {
1965 if (!isxdigit(in_name[i])) {
1966 return (B_FALSE);
1969 } else {
1970 return (B_FALSE);
1973 return (B_TRUE);
1976 static boolean_t
1977 is_iscsit_enabled(void)
1979 char *state;
1981 state = smf_get_state(ISCSIT_FMRI);
1982 if (state != NULL) {
1983 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
1984 free(state);
1985 return (B_TRUE);
1987 free(state);
1990 return (B_FALSE);
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
2000 void
2001 canonical_iscsi_name(char *tgt)
2003 if (IS_IQN_NAME(tgt)) {
2004 /* lowercase iqn names */
2005 iqnstr(tgt);
2006 } else {
2007 /* uppercase EUI-64 identifier */
2008 euistr(tgt);
2013 * Fold an iqn name to lower-case.
2015 static void
2016 iqnstr(char *s)
2018 if (s != NULL) {
2019 while (*s) {
2020 *s = tolower(*s);
2021 s++;
2027 * Fold the EUI-64 identifier of a eui name to upper-case.
2029 static void
2030 euistr(char *s)
2032 if (s != NULL) {
2033 char *l = s + 4;
2034 while (*l) {
2035 *l = toupper(*l);
2036 l++;
2041 static void
2042 free_empty_errlist(nvlist_t **errlist)
2044 if (errlist != NULL && *errlist != NULL) {
2045 assert(fnvlist_num_pairs(*errlist) == 0);
2046 nvlist_free(*errlist);
2047 *errlist = NULL;