8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libiscsit / common / libiscsit.c
blobe584999ac5be9b5c1ca2b6d024adfa799d17e7c1
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 if (cfg_nv) {
150 nvlist_free(cfg_nv);
153 return (ret);
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.
164 * Parameters:
165 * cfg A C representation of the current iSCSI configuration
167 * Return Values:
168 * 0 Success
169 * ENOMEM Could not allocate resources
170 * EINVAL Invalid it_config_t structure
171 * TBD ioctl() failed
172 * TBD could not save config to STMF
175 it_config_commit(it_config_t *cfg)
177 int ret;
178 nvlist_t *cfgnv = NULL;
179 char *packednv = NULL;
180 int iscsit_fd = -1;
181 size_t pnv_size;
182 iscsit_ioc_set_config_t iop;
183 it_tgt_t *tgtp;
185 if (!cfg) {
186 return (EINVAL);
189 ret = it_config_to_nv(cfg, &cfgnv);
190 if (ret == 0) {
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);
205 if (!packednv) {
206 ret = ENOMEM;
207 } else {
208 ret = nvlist_pack(cfgnv, &packednv, &pnv_size,
209 NV_ENCODE_NATIVE, 0);
212 if (ret == 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,
219 &iop)) != 0) {
220 ret = errno;
223 (void) close(iscsit_fd);
224 } else {
225 ret = errno;
229 if (packednv != NULL) {
230 free(packednv);
235 * Before saving the config persistently, remove any
236 * PROP_OLD_TARGET_NAME entries. This is only interesting to
237 * the active service.
239 if (ret == 0) {
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) {
245 continue;
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);
251 changed = B_TRUE;
255 if (changed) {
256 /* rebuild the config nvlist */
257 nvlist_free(cfgnv);
258 cfgnv = NULL;
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
272 * want them to be.
274 if (ret == 0) {
275 ret = stmfSetProviderDataProt(ISCSIT_MODNAME, cfgnv,
276 STMF_PORT_PROVIDER_TYPE, &(cfg->stmf_token));
278 if (ret == STMF_STATUS_SUCCESS) {
279 ret = 0;
280 } else if (ret == STMF_ERROR_NOMEM) {
281 ret = ENOMEM;
282 } else if (ret == STMF_ERROR_PROV_DATA_STALE) {
283 int st;
284 it_config_t *rcfg = NULL;
286 st = it_config_load(&rcfg);
287 if (st == 0) {
288 (void) it_config_commit(rcfg);
289 it_config_free(rcfg);
294 if (cfgnv) {
295 nvlist_free(cfgnv);
298 return (ret);
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.
309 * Parameters:
311 * cfg The current iSCSI configuration obtained from
312 * it_config_load()
313 * proplist nvlist_t containing properties for this target.
314 * errlist (optional) nvlist_t of errors encountered when
315 * validating the properties.
317 * Return Values:
318 * 0 Success
319 * EINVAL Invalid property
323 it_config_setprop(it_config_t *cfg, nvlist_t *proplist, nvlist_t **errlist)
325 int ret;
326 nvlist_t *errs = NULL;
327 it_portal_t *isns = NULL;
328 it_portal_t *pnext = NULL;
329 it_portal_t *newisnslist = NULL;
330 char **arr;
331 uint32_t count;
332 uint32_t newcount;
333 nvlist_t *cprops = NULL;
334 char *val = NULL;
336 if (!cfg || !proplist) {
337 return (EINVAL);
340 if (errlist) {
341 (void) nvlist_alloc(&errs, 0, 0);
342 *errlist = errs;
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);
351 } else {
352 ret = nvlist_alloc(&cprops, NV_UNIQUE_NAME, 0);
355 if (ret != 0) {
356 return (ret);
359 ret = nvlist_merge(cprops, proplist, 0);
360 if (ret != 0) {
361 nvlist_free(cprops);
362 return (ret);
366 * base64 encode the radius secret, if it's changed.
368 val = NULL;
369 (void) nvlist_lookup_string(proplist, PROP_RADIUS_SECRET, &val);
370 if (val) {
371 char bsecret[MAX_BASE64_LEN];
373 ret = it_val_pass(PROP_RADIUS_SECRET, val, errs);
375 if (ret == 0) {
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);
381 if (ret == 0) {
382 /* replace the value in the nvlist */
383 ret = nvlist_add_string(cprops,
384 PROP_RADIUS_SECRET, bsecret);
389 if (ret != 0) {
390 nvlist_free(cprops);
391 return (ret);
394 /* see if we need to remove the radius server setting */
395 val = NULL;
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 */
402 val = NULL;
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);
409 if (ret != 0) {
410 if (cprops) {
411 nvlist_free(cprops);
413 return (ret);
417 * Update iSNS server list, if exists in provided property list.
419 ret = nvlist_lookup_string_array(proplist, PROP_ISNS_SERVER,
420 &arr, &count);
422 if (ret == 0) {
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);
427 } else {
428 newisnslist = NULL;
429 newcount = 0;
430 (void) nvlist_remove_all(cprops, PROP_ISNS_SERVER);
433 if (ret == 0) {
434 isns = cfg->config_isns_svr_list;
435 while (isns) {
436 pnext = isns->portal_next;
437 free(isns);
438 isns = pnext;
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
447 * are added.
449 if (newcount > 0) {
450 int i = 0;
451 char **newarray;
453 newarray = malloc(sizeof (char *) * newcount);
454 if (newarray == NULL) {
455 ret = ENOMEM;
456 } else {
457 for (isns = newisnslist; isns != NULL;
458 isns = isns->portal_next) {
459 (void) sockaddr_to_str(
460 &(isns->portal_addr),
461 &(newarray[i++]));
463 (void) nvlist_add_string_array(cprops,
464 PROP_ISNS_SERVER, newarray,
465 newcount);
467 for (i = 0; i < newcount; i++) {
468 if (newarray[i]) {
469 free(newarray[i]);
472 free(newarray);
476 } else if (ret == ENOENT) {
477 /* not an error */
478 ret = 0;
481 if (ret == 0) {
482 /* replace the global properties list */
483 nvlist_free(cfg->config_global_properties);
484 cfg->config_global_properties = cprops;
485 } else {
486 if (cprops) {
487 nvlist_free(cprops);
491 if (ret == 0)
492 free_empty_errlist(errlist);
494 return (ret);
498 * Function: it_config_free()
500 * Free any resources associated with the it_config_t structure.
502 * Parameters:
503 * cfg A C representation of the current iSCSI configuration
505 void
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().
522 * Parameters:
523 * cfg The current iSCSI configuration obtained from
524 * it_config_load()
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.
531 * Return Values:
532 * 0 Success
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)
541 int ret = 0;
542 it_tgt_t *ptr;
543 it_tgt_t *cfgtgt;
544 char *namep;
545 char buf[ISCSI_NAME_LEN_MAX + 1];
547 if (!cfg || !tgt) {
548 return (EINVAL);
551 if (!tgt_name) {
552 /* generate a name */
553 ret = it_iqn_generate(buf, sizeof (buf), NULL);
554 if (ret != 0) {
555 return (ret);
557 } else {
558 /* validate the passed-in name */
559 if (!validate_iscsi_name(tgt_name)) {
560 return (EFAULT);
562 (void) strlcpy(buf, tgt_name, sizeof (buf));
563 canonical_iscsi_name(buf);
565 namep = 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) {
571 return (EEXIST);
573 cfgtgt = cfgtgt->tgt_next;
576 /* Too many targets? */
577 if (cfg->config_tgt_count >= MAX_TARGETS) {
578 return (E2BIG);
581 ptr = calloc(1, sizeof (it_tgt_t));
582 if (ptr == NULL) {
583 return (ENOMEM);
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++;
592 *tgt = ptr;
594 return (0);
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.
605 * Parameters:
607 * cfg The current iSCSI configuration obtained from
608 * it_config_load()
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.
614 * Return Values:
615 * 0 Success
616 * EINVAL Invalid property
620 it_tgt_setprop(it_config_t *cfg, it_tgt_t *tgt, nvlist_t *proplist,
621 nvlist_t **errlist)
623 int ret;
624 nvlist_t *errs = NULL;
625 nvlist_t *tprops = NULL;
626 char *val = NULL;
628 if (!cfg || !tgt || !proplist) {
629 return (EINVAL);
632 /* verify the target name in case the target node is renamed */
633 if (!validate_iscsi_name(tgt->tgt_name)) {
634 return (EINVAL);
636 canonical_iscsi_name(tgt->tgt_name);
638 if (errlist) {
639 (void) nvlist_alloc(&errs, 0, 0);
640 *errlist = errs;
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);
649 } else {
650 ret = nvlist_alloc(&tprops, NV_UNIQUE_NAME, 0);
653 if (ret != 0) {
654 return (ret);
657 ret = nvlist_merge(tprops, proplist, 0);
658 if (ret != 0) {
659 nvlist_free(tprops);
660 return (ret);
663 /* unset chap username or alias if requested */
664 val = NULL;
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);
670 val = NULL;
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 */
677 val = NULL;
678 (void) nvlist_lookup_string(proplist, PROP_TARGET_CHAP_SECRET, &val);
679 if (val) {
680 char bsecret[MAX_BASE64_LEN];
682 ret = it_val_pass(PROP_TARGET_CHAP_SECRET, val, errs);
684 if (ret == 0) {
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);
690 if (ret == 0) {
691 /* replace the value in the nvlist */
692 ret = nvlist_add_string(tprops,
693 PROP_TARGET_CHAP_SECRET, bsecret);
698 if (ret == 0) {
699 ret = it_validate_tgtprops(tprops, errs);
702 if (ret != 0) {
703 if (tprops) {
704 nvlist_free(tprops);
706 return (ret);
709 if (tgt->tgt_properties) {
710 nvlist_free(tgt->tgt_properties);
712 tgt->tgt_properties = tprops;
714 free_empty_errlist(errlist);
716 return (0);
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().
728 * Parameters:
729 * cfg The current iSCSI configuration obtained from
730 * it_config_load()
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.
736 * Return Values:
737 * 0 Success
738 * EBUSY Target is online
741 it_tgt_delete(it_config_t *cfg, it_tgt_t *tgt, boolean_t force)
743 int ret;
744 it_tgt_t *ptgt;
745 it_tgt_t *prev = NULL;
746 stmfDevid devid;
747 stmfTargetProperties props;
749 if (!cfg || !tgt) {
750 return (0);
753 ptgt = cfg->config_tgt_list;
754 while (ptgt != NULL) {
755 if (strcasecmp(tgt->tgt_name, ptgt->tgt_name) == 0) {
756 break;
758 prev = ptgt;
759 ptgt = ptgt->tgt_next;
762 if (!ptgt) {
763 return (0);
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) {
773 /* can't happen? */
774 return (EINVAL);
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) {
784 if (!force) {
785 return (EBUSY);
787 ret = stmfOfflineTarget(&devid);
788 if (ret != 0) {
789 return (EBUSY);
794 if (prev) {
795 prev->tgt_next = ptgt->tgt_next;
796 } else {
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--;
804 it_tgt_free(ptgt);
806 return (0);
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.
815 void
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().
830 * Parameters:
831 * cfg The current iSCSI configuration obtained from
832 * it_config_load()
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
841 * will be chosen.
843 * Return Values:
844 * 0 Success
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;
855 it_tpgt_t *cfgt;
856 char tagid_used[MAXTAG + 1];
857 uint16_t tagid = ISCSIT_DEFAULT_TPGT;
859 if (!cfg || !tgt || !tpgt || !tpg_name) {
860 return (EINVAL);
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) {
880 return (EEXIST);
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 */
893 tagid = tpgt_tag;
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) {
901 break;
904 if (tagid >= MAXTAG) {
905 return (E2BIG);
907 } else {
908 /* next available ID */
909 tagid++;
912 ptr = calloc(1, sizeof (it_tpgt_t));
913 if (!ptr) {
914 return (ENOMEM);
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++;
927 *tpgt = ptr;
929 return (0);
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().
940 * Parameters:
941 * cfg The current iSCSI configuration obtained from
942 * it_config_load()
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
947 void
948 it_tpgt_delete(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t *tpgt)
950 it_tpgt_t *ptr;
951 it_tpgt_t *prev = NULL;
953 if (!cfg || !tgt || !tpgt) {
954 return;
957 ptr = tgt->tgt_tpgt_list;
958 while (ptr) {
959 if (ptr->tpgt_tag == tpgt->tpgt_tag) {
960 break;
962 prev = ptr;
963 ptr = ptr->tpgt_next;
966 if (!ptr) {
967 return;
970 if (prev) {
971 prev->tpgt_next = ptr->tpgt_next;
972 } else {
973 tgt->tgt_tpgt_list = ptr->tpgt_next;
975 ptr->tpgt_next = NULL;
977 tgt->tgt_tpgt_count--;
978 tgt->tgt_generation++;
980 it_tpgt_free(ptr);
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.
989 void
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().
1004 * Parameters:
1005 * cfg The current iSCSI configuration obtained from
1006 * it_config_load()
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.
1015 * Return Values:
1016 * 0 Success
1017 * ENOMEM Cannot allocate resources
1018 * EINVAL Invalid parameter
1019 * EEXIST Requested portal in use by another target portal
1020 * group
1023 it_tpg_create(it_config_t *cfg, it_tpg_t **tpg, char *tpg_name,
1024 char *portal_ip_port)
1026 int ret;
1027 it_tpg_t *ptr;
1028 it_portal_t *portal = NULL;
1030 if (!cfg || !tpg || !tpg_name || !portal_ip_port) {
1031 return (EINVAL);
1034 *tpg = NULL;
1036 ptr = cfg->config_tpg_list;
1037 while (ptr) {
1038 if (strcmp(tpg_name, ptr->tpg_name) == 0) {
1039 break;
1041 ptr = ptr->tpg_next;
1044 if (ptr) {
1045 return (EEXIST);
1048 ptr = calloc(1, sizeof (it_tpg_t));
1049 if (!ptr) {
1050 return (ENOMEM);
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);
1058 if (ret != 0) {
1059 free(ptr);
1060 return (ret);
1063 ptr->tpg_next = cfg->config_tpg_list;
1064 cfg->config_tpg_list = ptr;
1065 cfg->config_tpg_count++;
1067 *tpg = ptr;
1069 return (0);
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().
1080 * Parameters:
1081 * cfg The current iSCSI configuration obtained from
1082 * it_config_load()
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.
1088 * Return Values:
1089 * 0 Success
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)
1096 it_tpg_t *ptr;
1097 it_tpg_t *prev = NULL;
1098 it_tgt_t *tgt;
1099 it_tpgt_t *tpgt;
1100 it_tpgt_t *ntpgt;
1102 if (!cfg || !tpg) {
1103 return (EINVAL);
1106 ptr = cfg->config_tpg_list;
1107 while (ptr) {
1108 if (strcmp(ptr->tpg_name, tpg->tpg_name) == 0) {
1109 break;
1111 prev = ptr;
1112 ptr = ptr->tpg_next;
1115 if (!ptr) {
1116 return (0);
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;
1124 while (tgt) {
1125 tpgt = tgt->tgt_tpgt_list;
1126 while (tpgt) {
1127 ntpgt = tpgt->tpgt_next;
1129 if (strcmp(tpgt->tpgt_tpg_name, tpg->tpg_name)
1130 == 0) {
1131 if (!force) {
1132 return (EBUSY);
1134 it_tpgt_delete(cfg, tgt, tpgt);
1137 tpgt = ntpgt;
1139 tgt = tgt->tgt_next;
1142 /* Now that it's not in use anywhere, remove the TPG */
1143 if (prev) {
1144 prev->tpg_next = ptr->tpg_next;
1145 } else {
1146 cfg->config_tpg_list = ptr->tpg_next;
1148 ptr->tpg_next = NULL;
1150 cfg->config_tpg_count--;
1152 it_tpg_free(ptr);
1154 return (0);
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.
1163 void
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().
1177 * Parameters:
1178 * cfg The current iSCSI configration obtained from
1179 * it_config_load()
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
1183 * the portal
1184 * portal_ip_port A string containing an appropriately formatted
1185 * IP address or IP address:port in either IPv4 or
1186 * IPv6 format.
1187 * Return Values:
1188 * 0 Success
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;
1198 it_portal_t *ptr;
1199 it_tpg_t *ctpg = NULL;
1201 if (!cfg || !tpg || !portal || !portal_ip_port) {
1202 return (EINVAL);
1205 if ((it_common_convert_sa(portal_ip_port, &sa, ISCSI_LISTEN_PORT))
1206 == NULL) {
1207 return (EINVAL);
1210 /* Check that this portal doesn't appear in any other tag */
1211 ctpg = cfg->config_tpg_list;
1212 while (ctpg) {
1213 ptr = ctpg->tpg_portal_list;
1214 for (; ptr != NULL; ptr = ptr->portal_next) {
1215 if (it_sa_compare(&(ptr->portal_addr), &sa) != 0) {
1216 continue;
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) {
1224 return (0);
1225 } else {
1226 /* Not allowed */
1227 return (EEXIST);
1230 ctpg = ctpg->tpg_next;
1233 ptr = calloc(1, sizeof (it_portal_t));
1234 if (!ptr) {
1235 return (ENOMEM);
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++;
1245 return (0);
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().
1255 * Parameters:
1256 * cfg The current iSCSI configration obtained from
1257 * it_config_load()
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
1261 * the portal
1263 void
1264 it_portal_delete(it_config_t *cfg, it_tpg_t *tpg, it_portal_t *portal)
1266 it_portal_t *ptr;
1267 it_portal_t *prev = NULL;
1269 if (!cfg || !tpg || !portal) {
1270 return;
1273 ptr = tpg->tpg_portal_list;
1274 while (ptr) {
1275 if (memcmp(&(ptr->portal_addr), &(portal->portal_addr),
1276 sizeof (ptr->portal_addr)) == 0) {
1277 break;
1279 prev = ptr;
1280 ptr = ptr->portal_next;
1283 if (!ptr) {
1284 return;
1287 if (prev) {
1288 prev->portal_next = ptr->portal_next;
1289 } else {
1290 tpg->tpg_portal_list = ptr->portal_next;
1292 tpg->tpg_portal_count--;
1293 tpg->tpg_generation++;
1295 free(ptr);
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().
1305 * Parameters:
1306 * cfg The current iSCSI configration obtained from
1307 * it_config_load()
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.
1312 * Return Values:
1313 * 0 Success
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)
1321 it_ini_t *ptr;
1323 if (!cfg || !ini || !ini_node_name) {
1324 return (EINVAL);
1328 * Ensure this is a valid ini name
1330 if (!validate_iscsi_name(ini_node_name)) {
1331 return (EFAULT);
1334 ptr = cfg->config_ini_list;
1335 while (ptr) {
1336 if (strcasecmp(ptr->ini_name, ini_node_name) == 0) {
1337 break;
1339 ptr = ptr->ini_next;
1342 if (ptr) {
1343 return (EEXIST);
1346 ptr = calloc(1, sizeof (it_ini_t));
1347 if (!ptr) {
1348 return (ENOMEM);
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++;
1359 *ini = ptr;
1361 return (0);
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.
1372 * Parameters:
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.
1379 * Return Values:
1380 * 0 Success
1381 * EINVAL Invalid property
1385 it_ini_setprop(it_ini_t *ini, nvlist_t *proplist, nvlist_t **errlist)
1387 int ret;
1388 nvlist_t *errs = NULL;
1389 nvlist_t *iprops = NULL;
1390 char *val = NULL;
1392 if (!ini || !proplist) {
1393 return (EINVAL);
1396 if (errlist) {
1397 (void) nvlist_alloc(&errs, 0, 0);
1398 *errlist = errs;
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);
1407 } else {
1408 ret = nvlist_alloc(&iprops, NV_UNIQUE_NAME, 0);
1411 if (ret != 0) {
1412 return (ret);
1415 ret = nvlist_merge(iprops, proplist, 0);
1416 if (ret != 0) {
1417 nvlist_free(iprops);
1418 return (ret);
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);
1433 if (ret == 0) {
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);
1439 if (ret == 0) {
1440 /* replace the value in the nvlist */
1441 ret = nvlist_add_string(iprops,
1442 PROP_CHAP_SECRET, bsecret);
1447 if (ret == 0) {
1448 ret = it_validate_iniprops(iprops, errs);
1451 if (ret != 0) {
1452 if (iprops) {
1453 nvlist_free(iprops);
1455 return (ret);
1458 if (ini->ini_properties) {
1459 nvlist_free(ini->ini_properties);
1461 ini->ini_properties = iprops;
1463 free_empty_errlist(errlist);
1465 return (0);
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().
1475 * Parameters:
1476 * cfg The current iSCSI configration obtained from
1477 * it_config_load()
1478 * ini Pointer to the it_ini_t structure representing
1479 * the initiator context.
1481 void
1482 it_ini_delete(it_config_t *cfg, it_ini_t *ini)
1484 it_ini_t *ptr;
1485 it_ini_t *prev = NULL;
1487 if (!cfg || !ini) {
1488 return;
1491 ptr = cfg->config_ini_list;
1492 while (ptr) {
1493 if (strcasecmp(ptr->ini_name, ini->ini_name) == 0) {
1494 break;
1496 prev = ptr;
1497 ptr = ptr->ini_next;
1500 if (!ptr) {
1501 return;
1504 if (prev) {
1505 prev->ini_next = ptr->ini_next;
1506 } else {
1507 cfg->config_ini_list = ptr->ini_next;
1510 ptr->ini_next = NULL; /* Only free this initiator */
1512 cfg->config_ini_count--;
1514 it_ini_free(ptr);
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.
1523 void
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.
1534 static int
1535 it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs)
1537 int errcnt = 0;
1538 nvpair_t *nvp = NULL;
1539 data_type_t nvtype;
1540 char *name;
1541 char *val;
1542 char *auth = NULL;
1544 if (!nvl) {
1545 return (0);
1548 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1549 name = nvpair_name(nvp);
1550 nvtype = nvpair_type(nvp);
1552 if (!name) {
1553 continue;
1556 val = NULL;
1557 if (strcmp(name, PROP_TARGET_CHAP_USER) == 0) {
1558 if (nvtype != DATA_TYPE_STRING) {
1559 PROPERR(errs, name,
1560 gettext("must be a string value"));
1561 errcnt++;
1562 continue;
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);
1573 if (!val) {
1574 PROPERR(errs, name,
1575 gettext("must be a string value"));
1576 errcnt++;
1577 continue;
1579 } else if (strcmp(name, PROP_ALIAS) == 0) {
1580 if (nvtype != DATA_TYPE_STRING) {
1581 PROPERR(errs, name,
1582 gettext("must be a string value"));
1583 errcnt++;
1584 continue;
1586 } else if (strcmp(name, PROP_AUTH) == 0) {
1587 if (nvtype == DATA_TYPE_STRING) {
1588 val = NULL;
1589 (void) nvpair_value_string(nvp, &val);
1592 if (!val) {
1593 PROPERR(errs, name,
1594 gettext("must be a string value"));
1595 errcnt++;
1596 continue;
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"));
1604 errcnt++;
1606 auth = val;
1607 continue;
1608 } else if (strcmp(name, PROP_OLD_TARGET_NAME) == 0) {
1609 continue;
1610 } else {
1611 /* unrecognized property */
1612 PROPERR(errs, name, gettext("unrecognized property"));
1613 errcnt++;
1617 if (errcnt) {
1618 return (EINVAL);
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);
1626 return (0);
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.
1634 static int
1635 it_validate_configprops(nvlist_t *nvl, nvlist_t *errs)
1637 int errcnt = 0;
1638 nvpair_t *nvp = NULL;
1639 data_type_t nvtype;
1640 char *name;
1641 char *val;
1642 struct sockaddr_storage sa;
1643 boolean_t update_rad_server = B_FALSE;
1644 char *rad_server;
1645 char *auth = NULL;
1647 if (!nvl) {
1648 return (0);
1651 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1652 name = nvpair_name(nvp);
1653 nvtype = nvpair_type(nvp);
1655 if (!name) {
1656 continue;
1659 val = NULL;
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) {
1667 if (!val) {
1668 PROPERR(errs, name,
1669 gettext("must be a string value"));
1670 errcnt++;
1672 } else if (strcmp(name, PROP_AUTH) == 0) {
1673 if (!val) {
1674 PROPERR(errs, name,
1675 gettext("must be a string value"));
1676 errcnt++;
1677 continue;
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"));
1685 errcnt++;
1688 auth = val;
1690 } else if (strcmp(name, PROP_ISNS_ENABLED) == 0) {
1691 if (nvtype != DATA_TYPE_BOOLEAN_VALUE) {
1692 PROPERR(errs, name,
1693 gettext("must be a boolean value"));
1694 errcnt++;
1696 } else if (strcmp(name, PROP_ISNS_SERVER) == 0) {
1697 char **arr = NULL;
1698 uint32_t acount = 0;
1700 (void) nvlist_lookup_string_array(nvl, name,
1701 &arr, &acount);
1703 while (acount > 0) {
1704 if (strcasecmp(arr[acount - 1], "none") == 0) {
1705 break;
1707 if ((it_common_convert_sa(arr[acount - 1],
1708 &sa, 0)) == NULL) {
1709 PROPERR(errs, arr[acount - 1],
1710 gettext("invalid address"));
1711 errcnt++;
1713 acount--;
1716 } else if (strcmp(name, PROP_RADIUS_SECRET) == 0) {
1717 if (!val) {
1718 PROPERR(errs, name,
1719 gettext("must be a string value"));
1720 errcnt++;
1721 continue;
1723 } else if (strcmp(name, PROP_RADIUS_SERVER) == 0) {
1724 struct sockaddr_storage sa;
1725 if (!val) {
1726 PROPERR(errs, name,
1727 gettext("must be a string value"));
1728 errcnt++;
1729 continue;
1732 if ((it_common_convert_sa(val, &sa,
1733 DEFAULT_RADIUS_PORT)) == NULL) {
1734 PROPERR(errs, name,
1735 gettext("invalid address"));
1736 errcnt++;
1737 } else {
1739 * rewrite this property to ensure port
1740 * number is added.
1743 if (sockaddr_to_str(&sa, &rad_server) == 0) {
1744 update_rad_server = B_TRUE;
1747 } else {
1748 /* unrecognized property */
1749 PROPERR(errs, name, gettext("unrecognized property"));
1750 errcnt++;
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);
1760 free(rad_server);
1764 * if auth = radius, ensure radius server & secret are set.
1766 if (auth) {
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"));
1772 errcnt++;
1774 if (!nvlist_exists(nvl, PROP_RADIUS_SECRET)) {
1775 PROPERR(errs, PROP_RADIUS_SECRET,
1776 gettext("missing required property"));
1777 errcnt++;
1782 if (errcnt) {
1783 return (EINVAL);
1786 return (0);
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.
1794 static int
1795 it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs)
1797 int errcnt = 0;
1798 nvpair_t *nvp = NULL;
1799 data_type_t nvtype;
1800 char *name;
1801 char *val;
1803 if (!nvl) {
1804 return (0);
1807 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1808 name = nvpair_name(nvp);
1809 nvtype = nvpair_type(nvp);
1811 if (!name) {
1812 continue;
1815 if (strcmp(name, PROP_CHAP_USER) == 0) {
1816 if (nvtype != DATA_TYPE_STRING) {
1817 PROPERR(errs, name,
1818 gettext("must be a string value"));
1819 errcnt++;
1820 continue;
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) {
1828 val = NULL;
1829 (void) nvpair_value_string(nvp, &val);
1832 if (!val) {
1833 PROPERR(errs, name,
1834 gettext("must be a string value"));
1835 errcnt++;
1836 continue;
1838 } else {
1839 /* unrecognized property */
1840 PROPERR(errs, name, gettext("unrecognized property"));
1841 errcnt++;
1845 if (errcnt) {
1846 return (EINVAL);
1849 return (0);
1852 static int
1853 it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix)
1855 int ret;
1856 uuid_t id;
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);
1865 } else {
1866 ret = snprintf(iqn_buf, iqn_buf_len, DEFAULT_IQN
1867 "%02d:%s", TARGET_NAME_VERS, id_str);
1870 if (ret > iqn_buf_len) {
1871 return (1);
1874 return (0);
1877 static int
1878 it_val_pass(char *name, char *val, nvlist_t *e)
1880 size_t sz;
1882 if (!name || !val) {
1883 return (EINVAL);
1887 * must be at least 12 chars and less than 256 chars cleartext.
1889 sz = strlen(val);
1892 * Since we will be automatically encoding secrets we don't really
1893 * need the prefix anymore.
1895 if (sz < 12) {
1896 PROPERR(e, name, gettext("secret too short"));
1897 } else if (sz > 255) {
1898 PROPERR(e, name, gettext("secret too long"));
1899 } else {
1900 /* all is well */
1901 return (0);
1904 return (1);
1908 * Function: validate_iscsi_name()
1910 * Ensures the passed-in string is a valid IQN or EUI iSCSI name
1913 boolean_t
1914 validate_iscsi_name(char *in_name)
1916 size_t in_len;
1917 int i;
1918 char month[3];
1920 if (in_name == NULL) {
1921 return (B_FALSE);
1924 in_len = strlen(in_name);
1925 if (in_len < 12) {
1926 return (B_FALSE);
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] != '.')) {
1941 return (B_FALSE);
1944 (void) strncpy(month, &(in_name[9]), 2);
1945 month[2] = '\0';
1947 i = atoi(month);
1948 if ((i < 0) || (i > 12)) {
1949 return (B_FALSE);
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)) {
1963 return (B_FALSE);
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) {
1970 return (B_FALSE);
1972 } else if (IS_EUI_NAME(in_name)) {
1974 * EUI names are "eui." + 16 hex chars
1976 if (in_len != 20) {
1977 return (B_FALSE);
1980 for (i = 4; i < in_len; i++) {
1981 if (!isxdigit(in_name[i])) {
1982 return (B_FALSE);
1985 } else {
1986 return (B_FALSE);
1989 return (B_TRUE);
1992 static boolean_t
1993 is_iscsit_enabled(void)
1995 char *state;
1997 state = smf_get_state(ISCSIT_FMRI);
1998 if (state != NULL) {
1999 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
2000 free(state);
2001 return (B_TRUE);
2003 free(state);
2006 return (B_FALSE);
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
2016 void
2017 canonical_iscsi_name(char *tgt)
2019 if (IS_IQN_NAME(tgt)) {
2020 /* lowercase iqn names */
2021 iqnstr(tgt);
2022 } else {
2023 /* uppercase EUI-64 identifier */
2024 euistr(tgt);
2029 * Fold an iqn name to lower-case.
2031 static void
2032 iqnstr(char *s)
2034 if (s != NULL) {
2035 while (*s) {
2036 *s = tolower(*s);
2037 s++;
2043 * Fold the EUI-64 identifier of a eui name to upper-case.
2045 static void
2046 euistr(char *s)
2048 if (s != NULL) {
2049 char *l = s + 4;
2050 while (*l) {
2051 *l = toupper(*l);
2052 l++;
2057 static void
2058 free_empty_errlist(nvlist_t **errlist)
2060 if (errlist != NULL && *errlist != NULL) {
2061 assert(fnvlist_num_pairs(*errlist) == 0);
2062 nvlist_free(*errlist);
2063 *errlist = NULL;