OpenSolaris_b135/cmd/iscsi/iscsitgtd/util.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <utility.h>
#include <fcntl.h>
#include <syslog.h>

#include <iscsitgt_impl.h>
#include "target.h"
#include "utility.h"
#include "errcode.h"
#include "isns_client.h"
#include <sys/scsi/generic/commands.h>
#include "mgmt_scf.h"
#include "t10_spc.h"

#define	CRC32_STR	"CRC32C"
#define	NONE_STR	"None"

static thick_provo_t	*thick_head,
			*thick_tail;
pthread_mutex_t		thick_mutex;

static Boolean_t connection_parameters_get(iscsi_conn_t *c, char *targ_name);
static Boolean_t util_create_guid_naa(char **guid);

void
util_init()
{
	(void) pthread_mutex_init(&thick_mutex, NULL);
}

/*
 * []----
 * | check_access -- see if the requesting initiator is in the ACL
 * |
 * | Optionally will also check to see if this initiator requires
 * | authentication.
 * []----
 */
Boolean_t
check_access(tgt_node_t *targ, char *initiator_name, Boolean_t req_chap)
{
	tgt_node_t	*acl;
	tgt_node_t	*inode		= NULL;
	tgt_node_t	*tgt_initiator	= NULL;
	char		*dummy;
	Boolean_t	valid		= False;
	Boolean_t	found_chap	= False;
	Boolean_t	access		= False;

	/*
	 * If ISNS is enable check for access privilege from isns server
	 */
	if (isns_enabled() == True) {
		if (tgt_find_value_str(targ, XML_ELEMENT_INAME, &dummy)
		    == False) {
			return (False);
		}
		access = isns_qry_initiator(dummy, initiator_name);
		free(dummy);
		if (req_chap == False) {
			return (access);
		}

		/* Need to check if CHAP is needed for initiator */
		while ((inode = tgt_node_next_child(main_config,
		    XML_ELEMENT_INIT, inode)) != NULL) {
			if (tgt_find_value_str(inode, XML_ELEMENT_INAME, &dummy)
			    == True) {
				if (strcmp(dummy, initiator_name) == 0) {
					free(dummy);
					if (tgt_find_value_str(inode,
					    XML_ELEMENT_CHAPSECRET, &dummy)
					    == True) {
						free(dummy);
						found_chap = True;
						break;
					}
				}
			}
		}
		if (access == True) {
			if ((req_chap == True) && (found_chap == True))
				access = False;
		}
		return (access);
	}

	/*
	 * If there's no ACL for this target everyone has access.
	 */
	if ((acl = tgt_node_next(targ, XML_ELEMENT_ACLLIST, NULL)) == NULL)
		return (True);

	/*
	 * Find the local initiator name and also save the knowledge
	 * if the initiator had a CHAP secret.
	 */
	inode = NULL;
	while ((inode = tgt_node_next_child(main_config, XML_ELEMENT_INIT,
	    inode)) != NULL) {
		if (tgt_find_value_str(inode, XML_ELEMENT_INAME, &dummy) ==
		    True) {
			if (strcmp(dummy, initiator_name) == 0) {
				free(dummy);
				if (tgt_find_value_str(inode,
				    XML_ELEMENT_CHAPSECRET, &dummy) == True) {
					free(dummy);
					found_chap = True;
				}
				break;
			} else {
				free(dummy);
			}
		}
	}

	if ((acl != NULL) && (inode == NULL))
		return (False);

	while ((tgt_initiator = tgt_node_next(acl, XML_ELEMENT_INIT,
	    tgt_initiator)) != NULL) {

		if (strcmp(inode->x_value, tgt_initiator->x_value) == 0) {
			valid = True;
			break;
		}
	}

	if (valid == True) {

		/*
		 * If req_chap is True it means the login code hasn't gone
		 * through the authentication phase and it's trying to
		 * determine if the initiator should have done so. If
		 * we find a CHAP-secret then this routine will fail.
		 * No CHAP-secret for an initiator just means that a
		 * simple ACL list is used. This can be spoofed easily
		 * enough and is mainly used to limit the number of
		 * targets an initiator would see.
		 */
		if ((req_chap == True) && (found_chap == True))
			valid = False;
	}

	return (valid);
}

/*
 * []----
 * | convert_local_tpgt -- Convert a local tpgt name to real addresses
 * |
 * | To simplify the configuration files targets only have a target portal
 * | group tag string(s) associated. In the main configuration file there's
 * | a tpgt element which has one or more ip-address elements. So the tag
 * | is located and the actual data is inserted into the outgoing stream.
 * []----
 */
static Boolean_t
convert_local_tpgt(char **text, int *text_length, char *local_tpgt)
{
	tgt_node_t	*tpgt	= NULL;
	tgt_node_t 	*x;
	char		buf[80];
	char		ipaddr[4];

	while ((tpgt = tgt_node_next_child(main_config, XML_ELEMENT_TPGT,
	    tpgt)) != NULL) {
		if (strcmp(tpgt->x_value, local_tpgt) == 0) {

			/*
			 * The only children of the tpgt element are
			 * ip-address elements. The value of each element is
			 * the string we need to use. So, we don't need to
			 * check the node's name to see if this is correct or
			 * not.
			 */
			if ((tpgt = tgt_node_next(tpgt, XML_ELEMENT_IPADDRLIST,
			    NULL)) == NULL) {
				return (False);
			}

			x = NULL;
			while ((x = tgt_node_next(tpgt, XML_ELEMENT_IPADDR, x))
			    != NULL) {
				if (inet_pton(AF_INET, x->x_value, &ipaddr)
				    == 1) {
					/*
					 * Valid IPv4 address
					 */
					(void) snprintf(buf, sizeof (buf),

					    "%s,%s", x->x_value, local_tpgt);
				} else {
					/*
					 * Invalid IPv4 address
					 * try with brackets (RFC2732)
					 */
					(void) snprintf(buf, sizeof (buf),
					    "[%s],%s", x->x_value, local_tpgt);
				}
				(void) add_text(text, text_length,
				    "TargetAddress", buf);
			}
			break;
		}
	}

	return (True);
}

/*
 * []----
 * | add_target_address -- find and add any target address information
 * []----
 */
static void
add_target_address(iscsi_conn_t *c, char **text, int *text_length,
    tgt_node_t *targ)
{
	tgt_node_t	*tpgt_list;
	tgt_node_t	*tpgt = NULL;
	struct sockaddr_in	*sp4;
	struct sockaddr_in6	*sp6;
	/*
	 * 7 is enough room for the largest TPGT of "65536", the ',' and a NULL
	 */
	char	buf[INET6_ADDRSTRLEN + 7];
	char	net_buf[INET6_ADDRSTRLEN];

	if ((tpgt_list = tgt_node_next(targ, XML_ELEMENT_TPGTLIST,
	    NULL)) == NULL) {
		if_target_address(text, text_length,
		    (struct sockaddr *)&c->c_target_sockaddr);
		return;
	}

	while ((tpgt = tgt_node_next_child(tpgt_list, XML_ELEMENT_TPGT,
	    tpgt)) != NULL) {
		if (convert_local_tpgt(text, text_length, tpgt->x_value) ==
		    False) {
			if (c->c_target_sockaddr.ss_family == AF_INET) {
				sp4 = (struct sockaddr_in *)
				    &c->c_target_sockaddr;
				(void) snprintf(buf, sizeof (buf), "%s,%s",
				    inet_ntop(sp4->sin_family,
				    (void *)&sp4->sin_addr,
				    net_buf, sizeof (net_buf)),
				    tpgt->x_value);
			} else {
				sp6 = (struct sockaddr_in6 *)
				    &c->c_target_sockaddr;
				(void) snprintf(buf, sizeof (buf), "[%s],%s",
				    inet_ntop(sp6->sin6_family,
				    (void *)&sp6->sin6_addr,
				    net_buf, sizeof (net_buf)),
				    tpgt->x_value);
			}
			(void) add_text(text, text_length, "TargetAddress",
			    buf);
		}
	}
}

/*
 * []----
 * | add_targets -- add TargetName and TargetAddress to text argument
 * |
 * | Add targets which this initiator is allowed to see based on
 * | the access_list associated with a target. If a target doesn't
 * | have an access list then let everyone see it.
 * []----
 */
static Boolean_t
add_targets(iscsi_conn_t *c, char **text, int *text_length)
{
	tgt_node_t	*targ		= NULL;
	Boolean_t	rval		= True;
	char		*targ_name	= NULL;

	while ((rval == True) && ((targ = tgt_node_next_child(targets_config,
	    XML_ELEMENT_TARG, targ)) != NULL)) {

		if (check_access(targ, c->c_sess->s_i_name, False) == True) {

			if (tgt_find_value_str(targ, XML_ELEMENT_INAME,
			    &targ_name) == False) {
				rval = False;
				break;
			}
			queue_prt(c->c_mgmtq, Q_CONN_LOGIN,
			    "CON%x    %24s = %s\n", c->c_num, "TargetName",
			    targ_name);

			(void) add_text(text, text_length, "TargetName",
			    targ_name);
			free(targ_name);
			add_target_address(c, text, text_length, targ);
		}
	}
	return (rval);
}

/*
 * []----
 * | add_text -- Add new name/value pair to possibly existing string
 * []----
 */
Boolean_t
add_text(char **text, int *current_length, char *name, char *val)
{
	int	dlen = *current_length;
	int	plen;
	char	*p;

	/*
	 * Length is 'name' + separator + 'value' + NULL
	 */
	plen = strlen(name) + 1 + strlen(val) + 1;

	if (dlen) {
		if ((p = (char *)realloc(*text, dlen + plen)) == NULL)
			return (False);
	} else {
		if ((p = (char *)malloc(plen)) == NULL)
			return (False);
	}

	*text = p;
	p = *text + dlen;

	(void) snprintf(p, plen, "%s%c%s", name, ISCSI_TEXT_SEPARATOR, val);
	*current_length = dlen + plen;

	return (True);
}

static void
send_named_msg(iscsi_conn_t *c, msg_type_t t, char *name)
{
	target_queue_t	*q = queue_alloc();
	msg_t		*m;
	name_request_t	n;

	n.nr_q		= q;
	n.nr_name	= name;

	queue_message_set(c->c_sessq, 0, t, &n);
	m = queue_message_get(q);
	queue_message_free(m);
	queue_free(q, NULL);
}

static Boolean_t
parse_digest_vals(Boolean_t *bp, char *name, char *val, char **text, int *len)
{
	Boolean_t	rval;

	/*
	 * It's the initiators data so we'll allow them
	 * to determine if CRC checks should be enabled
	 * or not. So, look at the first token, which
	 * declares their preference, and use that.
	 */
	if (strncmp(val, CRC32_STR, strlen(CRC32_STR)) == 0) {
		*bp = True;
		rval = add_text(text, len, name, CRC32_STR);
	} else if (strncmp(val, NONE_STR, strlen(NONE_STR)) == 0) {
		*bp = False;
		rval = add_text(text, len, name, NONE_STR);
	} else {
		*bp = False;
		rval = add_text(text, len, name, "Reject");
	}

	return (rval);
}

/*
 * []----
 * | parse_text -- receive text information from initiator and parse
 * |
 * | Read in the current data based on the amount which the login PDU says
 * | should be available. Add it to the end of previous data if it exists.
 * | Previous data would be from a PDU which had the 'C' bit set and was
 * | stored in the connection.
 * |
 * | Once values for parameter name has been selected store outgoing string
 * | in text message for response.
 * |
 * | If errcode is non-NULL the appropriate login error code will be
 * | stored.
 * []----
 */
Boolean_t
parse_text(iscsi_conn_t *c, int dlen, char **text, int *text_length,
    int *errcode)
{
	char		*p		= NULL;
	char		*n;
	char		*cur_pair;
	char		param_rsp[32];
	int		plen;		/* pair length */
	Boolean_t	rval		= True;
	char		*target_name    = NULL;
	char		*initiator_name = NULL;
	char		param_buf[16];

	if ((p = (char *)malloc(dlen)) == NULL)
		return (False);

	/*
	 * Read in data to buffer.
	 */
	if (read(c->c_fd, p, dlen) != dlen) {
		free(p);
		return (False);
	}

	queue_prt(c->c_mgmtq, Q_CONN_NONIO, "CON%x  Available text size %d\n",
	    c->c_num, dlen);

	/*
	 * Read in and toss any pad data
	 */
	if (dlen % ISCSI_PAD_WORD_LEN) {
		char junk[ISCSI_PAD_WORD_LEN];
		int pad_len = ISCSI_PAD_WORD_LEN - (dlen % ISCSI_PAD_WORD_LEN);

		if (read(c->c_fd, junk, pad_len) != pad_len) {
			free(p);
			return (False);
		}
	}

	if (c->c_text_area != NULL) {
		if ((n = (char *)realloc(c->c_text_area,
		    c->c_text_len + dlen)) == NULL) {
			free(p);
			return (False);
		}
		bcopy(p, n + c->c_text_len, dlen);

		/*
		 * No longer need the space allocated to 'p' since it
		 * will point to the aggregated area of all data.
		 */
		free(p);

		/*
		 * Point 'p' to this new area for parsing and save the
		 * combined length in dlen.
		 */
		p = n;
		dlen += c->c_text_len;

		/*
		 * Clear the indication that space has been allocated
		 */
		c->c_text_area = NULL;
		c->c_text_len = 0;
	}

	/*
	 * At this point 'p' points to the name/value parameters. Need
	 * to cycle through each pair.
	 */
	n = p;
	while (dlen > 0) {
		cur_pair = n;

		plen = strlen(n);
		if ((n = strchr(cur_pair, ISCSI_TEXT_SEPARATOR)) == NULL) {
			if (errcode != NULL)
				*errcode =
				    (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
				    ISCSI_LOGIN_STATUS_INIT_ERR;
			rval = False;
			break;
		} else
			*n++ = '\0';

		queue_prt(c->c_mgmtq, Q_CONN_LOGIN, "CON%x    %-24s = %s\n",
		    c->c_num, cur_pair, n);

		/*
		 * At this point, 'cur_pair' points at the name and 'n'
		 * points at the value.
		 */

		/*
		 * []--------------------------------------------------[]
		 * | The order of parameters processed matches the	|
		 * | the RFC in section 12.				|
		 * []--------------------------------------------------[]
		 */
		/*
		 * 12.1 -- HeaderDigest
		 * Negotiated
		 */
		if (strcmp("HeaderDigest", cur_pair) == 0) {

			rval = parse_digest_vals(&c->c_header_digest,
			    cur_pair, n, text, text_length);

		/*
		 * 12.1 -- DataDigest
		 * Negotiated
		 */
		} else if (strcmp("DataDigest", cur_pair) == 0) {

			rval = parse_digest_vals(&c->c_data_digest, cur_pair,
			    n, text, text_length);

		/*
		 * 12.2 -- MaxConnections
		 * Negotiated
		 */
		} else if (strcmp("MaxConnections", cur_pair) == 0) {

			/* ---- To be fixed ---- */
			c->c_max_connections = 1;
			(void) snprintf(param_rsp, sizeof (param_rsp),
			    "%d", c->c_max_connections);
			rval = add_text(text, text_length,
			    cur_pair, param_rsp);

		/*
		 * 12.3 -- SendTargets
		 * Declarative
		 */
		} else if (strcmp("SendTargets", cur_pair) == 0) {

			if ((c->c_sess->s_type != SessionDiscovery) &&
			    (strcmp("All", n) == 0)) {
				rval = add_text(text, text_length, cur_pair,
				    "Irrelevant");
			} else {
				rval = add_targets(c, text, text_length);
			}

		/*
		 * 12.4 -- TargetName
		 * Declarative
		 */
		} else if (strcmp("TargetName", cur_pair) == 0) {

			send_named_msg(c, msg_target_name, n);
			target_name = n;

		/*
		 * 12.5 -- IntiatorName
		 * Declarative
		 */
		} else if (strcmp("InitiatorName", cur_pair) == 0) {

			send_named_msg(c, msg_initiator_name, n);
			initiator_name = n;

		/* ---- Section 12.6 is handled within TargetName ---- */

		/*
		 * 12.7 -- InitiatorAlias
		 * Declarative
		 */
		} else if (strcmp("InitiatorAlias", cur_pair) == 0) {

			send_named_msg(c, msg_initiator_alias, n);

		/*
		 * Sections 12.8 (TargetAddress) and 12.9
		 * (TargetPortalGroupTag) are handled during the SendTargets
		 * processing.
		 */

		/*
		 * 12.10 -- IntialR2T
		 * Negotiated
		 */
		} else if (strcmp("InitialR2T", cur_pair) == 0) {

			c->c_initialR2T = True;
			rval = add_text(text, text_length, cur_pair, "Yes");

		/*
		 * 12.11 -- ImmediateData
		 * Negotiated
		 */
		} else if (strcmp("ImmediateData", cur_pair) == 0) {

			/*
			 * Since we can handle immediate data without
			 * a problem just echo back what the initiator
			 * sends. If the initiator decides to violate
			 * the spec by sending immediate data even though
			 * they've disabled it, it's their problem and
			 * we'll deal with the data.
			 */
			c->c_immediate_data = strcmp(n, "No") ? True : False;
			rval = add_text(text, text_length, cur_pair, n);

		/*
		 * 12.12 -- MaxRecvDataSegmentLength
		 * Declarative
		 */
		} else if (strcmp("MaxRecvDataSegmentLength", cur_pair) == 0) {

			c->c_max_recv_data = strtol(n, NULL, 0);
			rval = add_text(text, text_length, cur_pair, n);

		/*
		 * 12.13 -- MaxBurstLength
		 * Negotiated
		 */
		} else if (strcmp("MaxBurstLength", cur_pair) == 0) {

			c->c_max_burst_len = strtol(n, NULL, 0);
			rval = add_text(text, text_length, cur_pair, n);

		/*
		 * 12.14 -- FirstBurstLength
		 * Negotiated
		 */
		} else if (strcmp("FirstBurstLength", cur_pair) == 0) {

			/*
			 * We can handle anything the initiator wishes
			 * to shove in our direction. So, store the value
			 * in case we ever wish to validate input data,
			 * but there's no real need to do so.
			 */
			c->c_first_burst_len = strtol(n, NULL, 0);
			rval = add_text(text, text_length, cur_pair, n);

		/*
		 * 12.15 DefaultTime2Wait
		 * Negotiated
		 */
		} else if (strcmp("DefaultTime2Wait", cur_pair) == 0) {

			c->c_default_time_2_wait = strtol(n, NULL, 0);
			rval = add_text(text, text_length, cur_pair, n);

		/*
		 * 12.16 -- DefaultTime2Retain
		 * Negotiated
		 */
		} else if (strcmp("DefaultTime2Retain", cur_pair) == 0) {

			c->c_default_time_2_retain = strtol(n, NULL, 0);
			rval = add_text(text, text_length, cur_pair, n);

		/*
		 * 12.17 -- MaxOutstandingR2T
		 * Negotiated
		 */
		} else if (strcmp("MaxOutstandingR2T", cur_pair) == 0) {

			/*
			 * Save the value, but at most we'll toss out
			 * one R2T packet.
			 */
			c->c_max_outstanding_r2t = strtol(n, NULL, 0);
			rval = add_text(text, text_length, cur_pair, n);

		/*
		 * 12.18 -- DataPDUInOder
		 * Negotiated
		 */
		} else if (strcmp("DataPDUInOrder", cur_pair) == 0) {

			/*
			 * We can handle DataPDU's out of order and
			 * currently we'll only send them in order. We're
			 * to far removed from the hardware to see data
			 * coming off of the platters out of order so
			 * it's unlikely we'd ever implement this feature.
			 * Store the parameter and echo back the initiators
			 * request.
			 */
			c->c_data_pdu_in_order = strcmp(n, "Yes") == 0 ?
			    True : False;
			rval = add_text(text, text_length, cur_pair, n);

		/*
		 * 12.19 -- DataSequenceInOrder
		 * Negotiated
		 */
		} else if (strcmp("DataSequenceInOrder", cur_pair) == 0) {

			/*
			 * Currently we're set up to look at and require
			 * PDU sequence numbers be in order. The check
			 * now is only done as a prelude to supporting
			 * MC/S and guaranteeing the order of incoming
			 * packets on different connections.
			 */
			c->c_data_sequence_in_order = True;
			rval = add_text(text, text_length, cur_pair, "Yes");

		/*
		 * 12.20 -- ErrorRecoveryLevel
		 * Negotiated
		 */
		} else if (strcmp("ErrorRecoveryLevel", cur_pair) == 0) {

			c->c_erl = 0;
			(void) snprintf(param_rsp, sizeof (param_rsp),
			    "%d", c->c_erl);
			rval = add_text(text, text_length,
			    cur_pair, param_rsp);

		/*
		 * 12.21 -- SessionType
		 * Declarative
		 */
		} else if (strcmp("SessionType", cur_pair) == 0) {

			c->c_sess->s_type = strcmp(n, "Discovery") == 0 ?
			    SessionDiscovery : SessionNormal;


		/*
		 * Appendix A 3.1 -- IFMarker
		 * Negotiated
		 */
		} else if (strcmp("IFMarker", cur_pair) == 0) {

			c->c_ifmarker = False;
			rval = add_text(text, text_length, cur_pair, "No");

		/*
		 * Appendix A 3.1 -- OFMarker
		 * Negotiated
		 */
		} else if (strcmp("OFMarker", cur_pair) == 0) {

			c->c_ofmarker = False;
			rval = add_text(text, text_length, cur_pair, "No");

		} else if ((strcmp("AuthMethod", cur_pair) == 0) ||
		    (strcmp("CHAP_A", cur_pair) == 0) ||
		    (strcmp("CHAP_I", cur_pair) == 0) ||
		    (strcmp("CHAP_C", cur_pair) == 0) ||
		    (strcmp("CHAP_N", cur_pair) == 0) ||
		    (strcmp("CHAP_R", cur_pair) == 0)) {

			rval = add_text(&(c->auth_text), &c->auth_text_length,
			    cur_pair, n);

		} else {

			/*
			 * It's perfectly legitimate for an initiator to
			 * send us a parameter we don't currently understand.
			 * For example, an initiator that supports iSER will
			 * send an RDMA options parameter. If we respond with
			 * a valid return value it knows to switch to iSER
			 * for future processing.
			 */
			rval = add_text(text, text_length,
			    cur_pair, "NotUnderstood");

			/*
			 * Go ahead a log this information in case we see
			 * something unexpected.
			 */
			queue_prt(c->c_mgmtq, Q_CONN_ERRS,
			    "CON%x  Unknown parameter %s=%s\n",
			    c->c_num, cur_pair, n);
		}

		/*
		 * If parsed both Initiator and Target names have been parsed,
		 * then it is now time to load the connection parameters.
		 *
		 * This may fail because the target doesn't exist or the
		 * initiator doesn't have permission to access this target.
		 */
		if ((target_name != NULL) && (initiator_name != NULL)) {
			if ((rval = connection_parameters_get(c, target_name))
			    == False) {
				if ((errcode != NULL) && (*errcode == 0))
					*errcode =
					    (ISCSI_STATUS_CLASS_INITIATOR_ERR
					    << 8) |
					    ISCSI_LOGIN_STATUS_TGT_FORBIDDEN;
			} else if ((rval = add_text(text, text_length,
			    "TargetAlias", c->c_targ_alias)) == True) {

				/*
				 * Add TPGT now
				 */
				(void) snprintf(param_buf, sizeof (param_buf),
				    "%d", c->c_tpgt);
				rval = add_text(text, text_length,
				    "TargetPortalGroupTag", param_buf);
				target_name = initiator_name = NULL;
			}
		}

		if (rval == False) {
			/*
			 * Make sure the caller wants error status and that it
			 * hasn't already been set.
			 */
			if ((errcode != NULL) && (*errcode == 0))
				*errcode =
				    (ISCSI_STATUS_CLASS_TARGET_ERR << 8) |
				    ISCSI_LOGIN_STATUS_TARGET_ERROR;
			break;
		}

		/*
		 * next pair of parameters. 1 is added to include the NULL
		 * byte and the end of each string.
		 */
		n = cur_pair + plen + 1;
		dlen -= (plen + 1);
	}

	if (p != NULL)
		free(p);

	return (rval);
}

/*
 * Pre-seed connection parameters to default values
 * See RFC 3720 Section 12
 */
void
connection_parameters_default(iscsi_conn_t *c)
{
	c->c_max_connections = 1;		/* MaxConnections */
	c->c_tpgt = 1;				/* TargetPortalGroupTag */
	c->c_initialR2T = True;			/* InitialR2T */
	c->c_immediate_data = True;		/* ImmediateData */
	c->c_max_recv_data = 8192;		/* MaxRecvDataSegmentLength */
	c->c_max_burst_len = 262144;		/* MaxBurstLength */
	c->c_first_burst_len = 65536;		/* FirstBurstLength */
	c->c_default_time_2_wait = 2;		/* DefaultTime2Wait */
	c->c_default_time_2_retain = 20;	/* DefaultTime2Retain */
	c->c_max_outstanding_r2t = 1;		/* MaxOutStandingR2T */
	c->c_data_pdu_in_order = True;		/* DataPDUInOrder */
	c->c_data_sequence_in_order = True;	/* DataSequenceOrder */
	c->c_erl = 0;				/* ErrorRecoveryLevel */
}

/*
 * []----
 * | find_main_tpgt -- Looks up the IP address and finds a match TPGT
 * |
 * | If no TPGT for this address exists the routine returns 0 which
 * | is an illegal TPGT value.
 * []----
 */
static int
find_main_tpgt(struct sockaddr_storage *pst)
{
	char		ip_addr[16];
	tgt_node_t	*tpgt				= NULL;
	tgt_node_t	*ip_node			= NULL;
	struct in_addr	addr;
	struct in6_addr	addr6;

	/*
	 * Hardly can you believe that such struct-to-struct
	 * assignment IS valid.
	 */
	addr = ((struct sockaddr_in *)pst)->sin_addr;
	addr6 = ((struct sockaddr_in6 *)pst)->sin6_addr;

	while ((tpgt = tgt_node_next_child(main_config, XML_ELEMENT_TPGT,
	    tpgt)) != NULL) {

		ip_node = NULL;
		while ((ip_node = tgt_node_next(tpgt, XML_ELEMENT_IPADDR,
		    ip_node)) != NULL) {

			if (pst->ss_family == AF_INET) {

				if (inet_pton(AF_INET, ip_node->x_value,
				    ip_addr) != 1) {
					continue;
				}
				if (bcmp(ip_addr, &addr,
					sizeof (struct in_addr)) == 0) {
					return (atoi(tpgt->x_value));
				}
			} else if (pst->ss_family == AF_INET6) {

				if (inet_pton(AF_INET6, ip_node->x_value,
				    ip_addr) != 1) {
					continue;
				}
				if (bcmp(ip_addr, &addr6,
					sizeof (struct in6_addr)) == 0) {
					return (atoi(tpgt->x_value));
				}
			}
		}
	}

	return (0);
}

/*
 * convert_to_tpgt -- return a TPGT based on the target address
 *
 * If a target doesn't have a TPGT list then just return the default
 * value of 1. Otherwise determine which TPGT the target address is
 * part of and find that TPGT value in the list of TPGTs this target
 * is willing to expose. If the TPGT value is not found in the list
 * return zero which will break the connection.
 */
static int
convert_to_tpgt(iscsi_conn_t *c, tgt_node_t *targ)
{
	tgt_node_t	*list;
	tgt_node_t	*tpgt		= NULL;
	int		addr_tpgt, pos_tpgt;

	/*
	 * If this target doesn't have a list of target portal group tags
	 * just return the default which is 1.
	 */
	list = tgt_node_next(targ, XML_ELEMENT_TPGTLIST, NULL);
	if (list == NULL)
		return (1);

	/*
	 * If we don't find our IP in the general configuration list
	 * we'll use the default value which is 1 according to RFC3720.
	 */
	addr_tpgt = find_main_tpgt(&(c->c_target_sockaddr));

	while ((tpgt = tgt_node_next(list, XML_ELEMENT_TPGT, tpgt)) != NULL) {
		(void) tgt_find_value_int(tpgt, XML_ELEMENT_TPGT, &pos_tpgt);
		if (pos_tpgt == addr_tpgt) {
			return (addr_tpgt);
		}
	}

	return (0);
}

/*
 * []----
 * | find_target_node -- given a target IQN name, return the XML node
 * []----
 */
tgt_node_t *
find_target_node(char *targ_name)
{
	tgt_node_t	*tnode	= NULL;
	char		*iname;

	while ((tnode = tgt_node_next_child(targets_config, XML_ELEMENT_TARG,
	    tnode)) != NULL) {
		if (tgt_find_value_str(tnode, XML_ELEMENT_INAME, &iname) ==
		    True) {
			if (strcmp(iname, targ_name) == 0) {
				free(iname);
				return (tnode);
			} else
				free(iname);
		}
	}

	return (NULL);
}

static Boolean_t
connection_parameters_get(iscsi_conn_t *c, char *targ_name)
{
	tgt_node_t	*targ, *alias;
	Boolean_t	rval	= False;

	(void) pthread_rwlock_rdlock(&targ_config_mutex);
	if ((targ = find_target_node(targ_name)) != NULL) {

		if (check_access(targ, c->c_sess->s_i_name, False) == False) {
			(void) pthread_rwlock_unlock(&targ_config_mutex);
			return (False);
		}

		/*
		 * Have a valid node for our target. Start looking
		 * for connection oriented parameters.
		 */
		if ((c->c_tpgt = convert_to_tpgt(c, targ)) == 0) {
			(void) pthread_rwlock_unlock(&targ_config_mutex);
			return (False);
		}
		if ((alias = tgt_node_next(targ, XML_ELEMENT_ALIAS, NULL)) ==
		    NULL) {
			(void) tgt_find_value_str(targ, XML_ELEMENT_TARG,
			    &c->c_targ_alias);
		} else {
			(void) tgt_find_value_str(alias, XML_ELEMENT_ALIAS,
			    &c->c_targ_alias);
		}

		(void) tgt_find_value_int(targ, XML_ELEMENT_MAXCMDS,
		    &c->c_maxcmdsn);
		rval = True;
	}

	(void) pthread_rwlock_unlock(&targ_config_mutex);
	return (rval);
}

Boolean_t
validate_version(tgt_node_t *node, int *maj_p, int *min_p)
{
	char	*vers_str	= NULL;
	char	*minor_part;
	int	maj, min;

	if ((tgt_find_attr_str(node, XML_ELEMENT_VERS, &vers_str) == False) ||
	    (vers_str == NULL))
		return (False);

	maj = strtol(vers_str, &minor_part, 0);
	if ((maj > *maj_p) || (minor_part == NULL) || (*minor_part != '.')) {
		free(vers_str);
		return (False);
	}

	min	= strtol(&minor_part[1], NULL, 0);
	*maj_p	= maj;
	*min_p	= min;
	free(vers_str);

	return (True);
}

/*
 * []----
 * | sna_lt -- Serial Number Arithmetic, 32 bits, less than, RFC1982
 * []----
 */
int
sna_lt(uint32_t n1, uint32_t n2)
{
	return ((n1 != n2) &&
	    (((n1 < n2) && ((n2 - n1) < SNA32_CHECK)) ||
	    ((n1 > n2) && ((n1 - n2) > SNA32_CHECK))));
}

/*
 * []----
 * sna_lte -- Serial Number Arithmetic, 32 bits, less than, RFC1982
 * []----
 */
int
sna_lte(uint32_t n1, uint32_t n2)
{
	return ((n1 == n2) ||
	    (((n1 < n2) && ((n2 - n1) < SNA32_CHECK)) ||
	    ((n1 > n2) && ((n1 - n2) > SNA32_CHECK))));
}

/*
 * util_create_guid -- generate GUID based on the guid type
 * id_type:	SPC_INQUIRY_ID_TYPE_EUI -
 *		EUI-64 based 16-byte designator format;
 *		SPC_INQUIRY_ID_TYPE_NAA -
 *		NAA IEEE Registered Extended designator format.
 *
 * SPC-4 revision 11 section 7.6.3.5.4 and 7.6.3.6.5.
 *
 * Note that now this function is always called with parameter
 * id_type SPC_INQUIRY_ID_TYPE_NAA, therefore the code for creating
 * EUI-64 based 16-byte format GUID is no longer used. But in order
 * to keep backward compatiability and for future extension, all the
 * code that has been used for creating old GUIDs should be kept, to
 * make the format clear for all possible GUIDs targets might have.
 */
Boolean_t
util_create_guid(char **guid, uchar_t id_type)
{
	eui_16_t	eui;
	/*
	 * We only have room for 32bits of data in the GUID. The hiword/loword
	 * macros will not work on 64bit variables. The work, but produce
	 * invalid results on Big Endian based machines.
	 */
	uint32_t	tval = (uint_t)time((time_t *)0);
	size_t		guid_size;
	int		i, fd;

	/*
	 * Create the NAA (6) GUID.
	 */
	if (id_type == SPC_INQUIRY_ID_TYPE_NAA) {
		return (util_create_guid_naa(guid));
	}

	if ((mac_len == 0) && (if_find_mac(NULL) == False)) {

		/*
		 * By default strict GUID generation is enforced. This can
		 * be disabled by using the correct XML tag in the configuration
		 * file.
		 */
		if (enforce_strict_guid == True)
			return (False);

		/*
		 * There's no MAC address available and we've even tried
		 * a second time to get one. So fallback to using a random
		 * number for the MAC address.
		 */
		if ((fd = open("/dev/random", O_RDONLY)) < 0)
			return (False);
		if (read(fd, &eui, sizeof (eui)) != sizeof (eui))
			return (False);
		(void) close(fd);

		eui.e_vers		= SUN_EUI_16_VERS;
		eui.e_company_id[0]	= (SUN_EN >> 16) & 0xff;
		eui.e_company_id[1]	= (SUN_EN >> 8) & 0xff;
		eui.e_company_id[2]	= SUN_EN & 0xff;

	} else {
		bzero(&eui, sizeof (eui));

		eui.e_vers	= SUN_EUI_16_VERS;
		eui.e_company_id[0]	= (SUN_EN >> 16) & 0xff;
		eui.e_company_id[1]	= (SUN_EN >> 8) & 0xff;
		eui.e_company_id[2]	= SUN_EN & 0xff;
		eui.e_timestamp[0]	= hibyte(hiword(tval));
		eui.e_timestamp[1]	= lobyte(hiword(tval));
		eui.e_timestamp[2]	= hibyte(loword(tval));
		eui.e_timestamp[3]	= lobyte(loword(tval));
		for (i = 0; i < min(mac_len, sizeof (eui.e_mac)); i++) {
			eui.e_mac[i] = mac_addr[i];
		}

		/*
		 * To prevent duplicate GUIDs we need to sleep for one
		 * second here since part of the GUID is a time stamp with
		 * a one second resolution.
		 */
		(void) sleep(1);
	}

	if (tgt_xml_encode((uint8_t *)&eui, sizeof (eui), guid,
	    &guid_size) == False) {
		return (False);
	} else
		return (True);
}

Boolean_t
util_create_guid_naa(char **guid)
{
	naa_16_t	naa;
	/*
	 * We only have room for 32bits of data in the GUID. The hiword/loword
	 * macros will not work on 64bit variables. The work, but produce
	 * invalid results on Big Endian based machines.
	 */
	uint32_t	tval = (uint_t)time((time_t *)0);
	size_t		guid_size;
	int		i, fd;

	if ((mac_len == 0) && (if_find_mac(NULL) == False)) {

		/*
		 * By default strict GUID generation is enforced. This can
		 * be disabled by using the correct XML tag in the configuration
		 * file.
		 */
		if (enforce_strict_guid == True)
			return (False);

		/*
		 * There's no MAC address available and we've even tried
		 * a second time to get one. So fallback to using a random
		 * number for the MAC address.
		 */
		if ((fd = open("/dev/random", O_RDONLY)) < 0)
			return (False);
		if (read(fd, &naa, sizeof (naa)) != sizeof (naa))
			return (False);
		(void) close(fd);

	} else {
		bzero(&naa, sizeof (naa));

		/*
		 * Set vendor specific identifier and extension.
		 */
		naa.n_timestamp[0]	= hibyte(hiword(tval));
		naa.n_timestamp[1]	= lobyte(hiword(tval));
		naa.n_timestamp[2]	= hibyte(loword(tval));
		naa.n_timestamp[3]	= lobyte(loword(tval));
		for (i = 0; i < min(mac_len, sizeof (naa.n_mac)); i++) {
			naa.n_mac[i] = mac_addr[i];
		}

		/*
		 * To prevent duplicate GUIDs we need to sleep for one
		 * second here since part of the GUID is a time stamp with
		 * a one second resolution.
		 */
		(void) sleep(1);
	}

	/*
	 * Set NAA (6) and IEEE Company_ID.
	 */
	naa.n_naa		= SUN_NAA_16_TYPE;
	naa.n_company_id_hi	= (SUN_EN >> 20) & 0x0f;
	naa.n_company_id_b1	= (SUN_EN >> 12) & 0xff;
	naa.n_company_id_b2	= (SUN_EN >> 4) & 0xff;
	naa.n_company_id_lo	= SUN_EN & 0x0f;
	if (tgt_xml_encode((uint8_t *)&naa, sizeof (naa), guid,
	    &guid_size) == False) {
		return (False);
	} else
		return (True);
}

/*
 * []----
 * | create_geom -- based on size, determine best fit for CHS
 * |
 * | Given size in bytes, which will be adjusted to blocks, find
 * | the best fit for making (C * H * S == blocks)
 * |
 * | Note that the following algorithm was derived from the
 * | common disk label implementation, cmlb_convert_geometry().
 * |
 * []----
 */
void
create_geom(diskaddr_t size, int *cylinders, int *heads, int *spt)
{
	diskaddr_t	blocks = size >> 9;  /* 512 bytes/block */

	/*
	 * For all devices we calculate cylinders using the heads and sectors
	 * we assign based on capacity of the device.  The algorithm is
	 * designed to be compatible with the way other operating systems
	 * lay out fdisk tables for X86 and to insure that the cylinders never
	 * exceed 65535 to prevent problems with X86 ioctls that report
	 * geometry.
	 * For some smaller disk sizes we report geometry that matches those
	 * used by X86 BIOS usage. For larger disks, we use SPT that are
	 * multiples of 63, since other OSes that are not limited to 16-bits
	 * for cylinders stop at 63 SPT we make do by using multiples of 63 SPT.
	 *
	 * The following table (in order) illustrates some end result
	 * calculations:
	 *
	 * Maximum number of blocks	nhead	nsect
	 *
	 * 2097152 (1GB)		 64	 32
	 * 16777216 (8GB)		128	 32
	 * 1052819775 (502.02GB)	255	 63
	 * 2105639550 (0.98TB)		255	126
	 * 3158459325 (1.47TB)		255	189
	 * 4211279100 (1.96TB)		255	252
	 * 5264098875 (2.45TB)		255	315
	 * ...
	 */

	if (blocks <= 0x200000) {
		*heads = 64;
		*spt = 32;
	} else if (blocks <= 0x01000000) {
		*heads = 128;
		*spt = 32;
	} else {
		*heads = 255;

		/* make sectors-per-track be smallest multiple of 63 */
		*spt = ((blocks +
		    (UINT16_MAX * 255 * 63) - 1) /
		    (UINT16_MAX * 255 * 63)) * 63;

		if (*spt == 0)
			*spt = (UINT16_MAX / 63) * 63;
	}

	/* cyls/dsk = (sectors/dsk) / (sectors/trk * tracks/cyl) */
	*cylinders = blocks / (*spt * *heads);
}

/*
 * []----
 * | strtol_multiplier -- common method to deal with human type numbers
 * []----
 */
Boolean_t
strtoll_multiplier(char *str, uint64_t *sp)
{
	char		*m;
	uint64_t	size;

	size = strtoll(str, &m, 0);
	if (m && *m) {
		switch (*m) {
		case 't':
		case 'T':
			size *= 1024;
			/*FALLTHRU*/
		case 'g':
		case 'G':
			size *= 1024;
			/*FALLTHRU*/
		case 'm':
		case 'M':
			size *= 1024;
			/*FALLTHRU*/
		case 'k':
		case 'K':
			size *= 1024;
			break;

		default:
			return (False);
		}
	}

	*sp = size;
	return (True);
}

/*
 * []----
 * | util_title -- print out start/end title in consistent manner
 * []----
 */
void
util_title(target_queue_t *q, int type, int num, char *title)
{
	char	*type_str;
	int	len, pad;

	len	= strlen(title);
	pad	= len & 1;

	switch (type) {
	case Q_CONN_LOGIN:
	case Q_CONN_NONIO:
		type_str	= "CON";
		break;

	case Q_SESS_LOGIN:
	case Q_SESS_NONIO:
		type_str	= "SES";
		break;

	case Q_STE_NONIO:
		type_str	= "SAM";
		break;

	default:
		type_str	= "UGH";
		break;
	}

	queue_prt(q, type, "%s%x  ---- %*s%s%*s ----\n", type_str, num,
	    ((60 - len) / 2), "", title, ((60 - len) / 2) + pad, "");
}

/*
 * []----
 * | task_to_str -- convert task management event to string (DEBUG USE)
 * []----
 */
char *
task_to_str(int func)
{
	switch (func) {
	case ISCSI_TM_FUNC_ABORT_TASK:		return ("Abort");
	case ISCSI_TM_FUNC_ABORT_TASK_SET:	return ("Abort Set");
	case ISCSI_TM_FUNC_CLEAR_ACA:		return ("Clear ACA");
	case ISCSI_TM_FUNC_CLEAR_TASK_SET:	return ("Clear Task");
	case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:	return ("LUN Reset");
	case ISCSI_TM_FUNC_TARGET_WARM_RESET:	return ("Target Warm Reset");
	case ISCSI_TM_FUNC_TARGET_COLD_RESET:	return ("Target Cold Reset");
	case ISCSI_TM_FUNC_TASK_REASSIGN:	return ("Task Reassign");
	default:				return ("Unknown");
	}
}

/*
 * []----
 * | xml_rtn_msg -- create a common format for XML replies to management UI
 * []----
 */
void
xml_rtn_msg(char **buf, err_code_t code)
{
	char	lbuf[16];

	tgt_buf_add_tag_and_attr(buf, XML_ELEMENT_ERROR, "version='1.0'");
	(void) snprintf(lbuf, sizeof (lbuf), "%d", code);
	tgt_buf_add(buf, XML_ELEMENT_CODE, lbuf);
	tgt_buf_add(buf, XML_ELEMENT_MESSAGE, errcode_to_str(code));
	tgt_buf_add_tag(buf, XML_ELEMENT_ERROR, Tag_End);
}

/*
 * []----
 * | thick_provo_start -- start an initialization thread for targ/lun
 * []----
 */
void *
thick_provo_start(void *v)
{
	thick_provo_t	*tp	= (thick_provo_t *)v;
	msg_t		*m;
	Boolean_t	rval;
	char		*err	= NULL;

	/*
	 * Add this threads information to the main queue. This is
	 * used in case the administrator decides to remove the LU
	 * before the initialization is complete.
	 */
	(void) pthread_mutex_lock(&thick_mutex);
	if (thick_head == NULL) {
		thick_head = tp;
	} else {
		thick_tail->next = tp;
		tp->prev = thick_tail;
	}
	thick_tail = tp;
	(void) pthread_mutex_unlock(&thick_mutex);

	/*
	 * This let's the parent thread know this thread is running.
	 */
	queue_message_set(tp->q, 0, msg_mgmt_rply, 0);

	/* ---- Start the initialization of the LU ---- */
	rval = t10_thick_provision(tp->targ_name, tp->lun, tp->q);

	/* ---- Remove from the linked list ---- */
	(void) pthread_mutex_lock(&thick_mutex);
	if (tp->prev == NULL) {
		assert(tp == thick_head);
		thick_head = tp->next;
		if (tp->next == NULL) {
			assert(tp == thick_tail);
			thick_tail = NULL;
		} else
			tp->next->prev = NULL;
	} else {
		tp->prev->next = tp->next;
		if (tp->next != NULL)
			tp->next->prev = tp->prev;
		else
			thick_tail = tp->prev;
	}
	(void) pthread_mutex_unlock(&thick_mutex);

	/*
	 * There's a race condition where t10_thick_provision() could
	 * finish and before the thick_mutex lock is grabbed again
	 * that another thread running the thick_provo_stop() could
	 * find a match and send a shutdown message. If that happened
	 * that thread would wait forever in queue_message_get(). So,
	 * After this target/lun pair has been removed check the message
	 * queue one last time to see if there's a message available.
	 * If so, send an ack.
	 */
	m = queue_message_try_get(tp->q);
	if (m != NULL) {
		assert(m->msg_type == msg_shutdown);
		queue_message_set((target_queue_t *)m->msg_data, 0,
		    msg_shutdown_rsp, 0);
	}

	if (rval == True)
		iscsi_inventory_change(tp->targ_name);
	else {
		queue_prt(mgmtq, Q_GEN_ERRS, "Failed to initialize %s/%d\n",
		    tp->targ_name, tp->lun);
		syslog(LOG_ERR, "Failed to initialize %s, LU%d", tp->targ_name,
		    tp->lun);
		remove_target_common(tp->targ_name, tp->lun, &err);
		if (err != NULL) {

			/*
			 * There's not much we can do here. The most likely
			 * cause of not being able to remove the target is
			 * that it's LU 0 and there is currently another
			 * LU allocated.
			 */
			queue_prt(mgmtq, Q_GEN_ERRS,
			    "Failed to remove target\n");
			syslog(LOG_ERR, "Failed to remove target/lun after "
			    "initialization failure");
		}
	}

	free(tp->targ_name);
	queue_free(tp->q, NULL);
	free(tp);

	queue_message_set(mgmtq, 0, msg_pthread_join,
	    (void *)(uintptr_t)pthread_self());
	return (NULL);
}

/*
 * []----
 * | thick_provo_stop -- stop initialization thread for given targ/lun
 * []----
 */
void
thick_provo_stop(char *targ, int lun)
{
	thick_provo_t	*tp;
	target_queue_t	*q	= queue_alloc();

	(void) pthread_mutex_lock(&thick_mutex);
	tp = thick_head;
	while (tp) {
		if ((strcmp(tp->targ_name, targ) == 0) && (tp->lun == lun)) {
			queue_message_set(tp->q, 0, msg_shutdown, (void *)q);
			/*
			 * Drop the global mutex because it's entirely
			 * possible for a thick_provo_start thread to be
			 * in the early stages in which it will can call
			 * thick_provo_chk() from the T10 SAM code.
			 */
			(void) pthread_mutex_unlock(&thick_mutex);

			queue_message_free(queue_message_get(q));

			/*
			 * Pick the lock back up since it'll make the
			 * finish stage easier to deal with.
			 */
			(void) pthread_mutex_lock(&thick_mutex);
			break;
		}
		tp = tp->next;
	}
	(void) pthread_mutex_unlock(&thick_mutex);
	queue_free(q, NULL);
}

/*
 * []----
 * | thick_provo_chk_thr -- see if there's an initialization thread running
 * []----
 */
Boolean_t
thick_provo_chk_thr(char *targ, int lun)
{
	thick_provo_t	*tp;
	Boolean_t	rval = False;

	(void) pthread_mutex_lock(&thick_mutex);
	tp = thick_head;
	while (tp) {
		if ((strcmp(tp->targ_name, targ) == 0) && (tp->lun == lun)) {
			rval = True;
			break;
		}
		tp = tp->next;
	}
	(void) pthread_mutex_unlock(&thick_mutex);

	return (rval);
}

/*
 * []----
 * | remove_target_common -- remove targ/lun from system.
 * |
 * | This is a common function that's used both by the normal remove
 * | target code and when a write failure occurs during initialization.
 * | It will handle being given either the local target name or the full
 * | IQN name of the target.
 * []----
 */
void
remove_target_common(char *name, int lun_num, char **msg)
{
	tgt_node_t	*targ			= NULL;
	tgt_node_t	*list, *lun, *c;
	char		path[MAXPATHLEN];
	char		*tname			= NULL;
	char		*iname			= NULL;
	int		chk;

	while ((targ = tgt_node_next_child(targets_config, XML_ELEMENT_TARG,
	    targ)) != NULL) {
		/* ---- Look for a match on the friendly name ---- */
		if (strcmp(targ->x_value, name) == 0) {
			tname = name;
			break;
		}

		/* ---- Check to see if they gave the IQN name instead ---- */
		if ((tgt_find_value_str(targ, XML_ELEMENT_INAME, &iname) ==
		    True) && (strcmp(iname, name) == 0))
			break;
		else {
			free(iname);
			iname = NULL;
		}
	}

	/* ---- Check to see if it's already been removed ---- */
	if (targ == NULL) {
		return;
	}

	/*
	 * We need both the friendly and IQN names so figure out which wasn't
	 * given and find it's value.
	 */
	if (tname == NULL)
		tname = targ->x_value;
	if (iname == NULL) {
		if (tgt_find_value_str(targ, XML_ELEMENT_INAME, &iname) ==
		    False) {
			xml_rtn_msg(msg, ERR_INTERNAL_ERROR);
			return;
		}
	}

	if ((list = tgt_node_next(targ, XML_ELEMENT_LUNLIST, NULL)) == NULL)
		goto error;

	if (lun_num == 0) {

		/*
		 * LUN must be the last one removed, so check to
		 * see if others are still present.
		 */
		lun = NULL;
		while ((lun = tgt_node_next(list, XML_ELEMENT_LUN, lun)) !=
		    NULL) {
			if (tgt_find_value_int(lun, XML_ELEMENT_LUN, &chk) ==
			    False)
				goto error;

			if (chk != lun_num) {
				xml_rtn_msg(msg, ERR_LUN_ZERO_NOT_LAST);
				goto error;
			}
		}
	} else {

		/*
		 * Make sure the LU exists that's being removed
		 */
		lun = NULL;
		while ((lun = tgt_node_next(list, XML_ELEMENT_LUN, lun)) !=
		    NULL) {
			if (tgt_find_value_int(lun, XML_ELEMENT_LUN, &chk) ==
			    False)
				goto error;

			if (chk == lun_num) {
				lun = tgt_node_alloc(XML_ELEMENT_LUN, Int,
				    &lun_num);
				(void) tgt_node_remove(list, lun, MatchBoth);
				tgt_node_free(lun);
				break;
			}
		}
		if (lun == NULL) {
			xml_rtn_msg(msg, ERR_LUN_NOT_FOUND);
			goto error;
		}
	}

	/* ---- Say goodbye to that data ---- */
	(void) snprintf(path, sizeof (path), "%s/%s/%s%d", target_basedir,
	    iname, LUNBASE, lun_num);
	(void) unlink(path);
	(void) snprintf(path, sizeof (path), "%s/%s/%s%d", target_basedir,
	    iname, PARAMBASE, lun_num);
	(void) unlink(path);

	(void) mgmt_param_remove(tname, lun_num);

	/*
	 * If the was LUN 0 then do to the previous check
	 * we know that no other files exist in the target
	 * directory so the target information can be removed
	 * along with the directory.
	 */
	if (lun_num == 0) {
		(void) snprintf(path, sizeof (path), "%s/%s", target_basedir,
		    iname);
		(void) rmdir(path);

		/*
		 * Don't forget to remove the symlink to
		 * the target directory.
		 */
		(void) snprintf(path, sizeof (path), "%s/%s", target_basedir,
		    tname);
		(void) unlink(path);

		/*
		 * 'tname' is just a reference to the memory within
		 * the targets_config structure. So once the tgt_node_remove()
		 * is called 'tname' is no longer valid.
		 */
		c = tgt_node_alloc(XML_ELEMENT_TARG, String, tname);
		(void) tgt_node_remove(targets_config, c, MatchBoth);
		tgt_node_free(c);
	}

	/*
	 * Not much we can do here if we fail to updated the config.
	 */
	if (mgmt_config_save2scf() == False)
		syslog(LOG_ERR, "Failed to update target configuration!");

error:
	if (iname != NULL)
		free(iname);
}

/*
 * []----
 * | validate_xml
 * |
 * | This function checks if there is predefined entities &<>'" in xml request
 * []----
 */
Boolean_t
validate_xml(char *req)
{
	in_mark_t in_mark = in_none;

	if (req == NULL)
		return (False);
	for (; *req != '\0'; req++) {
		if (in_mark == in_none) {
			if (*req == '<') {
				in_mark = in_lt;
				continue;
			} else if (*req == '&') {
				in_mark = in_amp;
				continue;
			} else if (strchr("\"\'>", *req) != NULL) {
				return (False);
			}
		} else if (in_mark == in_lt) {
			if (*req == '>') {
				in_mark = in_none;
				continue;
			} else if (*req == '<') {
				return (False);
			}
		} else {
			if (*req == ';') {
				in_mark = in_none;
				continue;
			} else if (*req == '&' || *req == '<') {
				return (False);
			}
		}
	}

	if (in_mark == in_none)
		return (True);
	else
		return (False);
}

/*
 * []----
 * | get_local_name
 * |
 * | This function fetches local name from a iscsi-name
 * | Caller is responsible to free the string.
 * []----
 */
char *
get_local_name(char *iname)
{
	tgt_node_t	*targ = NULL;
	char		*str;
	char		*ret = NULL;

	while ((targ = tgt_node_next_child(targets_config, XML_ELEMENT_TARG,
	    targ)) != NULL) {
		if (tgt_find_value_str(targ, XML_ELEMENT_INAME, &str) == True) {
			if (strcmp(str, iname) == 0)
				ret = strdup(targ->x_value);
			free(str);
			if (ret != NULL)
				break;
		}
	}
	return (ret);
}