summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormcneal <none@none>2006-06-30 17:02:49 -0700
committermcneal <none@none>2006-06-30 17:02:49 -0700
commit36c5fee33fa8b822175d410202aebcf592c8d342 (patch)
tree6898ca550b2d4a40197087cb8a1c93ba44c834ae
parent0ce6acb835f8cec2ed5d14a190db6e2f98ed6d69 (diff)
downloadillumos-joyent-36c5fee33fa8b822175d410202aebcf592c8d342.tar.gz
PSARC 2005/441 iSCSI Target
6392513 Need iSCSI Target for Solaris
-rw-r--r--usr/src/Makefile.lint3
-rw-r--r--usr/src/Targetdirs1
-rw-r--r--usr/src/cmd/Makefile2
-rw-r--r--usr/src/cmd/iscsi/Makefile70
-rw-r--r--usr/src/cmd/iscsi/Makefile.iscsi34
-rw-r--r--usr/src/cmd/iscsi/common/iscsi_door.h159
-rw-r--r--usr/src/cmd/iscsi/common/local_types.h66
-rw-r--r--usr/src/cmd/iscsi/common/xml.c968
-rw-r--r--usr/src/cmd/iscsi/common/xml.h179
-rw-r--r--usr/src/cmd/iscsi/iscsitadm/Makefile81
-rw-r--r--usr/src/cmd/iscsi/iscsitadm/cmdparse.c934
-rw-r--r--usr/src/cmd/iscsi/iscsitadm/cmdparse.h283
-rw-r--r--usr/src/cmd/iscsi/iscsitadm/helper.c679
-rw-r--r--usr/src/cmd/iscsi/iscsitadm/helper.h91
-rw-r--r--usr/src/cmd/iscsi/iscsitadm/main.c1584
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/Makefile83
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/Makefile.com77
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/amd64/Makefile31
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/errcode.h118
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/i386/Makefile31
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_authclient.c3042
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_authglue.c374
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_cmd.c337
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_cmd.h167
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_conn.c1226
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_conn.h216
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_crc.c178
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_ffp.c953
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_ffp.h43
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_login.c843
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_login.h42
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c713
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.h142
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/iscsi_target.xml98
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/main.c975
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/mgmt.c259
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/mgmt_create.c1000
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/mgmt_list.c447
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/mgmt_modify.c692
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/mgmt_remove.c288
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/port.h48
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/queue.h268
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/radius.c514
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/radius.h243
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/sparcv9/Makefile32
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10.h623
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10_osd.c591
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10_osd.h174
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c1519
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10_sam.c2191
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c1865
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h188
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10_spc.c1121
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10_spc.h362
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10_ssc.c1627
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/t10_ssc.h317
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/target.h206
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/util.c1691
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/util_err.c195
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/util_ifname.c508
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/util_port.c260
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/util_queue.c483
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/utility.h96
-rw-r--r--usr/src/lib/libsecdb/exec_attr.txt1
-rw-r--r--usr/src/pkgdefs/Makefile2
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtr/Makefile38
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtr/pkginfo.tmpl50
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtr/prototype_com52
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtr/prototype_i38648
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtr/prototype_sparc48
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtu/Makefile37
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtu/depend51
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtu/pkginfo.tmpl49
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtu/preremove58
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtu/prototype_com48
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtu/prototype_i38652
-rw-r--r--usr/src/pkgdefs/SUNWiscsitgtu/prototype_sparc49
-rw-r--r--usr/src/pkgdefs/etc/exception_list_i3868
-rw-r--r--usr/src/pkgdefs/etc/exception_list_sparc8
-rw-r--r--usr/src/uts/common/io/emul64_bsd.c20
-rw-r--r--usr/src/uts/common/sys/Makefile3
-rw-r--r--usr/src/uts/common/sys/iscsi_authclient.h403
-rw-r--r--usr/src/uts/common/sys/iscsi_authclientglue.h49
-rw-r--r--usr/src/uts/common/sys/iscsi_protocol.h704
-rw-r--r--usr/src/uts/common/sys/scsi/generic/commands.h10
-rw-r--r--usr/src/uts/common/sys/scsi/generic/inquiry.h58
-rw-r--r--usr/src/uts/common/sys/scsi/impl/commands.h26
87 files changed, 34472 insertions, 31 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index 6c03f00518..09f6025dda 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -154,6 +154,7 @@ COMMON_SUBDIRS = \
cmd/ipcs \
cmd/isaexec \
cmd/isalist \
+ cmd/iscsi \
cmd/join \
cmd/kbd \
cmd/killall \
@@ -410,7 +411,7 @@ sparc_SUBDIRS= \
cmd/datadm \
cmd/dcs \
cmd/drd \
- cmd/fruadm \
+'ve cmd/fruadm \
cmd/prtdscp \
cmd/prtfru \
cmd/sckmd \
diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs
index 121a668fea..a91cde3c53 100644
--- a/usr/src/Targetdirs
+++ b/usr/src/Targetdirs
@@ -83,6 +83,7 @@ ROOT.SYS= \
/etc/fs/nfs \
/etc/fs/zfs \
/etc/ftpd \
+ /etc/iscsi \
/etc/rpcsec \
/etc/security \
/etc/gss \
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index cb3c89f635..d9b0d5c657 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -199,6 +199,7 @@ COMMON_SUBDIRS= \
ipf \
isainfo \
isalist \
+ iscsi \
java \
join \
kbd \
@@ -555,6 +556,7 @@ MSGSUBDIRS= \
hostname \
id \
isaexec \
+ iscsi \
join \
krb5 \
kstat \
diff --git a/usr/src/cmd/iscsi/Makefile b/usr/src/cmd/iscsi/Makefile
new file mode 100644
index 0000000000..79aac24e7c
--- /dev/null
+++ b/usr/src/cmd/iscsi/Makefile
@@ -0,0 +1,70 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Makefile definitions for volume management
+#
+#
+# cmd/iscsi/Makefile
+#
+
+include ../Makefile.cmd
+
+SUBDIRS = iscsitgtd iscsitadm
+POSUBDIRS = iscsitgtd iscsitadm
+POFILE = SUNW_ISCSI.po
+POFILES = $(POSUBDIRS:%=%/%.po)
+
+CAT= cat
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+cstyle := TARGET= cstyle
+_msg := TARGET= catalog
+
+.KEEP_STATE:
+
+all install cstyle lint _msg: $(SUBDIRS)
+
+clean: $(SUBDIRS)
+ $(RM) $(POFILES)
+
+clobber: $(SUBDIRS)
+ $(RM) $(POFILE)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+$(POFILE): $(POFILES)
+ $(BUILDPO.pofiles)
+
+_msg: $(POSUBDIRS) .WAIT $(MSGDOMAINPOFILE)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
diff --git a/usr/src/cmd/iscsi/Makefile.iscsi b/usr/src/cmd/iscsi/Makefile.iscsi
new file mode 100644
index 0000000000..ba44fd2201
--- /dev/null
+++ b/usr/src/cmd/iscsi/Makefile.iscsi
@@ -0,0 +1,34 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+CPPFLAGS += -D_FILE_OFFSET_BITS=64 -I/usr/include/libxml2
+
+ISCSISRC = $(SRC)/cmd/iscsi
+ISCSICOMMONDIR = $(ISCSISRC)/common
+
+COMMON_OBJS = xml.o
+COMMON_SRCS = $(COMMON_OBJS:%.o=$(ISCSICOMMONDIR)/%.c)
diff --git a/usr/src/cmd/iscsi/common/iscsi_door.h b/usr/src/cmd/iscsi/common/iscsi_door.h
new file mode 100644
index 0000000000..327c0fa104
--- /dev/null
+++ b/usr/src/cmd/iscsi/common/iscsi_door.h
@@ -0,0 +1,159 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ISCSI_DOOR_H
+#define _ISCSI_DOOR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ISCSI_TARGET_MGMT_DOOR "/var/run/iscsi_tgt_door"
+#define ISCSI_DOOR_REQ_SIGNATURE 0x53435349
+#define ISCSI_DOOR_REQ_VERSION_1 1
+#define ISCSI_DOOR_MAX_DATA_SIZE 8192
+
+
+#define ISCSI_DOOR_GETIPNODEBYNAME_REQ 0x0000
+#define ISCSI_DOOR_GETIPNODEBYNAME_CNF 0x4000
+#define ISCSI_DOOR_ERROR_IND 0x8000
+
+#define ISCSI_DOOR_STATUS_SUCCESS 0x00000000
+#define ISCSI_DOOR_STATUS_REQ_LENGTH 0x00000001
+#define ISCSI_DOOR_STATUS_REQ_FORMAT 0x00000002
+#define ISCSI_DOOR_STATUS_REQ_INVALID 0x00000003
+#define ISCSI_DOOR_STATUS_REQ_VERSION 0x00000004
+#define ISCSI_DOOR_STATUS_MORE 0x00000005
+
+typedef struct _iscsi_door_msg_hdr {
+ uint32_t signature;
+ uint32_t version;
+ uint32_t opcode;
+ uint32_t status;
+} iscsi_door_msg_hdr_t;
+
+typedef struct _getipnodebyname_req {
+ iscsi_door_msg_hdr_t hdr;
+ uint32_t name_offset;
+ uint32_t name_length;
+ uint32_t af;
+ uint32_t flags;
+} getipnodebyname_req_t;
+
+typedef struct _getipnodebyname_cnf {
+ iscsi_door_msg_hdr_t hdr;
+ uint32_t h_size_needed;
+ uint32_t h_addr_list_offset;
+ uint32_t h_addr_list_length;
+ uint32_t h_addrtype;
+ uint32_t h_addrlen;
+ uint32_t h_name_offset;
+ uint32_t h_name_len;
+ uint32_t h_alias_list_offset;
+ uint32_t h_alias_list_length;
+ int32_t error_num;
+} getipnodebyname_cnf_t;
+
+typedef union _iscsi_door_req {
+ iscsi_door_msg_hdr_t hdr;
+ getipnodebyname_req_t ginbn_req;
+} iscsi_door_req_t;
+
+typedef union _iscsi_door_cnf {
+ iscsi_door_msg_hdr_t hdr;
+ getipnodebyname_cnf_t ginbn_cnf;
+} iscsi_door_cnf_t;
+
+typedef union _iscsi_door_ind {
+ iscsi_door_msg_hdr_t hdr;
+ iscsi_door_msg_hdr_t error_ind;
+} iscsi_door_ind_t;
+
+typedef union _iscsi_door_msg {
+ iscsi_door_msg_hdr_t hdr;
+ iscsi_door_req_t req;
+ iscsi_door_cnf_t cnf;
+ iscsi_door_ind_t ind;
+} iscsi_door_msg_t;
+
+#ifdef _KERNEL
+
+/* Defines copied from netdb.h */
+#define HOST_NOT_FOUND 1 /* Authoritive Answer Host not found */
+#define TRY_AGAIN 2 /* Non-Authoritive Host not found, or SERVERFAIL */
+#define NO_RECOVERY 3 /* Non recoverable errors,FORMERR,REFUSED,NOTIMP */
+#define NO_DATA 4 /* Valid name, no data record of requested type */
+#define NO_ADDRESS NO_DATA /* no address, look for MX record */
+
+#define AI_V4MAPPED 0x0001 /* IPv4 mapped addresses if no IPv6 */
+#define AI_ALL 0x0002 /* IPv6 and IPv4 mapped addresses */
+#define AI_ADDRCONFIG 0x0004 /* AAAA or A records only if IPv6/IPv4 cnfgd */
+
+struct hostent {
+ char *h_name; /* official name of host */
+ char **h_aliases; /* alias list */
+ int h_addrtype; /* host address type */
+ int h_length; /* length of address */
+ char **h_addr_list; /* list of addresses from name server */
+};
+
+boolean_t
+iscsi_door_ini(void);
+
+boolean_t
+iscsi_door_term(void);
+
+boolean_t
+iscsi_door_bind(
+ int did
+);
+
+void
+kfreehostent(
+ struct hostent *hptr
+);
+
+struct hostent *
+kgetipnodebyname(
+ const char *name,
+ int af,
+ int flags,
+ int *error_num
+);
+
+#else /* !_KERNEL */
+
+#define kfreehostent freehostent
+#define kgetipnodebyname getipnodebyname
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ISCSI_DOOR_H */
diff --git a/usr/src/cmd/iscsi/common/local_types.h b/usr/src/cmd/iscsi/common/local_types.h
new file mode 100644
index 0000000000..46e614266f
--- /dev/null
+++ b/usr/src/cmd/iscsi/common/local_types.h
@@ -0,0 +1,66 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LOCAL_TYPES_H
+#define _LOCAL_TYPES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Block comment which describes the contents of this file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+/*
+ * Solaris typedefs boolean_t to be an enum with B_TRUE and B_FALSE.
+ * MacOS X typedefs boolean_t to be an int with #defines for TRUE & FALSE
+ * I like the use of enum's for return codes so that compilers can catch
+ * sloppy coding practices so I've defined a Boolean_t which is unique here.
+ */
+typedef enum {
+ False = 0,
+ True = 1
+} Boolean_t;
+
+#ifndef DTYPE_OSD
+#define DTYPE_OSD 0x11
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LOCAL_TYPES_H */
diff --git a/usr/src/cmd/iscsi/common/xml.c b/usr/src/cmd/iscsi/common/xml.c
new file mode 100644
index 0000000000..7f4e88d2e1
--- /dev/null
+++ b/usr/src/cmd/iscsi/common/xml.c
@@ -0,0 +1,968 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <libxml/xmlreader.h>
+#include <strings.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/stat.h>
+
+#include "xml.h"
+
+/*
+ * Forward declarations
+ */
+static char *strip_space(char *value);
+static xml_node_t *node_alloc();
+static void node_free(xml_node_t *x);
+static Boolean_t node_name(xml_node_t *x, const xmlChar *n);
+static Boolean_t node_value(xml_node_t *x, const xmlChar *n, Boolean_t s);
+static xml_node_t *node_parent(xml_node_t *x);
+static xml_node_t *node_child(xml_node_t *x);
+static xml_node_t *node_alloc_attr(xml_node_t *x);
+
+#define XML_COMMENT_STR "!--"
+#define XML_COMMENT_END "--"
+
+void
+xml_tree_free(xml_node_t *n)
+{
+ xml_node_t *c,
+ *c1;
+ if (n == NULL)
+ return;
+ for (c = n->x_child; c; ) {
+ c1 = c->x_sibling;
+ xml_tree_free(c);
+ c = c1;
+ }
+ for (c = n->x_attr; c; ) {
+ c1 = c->x_sibling;
+ node_free(c);
+ c = c1;
+ }
+ node_free(n);
+
+}
+
+void
+xml_walk(xml_node_t *n, int depth)
+{
+ int i;
+ xml_node_t *c;
+
+ if (n == NULL)
+ return;
+ for (i = 0; i < depth; i++)
+ (void) printf(" ");
+ if (n->x_name && n->x_value) {
+ if (strcmp(n->x_name, XML_COMMENT_STR) == 0) {
+ (void) printf("%s%s%s\n", XML_COMMENT_STR, n->x_value,
+ XML_COMMENT_END);
+ } else if (n->x_attr != NULL) {
+ (void) printf("%s(", n->x_name);
+ for (c = n->x_attr; c; c = c->x_sibling)
+ (void) printf("%s='%s'%c", c->x_name,
+ c->x_value,
+ (c->x_sibling != NULL) ? ',' : '\0');
+ (void) printf(")=%s\n", n->x_value);
+ } else {
+ (void) printf("%s=%s\n", n->x_name, n->x_value);
+ }
+ } else if (n->x_name) {
+ if (n->x_attr != NULL) {
+ (void) printf("%s (", n->x_name);
+ for (c = n->x_attr; c; c = c->x_sibling) {
+ if (c != n->x_attr)
+ (void) printf(" ");
+ (void) printf("%s='%s'", c->x_name,
+ c->x_value);
+ }
+ (void) printf(")...\n");
+ } else
+ (void) printf("%s...\n", n->x_name);
+ }
+ for (c = n->x_child; c; c = c->x_sibling)
+ xml_walk(c, depth + 1);
+}
+
+void
+xml_walk_to_buf(xml_node_t *n, char **buf)
+{
+ xml_node_t *c;
+
+ if (n == NULL)
+ return;
+ if (strcmp(n->x_name, XML_COMMENT_STR) == 0) {
+ xml_add_comment(buf, n->x_value);
+ return;
+ }
+ buf_add_node_attr(buf, n);
+ if (n->x_value != NULL)
+ buf_add_tag(buf, n->x_value, Tag_String);
+ for (c = n->x_child; c; c = c->x_sibling)
+ xml_walk_to_buf(c, buf);
+ buf_add_tag(buf, n->x_name, Tag_End);
+}
+
+/*
+ * []----
+ * | xml_update_config -- dump out in core node tree to XML parsable file
+ * |
+ * | The depth argument is only used to pad the output with white space
+ * | for indentation. This is meant to help the readability of the file
+ * | for humans.
+ * []----
+ */
+void
+xml_update_config(xml_node_t *t, int depth, FILE *output)
+{
+ int i;
+ xml_node_t *c;
+
+ if (t == NULL)
+ return;
+
+ for (i = 0; i < depth; i++)
+ (void) fprintf(output, " ");
+ if (strcmp(t->x_name, XML_COMMENT_STR) == 0) {
+ (void) fprintf(output, "<%s%s%s>\n", XML_COMMENT_STR,
+ t->x_value, XML_COMMENT_END);
+ return;
+ }
+ if ((t->x_child == NULL) && (t->x_value != NULL) &&
+ (t->x_attr == NULL) &&
+ (((depth * 4) + ((strlen(t->x_name) * 2) + 5) +
+ strlen(t->x_value)) < 80)) {
+
+ (void) fprintf(output, "<%s>%s</%s>\n", t->x_name, t->x_value,
+ t->x_name);
+
+ } else {
+ (void) fprintf(output, "<%s", t->x_name);
+ for (c = t->x_attr; c; c = c->x_sibling)
+ (void) fprintf(output, " %s='%s'", c->x_name,
+ c->x_value);
+ (void) fprintf(output, ">\n");
+ if (t->x_value) {
+ for (i = 0; i < depth + 1; i++)
+ (void) fprintf(output, " ");
+ (void) fprintf(output, "%s\n", t->x_value);
+ }
+ for (c = t->x_child; c; c = c->x_sibling)
+ xml_update_config(c, depth + 1, output);
+ for (i = 0; i < depth; i++)
+ (void) fprintf(output, " ");
+ (void) fprintf(output, "</%s>\n", t->x_name);
+ }
+}
+
+Boolean_t
+xml_dump2file(xml_node_t *root, char *path)
+{
+ FILE *output;
+
+ if ((output = fopen(path, "wb")) == NULL) {
+ syslog(LOG_ERR, "Cannot open the file: %s\n", path);
+ return (False);
+ } else {
+ xml_update_config(root, 0, output);
+ (void) fclose(output);
+
+ /*
+ * Make sure only root can access the file since we're
+ * optionally keeping CHAP secrets located here.
+ */
+ (void) chmod(path, 0600);
+
+ return (True);
+ }
+}
+
+char *common_attr_list[] = {
+ "name",
+ "version",
+ 0
+};
+
+Boolean_t
+xml_process_node(xmlTextReaderPtr r, xml_node_t **node)
+{
+ const xmlChar *name,
+ *value;
+ char **ap;
+ xmlElementType node_type;
+ xml_node_t *n,
+ *an;
+
+ n = *node;
+ if (n == NULL) {
+ n = node_alloc();
+ if (n == NULL)
+ return (False);
+ *node = n;
+ }
+
+ name = (xmlChar *)xmlTextReaderConstName(r);
+ if (name == NULL) {
+ node_free(n);
+ return (False);
+ }
+
+ node_type = (xmlElementType)xmlTextReaderNodeType(r);
+
+ if (xmlTextReaderAttributeCount(r) > 0) {
+
+ for (ap = common_attr_list; *ap; ap++) {
+ value = xmlTextReaderGetAttribute(r, (xmlChar *)*ap);
+ if (value != NULL) {
+
+ if ((an = node_alloc_attr(n)) == NULL)
+ return (False);
+ if (node_name(an, (xmlChar *)*ap) == False) {
+ node_free(an);
+ return (False);
+ }
+ if (node_value(an, value, True) == False) {
+ node_free(an);
+ return (False);
+ }
+ free((char *)value);
+ }
+ }
+ }
+ value = (xmlChar *)xmlTextReaderConstValue(r);
+
+ if (node_type == XML_ELEMENT_NODE) {
+ if (n->x_state != NodeAlloc) {
+ n = node_child(n);
+ *node = n;
+ if (n == NULL)
+ return (False);
+ }
+ if (node_name(n, name) == False) {
+ node_free(n);
+ return (False);
+ }
+ } else if ((value != NULL) && (node_type == XML_TEXT_NODE)) {
+ if (node_value(n, value, True) == False) {
+ node_free(n);
+ return (False);
+ }
+ } else if (node_type == XML_ELEMENT_DECL) {
+ n = node_parent(n);
+ if (n == NULL)
+ return (False);
+ *node = n;
+ } else if (node_type == XML_COMMENT_NODE) {
+ n = node_child(n);
+ if (node_name(n, (xmlChar *)XML_COMMENT_STR) == False) {
+ node_free(n);
+ return (False);
+ }
+ if (node_value(n, (xmlChar *)value, False) == False) {
+ node_free(n);
+ return (False);
+ }
+ } else if (node_type != XML_DTD_NODE) {
+ node_free(n);
+ return (False);
+ }
+ return (True);
+}
+
+Boolean_t
+xml_find_attr_str(xml_node_t *n, char *attr, char **value)
+{
+ xml_node_t *a;
+
+ if ((n == NULL) || (n->x_attr == NULL))
+ return (False);
+
+ for (a = n->x_attr; a; a = a->x_sibling)
+ if (strcmp(a->x_name, attr) == 0) {
+ *value = a->x_value ? strdup(a->x_value) : NULL;
+ return (True);
+ }
+ return (False);
+}
+
+Boolean_t
+xml_find_value_str(xml_node_t *n, char *name, char **value)
+{
+ xml_node_t *c;
+
+ if ((n == NULL) || (n->x_name == NULL))
+ return (False);
+
+ if (strcmp(n->x_name, name) == 0) {
+ *value = n->x_value ? strdup(n->x_value) : NULL;
+ return (True);
+ }
+ for (c = n->x_child; c; c = c->x_sibling) {
+ if (xml_find_value_str(c, name, value) == True)
+ return (True);
+ }
+ return (False);
+}
+
+Boolean_t
+xml_find_value_int(xml_node_t *n, char *name, int *value)
+{
+ xml_node_t *c;
+
+ if ((n == NULL) || (n->x_name == NULL))
+ return (False);
+
+ if (strcmp(n->x_name, name) == 0) {
+ if (n->x_value == NULL)
+ return (False);
+ *value = strtol(n->x_value, NULL, 0);
+ return (True);
+ }
+ for (c = n->x_child; c; c = c->x_sibling) {
+ if (xml_find_value_int(c, name, value) == True)
+ return (True);
+ }
+ return (False);
+}
+
+/*
+ * []----
+ * | xml_find_value_intchk -- if node exists, check to see if value is okay
+ * []----
+ */
+Boolean_t
+xml_find_value_intchk(xml_node_t *n, char *name, int *value)
+{
+ char *str,
+ chk[32];
+ Boolean_t rval;
+
+ if (xml_find_value_str(n, name, &str) == True) {
+
+ *value = strtol(str, NULL, 0);
+ /*
+ * Validate that the input string hasn't overrun what
+ * what an integer can handle. This is done by simply
+ * printing out the result of the conversion into a buffer
+ * and comparing it to the incoming string. That way when
+ * someone enters 4294967296 which strtol returns as 0
+ * we'll catch it.
+ */
+ if ((str[0] == '0') && (str[1] != '\0')) {
+ if (str[1] == 'x')
+ snprintf(chk, sizeof (chk), "0x%x", *value);
+ else if (str[1] == 'X')
+ snprintf(chk, sizeof (chk), "0X%x", *value);
+ else
+ snprintf(chk, sizeof (chk), "0%o", *value);
+ } else
+ snprintf(chk, sizeof (chk), "%d", *value);
+ if (strcmp(chk, str) == 0)
+ rval = True;
+ else
+ rval = False;
+ free(str);
+ return (rval);
+ } else
+ return (True);
+}
+
+Boolean_t
+xml_find_value_boolean(xml_node_t *n, char *name, Boolean_t *value)
+{
+ xml_node_t *c;
+
+ if ((n == NULL) || (n->x_name == NULL))
+ return (False);
+
+ if (strcmp(n->x_name, name) == 0) {
+ if (n->x_value == NULL)
+ return (False);
+ *value = strcmp(n->x_value, "true") == 0 ? True : False;
+ return (True);
+ }
+ for (c = n->x_child; c; c = c->x_sibling) {
+ if (xml_find_value_boolean(c, name, value) == True)
+ return (True);
+ }
+ return (False);
+}
+
+xml_node_t *
+xml_node_next(xml_node_t *n, char *name, xml_node_t *cur)
+{
+ xml_node_t *x,
+ *p;
+
+ if (n == NULL)
+ return (NULL);
+
+ if (cur != NULL) {
+ for (x = cur->x_sibling; x; x = x->x_sibling)
+ if (strcmp(x->x_name, name) == 0)
+ return (x);
+ return (NULL);
+ }
+
+ if (n->x_name == NULL)
+ return (NULL);
+
+ if (strcmp(n->x_name, name) == 0)
+ return (n);
+ for (x = n->x_child; x; x = x->x_sibling)
+ if ((p = xml_node_next(x, name, 0)) != NULL)
+ return (p);
+ return (NULL);
+}
+
+xml_node_t *
+xml_node_next_child(xml_node_t *n, char *name, xml_node_t *cur)
+{
+ if (cur != NULL) {
+ n = cur->x_sibling;
+ } else {
+ if (n != NULL)
+ n = n->x_child;
+ else
+ return (NULL);
+ }
+ while (n) {
+ if (strcmp(n->x_name, name) == 0)
+ return (n);
+ n = n->x_sibling;
+ }
+ return (NULL);
+}
+
+Boolean_t
+xml_add_child(xml_node_t *p, xml_node_t *c)
+{
+ xml_node_t *s;
+ if ((p == NULL) || (c == NULL))
+ return (False);
+
+ if (p->x_child == NULL)
+ p->x_child = c;
+ else {
+ for (s = p->x_child; s->x_sibling; s = s->x_sibling)
+ ;
+ s->x_sibling = c;
+ }
+ return (True);
+}
+
+xml_node_t *
+xml_alloc_node(char *name, xml_val_type_t type, void *value)
+{
+ xml_node_t *d = node_alloc();
+ int value_len;
+ char *value_str;
+
+ if (d == NULL)
+ return (NULL);
+ switch (type) {
+ case String:
+ value_len = strlen((char *)value) + 1;
+ break;
+ case Int:
+ value_len = sizeof (int) * 2 + 3;
+ break;
+ case Uint64:
+ value_len = sizeof (uint64_t) * 2 + 3;
+ break;
+ }
+ if ((value_str = (char *)calloc(sizeof (char), value_len)) == NULL)
+ return (NULL);
+ if (node_name(d, (xmlChar *)name) == False) {
+ free(value_str);
+ return (NULL);
+ }
+ switch (type) {
+ case String:
+ (void) snprintf(value_str, value_len, "%s", (char *)value);
+ break;
+ case Int:
+ (void) snprintf(value_str, value_len, "0x%x", *(int *)value);
+ break;
+ case Uint64:
+ (void) snprintf(value_str, value_len, "0x%llx",
+ *(uint64_t *)value);
+ break;
+ }
+ (void) node_value(d, (xmlChar *)value_str, True);
+
+ return (d);
+}
+
+Boolean_t
+xml_encode_bytes(uint8_t *ip, size_t ip_size, char **buf, size_t *buf_size)
+{
+ char *bp;
+ *buf_size = (ip_size * 2) + 1;
+
+ if ((*buf = (char *)malloc(*buf_size)) == NULL) {
+ *buf_size = 0;
+ return (False);
+ }
+
+ for (bp = *buf; ip_size; ip_size--) {
+ (void) sprintf(bp, "%.2x", *ip);
+ ip++;
+ bp += 2;
+ }
+
+ return (True);
+}
+
+Boolean_t
+xml_decode_bytes(char *buf, uint8_t **ip, size_t *ip_size)
+{
+ uint8_t *i;
+ size_t buf_size = strlen(buf);
+ *ip_size = buf_size / 2;
+
+ if ((*ip = (uint8_t *)malloc(*ip_size)) == NULL) {
+ *ip_size = 0;
+ return (False);
+ }
+
+ for (i = *ip; buf_size; buf_size -= 2) {
+ char x[3];
+ bcopy(buf, x, 2);
+ x[2] = 0;
+ *i++ = strtol(x, NULL, 16);
+ buf += 2;
+ }
+ return (True);
+}
+
+void
+xml_free_node(xml_node_t *node)
+{
+ node_free(node);
+}
+
+Boolean_t
+xml_remove_child(xml_node_t *parent, xml_node_t *child, match_type_t m)
+{
+ xml_node_t *s,
+ *c = NULL;
+
+ if ((parent == NULL) || (child == NULL))
+ return (False);
+
+ for (s = parent->x_child; s; c = s, s = s->x_sibling) {
+
+ /*
+ * See if the new child node matches one of the children
+ * in the parent.
+ */
+ if ((strcmp(s->x_name, child->x_name) == 0) &&
+ ((m == MatchName) || (strcmp(s->x_value,
+ child->x_value) == 0))) {
+
+ if (parent->x_child == s) {
+ parent->x_child = s->x_sibling;
+ } else {
+ c->x_sibling = s->x_sibling;
+ }
+ xml_tree_free(s);
+ break;
+ }
+ }
+ if (s == NULL)
+ return (False);
+ else
+ return (True);
+}
+
+void
+xml_replace_child(xml_node_t *parent, xml_node_t *child, match_type_t m)
+{
+ xml_node_t *s,
+ *c;
+
+ if ((parent == NULL) || (child == NULL))
+ return;
+
+ for (s = parent->x_child; s; s = s->x_sibling) {
+
+ /*
+ * See if the new child node matches one of the children
+ * in the parent.
+ */
+ if ((strcmp(s->x_name, child->x_name) == 0) &&
+ ((m == MatchName) || (strcmp(s->x_value,
+ child->x_value) == 0))) {
+
+ /*
+ * We have a match. Now save the values of the new
+ * child in this current node.
+ */
+ free(s->x_name);
+ free(s->x_value);
+ s->x_name = strdup(child->x_name);
+ s->x_value = strdup(child->x_value);
+ if (s->x_child) {
+ xml_tree_free(s->x_child);
+ s->x_child = NULL;
+ }
+ for (c = child->x_child; c; c = c->x_sibling)
+ (void) xml_add_child(s, xml_node_dup(c));
+ break;
+ }
+ }
+
+ if (s == NULL) {
+ /*
+ * Never found the child so add it
+ */
+ (void) xml_add_child(parent, xml_node_dup(child));
+ }
+}
+
+#ifdef notused
+/*
+ * Update node value when value type is usigned long long
+ */
+Boolean_t
+xml_update_value_ull(xml_node_t *node, char *name, uint64_t value)
+{
+ if (node == NULL || strcmp(name, node->x_name))
+ return (False);
+
+ if (strtoll(node->x_value, NULL, 0) == value)
+ return (True);
+ (void) snprintf(node->x_value, 64, "0x%llx", value);
+ return (True);
+}
+#endif
+
+Boolean_t
+xml_update_value_str(xml_node_t *node, char *name, char *str)
+{
+ if ((node == NULL) || (strcmp(name, node->x_name) != 0))
+ return (False);
+ if (node->x_value != NULL)
+ free(node->x_value);
+ node->x_value = strdup(str);
+ node->x_state = NodeValue;
+ return (True);
+}
+
+xml_node_t *
+xml_find_child(xml_node_t *n, char *name)
+{
+ xml_node_t *rval;
+
+ for (rval = n->x_child; rval; rval = rval->x_sibling)
+ if (strcmp(rval->x_name, name) == 0)
+ break;
+ return (rval);
+}
+
+xml_node_t *
+xml_node_dup(xml_node_t *n)
+{
+ xml_node_t *d = node_alloc(),
+ *c;
+
+ if (d == NULL)
+ return (NULL);
+ if (node_name(d, (xmlChar *)n->x_name) == False)
+ return (NULL);
+ if (node_value(d, (xmlChar *)n->x_value, True) == False)
+ return (NULL);
+ for (c = n->x_child; c; c = c->x_sibling)
+ (void) xml_add_child(d, xml_node_dup(c));
+ return (d);
+}
+
+/*
+ * []----
+ * | Utility functions
+ * []----
+ */
+static xml_node_t *
+node_alloc()
+{
+ xml_node_t *x = (xml_node_t *)calloc(sizeof (xml_node_t), 1);
+
+ if (x == NULL)
+ return (NULL);
+
+ x->x_state = NodeAlloc;
+ return (x);
+}
+
+static void
+node_free(xml_node_t *x)
+{
+ x->x_state = NodeFree;
+ if (x->x_name)
+ free(x->x_name);
+ if (x->x_value)
+ free(x->x_value);
+ free(x);
+}
+
+static Boolean_t
+node_name(xml_node_t *x, const xmlChar *n)
+{
+ assert(x->x_state == NodeAlloc);
+ if ((n == NULL) || (strlen((char *)n) == 0))
+ return (False);
+
+ x->x_state = NodeName;
+ x->x_name = strip_space((char *)n);
+ return (True);
+}
+
+static Boolean_t
+node_value(xml_node_t *x, const xmlChar *n, Boolean_t do_strip)
+{
+ assert(x->x_state == NodeName);
+ if ((n == NULL) || (strlen((char *)n) == NULL))
+ return (False);
+
+ x->x_state = NodeValue;
+ x->x_value = (do_strip == True) ?
+ strip_space((char *)n) : strdup((char *)n);
+ return (True);
+}
+
+static xml_node_t *
+node_parent(xml_node_t *x)
+{
+ return (x->x_parent);
+}
+
+static xml_node_t *
+node_child(xml_node_t *x)
+{
+ xml_node_t *n,
+ *next;
+
+ n = node_alloc();
+ if (x->x_child == NULL) {
+ x->x_child = n;
+ } else {
+ for (next = x->x_child; next->x_sibling; next = next->x_sibling)
+ ;
+ next->x_sibling = n;
+ }
+ if (n != NULL)
+ n->x_parent = x;
+ return (n);
+}
+
+static xml_node_t *
+node_alloc_attr(xml_node_t *x)
+{
+ xml_node_t *n,
+ *next;
+
+ n = node_alloc();
+ if (x->x_attr == NULL) {
+ x->x_attr = n;
+ } else {
+ for (next = x->x_attr; next->x_sibling; next = next->x_sibling)
+ ;
+ next->x_sibling = n;
+ }
+ if (n != NULL)
+ n->x_parent = x;
+ return (n);
+}
+
+void
+buf_add_str(char **b, char *str)
+{
+ int len,
+ olen = 0;
+ char *p = *b;
+
+ /*
+ * Make sure we have enough room for the string and tag characters
+ * plus a NULL byte.
+ */
+ if (str == NULL)
+ return;
+
+ len = strlen(str) + 1;
+ if (p == NULL) {
+ p = malloc(len);
+ } else {
+ olen = strlen(p);
+ p = realloc(p, olen + len);
+ }
+ (void) strcpy(p + olen, str);
+ *b = p;
+}
+
+/*
+ * []----
+ * | buf_add_tag -- adds string to buffer allocating space, sets up tags too
+ * |
+ * | Helper function to build a string by allocating memory as we go.
+ * | If the string argument 'str' is defined to be a start or end tag
+ * | as declared by 'type' argument add the appropriate characters.
+ * []----
+ */
+void
+buf_add_tag(char **b, char *str, val_type_t type)
+{
+ char *buf;
+ int len;
+
+ /*
+ * We will add potentially up to 3 extra characters plus the NULL byte
+ */
+ len = strlen(str) + 4;
+ if ((buf = malloc(len)) == NULL)
+ return;
+
+ (void) snprintf(buf, len, "%s%s%s%s", type == Tag_String ? "" : "<",
+ type == Tag_End ? "/" : "", str, type == Tag_String ? "" : ">");
+ buf_add_str(b, buf);
+ free(buf);
+}
+
+/*
+ * []----
+ * | buf_add_tag_and_attr -- variant on buf_add_tag which also gives attr
+ * []----
+ */
+void
+buf_add_tag_and_attr(char **b, char *str, char *attr)
+{
+ char *buf;
+ int len;
+
+ /*
+ * In addition to the 'str' and 'attr' strings the code will add
+ * three characters plus a null byte.
+ */
+ len = strlen(str) + strlen(attr) + 4;
+ if ((buf = malloc(len)) == NULL)
+ return;
+
+ (void) snprintf(buf, len, "<%s %s>", str, attr);
+ buf_add_str(b, buf);
+ free(buf);
+}
+
+void
+buf_add_node_attr(char **b, xml_node_t *x)
+{
+ char *buf;
+ xml_node_t *n;
+ int len;
+
+ /* ---- null byte and starting '<' character ---- */
+ len = strlen(x->x_name) + 2;
+ if ((buf = malloc(len)) == NULL)
+ return;
+ (void) snprintf(buf, len, "<%s", x->x_name);
+ buf_add_str(b, buf);
+ free(buf);
+
+ for (n = x->x_attr; n; n = n->x_sibling) {
+ len = strlen(n->x_name) + strlen(n->x_value) + 5;
+ if ((buf = malloc(len)) == NULL)
+ return;
+ (void) snprintf(buf, len, " %s='%s'", n->x_name, n->x_value);
+ buf_add_str(b, buf);
+ free(buf);
+ }
+ buf_add_str(b, ">");
+}
+
+void
+xml_add_comment(char **b, char *comment)
+{
+ char *p = *b;
+ int len,
+ olen;
+
+ if (comment == NULL)
+ return;
+
+ /*
+ * Room for the strings, plus the brackets and NULL byte
+ */
+ len = strlen(comment) + strlen(XML_COMMENT_STR) +
+ strlen(XML_COMMENT_END) + 3;
+
+ if (p == NULL)
+ p = malloc(len);
+ else {
+ olen = strlen(p);
+ p = realloc(p, olen + len);
+ }
+ (void) snprintf(p + olen, len, "<%s%s%s>", XML_COMMENT_STR, comment,
+ XML_COMMENT_END);
+ *b = p;
+}
+
+void
+xml_add_tag(char **b, char *element, char *cdata)
+{
+ buf_add_tag(b, element, Tag_Start);
+ if (cdata != NULL)
+ buf_add_tag(b, cdata, Tag_String);
+ buf_add_tag(b, element, Tag_End);
+}
+
+static char *
+strip_space(char *value)
+{
+ char *p,
+ *n;
+
+ for (p = value; p && *p; p++)
+ if (!isspace(*p))
+ break;
+ if ((p == NULL) || (*p == '\0'))
+ return (NULL);
+
+ p = strdup(p);
+ for (n = (p + strlen(p) - 1); n >= p; n--)
+ if (!isspace(*n)) {
+ n++;
+ break;
+ }
+ *n = '\0';
+ return (p);
+}
diff --git a/usr/src/cmd/iscsi/common/xml.h b/usr/src/cmd/iscsi/common/xml.h
new file mode 100644
index 0000000000..df373b111a
--- /dev/null
+++ b/usr/src/cmd/iscsi/common/xml.h
@@ -0,0 +1,179 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TARGET_XML_H
+#define _TARGET_XML_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Block comment which describes the contents of this file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libxml/xmlreader.h>
+
+#include "local_types.h"
+
+/*
+ * XML element defines.
+ */
+#define XML_ELEMENT_ERROR "error"
+#define XML_ELEMENT_CODE "code"
+#define XML_ELEMENT_MESSAGE "message"
+#define XML_ELEMENT_TRANSVERS "transport-version"
+#define XML_ELEMENT_PROPS "props"
+#define XML_ELEMENT_DATAOUT "data-out-size"
+#define XML_ELEMENT_BASEDIR "base-directory"
+#define XML_ELEMENT_CHAPSECRET "chap-secret"
+#define XML_ELEMENT_CHAPNAME "chap-name"
+#define XML_ELEMENT_RAD_ACCESS "radius-access"
+#define XML_ELEMENT_RAD_SERV "radius-server"
+#define XML_ELEMENT_RAD_SECRET "radius-secret"
+#define XML_ELEMENT_ISNS_ACCESS "isns-access"
+#define XML_ELEMENT_FAST "fast-write-ack"
+#define XML_ELEMENT_NAME "name"
+#define XML_ELEMENT_ACL "acl"
+#define XML_ELEMENT_ACLLIST "acl-list"
+#define XML_ELEMENT_TPGT "tpgt"
+#define XML_ELEMENT_TPGTLIST "tpgt-list"
+#define XML_ELEMENT_SIZE "size"
+#define XML_ELEMENT_LUN "lun"
+#define XML_ELEMENT_LUNLIST "lun-list"
+#define XML_ELEMENT_TYPE "type"
+#define XML_ELEMENT_ALIAS "alias"
+#define XML_ELEMENT_BACK "backing-store"
+#define XML_ELEMENT_DELETE_BACK "delete-backing-store"
+#define XML_ELEMENT_TARG "target"
+#define XML_ELEMENT_INIT "initiator"
+#define XML_ELEMENT_ADMIN "admin"
+#define XML_ELEMENT_INAME "iscsi-name"
+#define XML_ELEMENT_MAXRECV "maxrecv"
+#define XML_ELEMENT_IPADDR "ip-address"
+#define XML_ELEMENT_ALL "all"
+#define XML_ELEMENT_VERBOSE "verbose"
+#define XML_ELEMENT_LIST "list"
+#define XML_ELEMENT_RESULT "result"
+#define XML_ELEMENT_TIMECON "time-connected"
+#define XML_ELEMENT_READCMDS "read-commands"
+#define XML_ELEMENT_WRITECMDS "write-commands"
+#define XML_ELEMENT_READBLKS "read-blks"
+#define XML_ELEMENT_WRITEBLKS "write-blks"
+#define XML_ELEMENT_STATS "statistics"
+#define XML_ELEMENT_CONN "connection"
+#define XML_ELEMENT_LUNINFO "lun-information"
+#define XML_ELEMENT_VID "vid"
+#define XML_ELEMENT_PID "pid"
+#define XML_ELEMENT_GUID "guid"
+#define XML_ELEMENT_DTYPE "dtype"
+#define XML_ELEMENT_IOSTAT "iostat"
+#define XML_ELEMENT_MACADDR "mac-addr"
+#define XML_ELEMENT_MGMTPORT "mgmt-port"
+#define XML_ELEMENT_ISCSIPORT "iscsi-port"
+#define XML_ELEMENT_TARGLOG "target-log"
+#define XML_ELEMENT_DBGLVL "dbg-lvl"
+#define XML_ELEMENT_LOGLVL "qlog-lvl"
+#define XML_ELEMENT_DBGDAEMON "daemonize"
+#define XML_ELEMENT_ENFORCE "enforce-strict-guid"
+#define XML_ELEMENT_VERS "version"
+#define XML_ELEMENT_MMAP_LUN "mmap-lun"
+#define XML_ELEMENT_RPM "rpm"
+#define XML_ELEMENT_HEADS "heads"
+#define XML_ELEMENT_CYLINDERS "cylinders"
+#define XML_ELEMENT_SPT "spt"
+#define XML_ELEMENT_BPS "bps"
+#define XML_ELEMENT_INTERLEAVE "interleave"
+#define XML_ELEMENT_PARAMS "params"
+#define XML_ELEMENT_MAXCMDS "max-outstanding-cmds"
+#define XML_ELEMENT_THIN_PROVO "thin-provisioning"
+#define XML_ELEMENT_DISABLE_TPGS "disable-tpgs"
+#define XML_ELEMENT_STATUS "status"
+#define XML_ELEMENT_PROGRESS "progress"
+#define XML_ELEMENT_TIMESTAMPS "time-stamps"
+
+typedef enum {
+ NodeFree,
+ NodeAlloc,
+ NodeName,
+ NodeValue
+} xml_node_state;
+
+typedef enum { MatchName, MatchBoth } match_type_t;
+
+typedef struct xml_node {
+ struct xml_node *x_parent,
+ *x_child,
+ *x_sibling,
+ *x_attr;
+ char *x_name,
+ *x_value;
+ xml_node_state x_state;
+} xml_node_t;
+
+typedef enum val_type { Tag_String, Tag_Start, Tag_End } val_type_t;
+typedef enum xml_val_type { String, Int, Uint64 } xml_val_type_t;
+
+void xml_tree_free(xml_node_t *x);
+void xml_walk(xml_node_t *x, int depth);
+void xml_walk_to_buf(xml_node_t *n, char **buf);
+void xml_update_config(xml_node_t *t, int depth, FILE *output);
+void buf_add_str(char **b, char *str);
+void buf_add_tag(char **b, char *str, val_type_t type);
+void buf_add_tag_and_attr(char **b, char *str, char *attr);
+void buf_add_node_attr(char **b, xml_node_t *x);
+void xml_add_tag(char **b, char *element, char *cdata);
+void xml_add_comment(char **b, char *comment);
+void xml_replace_child(xml_node_t *parent, xml_node_t *child, match_type_t m);
+Boolean_t xml_remove_child(xml_node_t *parent, xml_node_t *child,
+ match_type_t m);
+Boolean_t xml_encode_bytes(uint8_t *ip, size_t ip_size, char **buf,
+ size_t *buf_size);
+Boolean_t xml_decode_bytes(char *buf, uint8_t **ip, size_t *ip_size);
+Boolean_t xml_find_value_str(xml_node_t *n, char *name, char **value);
+Boolean_t xml_find_value_int(xml_node_t *n, char *name, int *value);
+Boolean_t xml_find_value_intchk(xml_node_t *n, char *name, int *value);
+Boolean_t xml_update_value_ull(xml_node_t *root, char *name, uint64_t value);
+Boolean_t xml_dump2file(xml_node_t *root, char *path);
+Boolean_t xml_find_value_boolean(xml_node_t *n, char *name, Boolean_t *value);
+Boolean_t xml_find_attr_str(xml_node_t *n, char *attr, char **value);
+Boolean_t xml_process_node(xmlTextReaderPtr r, xml_node_t **node);
+Boolean_t xml_add_child(xml_node_t *p, xml_node_t *c);
+xml_node_t *xml_alloc_node(char *name, xml_val_type_t type, void *value);
+void xml_free_node(xml_node_t *node);
+xml_node_t *xml_node_next(xml_node_t *n, char *name, xml_node_t *cur);
+xml_node_t *xml_node_next_child(xml_node_t *n, char *name, xml_node_t *cur);
+xml_node_t *xml_node_dup(xml_node_t *n);
+xml_node_t *xml_find_child(xml_node_t *n, char *name);
+Boolean_t xml_update_value_str(xml_node_t *node, char *name, char *str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TARGET_XML_H */
diff --git a/usr/src/cmd/iscsi/iscsitadm/Makefile b/usr/src/cmd/iscsi/iscsitadm/Makefile
new file mode 100644
index 0000000000..a5a19dbc3b
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitadm/Makefile
@@ -0,0 +1,81 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = iscsitadm
+OBJS = main.o helper.o cmdparse.o
+SRCS =$(OBJS:%.o=%.c) $(COMMON_SRCS)
+POFILES= $(OBJS:%.o=%.po)
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/iscsi/Makefile.iscsi
+
+LDLIBS += -lxml2 -lsocket -lnsl -ldoor -lscf
+CFLAGS += $(CCVERBOSE) -I${ISCSICOMMONDIR}
+FILEMODE= 0555
+GROUP= bin
+
+#
+# We can't use the default lint target because there is not a libxml2
+# lint library and we'll get warnings. So, we'll just lint the local
+# source and not do the cross checks.
+#
+#lint := LINTFLAGS = -muxs -I$(ISCSICOMMONDIR)
+
+SUFFIX_LINT = .ln
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG)
+
+$(PROG): $(OBJS) $(COMMON_OBJS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(COMMON_OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+catalog: $(POFILE)
+
+lint := LINTFLAGS += -u
+lint := LINTFLAGS64 += -u
+
+lint: $(SRCS:%=%$(SUFFIX_LINT))
+
+%$(SUFFIX_LINT): %
+ ${LINT.c} -I. -I$(ISCSICOMMONDIR) -y -c $< && touch $@
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+%.o : $(ISCSICOMMONDIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+clean:
+ -$(RM) $(OBJS) $(COMMON_OBJS) *$(SUFFIX_LINT)
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/cmd/iscsi/iscsitadm/cmdparse.c b/usr/src/cmd/iscsi/iscsitadm/cmdparse.c
new file mode 100644
index 0000000000..d94e54538c
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitadm/cmdparse.c
@@ -0,0 +1,934 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <getopt.h>
+#include "cmdparse.h"
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/* Usage types */
+#define GENERAL_USAGE 1
+#define HELP_USAGE 2
+#define DETAIL_USAGE 3
+
+/* printable ascii character set len */
+#define MAXOPTIONS (uint_t)('~' - '!' + 1)
+
+/*
+ * MAXOPTIONSTRING is the max length of the options string used in getopt and
+ * will be the printable character set + ':' for each character,
+ * providing for options with arguments. e.g. "t:Cs:hglr:"
+ */
+#define MAXOPTIONSTRING MAXOPTIONS * 2
+
+/* standard command options table to support -?, -V */
+struct option standardCmdOptions[] = {
+ {"help", no_argument, NULL, '?'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+};
+
+/* standard subcommand options table to support -? */
+struct option standardSubCmdOptions[] = {
+ {"help", no_argument, NULL, '?'},
+ {NULL, 0, NULL, 0}
+};
+
+/* forward declarations */
+static int getSubcommand(char *, subcommand_t **);
+static char *getExecBasename(char *);
+static void usage(uint_t);
+static void subUsage(uint_t, subcommand_t *);
+static void subUsageObject(uint_t, subcommand_t *, object_t *);
+static int getObject(char *, object_t **);
+static int getObjectRules(uint_t, objectRules_t **);
+static char *getLongOption(int);
+static optionProp_t *getOptions(uint_t, uint_t);
+static char *getOptionArgDesc(int);
+
+/* global data */
+static struct option *_longOptions;
+static subcommand_t *_subcommands;
+static object_t *_objects;
+static objectRules_t *_objectRules;
+static optionRules_t *_optionRules;
+static optionTbl_t *_clientOptionTbl;
+static char *commandName;
+
+
+/*
+ * input:
+ * object - object value
+ * output:
+ * opCmd - pointer to opCmd_t structure allocated by caller
+ *
+ * On successful return, opCmd contains the rules for the value in
+ * object. On failure, the contents of opCmd is unspecified.
+ *
+ * Returns:
+ * zero on success
+ * non-zero on failure
+ *
+ */
+static int
+getObjectRules(uint_t object, objectRules_t **objectRules)
+{
+ objectRules_t *sp;
+
+ for (sp = _objectRules; sp->value; sp++) {
+ if (sp->value == object) {
+ *objectRules = sp;
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * input:
+ * arg - pointer to array of char containing object string
+ *
+ * output:
+ * object - pointer to object_t structure pointer
+ * on success, contains the matching object structure based on
+ * input object name
+ *
+ * Returns:
+ * zero on success
+ * non-zero otherwise
+ *
+ */
+static int
+getObject(char *arg, object_t **object)
+{
+
+ object_t *op;
+ int len;
+
+ for (op = _objects; op->name; op++) {
+ len = strlen(arg);
+ if (len == strlen(op->name) &&
+ strncasecmp(arg, op->name, len) == 0) {
+ *object = op;
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * input:
+ * arg - pointer to array of char containing subcommand string
+ * output:
+ * subcommand - pointer to subcommand_t pointer
+ * on success, contains the matching subcommand structure based on
+ * input subcommand name
+ *
+ * Returns:
+ * zero on success
+ * non-zero on failure
+ */
+static int
+getSubcommand(char *arg, subcommand_t **subcommand)
+{
+ subcommand_t *sp;
+ int len;
+
+ for (sp = _subcommands; sp->name; sp++) {
+ len = strlen(arg);
+ if (len == strlen(sp->name) &&
+ strncasecmp(arg, sp->name, len) == 0) {
+ *subcommand = sp;
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * input:
+ * object - object for which to get options
+ * subcommand - subcommand for which to get options
+ *
+ * Returns:
+ * on success, optionsProp_t pointer to structure matching input object
+ * value
+ * on failure, NULL is returned
+ */
+static optionProp_t *
+getOptions(uint_t object, uint_t subcommand)
+{
+ uint_t currObject;
+ optionRules_t *op = _optionRules;
+ while (op && ((currObject = op->objectValue) != 0)) {
+ if ((currObject == object) &&
+ (op->subcommandValue == subcommand)) {
+ return (&(op->optionProp));
+ }
+ op++;
+ }
+ return (NULL);
+}
+
+/*
+ * input:
+ * shortOption - short option character for which to return the
+ * associated long option string
+ *
+ * Returns:
+ * on success, long option name
+ * on failure, NULL
+ */
+static char *
+getLongOption(int shortOption)
+{
+ struct option *op;
+ for (op = _longOptions; op->name; op++) {
+ if (shortOption == op->val) {
+ return (op->name);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * input
+ * shortOption - short option character for which to return the
+ * option argument
+ * Returns:
+ * on success, argument string
+ * on failure, NULL
+ */
+static char *
+getOptionArgDesc(int shortOption)
+{
+ optionTbl_t *op;
+ for (op = _clientOptionTbl; op->name; op++) {
+ if (op->val == shortOption &&
+ op->has_arg == required_argument) {
+ return (op->argDesc);
+ }
+ }
+ return (NULL);
+}
+
+
+/*
+ * Print usage for a subcommand.
+ *
+ * input:
+ * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE
+ * subcommand - pointer to subcommand_t structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+subUsage(uint_t usageType, subcommand_t *subcommand)
+{
+ int i;
+ object_t *objp;
+
+
+ (void) fprintf(stdout, "%s:\t%s %s [",
+ gettext("Usage"), commandName, subcommand->name);
+
+ for (i = 0; standardSubCmdOptions[i].name; i++) {
+ (void) fprintf(stdout, "-%c",
+ standardSubCmdOptions[i].val);
+ if (standardSubCmdOptions[i+1].name)
+ (void) fprintf(stdout, ",");
+ }
+
+ (void) fprintf(stdout, "] %s [", "<OBJECT>");
+
+ for (i = 0; standardSubCmdOptions[i].name; i++) {
+ (void) fprintf(stdout, "-%c",
+ standardSubCmdOptions[i].val);
+ if (standardSubCmdOptions[i+1].name)
+ (void) fprintf(stdout, ",");
+ }
+
+ (void) fprintf(stdout, "] %s", "[<OPERAND>]");
+ (void) fprintf(stdout, "\n");
+
+ if (usageType == GENERAL_USAGE) {
+ return;
+ }
+
+ (void) fprintf(stdout, "%s:\n", gettext("Usage by OBJECT"));
+
+ /*
+ * iterate through object table
+ * For each object, print appropriate usage
+ * based on rules tables
+ */
+ for (objp = _objects; objp->value; objp++) {
+ subUsageObject(usageType, subcommand, objp);
+ }
+}
+
+/*
+ * Print usage for a subcommand and object.
+ *
+ * input:
+ * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE
+ * subcommand - pointer to subcommand_t structure
+ * objp - pointer to a object_t structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+subUsageObject(uint_t usageType, subcommand_t *subcommand, object_t *objp)
+{
+ int i;
+ objectRules_t *objRules = NULL;
+ opCmd_t *opCmd = NULL;
+ optionProp_t *options;
+ char *optionArgDesc;
+ char *longOpt;
+
+
+ if (getObjectRules(objp->value, &objRules) != 0) {
+ /*
+ * internal subcommand rules table error
+ * no object entry in object
+ */
+ assert(0);
+ }
+
+ opCmd = &(objRules->opCmd);
+
+ if (opCmd->invOpCmd & subcommand->value) {
+ return;
+ }
+
+ options = getOptions(objp->value, subcommand->value);
+
+ /* print generic subcommand usage */
+ (void) fprintf(stdout, "\t%s %s ", commandName, subcommand->name);
+
+ /* print object */
+ (void) fprintf(stdout, "%s ", objp->name);
+
+ /* print options if applicable */
+ if (options != NULL) {
+ if (options->required) {
+ (void) fprintf(stdout, "%s", gettext("<"));
+ } else {
+ (void) fprintf(stdout, "%s", gettext("["));
+ }
+ (void) fprintf(stdout, "%s", gettext("OPTIONS"));
+ if (options->required) {
+ (void) fprintf(stdout, "%s ", gettext(">"));
+ } else {
+ (void) fprintf(stdout, "%s ", gettext("]"));
+ }
+ }
+
+ /* print operand requirements */
+ if (opCmd->optOpCmd & subcommand->value) {
+ (void) fprintf(stdout, gettext("["));
+ }
+ if (!(opCmd->noOpCmd & subcommand->value)) {
+ (void) fprintf(stdout, gettext("<"));
+ if (objRules->operandDefinition) {
+ (void) fprintf(stdout, "%s",
+ objRules->operandDefinition);
+ } else {
+ /*
+ * Missing operand description
+ * from table
+ */
+ assert(0);
+ }
+ }
+ if (opCmd->multOpCmd & subcommand->value) {
+ (void) fprintf(stdout, gettext(" ..."));
+ }
+ if (!(opCmd->noOpCmd & subcommand->value)) {
+ (void) fprintf(stdout, gettext(">"));
+ }
+ if (opCmd->optOpCmd & subcommand->value) {
+ (void) fprintf(stdout, gettext("]"));
+ }
+
+ if (usageType == HELP_USAGE) {
+ (void) fprintf(stdout, "\n");
+ return;
+ }
+
+ /* print options for subcommand, object */
+ if (options != NULL && options->optionString != NULL) {
+ (void) fprintf(stdout, "\n\t%s:", gettext("OPTIONS"));
+ for (i = 0; i < strlen(options->optionString); i++) {
+ if ((longOpt = getLongOption(
+ options->optionString[i]))
+ == NULL) {
+ /* no long option exists for short option */
+ assert(0);
+ }
+ (void) fprintf(stdout, "\n\t\t-%c, --%s ",
+ options->optionString[i], longOpt);
+ optionArgDesc =
+ getOptionArgDesc(options->optionString[i]);
+ if (optionArgDesc != NULL) {
+ (void) fprintf(stdout, "<%s>", optionArgDesc);
+ }
+ if (options->exclusive &&
+ strchr(options->exclusive,
+ options->optionString[i])) {
+ (void) fprintf(stdout, " (%s)",
+ gettext("exclusive"));
+ }
+ }
+ }
+ (void) fprintf(stdout, "\n");
+}
+
+/*
+ * input:
+ * type of usage statement to print
+ *
+ * Returns:
+ * return value of subUsage
+ */
+static void
+usage(uint_t usageType)
+{
+ int i;
+ subcommand_t subcommand;
+ subcommand_t *sp;
+
+ /* print general command usage */
+ (void) fprintf(stdout, "%s:\t%s ",
+ gettext("Usage"), commandName);
+
+ for (i = 0; standardCmdOptions[i].name; i++) {
+ (void) fprintf(stdout, "-%c",
+ standardCmdOptions[i].val);
+ if (standardCmdOptions[i+1].name)
+ (void) fprintf(stdout, ",");
+ }
+
+ if (usageType == HELP_USAGE || usageType == GENERAL_USAGE) {
+ for (i = 0; standardSubCmdOptions[i].name; i++) {
+ (void) fprintf(stdout, ",--%s",
+ standardSubCmdOptions[i].name);
+ if (standardSubCmdOptions[i+1].name)
+ (void) fprintf(stdout, ",");
+ }
+ }
+
+ (void) fprintf(stdout, "\n");
+
+
+ /* print all subcommand usage */
+ for (sp = _subcommands; sp->name; sp++) {
+ subcommand.name = sp->name;
+ subcommand.value = sp->value;
+ if (usageType == HELP_USAGE) {
+ (void) fprintf(stdout, "\n");
+ }
+ subUsage(usageType, &subcommand);
+ }
+}
+
+/*
+ * input:
+ * execFullName - exec name of program (argv[0])
+ *
+ * Returns:
+ * command name portion of execFullName
+ */
+static char *
+getExecBasename(char *execFullname)
+{
+ char *lastSlash, *execBasename;
+
+ /* guard against '/' at end of command invocation */
+ for (;;) {
+ lastSlash = strrchr(execFullname, '/');
+ if (lastSlash == NULL) {
+ execBasename = execFullname;
+ break;
+ } else {
+ execBasename = lastSlash + 1;
+ if (*execBasename == '\0') {
+ *lastSlash = '\0';
+ continue;
+ }
+ break;
+ }
+ }
+ return (execBasename);
+}
+
+/*
+ * cmdParse is a parser that checks syntax of the input command against
+ * various rules tables.
+ *
+ * It provides usage feedback based upon the passed rules tables by calling
+ * two usage functions, usage, subUsage, and subUsageObject handling command,
+ * subcommand and object usage respectively.
+ *
+ * When syntax is successfully validated, the associated function is called
+ * using the subcommands table functions.
+ *
+ * Syntax is as follows:
+ * command subcommand object [<options>] [<operand>]
+ *
+ * There are two standard short and long options assumed:
+ * -?, --help Provides usage on a command or subcommand
+ * and stops further processing of the arguments
+ *
+ * -V, --version Provides version information on the command
+ * and stops further processing of the arguments
+ *
+ * These options are loaded by this function.
+ *
+ * input:
+ * argc, argv from main
+ * syntax rules tables (synTables_t structure)
+ * callArgs - void * passed by caller to be passed to subcommand function
+ *
+ * output:
+ * funcRet - pointer to int that holds subcommand function return value
+ *
+ * Returns:
+ *
+ * zero on successful syntax parse and function call
+ *
+ * 1 on unsuccessful syntax parse (no function has been called)
+ * This could be due to a version or help call or simply a
+ * general usage call.
+ *
+ * -1 check errno, call failed
+ *
+ * This module is not MT-safe.
+ *
+ */
+int
+cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs,
+ int *funcRet)
+{
+ int getoptargc;
+ char **getoptargv;
+ int opt;
+ int operInd;
+ int i, j;
+ int len;
+ char *versionString;
+ char optionStringAll[MAXOPTIONSTRING + 1];
+ optionProp_t *availOptions;
+ objectRules_t *objRules = NULL;
+ opCmd_t *opCmd = NULL;
+ subcommand_t *subcommand;
+ object_t *object;
+ cmdOptions_t cmdOptions[MAXOPTIONS + 1];
+ struct option *lp;
+ optionTbl_t *optionTbl;
+ struct option intLongOpt[MAXOPTIONS + 1];
+
+ /*
+ * Check for NULLs on mandatory input arguments
+ *
+ * Note: longOptionTbl and optionRulesTbl can be NULL in the case
+ * where there is no caller defined options
+ *
+ */
+ if (synTable.versionString == NULL ||
+ synTable.subcommandTbl == NULL ||
+ synTable.objectRulesTbl == NULL ||
+ synTable.objectTbl == NULL ||
+ funcRet == NULL) {
+ assert(0);
+ }
+
+
+ versionString = synTable.versionString;
+
+ /* set global command name */
+ commandName = getExecBasename(argv[0]);
+
+ /* Set unbuffered output */
+ setbuf(stdout, NULL);
+
+ /* load globals */
+ _subcommands = synTable.subcommandTbl;
+ _objectRules = synTable.objectRulesTbl;
+ _optionRules = synTable.optionRulesTbl;
+ _objects = synTable.objectTbl;
+ _clientOptionTbl = synTable.longOptionTbl;
+
+ /* There must be at least two arguments */
+ if (argc < 2) {
+ usage(GENERAL_USAGE);
+ return (1);
+ }
+
+ bzero(&intLongOpt[0], sizeof (intLongOpt));
+
+ /*
+ * load standard subcommand options to internal long options table
+ * Two separate getopt_long(3C) tables are used.
+ */
+ for (i = 0; standardSubCmdOptions[i].name; i++) {
+ intLongOpt[i].name = standardSubCmdOptions[i].name;
+ intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg;
+ intLongOpt[i].flag = standardSubCmdOptions[i].flag;
+ intLongOpt[i].val = standardSubCmdOptions[i].val;
+ }
+
+ /*
+ * copy caller's long options into internal long options table
+ * We do this for two reasons:
+ * 1) We need to use the getopt_long option structure internally
+ * 2) We need to prepend the table with the standard option
+ * for all subcommands (currently -?)
+ */
+ for (optionTbl = synTable.longOptionTbl;
+ optionTbl && optionTbl->name; optionTbl++, i++) {
+ if (i > MAXOPTIONS - 1) {
+ /* option table too long */
+ assert(0);
+ }
+ intLongOpt[i].name = optionTbl->name;
+ intLongOpt[i].has_arg = optionTbl->has_arg;
+ intLongOpt[i].flag = NULL;
+ intLongOpt[i].val = optionTbl->val;
+ }
+
+ /* set option table global */
+ _longOptions = &intLongOpt[0];
+
+
+ /*
+ * Check for help/version request immediately following command
+ * '+' in option string ensures POSIX compliance in getopt_long()
+ * which means that processing will stop at first non-option
+ * argument.
+ */
+ while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions,
+ NULL)) != EOF) {
+ switch (opt) {
+ case '?':
+ /*
+ * getopt can return a '?' when no
+ * option letters match string. Check for
+ * the 'real' '?' in optopt.
+ */
+ if (optopt == '?') {
+ usage(HELP_USAGE);
+ return (1);
+ } else {
+ usage(GENERAL_USAGE);
+ return (1);
+ }
+ case 'V':
+ (void) fprintf(stdout, "%s: %s %s\n",
+ commandName, gettext("Version"),
+ versionString);
+ return (1);
+ default:
+ break;
+ }
+ }
+
+ /*
+ * subcommand is always in the second argument. If there is no
+ * recognized subcommand in the second argument, print error,
+ * general usage and then return.
+ */
+ if (getSubcommand(argv[1], &subcommand) != 0) {
+ (void) fprintf(stderr, "%s: %s\n",
+ commandName, gettext("invalid subcommand"));
+ usage(GENERAL_USAGE);
+ return (1);
+ }
+
+ if (argc == 2) {
+ (void) fprintf(stderr, "%s: %s\n",
+ commandName, gettext("missing object"));
+ subUsage(GENERAL_USAGE, subcommand);
+ return (1);
+ }
+
+ getoptargv = argv;
+ getoptargv++;
+ getoptargc = argc;
+ getoptargc -= 1;
+
+ while ((opt = getopt_long(getoptargc, getoptargv, "+?",
+ standardSubCmdOptions, NULL)) != EOF) {
+ switch (opt) {
+ case '?':
+ /*
+ * getopt can return a '?' when no
+ * option letters match string. Check for
+ * the 'real' '?' in optopt.
+ */
+ if (optopt == '?') {
+ subUsage(HELP_USAGE, subcommand);
+ return (1);
+ } else {
+ subUsage(GENERAL_USAGE, subcommand);
+ return (1);
+ }
+ default:
+ break;
+ }
+ }
+
+
+ /*
+ * object is always in the third argument. If there is no
+ * recognized object in the third argument, print error,
+ * help usage for the subcommand and then return.
+ */
+ if (getObject(argv[2], &object) != 0) {
+ (void) fprintf(stderr, "%s: %s\n",
+ commandName, gettext("invalid object"));
+ subUsage(HELP_USAGE, subcommand);
+ return (1);
+ }
+
+ if (getObjectRules(object->value, &objRules) != 0) {
+ /*
+ * internal subcommand rules table error
+ * no object entry in object table
+ */
+ assert(0);
+ }
+
+ opCmd = &(objRules->opCmd);
+
+ /*
+ * Is command valid for this object?
+ */
+ if (opCmd->invOpCmd & subcommand->value) {
+ (void) fprintf(stderr, "%s: %s %s\n", commandName,
+ gettext("invalid subcommand for"), object->name);
+ subUsage(HELP_USAGE, subcommand);
+ return (1);
+ }
+
+ /*
+ * offset getopt arg begin since
+ * getopt(3C) assumes options
+ * follow first argument
+ */
+ getoptargv = argv;
+ getoptargv++;
+ getoptargv++;
+ getoptargc = argc;
+ getoptargc -= 2;
+
+ bzero(optionStringAll, sizeof (optionStringAll));
+ bzero(&cmdOptions[0], sizeof (cmdOptions));
+
+ j = 0;
+ /*
+ * Build optionStringAll from long options table
+ */
+ for (lp = _longOptions; lp->name; lp++, j++) {
+ /* sanity check on string length */
+ if (j + 1 >= sizeof (optionStringAll)) {
+ /* option table too long */
+ assert(0);
+ }
+ optionStringAll[j] = lp->val;
+ if (lp->has_arg == required_argument) {
+ optionStringAll[++j] = ':';
+ }
+ }
+
+ i = 0;
+ /*
+ * Run getopt for all arguments against all possible options
+ * Store all options/option arguments in an array for retrieval
+ * later.
+ * Once all options are retrieved, check against object
+ * and subcommand (option rules table) for validity.
+ * This is done later.
+ */
+ while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll,
+ _longOptions, NULL)) != EOF) {
+ switch (opt) {
+ case '?':
+ if (optopt == '?') {
+ subUsageObject(DETAIL_USAGE,
+ subcommand, object);
+ return (1);
+ } else {
+ subUsage(GENERAL_USAGE, subcommand);
+ return (1);
+ }
+ default:
+ cmdOptions[i].optval = opt;
+ if (optarg) {
+ len = strlen(optarg);
+ if (len > sizeof (cmdOptions[i].optarg)
+ - 1) {
+ (void) fprintf(stderr,
+ "%s: %s\n",
+ commandName,
+ gettext("option too long"));
+ errno = EINVAL;
+ return (-1);
+ }
+ (void) strncpy(cmdOptions[i].optarg,
+ optarg, len);
+ }
+ i++;
+ break;
+ }
+ }
+
+ /*
+ * increment past last option
+ */
+ operInd = optind + 2;
+
+ /*
+ * Check validity of given options, if any were given
+ */
+
+ /* get option string for this object and subcommand */
+ availOptions = getOptions(object->value, subcommand->value);
+
+ if (cmdOptions[0].optval != 0) { /* options were input */
+ if (availOptions == NULL) { /* no options permitted */
+ (void) fprintf(stderr, "%s: %s\n",
+ commandName, gettext("no options permitted"));
+ subUsageObject(HELP_USAGE, subcommand, object);
+ return (1);
+ }
+ for (i = 0; cmdOptions[i].optval; i++) {
+ /* Check for invalid options */
+ if (availOptions->optionString == NULL) {
+ /*
+ * internal option table error
+ * There must be an option string if
+ * there is an entry in the table
+ */
+ assert(0);
+ }
+ /* is the option in the available option string? */
+
+ if (!(strchr(availOptions->optionString,
+ cmdOptions[i].optval))) {
+ (void) fprintf(stderr,
+ "%s: '-%c': %s\n",
+ commandName, cmdOptions[i].optval,
+ gettext("invalid option"));
+ subUsageObject(DETAIL_USAGE, subcommand,
+ object);
+ return (1);
+
+ /* Check for exclusive options */
+ } else if (cmdOptions[1].optval != 0 &&
+ availOptions->exclusive &&
+ strchr(availOptions->exclusive,
+ cmdOptions[i].optval)) {
+ (void) fprintf(stderr,
+ "%s: '-%c': %s\n",
+ commandName, cmdOptions[i].optval,
+ gettext("is an exclusive option"));
+ subUsageObject(DETAIL_USAGE, subcommand,
+ object);
+ return (1);
+ }
+ }
+ } else { /* no options were input */
+ if (availOptions != NULL && (availOptions->required)) {
+ (void) fprintf(stderr, "%s: %s\n", commandName,
+ gettext("at least one option required"));
+ subUsageObject(DETAIL_USAGE, subcommand,
+ object);
+ return (1);
+ }
+ }
+
+ /*
+ * If there are no more arguments (operands),
+ * check to see if this is okay
+ */
+ if ((operInd == argc) &&
+ (opCmd->reqOpCmd & subcommand->value)) {
+ (void) fprintf(stderr, "%s: %s %s %s\n",
+ commandName, subcommand->name, object->name,
+ gettext("requires an operand"));
+ subUsageObject(HELP_USAGE, subcommand, object);
+ return (1);
+ }
+
+ /*
+ * If there are more operands,
+ * check to see if this is okay
+ */
+ if ((argc > operInd) && (opCmd->noOpCmd & subcommand->value)) {
+ (void) fprintf(stderr, "%s: %s %s %s\n", commandName,
+ subcommand->name, object->name,
+ gettext("takes no operands"));
+ subUsageObject(HELP_USAGE, subcommand, object);
+ return (1);
+ }
+
+ /*
+ * If there is more than one more operand,
+ * check to see if this is okay
+ */
+ if ((argc > operInd) && ((argc - operInd) != 1) &&
+ !(opCmd->multOpCmd & subcommand->value)) {
+ (void) fprintf(stderr, "%s: %s %s %s\n", commandName,
+ subcommand->name, object->name,
+ gettext("accepts only a single operand"));
+ subUsageObject(HELP_USAGE, subcommand, object);
+ return (1);
+ }
+
+ /* Finished syntax checks */
+
+
+ /* Call appropriate function */
+ *funcRet = subcommand->handler(argc - operInd, &argv[operInd],
+ object->value, &cmdOptions[0], callArgs);
+
+ return (0);
+}
diff --git a/usr/src/cmd/iscsi/iscsitadm/cmdparse.h b/usr/src/cmd/iscsi/iscsitadm/cmdparse.h
new file mode 100644
index 0000000000..6a8fe56c1d
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitadm/cmdparse.h
@@ -0,0 +1,283 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CMDPARSE_H
+#define _CMDPARSE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "getopt.h"
+
+/* subcommands must have a single bit on and must have exclusive values */
+#define SUBCOMMAND_BASE 1
+#define SUBCOMMAND(x) (SUBCOMMAND_BASE << x)
+
+#define OBJECT_BASE 1
+#define OBJECT(x) (OBJECT_BASE << x)
+
+/* maximum length of an option argument */
+#define MAXOPTARGLEN 256
+
+
+/*
+ * Add objects here
+ *
+ * EXAMPLE:
+ * object_t object[] = {
+ * {"target", TARGET},
+ * {NULL, 0}
+ * };
+ */
+typedef struct _object {
+ char *name;
+ uint_t value;
+} object_t;
+
+/*
+ * This structure is passed into the caller's callback function and
+ * will contain a list of all options entered and their associated
+ * option arguments if applicable
+ */
+typedef struct _cmdOptions {
+ int optval;
+ char optarg[MAXOPTARGLEN + 1];
+} cmdOptions_t;
+
+
+/*
+ * list of objects, subcommands, valid short options, required flag and
+ * exlusive option string
+ *
+ * objectValue -> object
+ * subcommandValue -> subcommand value
+ * optionProp.optionString -> short options that are valid
+ * optionProp.required -> flag indicating whether at least one option is
+ * required
+ * optionProp.exclusive -> short options that are required to be exclusively
+ * entered
+ *
+ *
+ * If it's not here, there are no options for that object.
+ *
+ * The long options table specifies whether an option argument is required.
+ *
+ *
+ * EXAMPLE:
+ *
+ * Based on DISCOVERY entry below:
+ *
+ * MODIFY DISCOVERY accepts -i, -s, -t and -l
+ * MODIFY DISCOVERY requires at least one option
+ * MODIFY DISCOVERY has no exclusive options
+ *
+ *
+ * optionRules_t optionRules[] = {
+ * {DISCOVERY, MODIFY, "istl", B_TRUE, NULL},
+ * {0, 0, NULL, 0, NULL}
+ * };
+ */
+typedef struct _optionProp {
+ char *optionString;
+ boolean_t required;
+ char *exclusive;
+} optionProp_t;
+
+typedef struct _optionRules {
+ uint_t objectValue;
+ uint_t subcommandValue;
+ optionProp_t optionProp;
+} optionRules_t;
+
+/*
+ * Rules for subcommands and object operands
+ *
+ * Every object requires an entry
+ *
+ * value, reqOpCmd, optOpCmd, noOpCmd, invCmd, multOpCmd
+ *
+ * value -> numeric value of object
+ *
+ * The following five fields are comprised of values that are
+ * a bitwise OR of the subcommands related to the object
+ *
+ * reqOpCmd -> subcommands that must have an operand
+ * optOpCmd -> subcommands that may have an operand
+ * noOpCmd -> subcommands that will have no operand
+ * invCmd -> subcommands that are invalid
+ * multOpCmd -> subcommands that can accept multiple operands
+ * operandDefinition -> Usage definition for the operand of this object
+ *
+ *
+ * EXAMPLE:
+ *
+ * based on TARGET entry below:
+ * MODIFY and DELETE subcomamnds require an operand
+ * LIST optionally requires an operand
+ * There are no subcommands that requires that no operand is specified
+ * ADD and REMOVE are invalid subcommands for this operand
+ * DELETE can accept multiple operands
+ *
+ * objectRules_t objectRules[] = {
+ * {TARGET, MODIFY|DELETE, LIST, 0, ADD|REMOVE, DELETE,
+ * "target-name"},
+ * {0, 0, 0, 0, 0, NULL}
+ * };
+ */
+typedef struct _opCmd {
+ uint_t reqOpCmd;
+ uint_t optOpCmd;
+ uint_t noOpCmd;
+ uint_t invOpCmd;
+ uint_t multOpCmd;
+} opCmd_t;
+
+typedef struct _objectRules {
+ uint_t value;
+ opCmd_t opCmd;
+ char *operandDefinition;
+} objectRules_t;
+
+
+/*
+ * subcommand callback function
+ *
+ * argc - number of arguments in argv
+ * argv - operand arguments
+ * options - options entered on command line
+ * callData - pointer to caller data to be passed to subcommand function
+ */
+typedef int (*handler_t)(int argc, char *argv[], int, cmdOptions_t *options,
+ void *callData);
+
+/*
+ * Add new subcommands here
+ *
+ * EXAMPLE:
+ * subcommand_t subcommands[] = {
+ * {"add", ADD, addFunc},
+ * {NULL, 0, NULL}
+ * };
+ */
+typedef struct _subcommand {
+ char *name;
+ uint_t value;
+ handler_t handler;
+} subcommand_t;
+
+#define required_arg required_argument
+#define no_arg no_argument
+
+/*
+ * Add short options and long options here
+ *
+ * name -> long option name
+ * has_arg -> required_arg, no_arg
+ * val -> short option character
+ * argDesc -> description of option argument
+ *
+ * Note: This structure may not be used if your CLI has no
+ * options. However, -?, --help and -V, --version will still be supported
+ * as they are standard for every CLI.
+ *
+ * EXAMPLE:
+ *
+ * optionTbl_t options[] = {
+ * {"filename", arg_required, 'f', "out-filename"},
+ * {NULL, 0, 0}
+ * };
+ *
+ */
+typedef struct _optionTbl {
+ char *name;
+ int has_arg;
+ int val;
+ char *argDesc;
+} optionTbl_t;
+
+/*
+ * After tables are set, assign them to this structure
+ * for passing into cmdparse()
+ */
+typedef struct _synTables {
+ char *versionString;
+ optionTbl_t *longOptionTbl;
+ subcommand_t *subcommandTbl;
+ object_t *objectTbl;
+ objectRules_t *objectRulesTbl;
+ optionRules_t *optionRulesTbl;
+} synTables_t;
+
+/*
+ * cmdParse is a parser that checks syntax of the input command against
+ * various rules tables.
+ *
+ * When syntax is successfully validated, the function associated with the
+ * subcommand is called using the subcommands table functions.
+ *
+ * Syntax for the command is as follows:
+ *
+ * command subcommand [<options>] object [<operand ...>]
+ *
+ *
+ * There are two standard short and long options assumed:
+ * -?, --help Provides usage on a command or subcommand
+ * and stops further processing of the arguments
+ *
+ * -V, --version Provides version information on the command
+ * and stops further processing of the arguments
+ *
+ * These options are loaded by this function.
+ *
+ * input:
+ * argc, argv from main
+ * syntax rules tables (synTables_t structure)
+ * callArgs - void * passed by caller to be passed to subcommand function
+ *
+ * output:
+ * funcRet - pointer to int that holds subcommand function return value
+ *
+ * Returns:
+ *
+ * zero on successful syntax parse and function call
+ *
+ * 1 on unsuccessful syntax parse (no function has been called)
+ * This could be due to a version or help call or simply a
+ * general usage call.
+ *
+ * -1 check errno, call failed
+ *
+ */
+int cmdParse(int numOperands, char *operands[], synTables_t synTables,
+ void *callerArgs, int *funcRet);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CMDPARSE_H */
diff --git a/usr/src/cmd/iscsi/iscsitadm/helper.c b/usr/src/cmd/iscsi/iscsitadm/helper.c
new file mode 100644
index 0000000000..cf3768f348
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitadm/helper.c
@@ -0,0 +1,679 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <widec.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <limits.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/iscsi_protocol.h>
+#include <door.h>
+#include <iscsi_door.h>
+#include <sys/scsi/generic/inquiry.h>
+#include <sys/mman.h>
+#include <sys/filio.h>
+#include <libxml/xmlreader.h>
+#include <libscf.h>
+#include <fcntl.h>
+
+#include "local_types.h"
+#include "cmdparse.h"
+#include "utility.h"
+#include "xml.h"
+#include "helper.h"
+
+extern char *cmdName;
+
+#define WAIT_FOR_SERVICE 15
+#define WAIT_FOR_DOOR 15
+static char *service = "system/iscsitgt:default";
+static stat_delta_t *stat_head;
+
+static Boolean_t
+is_online()
+{
+ char *s;
+ Boolean_t rval = False;
+
+ if ((s = smf_get_state(service)) != NULL) {
+ if (strcmp(s, SCF_STATE_STRING_ONLINE) == 0)
+ rval = True;
+ free(s);
+ }
+
+ return (rval);
+}
+
+Boolean_t
+check_and_online()
+{
+ int i,
+ fd;
+
+ if (is_online() == False) {
+ if (smf_enable_instance(service, 0) != 0) {
+ return (False);
+ }
+ for (i = 0; i < WAIT_FOR_SERVICE; i++) {
+ if (is_online() == True)
+ break;
+ (void) sleep(1);
+ }
+ if (i == WAIT_FOR_SERVICE) {
+ return (False);
+ }
+ }
+
+ for (i = 0; i < WAIT_FOR_DOOR; i++) {
+ if ((fd = open(ISCSI_TARGET_MGMT_DOOR, 0)) >= 0) {
+ (void) close(fd);
+ return (True);
+ }
+ (void) sleep(1);
+ }
+ return (False);
+}
+
+/*
+ * []----
+ * | buffer_xml -- buffer incoming XML response until complete
+ * |
+ * | Incoming data from target may not be a complete XML message. So,
+ * | we need to wait until we've got everything otherwise the XML routines
+ * | will generate a parsing error for a short buffer.
+ * []----
+ */
+Boolean_t
+buffer_xml(char *s, char **storage, xml_node_t **np)
+{
+ xml_node_t *node = NULL;
+ xmlTextReaderPtr r;
+ char *p,
+ *e,
+ *end_tag,
+ hold_ch;
+
+ p = *storage;
+ if (s != NULL) {
+ if (p == NULL) {
+ p = strdup(s);
+ } else {
+ p = realloc(p, strlen(p) + strlen(s) + 1);
+ (void) strcat(p, s);
+ }
+ }
+ if (p == NULL) {
+ return (False);
+ }
+
+ if (*p != '<') {
+ return (False);
+ }
+
+ if ((e = strchr(p, '>')) == NULL) {
+ return (False);
+ }
+
+ /*
+ * The +3 is for the slash, closing tag character and null
+ * For example if p is pointing at a string which starts with
+ * "<foo>...."
+ * p will point at '<' and e will point at '>'. e - p is 4, yet
+ * the tag length is really 5 characters. We will need to create
+ * the end tag which also has a slash and NULL byte.
+ */
+ if ((end_tag = malloc(e - p + 3)) == NULL) {
+ return (False);
+ }
+
+ end_tag[0] = '<';
+ end_tag[1] = '/';
+
+ /*
+ * Copy in the tag value and the closing tag character '>'.
+ */
+ bcopy(p + 1, &end_tag[2], e - p);
+
+ /*
+ * Add the null byte
+ */
+ end_tag[e - p + 2] = '\0';
+
+ /*
+ * Do we have the closing string yet? If not, just return
+ */
+ if ((e = strstr(p, end_tag)) == NULL) {
+ *storage = p;
+ return (False);
+ }
+
+ /*
+ * Move past the closing tag and free the end_tag memory
+ */
+ e += strlen(end_tag);
+ free(end_tag);
+
+ /*
+ * NULL terminate the string and remember to save that character
+ * so that we can restore it later.
+ */
+ hold_ch = *e;
+ *e = '\0';
+
+ if ((r = (xmlTextReaderPtr)xmlReaderForMemory(p, strlen(p), NULL,
+ NULL, 0)) == NULL)
+ return (False);
+
+ while (xmlTextReaderRead(r) == 1) {
+ if (xml_process_node(r, &node) == False)
+ break;
+ }
+
+ *np = node;
+
+ xmlFreeTextReader(r);
+
+ *e = hold_ch;
+ for (; isspace(*e); e++)
+ ;
+ if (*e != '\0') {
+ *storage = strdup(e);
+ } else
+ *storage = NULL;
+ free(p);
+ return (True);
+}
+
+xml_node_t *
+send_data(char *hostname, char *first_str)
+{
+ struct sockaddr_in sin;
+ struct hostent *hp;
+ int s,
+ min,
+ nmsgs,
+ nbytes,
+ error_num,
+ port = 3269;
+ struct pollfd fds[1];
+ nfds_t nfds = 1;
+ char input[128],
+ *storage = NULL;
+ xml_node_t *node = NULL;
+
+ if (hostname != NULL) {
+ if ((hp = getipnodebyname(hostname, AF_INET,
+ AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED,
+ &error_num)) == NULL) {
+ (void) printf("Can't find IP addr for %s\n", hostname);
+ exit(1);
+ }
+
+ bzero(&sin, sizeof (sin));
+ sin.sin_family = hp->h_addrtype;
+ sin.sin_port = ntohs(port);
+ bcopy(hp->h_addr_list[0], &sin.sin_addr, hp->h_length);
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ (void) printf("Failed to open socket\n");
+ exit(1);
+ }
+
+ if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ (void) printf("Failed to connect\n");
+ exit(1);
+ }
+
+ fds[0].fd = s;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+
+ if (write(s, first_str, strlen(first_str)) !=
+ strlen(first_str)) {
+ perror("Failed to send request");
+ exit(1);
+ }
+ while (poll(fds, nfds, -1) != -1) {
+ if ((nmsgs = ioctl(s, FIONREAD, &nbytes)) < 0)
+ exit(1);
+ if ((nmsgs == 0) && (nbytes == 0))
+ exit(1);
+ min = MIN(nbytes, sizeof (input) - 1);
+ if (read(s, input, min) != min)
+ exit(1);
+ input[min] = '\0';
+
+ if (buffer_xml(input, &storage, &node) == True) {
+ break;
+ }
+ }
+ } else {
+ door_arg_t d;
+ xmlTextReaderPtr r;
+
+ d.data_ptr = first_str;
+ d.data_size = strlen(first_str) + 1;
+ d.desc_ptr = NULL;
+ d.desc_num = 0;
+ d.rbuf = NULL;
+ d.rsize = 0;
+
+ if (((s = open(ISCSI_TARGET_MGMT_DOOR, 0)) < 0) ||
+ (door_call(s, &d) < 0)) {
+ if (s != -1)
+ (void) close(s);
+
+ if (check_and_online() == False) {
+ (void) fprintf(stderr,
+ "iscsitadm: SMF service unavailable\n");
+ exit(1);
+ }
+
+ if ((s = open(ISCSI_TARGET_MGMT_DOOR, 0)) < 0) {
+ (void) fprintf(stderr,
+ "iscsitadm: Failed to open iSCSI target"
+ " management door\n");
+ exit(1);
+ }
+ if (door_call(s, &d) < 0) {
+ perror("door_call");
+ exit(1);
+ }
+ }
+
+ if ((r = (xmlTextReaderPtr)xmlReaderForMemory(d.rbuf,
+ strlen(d.rbuf), NULL, NULL, 0)) == NULL) {
+ perror("xmlReaderForMemory");
+ exit(1);
+ }
+ while (xmlTextReaderRead(r) == 1)
+ if (xml_process_node(r, &node) == False)
+ break;
+ xmlFreeTextReader(r);
+ (void) munmap(d.rbuf, d.rsize);
+ }
+ (void) close(s);
+ return (node);
+}
+
+/*
+ * Retrieve CHAP secret from input
+ */
+int
+getSecret(char *secret, int *secretLen, int minSecretLen, int maxSecretLen)
+{
+ char *chapSecret;
+
+ /* XXX Should we prompt for hex or ascii printable input? */
+
+ /* get password */
+ chapSecret = getpassphrase(gettext("Enter secret:"));
+
+ if (strlen(chapSecret) > maxSecretLen) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("secret too long"));
+ *secret = NULL;
+ return (1);
+ }
+
+ if (strlen(chapSecret) < minSecretLen) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("secret too short"));
+ *secret = NULL;
+ return (1);
+ }
+
+ (void) strcpy(secret, chapSecret);
+
+ chapSecret = getpassphrase(gettext("Re-enter secret:"));
+ if (strcmp(secret, chapSecret) != 0) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("secret not changed"));
+ *secret = NULL;
+ return (1);
+ }
+ *secretLen = strlen(chapSecret);
+ return (0);
+}
+
+void
+iSCSINameCheckStatusDisplay(iSCSINameCheckStatusType status)
+{
+ switch (status) {
+ case iSCSINameLenZero:
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName, gettext("empty iSCSI name."));
+ break;
+ case iSCSINameLenExceededMax:
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("iSCSI name exceeded maximum length."));
+ break;
+ case iSCSINameUnknownType:
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("unknown iSCSI name type."));
+ break;
+ case iSCSINameIqnFormatError:
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("iqn formatting error."));
+ break;
+ case iSCSINameEUIFormatError:
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("eui formatting error."));
+ break;
+ }
+}
+
+/*
+ * This helper function could go into a utility module for general use.
+ */
+int
+parseAddress(char *address_port_str,
+ uint16_t defaultPort,
+ char *address_str,
+ size_t address_str_len,
+ uint16_t *port,
+ boolean_t *isIpv6)
+{
+ char port_str[64];
+ int tmp_port;
+
+ if (address_port_str[0] == '[') {
+ /* IPv6 address */
+ char *close_bracket_pos;
+ close_bracket_pos = strchr(address_port_str, ']');
+ if (!close_bracket_pos) {
+ syslog(LOG_USER|LOG_DEBUG,
+ "IP address format error: %s\n", address_str);
+ return (PARSE_ADDR_MISSING_CLOSING_BRACKET);
+ }
+
+ *close_bracket_pos = NULL;
+ (void) strlcpy(address_str, &address_port_str[1],
+ address_str_len);
+
+ /* Extract the port number */
+ close_bracket_pos++;
+ if (*close_bracket_pos == ':') {
+ close_bracket_pos++;
+ if (*close_bracket_pos != NULL) {
+ (void) strlcpy(port_str, close_bracket_pos,
+ 64);
+ tmp_port = atoi(port_str);
+ if (((tmp_port > 0) &&
+ (tmp_port > USHRT_MAX)) ||
+ (tmp_port < 0)) {
+ /* Port number out of range */
+ syslog(LOG_USER|LOG_DEBUG,
+ "Specified port out of range: %d",
+ tmp_port);
+ return (PARSE_ADDR_PORT_OUT_OF_RANGE);
+ } else {
+ *port = (uint16_t)tmp_port;
+ }
+ } else {
+ *port = defaultPort;
+ }
+ } else {
+ *port = defaultPort;
+ }
+
+ *isIpv6 = B_TRUE;
+ } else {
+ /* IPv4 address */
+ char *colon_pos;
+ colon_pos = strchr(address_port_str, ':');
+ if (!colon_pos) {
+ /* No port number specified. */
+ *port = defaultPort;
+ (void) strlcpy(address_str, address_port_str,
+ address_str_len);
+ } else {
+ *colon_pos = (char)NULL;
+ (void) strlcpy(address_str, address_port_str,
+ address_str_len);
+
+ /* Extract the port number */
+ colon_pos++;
+ if (*colon_pos != NULL) {
+ (void) strlcpy(port_str, colon_pos, 64);
+ tmp_port = atoi(port_str);
+ if (((tmp_port > 0) &&
+ (tmp_port > USHRT_MAX)) ||
+ (tmp_port < 0)) {
+ /* Port number out of range */
+ syslog(LOG_USER|LOG_DEBUG,
+ "Specified port out of range: %d",
+ tmp_port);
+ return (PARSE_ADDR_PORT_OUT_OF_RANGE);
+ } else {
+ *port = (uint16_t)tmp_port;
+ }
+ } else {
+ *port = defaultPort;
+ }
+ }
+
+ *isIpv6 = B_FALSE;
+ }
+
+ return (PARSE_ADDR_OK);
+}
+
+/*
+ * []----
+ * | Following routine (number_to_scaled_string) is lifted
+ * | from usr/src/cmd/fs.d/df.c
+ * []----
+ */
+/*
+ * Convert an unsigned long long to a string representation and place the
+ * result in the caller-supplied buffer.
+ * The given number is in units of "unit_from" size,
+ * this will first be converted to a number in 1024 or 1000 byte size,
+ * depending on the scaling factor.
+ * Then the number is scaled down until it is small enough to be in a good
+ * human readable format i.e. in the range 0 thru scale-1.
+ * If it's smaller than 10 there's room enough to provide one decimal place.
+ * The value "(unsigned long long)-1" is a special case and is always
+ * converted to "-1".
+ * Returns a pointer to the caller-supplied buffer.
+ */
+char *
+number_to_scaled_string(
+ char *buf, /* put the result here */
+ unsigned long long number, /* convert this number */
+ int unit_from,
+ int scale)
+{
+ unsigned long long save = 0;
+ char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
+ char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */
+
+ if ((long long)number == (long long)-1) {
+ (void) strcpy(buf, "-1");
+ return (buf);
+ }
+
+ if ((number < scale) && (unit_from == 1)) {
+ (void) sprintf(buf, "%4llu", number);
+ return (buf);
+ }
+ /*
+ * Convert number from unit_from to given scale (1024 or 1000).
+ * This means multiply number by unit_from and divide by scale.
+ *
+ * Would like to multiply by unit_from and then divide by scale,
+ * but if the first multiplication would overflow, then need to
+ * divide by scale and then multiply by unit_from.
+ */
+ if (number > (UINT64_MAX / (unsigned long long)unit_from)) {
+ number = (number / (unsigned long long)scale) *
+ (unsigned long long)unit_from;
+ } else {
+ number = (number * (unsigned long long)unit_from) /
+ (unsigned long long)scale;
+ }
+
+ /*
+ * Now we have number as a count of scale units.
+ * Stop scaling when we reached exa bytes, then something is
+ * probably wrong with our number.
+ */
+
+ while ((number >= scale) && (*uom != 'E')) {
+ uom++; /* next unit of measurement */
+ save = number;
+ number = (number + (scale / 2)) / scale;
+ }
+ /* check if we should output a decimal place after the point */
+ if (save && ((save / scale) < 10)) {
+ /* sprintf() will round for us */
+ float fnum = (float)save / scale;
+ (void) sprintf(buf, "%2.1f%c", fnum, *uom);
+ } else {
+ (void) sprintf(buf, "%4llu%c", number, *uom);
+ }
+ return (buf);
+}
+
+void
+stats_load_counts(xml_node_t *n, stat_delta_t *d)
+{
+ xml_node_t *conn = NULL,
+ *lun;
+ char *val;
+
+ bzero(d, sizeof (*d));
+ d->device = n->x_value;
+
+ while (conn = xml_node_next(n, XML_ELEMENT_CONN, conn)) {
+ lun = NULL;
+ while (lun = xml_node_next(conn, XML_ELEMENT_LUN, lun)) {
+ if (xml_find_value_str(lun, XML_ELEMENT_READCMDS,
+ &val) == True) {
+ d->read_cmds += strtoll(val, NULL, 0);
+ free(val);
+ }
+ if (xml_find_value_str(lun, XML_ELEMENT_WRITECMDS,
+ &val) == True) {
+ d->write_cmds += strtoll(val, NULL, 0);
+ free(val);
+ }
+ if (xml_find_value_str(lun, XML_ELEMENT_READBLKS,
+ &val) == True) {
+ d->read_blks += strtoll(val, NULL, 0);
+ free(val);
+ }
+ if (xml_find_value_str(lun, XML_ELEMENT_WRITEBLKS,
+ &val) == True) {
+ d->write_blks += strtoll(val, NULL, 0);
+ free(val);
+ }
+ }
+ }
+}
+
+stat_delta_t *
+stats_prev_counts(stat_delta_t *cp)
+{
+ stat_delta_t *n;
+
+ for (n = stat_head; n; n = n->next) {
+ if (strcmp(n->device, cp->device) == 0)
+ return (n);
+ }
+ if ((n = calloc(1, sizeof (*n))) == NULL)
+ return (NULL);
+ n->device = strdup(cp->device);
+ if (stat_head == NULL)
+ stat_head = n;
+ else {
+ n->next = stat_head;
+ stat_head = n;
+ }
+ return (n);
+}
+
+void
+stats_update_counts(stat_delta_t *p, stat_delta_t *c)
+{
+ p->read_cmds += c->read_cmds - p->read_cmds;
+ p->write_cmds += c->write_cmds - p->write_cmds;
+ p->read_blks += c->read_blks - p->read_blks;
+ p->write_blks += c->write_blks - p->write_blks;
+}
+
+void
+stats_free()
+{
+ stat_delta_t *n;
+
+ /* CSTYLED */
+ for (;stat_head;) {
+ n = stat_head->next;
+ free(stat_head->device);
+ free(stat_head);
+ stat_head = n;
+ }
+}
+
+static char spaces[128];
+
+/*
+ * []----
+ * | dospace -- generate a string which has the appropriate number of spaces
+ * |
+ * | NOTE: Since this function modifies a static buffer usage of this
+ * | function may not be what's expected. For example:
+ * | printf("%sfoo%sbar\n", dospace(1), dospace(2)); would produce
+ * | ' foo bar'
+ * | instead of
+ * | ' foo bar'
+ * []----
+ */
+char *
+dospace(int n)
+{
+ (void) memset(spaces, ' ', sizeof (spaces));
+ spaces[sizeof (spaces) - 1] = '\0';
+
+ if (n < sizeof (spaces))
+ spaces[n * 4] = '\0';
+ return (spaces);
+}
diff --git a/usr/src/cmd/iscsi/iscsitadm/helper.h b/usr/src/cmd/iscsi/iscsitadm/helper.h
new file mode 100644
index 0000000000..543e44d259
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitadm/helper.h
@@ -0,0 +1,91 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _HELPER_H
+#define _HELPER_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_ISCSI_NAME_LEN 223
+#define MAX_ADDRESS_LEN 255
+#define MIN_CHAP_SECRET_LEN 12
+#define MAX_CHAP_SECRET_LEN 16
+#define DEFAULT_ISCSI_PORT 3260
+#define DEFAULT_RADIUS_PORT 1812
+#define MAX_CHAP_NAME_LEN 512
+
+/* forward declarations */
+#define PARSE_ADDR_OK 0
+#define PARSE_ADDR_MISSING_CLOSING_BRACKET 1
+#define PARSE_ADDR_PORT_OUT_OF_RANGE 2
+#define PARSE_TARGET_OK 0
+#define PARSE_TARGET_INVALID_TPGT 1
+#define PARSE_TARGET_INVALID_ADDR 2
+
+typedef enum iSCSINameCheckStatus {
+ iSCSINameCheckOK,
+ iSCSINameLenZero,
+ iSCSINameLenExceededMax,
+ iSCSINameUnknownType,
+ iSCSINameIqnFormatError,
+ iSCSINameEUIFormatError
+} iSCSINameCheckStatusType;
+
+typedef struct stat_delta {
+ struct stat_delta *next;
+ char *device;
+ size_t read_cmds,
+ write_cmds,
+ read_blks,
+ write_blks;
+} stat_delta_t;
+
+/* helper functions */
+int getSecret(char *, int *, int, int);
+xml_node_t *send_data(char *hostname, char *first_str);
+int parseAddress(char *address_port_str, uint16_t defaultPort,
+ char *address_str, size_t address_str_len,
+ uint16_t *port, boolean_t *isIpv6);
+char *number_to_scaled_string(
+ char *buf,
+ unsigned long long number,
+ int unit_from,
+ int scale);
+void stats_load_counts(xml_node_t *n, stat_delta_t *d);
+stat_delta_t *stats_prev_counts(stat_delta_t *cp);
+void stats_update_counts(stat_delta_t *p, stat_delta_t *c);
+void stats_free();
+char *dospace(int n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HELPER_H */
diff --git a/usr/src/cmd/iscsi/iscsitadm/main.c b/usr/src/cmd/iscsi/iscsitadm/main.c
new file mode 100644
index 0000000000..60e010dddc
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitadm/main.c
@@ -0,0 +1,1584 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <limits.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <zone.h>
+
+#include "cmdparse.h"
+#include "xml.h"
+#include "helper.h"
+
+#define CREATE SUBCOMMAND(0)
+#define LIST SUBCOMMAND(1)
+#define MODIFY SUBCOMMAND(2)
+#define DELETE SUBCOMMAND(3)
+#define SHOW SUBCOMMAND(4)
+
+#define TARGET OBJECT(0)
+#define INITIATOR OBJECT(1)
+#define ADMIN OBJECT(2)
+#define TPGT OBJECT(3)
+#define STATS OBJECT(4)
+
+#define VERSION_STRING_MAX_LEN 10
+#define MAX_IPADDRESS_LEN 128
+
+/*
+ * Version number:
+ * MAJOR - This should only change when there is an incompatible change made
+ * to the interfaces or the output.
+ *
+ * MINOR - This should change whenever there is a new command or new feature
+ * with no incompatible change.
+ */
+#define VERSION_STRING_MAJOR "1"
+#define VERSION_STRING_MINOR "0"
+
+#define OPT_ENABLE "enable"
+#define OPT_DISABLE "disable"
+#define OPT_TRUE "true"
+#define OPT_FALSE "false"
+
+/* subcommand functions */
+static int createFunc(int, char **, int, cmdOptions_t *, void *);
+static int listFunc(int, char **, int, cmdOptions_t *, void *);
+static int modifyFunc(int, char **, int, cmdOptions_t *, void *);
+static int deleteFunc(int, char **, int, cmdOptions_t *, void *);
+static int showFunc(int, char **, int, cmdOptions_t *, void *);
+
+/* object functions per subcommand */
+static int createTarget(int, char *[], cmdOptions_t *);
+static int createInitiator(int, char *[], cmdOptions_t *);
+static int createTpgt(int, char *[], cmdOptions_t *);
+static int modifyTarget(int, char *[], cmdOptions_t *);
+static int modifyInitiator(int, char *[], cmdOptions_t *);
+static int modifyTpgt(int, char *[], cmdOptions_t *);
+static int modifyAdmin(int, char *[], cmdOptions_t *);
+static int deleteTarget(int, char *[], cmdOptions_t *);
+static int deleteInitiator(int, char *[], cmdOptions_t *);
+static int deleteTpgt(int, char *[], cmdOptions_t *);
+static int listTarget(int, char *[], cmdOptions_t *);
+static int listInitiator(int, char *[], cmdOptions_t *);
+static int listTpgt(int, char *[], cmdOptions_t *);
+static int showAdmin(int, char *[], cmdOptions_t *);
+static int showStats(int, char *[], cmdOptions_t *);
+
+/* globals */
+char *cmdName;
+static char *hostname = NULL;
+
+/*
+ * Add new options here
+ */
+optionTbl_t longOptions[] = {
+ {"size", required_arg, 'z', "size k/m/g/t"},
+ {"type", required_arg, 't', "disk/tape/osd/raw"},
+ {"lun", required_arg, 'u', "number"},
+ {"alias", required_arg, 'a', "value"},
+ {"backing-store", required_arg, 'b', "pathname"},
+ {"tpgt", required_arg, 'p', "tpgt number"},
+ {"acl", required_arg, 'l', "local initiator"},
+ {"maxrecv", required_arg, 'm', "max recv data segment length"},
+ {"chap-secret", no_arg, 'C', NULL},
+ {"chap-name", required_arg, 'H', "chap username"},
+ {"iqn", required_arg, 'n', "iSCSI node name"},
+ {"ip-address", required_arg, 'i', "ip address"},
+ {"base-directory", required_arg, 'd', "directory"},
+ {"radius-access", required_arg, 'R', "enable/disable"},
+ {"radius-server", required_arg, 'r', "hostname[:port]"},
+ {"radius-secret", no_arg, 'P', NULL},
+ {"isns-access", required_arg, 'S', "enable/disable"},
+ {"fast-write-ack", required_arg, 'f', "enable/disable"},
+ {"verbose", no_arg, 'v', NULL},
+ {"interval", required_arg, 'I', "seconds"},
+ {"count", required_arg, 'N', "number"},
+ {"all", no_arg, 'A', NULL},
+ {NULL, 0, 0, 0}
+};
+
+/*
+ * Add new subcommands here
+ */
+subcommand_t subcommands[] = {
+ {"create", CREATE, createFunc},
+ {"list", LIST, listFunc},
+ {"modify", MODIFY, modifyFunc},
+ {"delete", DELETE, deleteFunc},
+ {"show", SHOW, showFunc},
+ {NULL, 0, NULL}
+};
+
+/*
+ * Add objects here
+ */
+object_t objects[] = {
+ {"target", TARGET},
+ {"initiator", INITIATOR},
+ {"admin", ADMIN},
+ {"tpgt", TPGT},
+ {"stats", STATS},
+ {NULL, 0}
+};
+
+/*
+ * Rules for subcommands and objects
+ * ReqiredOp, OptioalOp, NoOp, InvalidOp, MultiOp
+ */
+objectRules_t objectRules[] = {
+ /*
+ * create/modify/delete subcmd requires an operand
+ * list subcmd optionally requires an operand
+ * no subcmd requires no operand
+ * no subcmd is invalid for this operand
+ * no subcmd can accept multiple operands
+ */
+ {TARGET, CREATE|MODIFY|DELETE, LIST, 0, SHOW, 0, "local-target"},
+ /*
+ * create/modify/delete subcmd requires an operand
+ * list subcmd optionally requires an operand
+ * no subcmd requires no operand
+ * no subcmd is invalid for this operand
+ * no subcmd can accept multiple operands
+ */
+ {INITIATOR, CREATE|MODIFY|DELETE, LIST, 0, SHOW, 0, "local-initiator"},
+ /*
+ * no subcmd requires an operand
+ * no subcmd optionally requires an operand
+ * modify/list subcmd requires no operand
+ * create/delete subcmd are invlaid for this operand
+ * no subcmd can accept multiple operands
+ */
+ {ADMIN, 0, 0, MODIFY|SHOW, CREATE|DELETE|LIST, 0, NULL},
+ /*
+ * create/modify/delete subcmd requires an operand
+ * list subcmd optionally requires an operand
+ * no subcmd requires no operand
+ * no subcmd is invalid for this operand
+ * no subcmd can accept multiple operands
+ */
+ {TPGT, CREATE|MODIFY|DELETE, LIST, 0, SHOW, 0, "local-tpgt"},
+ /*
+ * no subcmd requires an operand
+ * list subcmd optionally requires an operand
+ * no subcmd requires no operand
+ * create/delete/modify subcmd are invalid for this operand
+ * no subcmd can accept multiple operands
+ */
+ {STATS, 0, SHOW, 0, CREATE|MODIFY|DELETE|LIST, 0, "local-target"},
+ {0, 0, 0, 0, 0, NULL}
+};
+
+/*
+ * list of objects, subcommands, valid short options, required flag and
+ * exclusive option string
+ *
+ * If it's not here, there are no options for that object.
+ */
+optionRules_t optionRules[] = {
+ {TARGET, CREATE, "tuzab", B_TRUE, NULL},
+ {TARGET, MODIFY, "plamzu", B_TRUE, NULL},
+ {TARGET, DELETE, "ulp", B_TRUE, NULL},
+ {TARGET, LIST, "v", B_FALSE, NULL},
+ {INITIATOR, CREATE, "n", B_TRUE, NULL},
+ {INITIATOR, MODIFY, "CH", B_TRUE, NULL},
+ {INITIATOR, DELETE, "A", B_TRUE, NULL},
+ {INITIATOR, LIST, "v", B_FALSE, NULL},
+ {TPGT, MODIFY, "i", B_TRUE, NULL},
+ {TPGT, DELETE, "Ai", B_TRUE, NULL},
+ {TPGT, LIST, "v", B_FALSE, NULL},
+ {ADMIN, MODIFY, "dHCRrPSf", B_TRUE, NULL},
+ {STATS, SHOW, "vIN", B_FALSE, NULL},
+};
+
+
+
+/*ARGSUSED*/
+static int
+createFunc(int operandLen, char *operand[], int object, cmdOptions_t *options,
+ void *addArgs)
+{
+ int ret;
+
+ switch (object) {
+ case TARGET:
+ ret = createTarget(operandLen, operand, options);
+ break;
+ case INITIATOR:
+ ret = createInitiator(operandLen, operand, options);
+ break;
+ case TPGT:
+ ret = createTpgt(operandLen, operand, options);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName, gettext("unknown object"));
+ ret = 1;
+ break;
+ }
+ return (ret);
+}
+
+/*ARGSUSED*/
+static int
+listFunc(int operandLen, char *operand[], int object, cmdOptions_t *options,
+ void *addArgs)
+{
+ int ret;
+
+ switch (object) {
+ case TARGET:
+ ret = listTarget(operandLen, operand, options);
+ break;
+ case INITIATOR:
+ ret = listInitiator(operandLen, operand, options);
+ break;
+ case TPGT:
+ ret = listTpgt(operandLen, operand, options);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName, gettext("unknown object"));
+ ret = 1;
+ break;
+ }
+ return (ret);
+}
+
+/*ARGSUSED*/
+static int
+showFunc(int operandLen, char *operand[], int object, cmdOptions_t *options,
+ void *addArgs)
+{
+ int ret;
+
+ switch (object) {
+ case STATS:
+ ret = showStats(operandLen, operand, options);
+ break;
+ case ADMIN:
+ ret = showAdmin(operandLen, operand, options);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName, gettext("unknown object"));
+ ret = 1;
+ break;
+ }
+ return (ret);
+}
+
+/*ARGSUSED*/
+static int
+modifyFunc(int operandLen, char *operand[], int object, cmdOptions_t *options,
+ void *addArgs)
+{
+ int ret;
+
+ switch (object) {
+ case TARGET:
+ ret = modifyTarget(operandLen, operand, options);
+ break;
+ case INITIATOR:
+ ret = modifyInitiator(operandLen, operand, options);
+ break;
+ case TPGT:
+ ret = modifyTpgt(operandLen, operand, options);
+ break;
+ case ADMIN:
+ ret = modifyAdmin(operandLen, operand, options);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName, gettext("unknown object"));
+ ret = 1;
+ break;
+ }
+ return (ret);
+}
+
+/*ARGSUSED*/
+static int
+deleteFunc(int operandLen, char *operand[], int object, cmdOptions_t *options,
+ void *addArgs)
+{
+ int ret;
+
+ switch (object) {
+ case TARGET:
+ ret = deleteTarget(operandLen, operand, options);
+ break;
+ case INITIATOR:
+ ret = deleteInitiator(operandLen, operand, options);
+ break;
+ case TPGT:
+ ret = deleteTpgt(operandLen, operand, options);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName, gettext("unknown object"));
+ ret = 1;
+ break;
+ }
+ return (ret);
+}
+
+static int
+formatErrString(xml_node_t *node)
+{
+ int code = 0,
+ rtn = 0;
+ char *msg = NULL;
+
+ if ((strcmp(node->x_name, XML_ELEMENT_ERROR) == 0) &&
+ (xml_find_value_int(node, XML_ELEMENT_CODE, &code) == B_TRUE) &&
+ (xml_find_value_str(node, XML_ELEMENT_MESSAGE, &msg) == B_TRUE)) {
+
+ /*
+ * 1000 is the success code, so we don't need to display
+ * the success message.
+ */
+ if (code != 1000) {
+ (void) fprintf(stderr, "%s: %s %s\n",
+ cmdName, gettext("Error"), msg);
+ rtn = 1;
+ }
+ } else {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("Bad XML response"));
+ rtn = 1;
+ }
+ if (msg)
+ free(msg);
+ return (rtn);
+}
+
+/*ARGSUSED*/
+static int
+createTarget(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+ cmdOptions_t *optionList = options;
+
+ if (operand == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "create", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_TARG, Tag_Start);
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ for (; optionList->optval; optionList++) {
+ switch (optionList->optval) {
+ case 't': /* type */
+ if ((strcmp(optionList->optarg, "disk")) &&
+ (strcmp(optionList->optarg, "tape")) &&
+ (strcmp(optionList->optarg, "raw")) &&
+ (strcmp(optionList->optarg, "osd"))) {
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown type"));
+ free(first_str);
+ return (1);
+ } else {
+ xml_add_tag(&first_str,
+ XML_ELEMENT_TYPE,
+ optionList->optarg);
+ }
+ break;
+ case 'z': /* size */
+ xml_add_tag(&first_str, XML_ELEMENT_SIZE,
+ optionList->optarg);
+ break;
+ case 'u': /* lun number */
+ xml_add_tag(&first_str, XML_ELEMENT_LUN,
+ optionList->optarg);
+ break;
+ case 'a': /* alias */
+ xml_add_tag(&first_str, XML_ELEMENT_ALIAS,
+ optionList->optarg);
+ break;
+ case 'b': /* backing store */
+ xml_add_tag(&first_str, XML_ELEMENT_BACK,
+ optionList->optarg);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_TARG, Tag_End);
+ buf_add_tag(&first_str, "create", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+ return (formatErrString(node));
+}
+
+/*ARGSUSED*/
+static int
+createInitiator(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+ cmdOptions_t *optionList = options;
+
+ if (operand == NULL)
+ return (1);
+ if (options == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "create", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_INIT, Tag_Start);
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ switch (optionList->optval) {
+ case 'n': /* iqn */
+ xml_add_tag(&first_str, XML_ELEMENT_INAME, optionList->optarg);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_INIT, Tag_End);
+ buf_add_tag(&first_str, "create", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+ return (formatErrString(node));
+}
+
+/*ARGSUSED*/
+static int
+createTpgt(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+
+ if (operand == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "create", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_TPGT, Tag_Start);
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+ buf_add_tag(&first_str, XML_ELEMENT_TPGT, Tag_End);
+ buf_add_tag(&first_str, "create", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+ return (formatErrString(node));
+}
+
+/*ARGSUSED*/
+static int
+modifyTarget(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+ cmdOptions_t *optionList = options;
+
+ if (operand == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "modify", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_TARG, Tag_Start);
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ for (; optionList->optval; optionList++) {
+ switch (optionList->optval) {
+ case 'p': /* tpgt number */
+ xml_add_tag(&first_str, XML_ELEMENT_TPGT,
+ optionList->optarg);
+ break;
+ case 'l': /* acl */
+ xml_add_tag(&first_str, XML_ELEMENT_ACL,
+ optionList->optarg);
+ break;
+ case 'a': /* alias */
+ xml_add_tag(&first_str, XML_ELEMENT_ALIAS,
+ optionList->optarg);
+ break;
+ case 'm': /* max recv */
+ xml_add_tag(&first_str, XML_ELEMENT_MAXRECV,
+ optionList->optarg);
+ break;
+ case 'z': /* grow lun size */
+ xml_add_tag(&first_str, XML_ELEMENT_SIZE,
+ optionList->optarg);
+ break;
+ case 'u':
+ xml_add_tag(&first_str, XML_ELEMENT_LUN,
+ optionList->optarg);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_TARG, Tag_End);
+ buf_add_tag(&first_str, "modify", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+ return (formatErrString(node));
+}
+
+/*ARGSUSED*/
+static int
+modifyInitiator(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+ cmdOptions_t *optionList = options;
+ char chapSecret[MAX_CHAP_SECRET_LEN];
+ int secretLen = 0;
+ int ret = 0;
+
+ if (operand == NULL)
+ return (1);
+ if (options == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "modify", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_INIT, Tag_Start);
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ for (; optionList->optval; optionList++) {
+ switch (optionList->optval) {
+ case 'H': /* chap-name */
+ xml_add_tag(&first_str, XML_ELEMENT_CHAPNAME,
+ optionList->optarg);
+ break;
+ case 'C': /* chap-secret */
+ ret = getSecret((char *)&chapSecret[0], &secretLen,
+ MIN_CHAP_SECRET_LEN, MAX_CHAP_SECRET_LEN);
+ if (ret != 0) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("Cannot read CHAP secret"));
+ return (ret);
+ }
+ chapSecret[secretLen] = '\0';
+ xml_add_tag(&first_str, XML_ELEMENT_CHAPSECRET,
+ chapSecret);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+ }
+ buf_add_tag(&first_str, XML_ELEMENT_INIT, Tag_End);
+ buf_add_tag(&first_str, "modify", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+ return (formatErrString(node));
+}
+
+/*ARGSUSED*/
+static int
+modifyTpgt(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+ cmdOptions_t *optionList = options;
+ boolean_t isIpv6 = B_FALSE;
+ uint16_t port;
+ char IpAddress[MAX_IPADDRESS_LEN];
+
+ if (operand == NULL)
+ return (1);
+ if (optionList == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "modify", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_TPGT, Tag_Start);
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ switch (optionList->optval) {
+ case 'i': /* ip address */
+ if (parseAddress(optionList->optarg, 0,
+ IpAddress, 256, &port, &isIpv6) !=
+ PARSE_ADDR_OK) {
+ return (1);
+ }
+ xml_add_tag(&first_str, XML_ELEMENT_IPADDR, IpAddress);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_TPGT, Tag_End);
+ buf_add_tag(&first_str, "modify", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+ return (formatErrString(node));
+}
+
+/*ARGSUSED*/
+static int
+modifyAdmin(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+ cmdOptions_t *optionList = options;
+ char chapSecret[MAX_CHAP_SECRET_LEN],
+ olddir[MAXPATHLEN],
+ newdir[MAXPATHLEN];
+ int secretLen = 0;
+ int ret = 0;
+
+ if (operand == NULL)
+ return (1);
+ if (options == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "modify", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_ADMIN, Tag_Start);
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ for (; optionList->optval; optionList++) {
+ switch (optionList->optval) {
+ case 'd': /* base directory */
+ (void) getcwd(olddir, sizeof (olddir));
+
+ /*
+ * Attempt to create the new base directory.
+ * This may fail for one of two reasons.
+ * (a) The path given is invalid or (b) it
+ * already exists. If (a) is true then then
+ * following chdir() will fail and the user
+ * notified. If (b) is true, then chdir() will
+ * succeed.
+ */
+ (void) mkdir(optionList->optarg, 0700);
+
+ if (chdir(optionList->optarg) == -1) {
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName, gettext("Invalid path"));
+ free(first_str);
+ return (1);
+ }
+ (void) getcwd(newdir, sizeof (newdir));
+ xml_add_tag(&first_str, XML_ELEMENT_BASEDIR,
+ newdir);
+ chdir(olddir);
+ break;
+ case 'H': /* chap name */
+ xml_add_tag(&first_str, XML_ELEMENT_CHAPNAME,
+ optionList->optarg);
+ break;
+ case 'C': /* chap secert */
+ ret = getSecret((char *)&chapSecret[0],
+ &secretLen,
+ MIN_CHAP_SECRET_LEN,
+ MAX_CHAP_SECRET_LEN);
+ if (ret != 0) {
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName,
+ gettext("Cannot read CHAP secret"));
+ free(first_str);
+ return (ret);
+ }
+ chapSecret[secretLen] = '\0';
+ xml_add_tag(&first_str, XML_ELEMENT_CHAPSECRET,
+ chapSecret);
+ break;
+ case 'R': /* radius access */
+ if (strcmp(optionList->optarg,
+ OPT_ENABLE) == 0) {
+ xml_add_tag(&first_str,
+ XML_ELEMENT_RAD_ACCESS, OPT_TRUE);
+ } else
+ if (strcmp(optionList->optarg,
+ OPT_DISABLE) == 0) {
+ xml_add_tag(&first_str,
+ XML_ELEMENT_RAD_ACCESS, OPT_FALSE);
+ } else {
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName,
+ gettext("Option value should be"
+ "enable/disable"));
+ free(first_str);
+ return (1);
+ }
+ break;
+ case 'r': /* radius server */
+ xml_add_tag(&first_str, XML_ELEMENT_RAD_SERV,
+ optionList->optarg);
+ break;
+ case 'P': /* radius secret */
+ ret = getSecret((char *)&chapSecret[0],
+ &secretLen, MIN_CHAP_SECRET_LEN,
+ MAX_CHAP_SECRET_LEN);
+ if (ret != 0) {
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName,
+ gettext("Cannot read RADIUS "
+ "secret"));
+ free(first_str);
+ return (ret);
+ }
+ chapSecret[secretLen] = '\0';
+ xml_add_tag(&first_str, XML_ELEMENT_RAD_SECRET,
+ chapSecret);
+ break;
+ case 'S': /* iSNS access */
+ if (strcmp(optionList->optarg,
+ OPT_ENABLE) == 0) {
+ xml_add_tag(&first_str,
+ XML_ELEMENT_ISNS_ACCESS, OPT_TRUE);
+ } else
+ if (strcmp(optionList->optarg,
+ OPT_DISABLE) == 0) {
+ xml_add_tag(&first_str,
+ XML_ELEMENT_ISNS_ACCESS, OPT_FALSE);
+ } else {
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName,
+ gettext("Option value should be"
+ "enable/disable"));
+ free(first_str);
+ return (1);
+ }
+ break;
+ case 'f': /* fast write back */
+ if (strcmp(optionList->optarg,
+ OPT_ENABLE) == 0) {
+ xml_add_tag(&first_str,
+ XML_ELEMENT_FAST, OPT_TRUE);
+ } else
+ if (strcmp(optionList->optarg,
+ OPT_DISABLE) == 0) {
+ xml_add_tag(&first_str,
+ XML_ELEMENT_FAST, OPT_FALSE);
+ } else {
+ (void) fprintf(stderr, "%s: %s\n",
+ cmdName,
+ gettext("Option value should be"
+ "enable/disable"));
+ free(first_str);
+ return (1);
+ }
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_ADMIN, Tag_End);
+ buf_add_tag(&first_str, "modify", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+ return (formatErrString(node));
+}
+
+/*ARGSUSED*/
+static int
+deleteTarget(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+ cmdOptions_t *optionList = options;
+
+ if (operand == NULL)
+ return (1);
+ if (options == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "delete", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_TARG, Tag_Start);
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ switch (optionList->optval) {
+ case 'u': /* all */
+ xml_add_tag(&first_str, XML_ELEMENT_LUN, optionList->optarg);
+ break;
+ case 'l': /* acl */
+ xml_add_tag(&first_str, XML_ELEMENT_ACL, optionList->optarg);
+ break;
+ case 'p': /* tpgt number */
+ xml_add_tag(&first_str, XML_ELEMENT_TPGT, optionList->optarg);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_TARG, Tag_End);
+ buf_add_tag(&first_str, "delete", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+ return (formatErrString(node));
+}
+
+/*ARGSUSED*/
+static int
+deleteInitiator(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+ cmdOptions_t *optionList = options;
+
+ if (operand == NULL)
+ return (1);
+ if (options == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "delete", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_INIT, Tag_Start);
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ switch (optionList->optval) {
+ case 'A': /* all */
+ xml_add_tag(&first_str, XML_ELEMENT_ALL, optionList->optarg);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_INIT, Tag_End);
+ buf_add_tag(&first_str, "delete", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+ return (formatErrString(node));
+}
+
+/*ARGSUSED*/
+static int
+deleteTpgt(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+ cmdOptions_t *optionList = options;
+ boolean_t isIpv6 = B_FALSE;
+ uint16_t port;
+ char IpAddress[MAX_IPADDRESS_LEN];
+
+ if (operand == NULL)
+ return (1);
+ if (options == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "delete", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_TPGT, Tag_Start);
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ switch (optionList->optval) {
+ case 'A': /* all */
+ xml_add_tag(&first_str, XML_ELEMENT_ALL, optionList->optarg);
+ break;
+ case 'i': /* ip address */
+ if (parseAddress(optionList->optarg, 0,
+ IpAddress, 256, &port, &isIpv6) !=
+ PARSE_ADDR_OK) {
+ return (1);
+ }
+ xml_add_tag(&first_str, XML_ELEMENT_IPADDR, IpAddress);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_TPGT, Tag_End);
+ buf_add_tag(&first_str, "delete", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+ return (formatErrString(node));
+}
+
+static int
+listTarget(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node = NULL;
+ xml_node_t *n1 = NULL; /* pointer to node (depth=1) */
+ xml_node_t *n2 = NULL; /* pointer to node (depth=2) */
+ xml_node_t *n3 = NULL; /* pointer to node (depth=3) */
+ xml_node_t *n4 = NULL; /* pointer to node (depth=4) */
+ int conns;
+ char buf[32];
+ Boolean_t verbose = False;
+
+ if (operand == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "list", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_TARG, Tag_Start);
+
+ if (operandLen)
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ /*
+ * Always retrieve the iostats which will give us the
+ * connection count information even if we're not doing
+ * a verbose output.
+ */
+ xml_add_tag(&first_str, XML_ELEMENT_IOSTAT, OPT_TRUE);
+
+ if (options) {
+ switch (options->optval) {
+ case 0:
+ break;
+ case 'v':
+ xml_add_tag(&first_str, XML_ELEMENT_LUNINFO, OPT_TRUE);
+ verbose = True;
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n", cmdName,
+ options->optval, gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_TARG, Tag_End);
+ buf_add_tag(&first_str, "list", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+
+ if (strcmp(node->x_name, XML_ELEMENT_RESULT)) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("Bad XML response"));
+ return (1);
+ }
+
+ n1 = NULL;
+ while ((n1 = xml_node_next_child(node, XML_ELEMENT_TARG, n1)) != NULL) {
+ (void) printf("%s: %s\n", gettext("Target"), n1->x_value);
+ n2 = xml_node_next_child(n1, XML_ELEMENT_INAME, NULL);
+ (void) printf("%s%s: %s\n", dospace(1), gettext("iSCSI Name"),
+ n2 ? n2->x_value : gettext("Not set"));
+
+ if ((n2 = xml_node_next_child(n1, XML_ELEMENT_ALIAS, NULL)) !=
+ NULL)
+ (void) printf("%s%s: %s\n", dospace(1),
+ gettext("Alias"), n2->x_value);
+
+ if ((n2 = xml_node_next_child(n1, XML_ELEMENT_MAXRECV, NULL)) !=
+ NULL)
+ (void) printf("%s%s: %s\n", dospace(1),
+ gettext("MaxRecv"), n2->x_value);
+
+ /*
+ * Count the number of connections available.
+ */
+ n2 = NULL;
+ conns = 0;
+ while (n2 = xml_node_next_child(n1, XML_ELEMENT_CONN, n2))
+ conns++;
+ (void) printf("%s%s: %d\n", dospace(1), gettext("Connections"),
+ conns);
+
+ if (verbose == False)
+ continue;
+
+ /*
+ * Displaying the individual connections must be done
+ * first when verbose is turned on because you'll notice
+ * above that we've left the output hanging with a label
+ * indicating connections are coming next.
+ */
+ n2 = NULL;
+ while (n2 = xml_node_next_child(n1, XML_ELEMENT_CONN, n2)) {
+ (void) printf("%s%s:\n", dospace(2),
+ gettext("Initiator"));
+ (void) printf("%s%s: %s\n", dospace(3),
+ gettext("iSCSI Name"), n2->x_value);
+ n3 = xml_node_next_child(n2, XML_ELEMENT_ALIAS, NULL);
+ (void) printf("%s%s: %s\n", dospace(3),
+ gettext("Alias"),
+ n3 ? n3->x_value : gettext("unknown"));
+ }
+
+ (void) printf("%s%s:\n", dospace(1), gettext("ACL list"));
+ n2 = xml_node_next_child(n1, XML_ELEMENT_ACLLIST, NULL);
+ n3 = NULL;
+ while (n3 = xml_node_next_child(n2, XML_ELEMENT_INIT, n3)) {
+ (void) printf("%s%s: %s\n", dospace(2),
+ gettext("Initiator"),
+ n3->x_value);
+ }
+
+ (void) printf("%s%s:\n", dospace(1), gettext("TPGT list"));
+ n2 = xml_node_next_child(n1, XML_ELEMENT_TPGTLIST, NULL);
+ n3 = NULL;
+ while (n3 = xml_node_next_child(n2, XML_ELEMENT_TPGT, n3)) {
+ (void) printf("%s%s: %s\n", dospace(2),
+ gettext("TPGT"),
+ n3->x_value);
+ }
+
+ (void) printf("%s%s:\n", dospace(1),
+ gettext("LUN information"));
+ n2 = xml_node_next_child(n1, XML_ELEMENT_LUNINFO, NULL);
+ n3 = NULL;
+ while (n3 = xml_node_next_child(n2, XML_ELEMENT_LUN, n3)) {
+ (void) printf("%s%s: %s\n", dospace(2), gettext("LUN"),
+ n3->x_value);
+
+ n4 = xml_node_next_child(n3, XML_ELEMENT_GUID, NULL);
+ (void) printf("%s%s: %s\n", dospace(3), gettext("GUID"),
+ n4 ? n4->x_value : gettext("unknown"));
+
+ n4 = xml_node_next_child(n3, XML_ELEMENT_VID, NULL);
+ (void) printf("%s%s: %s\n", dospace(3), gettext("VID"),
+ n4 ? n4->x_value : gettext("unknown"));
+
+ n4 = xml_node_next_child(n3, XML_ELEMENT_PID, NULL);
+ (void) printf("%s%s: %s\n", dospace(3), gettext("PID"),
+ n4 ? n4->x_value : gettext("unknown"));
+
+ n4 = xml_node_next_child(n3, XML_ELEMENT_DTYPE, NULL);
+ (void) printf("%s%s: %s\n", dospace(3), gettext("Type"),
+ n4 ? n4->x_value : gettext("unknown"));
+
+ n4 = xml_node_next_child(n3, XML_ELEMENT_SIZE, NULL);
+ if (n4 && (strtol(n4->x_value, NULL, 0) != 0)) {
+ (void) printf("%s%s: %s\n", dospace(3),
+ gettext("Size"),
+ number_to_scaled_string(buf,
+ strtoll(n4->x_value,
+ NULL, 0), 512, 1024));
+ } else {
+ (void) printf("%s%s: %s\n", dospace(3),
+ gettext("Size"), gettext("unknown"));
+ }
+
+ n4 = xml_node_next_child(n3, XML_ELEMENT_BACK, NULL);
+ if (n4) {
+ (void) printf("%s%s: %s\n", dospace(3),
+ gettext("Backing store"), n4->x_value);
+ }
+
+ n4 = xml_node_next_child(n3, XML_ELEMENT_STATUS, NULL);
+ (void) printf("%s%s: %s\n", dospace(3),
+ gettext("Status"),
+ n4 ? n4->x_value : gettext("unknown"));
+ }
+ }
+
+ return (0);
+}
+
+static int
+listInitiator(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node;
+ xml_node_t *n1 = NULL; /* pointer to node (depth=1) */
+ xml_node_t *n2 = NULL; /* pointer to node (depth=2) */
+ Boolean_t verbose = False;
+ cmdOptions_t *optionList = options;
+
+ if (operand == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "list", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_INIT, Tag_Start);
+
+ if (operandLen) {
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+ }
+ if (optionList) {
+ switch (optionList->optval) {
+ case 0:
+ break;
+ case 'v':
+ verbose = True;
+ xml_add_tag(&first_str,
+ XML_ELEMENT_VERBOSE, OPT_TRUE);
+ break;
+
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_INIT, Tag_End);
+ buf_add_tag(&first_str, "list", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+
+ if (strcmp(node->x_name, XML_ELEMENT_RESULT)) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("Bad XML response"));
+ return (1);
+ }
+
+ n1 = NULL;
+ while (n1 = xml_node_next_child(node, XML_ELEMENT_INIT, n1)) {
+ (void) printf("%s: %s\n", gettext("Initiator"), n1->x_value);
+
+ n2 = xml_node_next_child(n1, XML_ELEMENT_INAME, NULL);
+ (void) printf("%s%s: %s\n", dospace(1), gettext("iSCSI Name"),
+ n2 ? n2->x_value : gettext("Not set"));
+
+ n2 = xml_node_next_child(n1, XML_ELEMENT_CHAPNAME, NULL);
+ (void) printf("%s%s: %s\n", dospace(1), gettext("CHAP Name"),
+ n2 ? n2->x_value : gettext("Not set"));
+
+ if (verbose == True) {
+ n2 = xml_node_next_child(n1, XML_ELEMENT_CHAPSECRET,
+ NULL);
+ (void) printf("%s%s: %s\n", dospace(1),
+ gettext("CHAP Secret"),
+ n2 ? gettext("Set") : gettext("Not set"));
+ }
+
+ }
+
+ return (0);
+}
+
+static int
+listTpgt(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node = NULL;
+ xml_node_t *n1 = NULL; /* pointer to node (depth=1) */
+ xml_node_t *n2 = NULL; /* pointer to node (depth=2) */
+ cmdOptions_t *optionList = options;
+ Boolean_t verbose = False;
+ int addrs;
+
+ if (operand == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "list", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_TPGT, Tag_Start);
+
+ if (operandLen)
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+ if (optionList) {
+ switch (optionList->optval) {
+ case 0: /* no options, treat as --verbose */
+ break;
+ case 'v':
+ verbose = True;
+ xml_add_tag(&first_str,
+ XML_ELEMENT_VERBOSE, OPT_TRUE);
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n",
+ cmdName, optionList->optval,
+ gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_TPGT, Tag_End);
+ buf_add_tag(&first_str, "list", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+
+ if (strcmp(node->x_name, XML_ELEMENT_RESULT)) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("Bad XML response"));
+ return (1);
+ }
+
+ n1 = NULL;
+ while (n1 = xml_node_next_child(node, XML_ELEMENT_TPGT, n1)) {
+ (void) printf("%s: %s\n", gettext("TPGT"), n1->x_value);
+ n2 = NULL;
+ addrs = 0;
+ while (n2 = xml_node_next_child(n1, XML_ELEMENT_IPADDR, n2)) {
+ if (verbose == True)
+ (void) printf("%s%s: %s\n", dospace(1),
+ gettext("IP Address"),
+ n2 ? n2->x_value : gettext("Not set"));
+ addrs++;
+ }
+
+ if (verbose == False) {
+ (void) printf("%s%s: %d\n", dospace(1),
+ gettext("IP Address count"), addrs);
+ } else if (addrs == 0) {
+
+ /*
+ * Verbose is true, but there where no addresses
+ * for this TPGT. To keep the output consistent
+ * dump a "Not set" string out.
+ */
+ (void) printf("%s%s: %s\n", dospace(1),
+ gettext("IP Address"), gettext("Not set"));
+ }
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+showAdmin(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL;
+ xml_node_t *node = NULL;
+ xml_node_t *n1 = NULL; /* pointer to node (depth=1) */
+ xml_node_t *n2 = NULL; /* pointer to node (depth=2) */
+
+ if (operand == NULL)
+ return (1);
+
+ buf_add_tag(&first_str, "list", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_ADMIN, Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_ADMIN, Tag_End);
+ buf_add_tag(&first_str, "list", Tag_End);
+
+ node = send_data(hostname, first_str);
+ free(first_str);
+
+ if (strcmp(node->x_name, XML_ELEMENT_RESULT)) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("Bad XML response"));
+ return (1);
+ }
+
+ (void) printf("%s:\n", cmdName);
+
+ n1 = xml_node_next_child(node, XML_ELEMENT_ADMIN, NULL);
+ if (n1 == NULL) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("Bad XML response"));
+ return (1);
+ }
+
+ n2 = xml_node_next_child(n1, XML_ELEMENT_BASEDIR, NULL);
+ (void) printf("%s%s: %s\n", dospace(1), gettext("Base Directory"),
+ n2 ? n2->x_value : gettext("Not set"));
+
+ n2 = xml_node_next_child(n1, XML_ELEMENT_CHAPNAME, NULL);
+ (void) printf("%s%s: %s\n", dospace(1), gettext("CHAP Name"),
+ n2 ? n2->x_value : gettext("Not set"));
+
+ n2 = xml_node_next_child(n1, XML_ELEMENT_RAD_ACCESS, NULL);
+ (void) printf("%s%s: ", dospace(1), gettext("RADIUS Access"));
+ if (n2) {
+ if (strcmp(n2->x_value, OPT_TRUE) == 0)
+ (void) printf("%s\n", gettext("Enabled"));
+ else
+ (void) printf("%s\n", gettext("Disabled"));
+ } else
+ (void) printf("%s\n", gettext("Not set"));
+
+ n2 = xml_node_next_child(n1, XML_ELEMENT_RAD_SERV, NULL);
+ (void) printf("%s%s: %s\n", dospace(1), gettext("RADIUS Server"),
+ n2 ? n2->x_value : gettext("Not set"));
+
+ n2 = xml_node_next_child(n1, XML_ELEMENT_ISNS_ACCESS, NULL);
+ (void) printf("%s%s: ", dospace(1), gettext("iSNS Access"));
+ if (n2) {
+ if (strcmp(n2->x_value, OPT_TRUE) == 0)
+ (void) printf("%s\n", gettext("Enabled"));
+ else
+ (void) printf("%s\n", gettext("Disabled"));
+ } else
+ (void) printf("%s\n", gettext("Not set"));
+
+ n2 = xml_node_next_child(n1, XML_ELEMENT_FAST, NULL);
+ (void) printf("%s%s: ", dospace(1), gettext("Fast Write ACK"));
+ if (n2) {
+ if (strcmp(n2->x_value, OPT_TRUE) == 0)
+ (void) printf("%s\n", gettext("Enabled"));
+ else
+ (void) printf("%s\n", gettext("Disabled"));
+ } else
+ (void) printf("%s\n", gettext("Not set"));
+
+ return (0);
+}
+
+static int
+showStats(int operandLen, char *operand[], cmdOptions_t *options)
+{
+ char *first_str = NULL,
+ scale_buf[16];
+ xml_node_t *node,
+ *n1;
+ int interval = -1,
+ count = -1,
+ header;
+ stat_delta_t cur_data,
+ *pd;
+
+ buf_add_tag(&first_str, "list", Tag_Start);
+ buf_add_tag(&first_str, XML_ELEMENT_TARG, Tag_Start);
+
+ xml_add_tag(&first_str, XML_ELEMENT_IOSTAT, OPT_TRUE);
+ if (operandLen)
+ xml_add_tag(&first_str, XML_ELEMENT_NAME, operand[0]);
+
+ for (; options->optval; options++) {
+ switch (options->optval) {
+ case 0:
+ break;
+ case 'I': /* optarg = refresh interval */
+ interval = atoi(options->optarg);
+ if (interval == 0) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("interval must be non-zero"));
+ free(first_str);
+ return (1);
+ }
+ break;
+ case 'N':
+ count = atoi(options->optarg);
+ if (count == 0) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("count must be non-zero"));
+ free(first_str);
+ return (1);
+ }
+ break;
+ default:
+ (void) fprintf(stderr, "%s: %c: %s\n", cmdName,
+ options->optval, gettext("unknown option"));
+ free(first_str);
+ return (1);
+ }
+ }
+
+ buf_add_tag(&first_str, XML_ELEMENT_TARG, Tag_End);
+ buf_add_tag(&first_str, "list", Tag_End);
+
+ header = 1;
+ /*CONSTANTCONDITION*/
+ while (1) {
+ if (--header == 0) {
+ (void) printf("%20s %12s %12s\n", " ",
+ gettext("operations"), gettext("bandwidth "));
+ (void) printf("%-20s %5s %5s %5s %5s\n",
+ gettext("device"), gettext("read"),
+ gettext("write"), gettext("read"),
+ gettext("write"));
+ (void) printf("%-20s %5s %5s %5s %5s\n",
+ "--------------------", "-----", "-----",
+ "-----", "-----");
+ header = 20;
+ }
+ node = send_data(hostname, first_str);
+
+ if (strcmp(node->x_name, XML_ELEMENT_RESULT)) {
+ (void) fprintf(stderr, "%s: %s\n", cmdName,
+ gettext("Bad XML response"));
+ free(first_str);
+ xml_tree_free(node);
+ stats_free();
+ return (1);
+ }
+
+ n1 = NULL;
+ while (n1 = xml_node_next_child(node, XML_ELEMENT_TARG, n1)) {
+ stats_load_counts(n1, &cur_data);
+ if ((pd = stats_prev_counts(&cur_data)) == NULL) {
+ free(first_str);
+ xml_tree_free(node);
+ return (1);
+ }
+ (void) printf("%-20s ", pd->device);
+ (void) printf("%5s ",
+ number_to_scaled_string(scale_buf,
+ cur_data.read_cmds - pd->read_cmds, 1, 1024));
+ (void) printf("%5s ",
+ number_to_scaled_string(scale_buf,
+ cur_data.write_cmds - pd->write_cmds, 1, 1024));
+ (void) printf("%5s ",
+ number_to_scaled_string(scale_buf,
+ cur_data.read_blks - pd->read_blks, 512, 1024));
+ (void) printf("%5s\n",
+ number_to_scaled_string(scale_buf,
+ cur_data.write_blks - pd->write_blks, 512, 1024));
+ stats_update_counts(pd, &cur_data);
+ }
+ xml_tree_free(node);
+
+ if (count == -1) {
+ if (interval == -1)
+ /* No count or internal, do it just once */
+ break;
+ else
+ (void) sleep(interval);
+ } else if (--count) {
+ if (interval == -1)
+ break;
+ else
+ (void) sleep(interval);
+ } else
+ break;
+ }
+
+ stats_free();
+ free(first_str);
+ return (0);
+}
+
+/*
+ * input:
+ * execFullName - exec name of program (argv[0])
+ *
+ * Returns:
+ * command name portion of execFullName
+ */
+static char *
+getExecBasename(char *execFullname)
+{
+ char *lastSlash, *execBasename;
+
+ /* guard against '/' at end of command invocation */
+ for (;;) {
+ lastSlash = strrchr(execFullname, '/');
+ if (lastSlash == NULL) {
+ execBasename = execFullname;
+ break;
+ } else {
+ execBasename = lastSlash + 1;
+ if (*execBasename == '\0') {
+ *lastSlash = '\0';
+ continue;
+ }
+ break;
+ }
+ }
+ return (execBasename);
+}
+
+/*
+ * main calls a parser that checks syntax of the input command against
+ * various rules tables.
+ *
+ * The parser provides usage feedback based upon same tables by calling
+ * two usage functions, usage and subUsage, handling command and subcommand
+ * usage respectively.
+ *
+ * The parser handles all printing of usage syntactical errors
+ *
+ * When syntax is successfully validated, the parser calls the associated
+ * function using the subcommands table functions.
+ *
+ * Syntax is as follows:
+ * command subcommand [options] resource-type [<object>]
+ *
+ * The return value from the function is placed in funcRet
+ */
+int
+main(int argc, char *argv[])
+{
+ synTables_t synTables;
+ char versionString[VERSION_STRING_MAX_LEN];
+ int ret;
+ int funcRet;
+ void *subcommandArgs = NULL;
+
+ /* set global command name */
+ cmdName = getExecBasename(argv[0]);
+
+ if (getzoneid() != GLOBAL_ZONEID) {
+ (void) fprintf(stderr,
+ "%s: this command is only available in the 'global' "
+ "zone\n", cmdName);
+ exit(1);
+ }
+
+ (void) snprintf(versionString, sizeof (versionString), "%s.%s",
+ VERSION_STRING_MAJOR, VERSION_STRING_MINOR);
+ synTables.versionString = versionString;
+ synTables.longOptionTbl = &longOptions[0];
+ synTables.subcommandTbl = &subcommands[0];
+ synTables.objectTbl = &objects[0];
+ synTables.objectRulesTbl = &objectRules[0];
+ synTables.optionRulesTbl = &optionRules[0];
+
+ /* call the CLI parser */
+ ret = cmdParse(argc, argv, synTables, subcommandArgs, &funcRet);
+ if (ret == 1) {
+ (void) printf("%s %s(1M)\n",
+ gettext("For more information, please see"), cmdName);
+ return (1);
+ } else if (ret == -1) {
+ perror(cmdName);
+ return (1);
+ }
+
+ return (funcRet);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/Makefile b/usr/src/cmd/iscsi/iscsitgtd/Makefile
new file mode 100644
index 0000000000..da0edb9e7e
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/Makefile
@@ -0,0 +1,83 @@
+#
+# 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
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/iscsi/iscsitgtd/Makefile
+
+PROG= iscsitgtd
+OBJS = main.o mgmt.o mgmt_create.o mgmt_list.o mgmt_modify.o mgmt_remove.o
+OBJS += iscsi_authclient.o iscsi_authglue.o iscsi_cmd.o iscsi_conn.o
+OBJS += iscsi_crc.o iscsi_ffp.o iscsi_login.o iscsi_sess.o radius.o
+OBJS += t10_sam.o t10_spc.o t10_sbc.o t10_raw_if.o t10_ssc.o t10_osd.o
+OBJS += util.o util_err.o util_ifname.o util_port.o util_queue.o
+POFILE= iscsitgtd.po
+POFILES = $(OBJS:%.o=%.po)
+
+include ../../Makefile.cmd
+include $(SRC)/cmd/iscsi/Makefile.iscsi
+
+$(64ONLY)SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+MANIFEST = iscsi_target.xml
+
+ROOTMANIFESTDIR = $(ROOTSVCSYSTEM)
+$(ROOTSVCSYSTEM)/iscsi_target.xml := OWNER = root
+$(ROOTSVCSYSTEM)/iscsi_target.xml := GROUP = bin
+$(ROOTSVCSYSTEM)/iscsi_target.xml := FILEMODE = 0444
+
+CPPFLAGS += -D_FILE_OFFSET_BITS=64 -I${ISCSICOMMONDIR}
+
+all := TARGET = all
+install := TARGET = install
+clean := TARGET = clean
+clobber := TARGET = clobber
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all: $(SUBDIRS)
+
+clean clobber lint: $(SUBDIRS)
+
+install: $(SUBDIRS) $(ROOTMANIFEST)
+ -$(RM) $(ROOTUSRSBINPROG)
+ -$(LN) $(ISAEXEC) $(ROOTUSRSBINPROG)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+catalog: $(POFILE)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+check: $(CHKMANIFEST)
+
+FRC:
+
+include ../../Makefile.targ
+
diff --git a/usr/src/cmd/iscsi/iscsitgtd/Makefile.com b/usr/src/cmd/iscsi/iscsitgtd/Makefile.com
new file mode 100644
index 0000000000..ff7064d4da
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/Makefile.com
@@ -0,0 +1,77 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/iscsi/iscsitgtd/Makefile.com
+
+PROG= iscsitgtd
+OBJS = main.o mgmt.o mgmt_create.o mgmt_list.o mgmt_modify.o mgmt_remove.o
+OBJS += iscsi_authclient.o iscsi_authglue.o iscsi_cmd.o iscsi_conn.o
+OBJS += iscsi_crc.o iscsi_ffp.o iscsi_login.o iscsi_sess.o radius.o
+OBJS += t10_sam.o t10_spc.o t10_sbc.o t10_raw_if.o t10_ssc.o t10_osd.o
+OBJS += util.o util_err.o util_ifname.o util_port.o util_queue.o
+SRCS= $(OBJS:%.o=../%.c) $(COMMON_SRCS)
+
+include ../../../Makefile.cmd
+include $(SRC)/cmd/iscsi/Makefile.iscsi
+
+SUFFIX_LINT = .ln
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -D_LARGEFILE64_SOURCE=1 -I$(ISCSICOMMONDIR) -I/usr/include/libxml2
+CFLAGS64 += $(CCVERBOSE)
+
+GROUP=sys
+
+CLEANFILES += $(OBJS)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+LDLIBS += -luuid -lxml2 -lsocket -lnsl -ldoor -lavl -lmd5 -ladm -lefi
+$(PROG): $(OBJS) $(COMMON_OBJS)
+ $(LINK.c) $(OBJS) $(COMMON_OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+lint := LINTFLAGS += -u
+lint := LINTFLAGS64 += -u
+
+# lint: lint_SRCS
+lint: $(SRCS:../%=%$(SUFFIX_LINT))
+
+%$(SUFFIX_LINT): ../%
+ ${LINT.c} -I.. ${INCLUDES} -y -c $< && touch $@
+
+%.o: $(ISCSICOMMONDIR)/%.c
+ $(COMPILE.c) $<
+
+%.o: ../%.c
+ $(COMPILE.c) $<
+
+clean:
+ $(RM) $(CLEANFILES) $(COMMON_OBJS) *$(SUFFIX_LINT)
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/iscsi/iscsitgtd/amd64/Makefile b/usr/src/cmd/iscsi/iscsitgtd/amd64/Makefile
new file mode 100644
index 0000000000..cee9d7ffbe
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/amd64/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.cmd.64
+
+install: all $(ROOTUSRSBINPROG64)
diff --git a/usr/src/cmd/iscsi/iscsitgtd/errcode.h b/usr/src/cmd/iscsi/iscsitgtd/errcode.h
new file mode 100644
index 0000000000..cf3a38e52e
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/errcode.h
@@ -0,0 +1,118 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TARGET_ERRCODE_H
+#define _TARGET_ERRCODE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Block comment which describes the contents of this file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ ERR_SUCCESS = 1000,
+ ERR_NULL_XML_MESSAGE,
+ ERR_SYNTAX_EMPTY,
+ ERR_SYNTAX_MISSING_ALL,
+ ERR_SYNTAX_MISSING_BACKING_STORE,
+ ERR_SYNTAX_MISSING_INAME,
+ ERR_SYNTAX_MISSING_IPADDR,
+ ERR_SYNTAX_MISSING_NAME,
+ ERR_SYNTAX_MISSING_OBJECT,
+ ERR_SYNTAX_MISSING_OPERAND,
+ ERR_SYNTAX_MISSING_SIZE,
+ ERR_SYNTAX_MISSING_TYPE,
+ ERR_SYNTAX_EMPTY_ACL,
+ ERR_SYNTAX_EMPTY_ALIAS,
+ ERR_SYNTAX_EMPTY_CHAPNAME,
+ ERR_SYNTAX_EMPTY_CHAPSECRET,
+ ERR_SYNTAX_EMPTY_IPADDR,
+ ERR_SYNTAX_EMPTY_MAXRECV,
+ ERR_SYNTAX_EMPTY_TPGT,
+ ERR_SYNTAX_INVALID_NAME,
+ ERR_INVALID_COMMAND,
+ ERR_INVALID_OBJECT,
+ ERR_INVALID_IP,
+ ERR_INVALID_BASEDIR,
+ ERR_INVALID_TPGT,
+ ERR_INVALID_MAXRECV,
+ ERR_INVALID_RADSRV,
+ ERR_INVALID_SIZE,
+ ERR_INIT_EXISTS,
+ ERR_NAME_TO_LONG,
+ ERR_LUN_EXISTS,
+ ERR_TPGT_EXISTS,
+ ERR_ACL_NOT_FOUND,
+ ERR_INIT_NOT_FOUND,
+ ERR_TARG_NOT_FOUND,
+ ERR_LUN_NOT_FOUND,
+ ERR_LUN_INVALID_RANGE,
+ ERR_TPGT_NOT_FOUND,
+ ERR_ACCESS_RAW_DEVICE_FAILED,
+ ERR_CREATE_METADATA_FAILED,
+ ERR_CREATE_SYMLINK_FAILED,
+ ERR_CREATE_NAME_TO_LONG,
+ ERR_DISK_BACKING_MUST_BE_REGULAR_FILE,
+ ERR_DISK_BACKING_NOT_VALID_RAW,
+ ERR_DISK_BACKING_SIZE_OR_FILE,
+ ERR_STAT_BACKING_FAILED,
+ ERR_RAW_PART_NOT_CAP,
+ ERR_CREATE_TARGET_DIR_FAILED,
+ ERR_ENCODE_GUID_FAILED,
+ ERR_INIT_XML_READER_FAILED,
+ ERR_OPEN_PARAM_FILE_FAILED,
+ ERR_UPDATE_MAINCFG_FAILED,
+ ERR_UPDATE_TARGCFG_FAILED,
+ ERR_VALID_TARG_EXIST,
+ ERR_TARGCFG_MISSING_INAME,
+ ERR_NO_MATCH,
+ ERR_NO_MEM,
+ ERR_LUN_ZERO_NOT_LAST,
+ ERR_LUN_ZERO_NOT_FIRST,
+ ERR_SIZE_MOD_BLOCK,
+ ERR_CANT_SHRINK_LU,
+ ERR_RESIZE_WRONG_TYPE,
+ ERR_RESIZE_WRONG_DTYPE,
+ ERR_LUN_NOT_GROWN,
+ ERR_FILE_TO_BIG,
+ ERR_FAILED_TO_CREATE_LU,
+ ERR_TAPE_NOT_SUPPORTED_IN_32BIT,
+ ERR_INTERNAL_ERROR
+} err_code_t;
+
+char *
+errcode_to_str(err_code_t err_code);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TARGET_ERRCODE_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/i386/Makefile b/usr/src/cmd/iscsi/iscsitgtd/i386/Makefile
new file mode 100644
index 0000000000..fde14781e6
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/i386/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/iscsi/iscsitgt/i386/Makefile
+
+include ../Makefile.com
+
+install: all $(ROOTUSRSBINPROG32)
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_authclient.c b/usr/src/cmd/iscsi/iscsitgtd/iscsi_authclient.c
new file mode 100644
index 0000000000..8aa279ed0e
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_authclient.c
@@ -0,0 +1,3042 @@
+/*
+ * 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 2000 by Cisco Systems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * FIXME: If this is true we have some problems. draft 15!?
+ *
+ * This file implements the iSCSI CHAP authentication method based on
+ * draft-ietf-ips-iscsi-15.txt. The code in this file is meant
+ * to be platform independent, and makes use of only limited library
+ * functions, presently only string.h. Platform dependent routines
+ * are defined in iscsi_authclient.h, but implemented in another file.
+ *
+ * This code in this files assumes a single thread of execution
+ * for each IscsiAuthClient structure, and does no locking.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef _KERNEL
+
+#include "iscsi.h"
+
+#else
+
+#include <strings.h>
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#endif
+
+#include <sys/iscsi_authclient.h>
+
+struct iscsiAuthKeyInfo_t {
+ const char *name;
+};
+typedef struct iscsiAuthKeyInfo_t IscsiAuthKeyInfo;
+
+
+IscsiAuthClientGlobalStats iscsiAuthClientGlobalStats;
+
+/*
+ * Note: The ordering of this table must match the order
+ * defined by IscsiAuthKeyType in iscsiAuthClient.h.
+ */
+static IscsiAuthKeyInfo iscsiAuthClientKeyInfo[iscsiAuthKeyTypeMaxCount] = {
+ {"AuthMethod"},
+ {"CHAP_A"},
+ {"CHAP_N"},
+ {"CHAP_R"},
+ {"CHAP_I"},
+ {"CHAP_C"}
+};
+
+static const char iscsiAuthClientHexString[] = "0123456789abcdefABCDEF";
+static const char iscsiAuthClientBase64String[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char iscsiAuthClientAuthMethodChapOptionName[] = "CHAP";
+
+
+static int
+iscsiAuthClientCheckString(const char *s,
+ unsigned int maxLength, unsigned int *pOutLength)
+{
+ unsigned int length;
+
+ if (!s) {
+ return (TRUE);
+ }
+
+ for (length = 0; length < maxLength; length++) {
+ if (*s++ == '\0') {
+ if (pOutLength) {
+ *pOutLength = length;
+ }
+ return (FALSE);
+ }
+ }
+
+ return (TRUE);
+}
+
+
+static int
+iscsiAuthClientStringCopy(char *stringOut, const char *stringIn,
+ unsigned int length)
+{
+ if (!stringOut || !stringIn || length == 0) {
+ return (TRUE);
+ }
+
+ while ((*stringOut++ = *stringIn++) != '\0') {
+ if (--length == 0) {
+ stringOut--;
+ *stringOut = '\0';
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+static int
+iscsiAuthClientStringAppend(char *stringOut, const char *stringIn,
+ unsigned int length)
+{
+ if (!stringOut || !stringIn || length == 0) {
+ return (TRUE);
+ }
+
+ while (*stringOut++ != '\0') {
+ if (--length == 0) {
+ stringOut--;
+ *stringOut = '\0';
+ return (TRUE);
+ }
+ }
+
+ stringOut--;
+
+ while ((*stringOut++ = *stringIn++) != '\0') {
+ if (--length == 0) {
+ stringOut--;
+ *stringOut = '\0';
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+static int
+iscsiAuthClientStringIndex(const char *s, int c)
+{
+ int n = 0;
+
+ while (*s != '\0') {
+ if (*s++ == c) {
+ return (n);
+ }
+ n++;
+ }
+
+ return (-1);
+}
+
+
+static int
+iscsiAuthClientCheckNodeType(int nodeType)
+{
+ if (nodeType == iscsiAuthNodeTypeInitiator ||
+ nodeType == iscsiAuthNodeTypeTarget) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+static int
+iscsiAuthClientCheckVersion(int value)
+{
+ if (value == iscsiAuthVersionDraft8 || value == iscsiAuthVersionRfc) {
+
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+static int
+iscsiAuthClientCheckAuthMethodOption(int value)
+{
+ if (value == iscsiAuthOptionNone || value == iscsiAuthMethodChap) {
+
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+static const char *
+iscsiAuthClientAuthMethodOptionToText(IscsiAuthClient * client, int value)
+{
+ const char *s;
+
+ switch (value) {
+ case iscsiAuthOptionReject:
+ s = client->rejectOptionName;
+ break;
+
+ case iscsiAuthOptionNone:
+ s = client->noneOptionName;
+ break;
+
+ case iscsiAuthMethodChap:
+ s = iscsiAuthClientAuthMethodChapOptionName;
+ break;
+
+ default:
+ s = 0;
+ }
+
+ return (s);
+}
+
+
+static int
+iscsiAuthClientCheckChapAlgorithmOption(int chapAlgorithm)
+{
+ if (chapAlgorithm == iscsiAuthOptionNone ||
+ chapAlgorithm == iscsiAuthChapAlgorithmMd5) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+static int
+iscsiAuthClientDataToHex(unsigned char *data, unsigned int dataLength,
+ char *text, unsigned int textLength)
+{
+ unsigned long n;
+
+ if (!text || textLength == 0) {
+ return (TRUE);
+ }
+
+ if (!data || dataLength == 0) {
+ *text = '\0';
+ return (TRUE);
+ }
+
+ if (textLength < 3) {
+ *text = '\0';
+ return (TRUE);
+ }
+
+ *text++ = '0';
+ *text++ = 'x';
+
+ textLength -= 2;
+
+ while (dataLength > 0) {
+
+ if (textLength < 3) {
+ *text = '\0';
+ return (TRUE);
+ }
+
+ n = *data++;
+ dataLength--;
+
+ *text++ = iscsiAuthClientHexString[(n >> 4) & 0xf];
+ *text++ = iscsiAuthClientHexString[n & 0xf];
+
+ textLength -= 2;
+ }
+
+ *text = '\0';
+
+ return (FALSE);
+}
+
+
+static int
+iscsiAuthClientDataToBase64(unsigned char *data, unsigned int dataLength,
+ char *text, unsigned int textLength)
+{
+ unsigned long n;
+
+ if (!text || textLength == 0) {
+ return (TRUE);
+ }
+
+ if (!data || dataLength == 0) {
+ *text = '\0';
+ return (TRUE);
+ }
+
+ if (textLength < 3) {
+ *text = '\0';
+ return (TRUE);
+ }
+
+ *text++ = '0';
+ *text++ = 'b';
+
+ textLength -= 2;
+
+ while (dataLength >= 3) {
+
+ if (textLength < 5) {
+ *text = '\0';
+ return (TRUE);
+ }
+
+ n = *data++;
+ n = (n << 8) | *data++;
+ n = (n << 8) | *data++;
+ dataLength -= 3;
+
+ *text++ = iscsiAuthClientBase64String[(n >> 18) & 0x3f];
+ *text++ = iscsiAuthClientBase64String[(n >> 12) & 0x3f];
+ *text++ = iscsiAuthClientBase64String[(n >> 6) & 0x3f];
+ *text++ = iscsiAuthClientBase64String[n & 0x3f];
+
+ textLength -= 4;
+ }
+
+ if (dataLength == 1) {
+
+ if (textLength < 5) {
+ *text = '\0';
+ return (TRUE);
+ }
+
+ n = *data++;
+ n = n << 4;
+
+ *text++ = iscsiAuthClientBase64String[(n >> 6) & 0x3f];
+ *text++ = iscsiAuthClientBase64String[n & 0x3f];
+ *text++ = '=';
+ *text++ = '=';
+
+ } else if (dataLength == 2) {
+
+ if (textLength < 5) {
+ return (TRUE);
+ }
+
+ n = *data++;
+ n = (n << 8) | *data++;
+ n = n << 2;
+
+ *text++ = iscsiAuthClientBase64String[(n >> 12) & 0x3f];
+ *text++ = iscsiAuthClientBase64String[(n >> 6) & 0x3f];
+ *text++ = iscsiAuthClientBase64String[n & 0x3f];
+ *text++ = '=';
+ }
+
+ *text = '\0';
+
+ return (FALSE);
+}
+
+
+static int
+iscsiAuthClientDataToText(int base64, unsigned char *data,
+ unsigned int dataLength, char *text, unsigned int textLength)
+{
+ int status;
+
+ if (base64) {
+ status = iscsiAuthClientDataToBase64(
+ data, dataLength, text, textLength);
+ } else {
+ status = iscsiAuthClientDataToHex(
+ data, dataLength, text, textLength);
+ }
+
+ return (status);
+}
+
+
+static int
+iscsiAuthClientHexToData(const char *text, unsigned int textLength,
+ unsigned char *data, unsigned int *pDataLength)
+{
+ int i;
+ unsigned int n1;
+ unsigned int n2;
+ unsigned int dataLength = *pDataLength;
+
+ if ((textLength % 2) == 1) {
+ i = iscsiAuthClientStringIndex(iscsiAuthClientHexString,
+ *text++);
+ if (i < 0) {
+ return (TRUE); /* error, bad character */
+ }
+
+ if (i > 15)
+ i -= 6;
+ n2 = i;
+
+ if (dataLength < 1) {
+ return (TRUE); /* error, too much data */
+ }
+
+ *data++ = n2;
+ dataLength--;
+ }
+
+ while (*text != '\0') {
+
+ i = iscsiAuthClientStringIndex(
+ iscsiAuthClientHexString, *text++);
+ if (i < 0) {
+ return (TRUE); /* error, bad character */
+ }
+
+ if (i > 15)
+ i -= 6;
+ n1 = i;
+
+ if (*text == '\0') {
+ return (TRUE); /* error, odd string length */
+ }
+
+ i = iscsiAuthClientStringIndex(
+ iscsiAuthClientHexString, *text++);
+ if (i < 0) {
+ return (TRUE); /* error, bad character */
+ }
+
+ if (i > 15)
+ i -= 6;
+ n2 = i;
+
+ if (dataLength < 1) {
+ return (TRUE); /* error, too much data */
+ }
+
+ *data++ = (n1 << 4) | n2;
+ dataLength--;
+ }
+
+ if (dataLength >= *pDataLength) {
+ return (TRUE); /* error, no data */
+ }
+
+ *pDataLength = *pDataLength - dataLength;
+
+ return (FALSE); /* no error */
+}
+
+
+static int
+iscsiAuthClientBase64ToData(const char *text, unsigned int textLength,
+ unsigned char *data, unsigned int *pDataLength)
+{
+ int i;
+ unsigned int n;
+ unsigned int count;
+ unsigned int dataLength = *pDataLength;
+
+ textLength = textLength; /* not used */
+
+ n = 0;
+ count = 0;
+
+ while (*text != '\0' && *text != '=') {
+
+ i = iscsiAuthClientStringIndex(
+ iscsiAuthClientBase64String, *text++);
+ if (i < 0) {
+ return (TRUE); /* error, bad character */
+ }
+
+ n = (n << 6 | (unsigned int)i);
+ count++;
+
+ if (count >= 4) {
+ if (dataLength < 3) {
+ return (TRUE); /* error, too much data */
+ }
+ *data++ = n >> 16;
+ *data++ = n >> 8;
+ *data++ = n;
+ dataLength -= 3;
+ n = 0;
+ count = 0;
+ }
+ }
+
+ while (*text != '\0') {
+ if (*text++ != '=') {
+ return (TRUE); /* error, bad pad */
+ }
+ }
+
+ if (count == 0) {
+ /*
+ * do nothing
+ */
+ /* EMPTY */
+ } else if (count == 2) {
+ if (dataLength < 1) {
+ return (TRUE); /* error, too much data */
+ }
+ n = n >> 4;
+ *data++ = n;
+ dataLength--;
+ } else if (count == 3) {
+ if (dataLength < 2) {
+ return (TRUE); /* error, too much data */
+ }
+ n = n >> 2;
+ *data++ = n >> 8;
+ *data++ = n;
+ dataLength -= 2;
+ } else {
+ return (TRUE); /* bad encoding */
+ }
+
+ if (dataLength >= *pDataLength) {
+ return (TRUE); /* error, no data */
+ }
+
+ *pDataLength = *pDataLength - dataLength;
+
+ return (FALSE); /* no error */
+}
+
+
+static int
+iscsiAuthClientTextToData(const char *text, unsigned char *data,
+ unsigned int *dataLength)
+{
+ int status;
+ unsigned int textLength;
+
+ status = iscsiAuthClientCheckString(text,
+ 2 + 2 * iscsiAuthLargeBinaryMaxLength + 1, &textLength);
+
+ if (status) {
+ return (status);
+ }
+
+ if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) {
+ /*
+ * skip prefix
+ */
+ text += 2;
+ textLength -= 2;
+ status = iscsiAuthClientHexToData(text,
+ textLength, data, dataLength);
+ } else if (text[0] == '0' && (text[1] == 'b' || text[1] == 'B')) {
+ /*
+ * skip prefix
+ */
+ text += 2;
+ textLength -= 2;
+ status = iscsiAuthClientBase64ToData(text,
+ textLength, data, dataLength);
+ } else {
+ status = TRUE; /* prefix not recognized. */
+ }
+
+ return (status);
+}
+
+
+static IscsiAuthDebugStatus
+iscsiAuthClientChapComputeResponse(IscsiAuthClient * client,
+ int remoteAuthentication, unsigned int id,
+ unsigned char *challengeData, unsigned int challengeLength,
+ unsigned char *responseData)
+{
+ unsigned char idData[1];
+ IscsiAuthMd5Context context;
+ unsigned char outData[iscsiAuthStringMaxLength];
+ unsigned int outLength = iscsiAuthStringMaxLength;
+
+ if (!client->passwordPresent) {
+ return (iscsiAuthDebugStatusLocalPasswordNotSet);
+ }
+
+ iscsiAuthMd5Init(&context);
+
+ /*
+ * id byte
+ */
+ idData[0] = id;
+ iscsiAuthMd5Update(&context, idData, 1);
+
+ /*
+ * decrypt password
+ */
+ if (iscsiAuthClientData(outData, &outLength,
+ client->passwordData, client->passwordLength)) {
+
+ return (iscsiAuthDebugStatusPasswordDecryptFailed);
+ }
+
+ if (!remoteAuthentication && !client->ipSec && outLength < 12) {
+ return (iscsiAuthDebugStatusPasswordTooShortWithNoIpSec);
+ }
+
+ /*
+ * shared secret
+ */
+ iscsiAuthMd5Update(&context, outData, outLength);
+
+ /*
+ * clear decrypted password
+ */
+ bzero(outData, iscsiAuthStringMaxLength);
+
+ /*
+ * challenge value
+ */
+ iscsiAuthMd5Update(&context, challengeData, challengeLength);
+
+ iscsiAuthMd5Final(responseData, &context);
+
+ return (iscsiAuthDebugStatusNotSet); /* no error */
+}
+
+
+static void
+iscsiAuthClientInitKeyBlock(IscsiAuthKeyBlock * keyBlock)
+{
+ char *stringBlock = keyBlock->stringBlock;
+
+ bzero(keyBlock, sizeof (*keyBlock));
+ keyBlock->stringBlock = stringBlock;
+}
+
+
+static void
+iscsiAuthClientSetKeyValue(IscsiAuthKeyBlock * keyBlock,
+ int keyType, const char *keyValue)
+{
+ unsigned int length;
+ char *string;
+
+ if (keyBlock->key[keyType].valueSet) {
+ keyBlock->duplicateSet = TRUE;
+ return;
+ }
+
+ keyBlock->key[keyType].valueSet = TRUE;
+
+ if (!keyValue) {
+ return;
+ }
+
+ if (iscsiAuthClientCheckString(keyValue,
+ iscsiAuthStringMaxLength, &length)) {
+ keyBlock->stringTooLong = TRUE;
+ return;
+ }
+
+ length += 1;
+
+ if ((keyBlock->blockLength + length) > iscsiAuthStringBlockMaxLength) {
+ keyBlock->tooMuchData = TRUE;
+ return;
+ }
+
+ string = &keyBlock->stringBlock[keyBlock->blockLength];
+
+ if (iscsiAuthClientStringCopy(string, keyValue, length)) {
+ keyBlock->tooMuchData = TRUE;
+ return;
+ }
+ keyBlock->blockLength += length;
+
+ keyBlock->key[keyType].string = string;
+ keyBlock->key[keyType].present = TRUE;
+}
+
+
+static const char *
+iscsiAuthClientGetKeyValue(IscsiAuthKeyBlock * keyBlock, int keyType)
+{
+ keyBlock->key[keyType].processed = TRUE;
+
+ if (!keyBlock->key[keyType].present) {
+ return (0);
+ }
+
+ return (keyBlock->key[keyType].string);
+}
+
+
+static void
+iscsiAuthClientCheckKey(IscsiAuthClient * client,
+ int keyType,
+ int *negotiatedOption,
+ unsigned int optionCount,
+ int *optionList, const char *(*valueToText) (IscsiAuthClient *, int))
+{
+ const char *keyValue;
+ int length;
+ unsigned int i;
+
+ keyValue = iscsiAuthClientGetKeyValue(&client->recvKeyBlock, keyType);
+ if (!keyValue) {
+ *negotiatedOption = iscsiAuthOptionNotPresent;
+ return;
+ }
+
+ while (*keyValue != '\0') {
+
+ length = 0;
+
+ while (*keyValue != '\0' && *keyValue != ',') {
+ client->scratchKeyValue[length++] = *keyValue++;
+ }
+
+ if (*keyValue == ',')
+ keyValue++;
+ client->scratchKeyValue[length++] = '\0';
+
+ for (i = 0; i < optionCount; i++) {
+ const char *s = (*valueToText) (client, optionList[i]);
+
+ if (!s)
+ continue;
+
+ if (strcmp(client->scratchKeyValue, s) == 0) {
+ *negotiatedOption = optionList[i];
+ return;
+ }
+ }
+ }
+
+ *negotiatedOption = iscsiAuthOptionReject;
+}
+
+
+static void
+iscsiAuthClientSetKey(IscsiAuthClient * client,
+ int keyType,
+ unsigned int optionCount,
+ int *optionList, const char *(*valueToText) (IscsiAuthClient *, int))
+{
+ unsigned int i;
+
+ if (optionCount == 0) {
+ /*
+ * No valid options to send, but we always want to
+ * send something.
+ */
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock, keyType,
+ client->noneOptionName);
+ return;
+ }
+
+ if (optionCount == 1 && optionList[0] == iscsiAuthOptionNotPresent) {
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock, keyType, 0);
+ return;
+ }
+
+ for (i = 0; i < optionCount; i++) {
+ const char *s = (*valueToText) (client, optionList[i]);
+
+ if (!s)
+ continue;
+
+ if (i == 0) {
+ (void) iscsiAuthClientStringCopy(client->scratchKeyValue,
+ s, iscsiAuthStringMaxLength);
+ } else {
+ (void) iscsiAuthClientStringAppend(client->scratchKeyValue,
+ ",", iscsiAuthStringMaxLength);
+ (void) iscsiAuthClientStringAppend(client->scratchKeyValue,
+ s, iscsiAuthStringMaxLength);
+ }
+ }
+
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock,
+ keyType, client->scratchKeyValue);
+}
+
+
+static void
+iscsiAuthClientCheckAuthMethodKey(IscsiAuthClient * client)
+{
+ iscsiAuthClientCheckKey(client,
+ iscsiAuthKeyTypeAuthMethod,
+ &client->negotiatedAuthMethod,
+ client->authMethodValidCount,
+ client->authMethodValidList, iscsiAuthClientAuthMethodOptionToText);
+}
+
+
+static void
+iscsiAuthClientSetAuthMethodKey(IscsiAuthClient * client,
+ unsigned int authMethodCount, int *authMethodList)
+{
+ iscsiAuthClientSetKey(client, iscsiAuthKeyTypeAuthMethod,
+ authMethodCount, authMethodList,
+ iscsiAuthClientAuthMethodOptionToText);
+}
+
+
+static void
+iscsiAuthClientCheckChapAlgorithmKey(IscsiAuthClient * client)
+{
+ const char *keyValue;
+ int length;
+ unsigned long number;
+ unsigned int i;
+
+ keyValue = iscsiAuthClientGetKeyValue(&client->recvKeyBlock,
+ iscsiAuthKeyTypeChapAlgorithm);
+ if (!keyValue) {
+ client->negotiatedChapAlgorithm = iscsiAuthOptionNotPresent;
+ return;
+ }
+
+ while (*keyValue != '\0') {
+ length = 0;
+
+ while (*keyValue != '\0' && *keyValue != ',') {
+ client->scratchKeyValue[length++] = *keyValue++;
+ }
+
+ if (*keyValue == ',')
+ keyValue++;
+ client->scratchKeyValue[length++] = '\0';
+
+ if (iscsiAuthClientTextToNumber(client->scratchKeyValue,
+ &number)) {
+ continue;
+ }
+
+ for (i = 0; i < client->chapAlgorithmCount; i++) {
+
+ if (number == (unsigned long)client->
+ chapAlgorithmList[i]) {
+ client->negotiatedChapAlgorithm = number;
+ return;
+ }
+ }
+ }
+
+ client->negotiatedChapAlgorithm = iscsiAuthOptionReject;
+}
+
+
+static void
+iscsiAuthClientSetChapAlgorithmKey(IscsiAuthClient * client,
+ unsigned int chapAlgorithmCount, int *chapAlgorithmList)
+{
+ unsigned int i;
+
+ if (chapAlgorithmCount == 0) {
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock,
+ iscsiAuthKeyTypeChapAlgorithm, 0);
+ return;
+ }
+
+ if (chapAlgorithmCount == 1 &&
+ chapAlgorithmList[0] == iscsiAuthOptionNotPresent) {
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock,
+ iscsiAuthKeyTypeChapAlgorithm, 0);
+ return;
+ }
+
+ if (chapAlgorithmCount == 1 &&
+ chapAlgorithmList[0] == iscsiAuthOptionReject) {
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock,
+ iscsiAuthKeyTypeChapAlgorithm, client->rejectOptionName);
+ return;
+ }
+
+ for (i = 0; i < chapAlgorithmCount; i++) {
+ char s[20];
+
+ iscsiAuthClientNumberToText(chapAlgorithmList[i],
+ s, sizeof (s));
+
+ if (i == 0) {
+ (void) iscsiAuthClientStringCopy(client->scratchKeyValue, s,
+ iscsiAuthStringMaxLength);
+ } else {
+ (void) iscsiAuthClientStringAppend(client->scratchKeyValue,
+ ",", iscsiAuthStringMaxLength);
+ (void) iscsiAuthClientStringAppend(client->scratchKeyValue,
+ s, iscsiAuthStringMaxLength);
+ }
+ }
+
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock,
+ iscsiAuthKeyTypeChapAlgorithm, client->scratchKeyValue);
+}
+
+
+static void
+iscsiAuthClientNextPhase(IscsiAuthClient * client)
+{
+ switch (client->phase) {
+ case iscsiAuthPhaseConfigure:
+ client->phase = iscsiAuthPhaseNegotiate;
+ break;
+
+ case iscsiAuthPhaseNegotiate:
+ client->phase = iscsiAuthPhaseAuthenticate;
+
+ if (client->negotiatedAuthMethod ==
+ iscsiAuthOptionReject ||
+ client->negotiatedAuthMethod ==
+ iscsiAuthOptionNotPresent ||
+ client->negotiatedAuthMethod == iscsiAuthOptionNone) {
+
+ client->localState = iscsiAuthLocalStateDone;
+ client->remoteState = iscsiAuthRemoteStateDone;
+
+ if (client->authRemote) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ } else {
+ client->remoteAuthStatus = iscsiAuthStatusPass;
+ }
+
+ switch (client->negotiatedAuthMethod) {
+ case iscsiAuthOptionReject:
+ client->debugStatus =
+ iscsiAuthDebugStatusAuthMethodReject;
+ break;
+
+ case iscsiAuthOptionNotPresent:
+ client->debugStatus =
+ iscsiAuthDebugStatusAuthMethodNotPresent;
+ break;
+
+ case iscsiAuthOptionNone:
+ client->debugStatus =
+ iscsiAuthDebugStatusAuthMethodNone;
+ }
+
+ } else if (client->negotiatedAuthMethod ==
+ iscsiAuthMethodChap) {
+ client->localState = iscsiAuthLocalStateSendAlgorithm;
+ client->remoteState = iscsiAuthRemoteStateSendAlgorithm;
+ } else {
+ client->localState = iscsiAuthLocalStateDone;
+ client->remoteState = iscsiAuthRemoteStateDone;
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->debugStatus = iscsiAuthDebugStatusAuthMethodBad;
+ }
+
+ break;
+
+ case iscsiAuthPhaseAuthenticate:
+ client->phase = iscsiAuthPhaseDone;
+ break;
+
+ case iscsiAuthPhaseDone:
+ case iscsiAuthPhaseError:
+ default:
+ client->phase = iscsiAuthPhaseError;
+ }
+}
+
+
+static void
+iscsiAuthClientLocalAuthentication(IscsiAuthClient * client)
+{
+ unsigned int chapIdentifier;
+ unsigned char responseData[iscsiAuthChapResponseLength];
+ unsigned long number;
+ int status;
+ IscsiAuthDebugStatus debugStatus;
+ const char *chapIdentifierKeyValue;
+ const char *chapChallengeKeyValue;
+
+ switch (client->localState) {
+ case iscsiAuthLocalStateSendAlgorithm:
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ iscsiAuthClientSetChapAlgorithmKey(
+ client, client->chapAlgorithmCount,
+ client->chapAlgorithmList);
+ client->localState = iscsiAuthLocalStateRecvAlgorithm;
+ break;
+ }
+
+ /* FALLTHRU */
+
+ case iscsiAuthLocalStateRecvAlgorithm:
+ iscsiAuthClientCheckChapAlgorithmKey(client);
+
+ if (client->nodeType == iscsiAuthNodeTypeTarget) {
+
+ iscsiAuthClientSetChapAlgorithmKey(client, 1,
+ &client->negotiatedChapAlgorithm);
+ }
+
+ /*
+ * Make sure only supported CHAP algorithm is used.
+ */
+ if (client->negotiatedChapAlgorithm ==
+ iscsiAuthOptionNotPresent) {
+ client->localState = iscsiAuthLocalStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapAlgorithmExpected;
+ break;
+
+ } else if (client->negotiatedChapAlgorithm ==
+ iscsiAuthOptionReject) {
+ client->localState = iscsiAuthLocalStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapAlgorithmReject;
+ break;
+
+ } else if (client->negotiatedChapAlgorithm !=
+ iscsiAuthChapAlgorithmMd5) {
+ client->localState = iscsiAuthLocalStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapAlgorithmBad;
+ break;
+ }
+
+ if (client->nodeType == iscsiAuthNodeTypeTarget) {
+
+ client->localState = iscsiAuthLocalStateRecvChallenge;
+ break;
+ }
+
+ /* FALLTHRU */
+
+ case iscsiAuthLocalStateRecvChallenge:
+ chapIdentifierKeyValue = iscsiAuthClientGetKeyValue(
+ &client->recvKeyBlock, iscsiAuthKeyTypeChapIdentifier);
+ chapChallengeKeyValue = iscsiAuthClientGetKeyValue(
+ &client->recvKeyBlock, iscsiAuthKeyTypeChapChallenge);
+
+ if (client->nodeType == iscsiAuthNodeTypeTarget) {
+ if (!chapIdentifierKeyValue && !chapChallengeKeyValue) {
+ client->localState = iscsiAuthLocalStateDone;
+ break;
+ }
+ }
+
+ if (!chapIdentifierKeyValue) {
+ client->localState = iscsiAuthLocalStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapIdentifierExpected;
+ break;
+ }
+
+ if (!chapChallengeKeyValue) {
+ client->localState = iscsiAuthLocalStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapChallengeExpected;
+ break;
+ }
+
+ status = iscsiAuthClientTextToNumber(
+ chapIdentifierKeyValue, &number);
+
+ if (status || (255 < number)) {
+ client->localState = iscsiAuthLocalStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapIdentifierBad;
+ break;
+ }
+ chapIdentifier = number;
+
+ if (client->recvChapChallengeStatus) {
+ client->localState = iscsiAuthLocalStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapChallengeBad;
+ break;
+ }
+
+ if (client->nodeType == iscsiAuthNodeTypeTarget &&
+ client->recvChapChallenge.length ==
+ client->sendChapChallenge.length &&
+ bcmp(client->recvChapChallenge.largeBinary,
+ client->sendChapChallenge.largeBinary,
+ client->sendChapChallenge.length) == 0) {
+
+ client->localState = iscsiAuthLocalStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapChallengeReflected;
+ break;
+ }
+
+ debugStatus = iscsiAuthClientChapComputeResponse(client,
+ FALSE,
+ chapIdentifier,
+ client->recvChapChallenge.largeBinary,
+ client->recvChapChallenge.length, responseData);
+
+ if (debugStatus != iscsiAuthDebugStatusNotSet) {
+ client->localState = iscsiAuthLocalStateError;
+ client->debugStatus = debugStatus;
+ break;
+ }
+
+ (void) iscsiAuthClientDataToText(client->base64,
+ responseData, iscsiAuthChapResponseLength,
+ client->scratchKeyValue, iscsiAuthStringMaxLength);
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock,
+ iscsiAuthKeyTypeChapResponse, client->scratchKeyValue);
+
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock,
+ iscsiAuthKeyTypeChapUsername, client->username);
+
+ client->localState = iscsiAuthLocalStateDone;
+ break;
+
+ case iscsiAuthLocalStateDone:
+ break;
+
+ case iscsiAuthLocalStateError:
+ default:
+ client->phase = iscsiAuthPhaseError;
+ }
+}
+
+
+static void
+iscsiAuthClientRemoteAuthentication(IscsiAuthClient * client)
+{
+ unsigned char idData[1];
+ unsigned char responseData[iscsiAuthStringMaxLength];
+ unsigned int responseLength = iscsiAuthStringMaxLength;
+ unsigned char myResponseData[iscsiAuthChapResponseLength];
+ int status;
+ IscsiAuthDebugStatus debugStatus;
+ const char *chapResponseKeyValue;
+ const char *chapUsernameKeyValue;
+
+ switch (client->remoteState) {
+ case iscsiAuthRemoteStateSendAlgorithm:
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ client->remoteState = iscsiAuthRemoteStateSendChallenge;
+ break;
+ }
+
+ /* FALLTHRU */
+
+ case iscsiAuthRemoteStateSendChallenge:
+ if (!client->authRemote) {
+ client->remoteAuthStatus = iscsiAuthStatusPass;
+ client->debugStatus =
+ iscsiAuthDebugStatusAuthRemoteFalse;
+ client->remoteState = iscsiAuthRemoteStateDone;
+ break;
+ }
+
+ iscsiAuthRandomSetData(idData, 1);
+ client->sendChapIdentifier = idData[0];
+
+ iscsiAuthClientNumberToText(client->sendChapIdentifier,
+ client->scratchKeyValue, iscsiAuthStringMaxLength);
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock,
+ iscsiAuthKeyTypeChapIdentifier, client->scratchKeyValue);
+
+ client->sendChapChallenge.length = client->chapChallengeLength;
+ iscsiAuthRandomSetData(client->sendChapChallenge.largeBinary,
+ client->sendChapChallenge.length);
+
+ iscsiAuthClientSetKeyValue(&client->sendKeyBlock,
+ iscsiAuthKeyTypeChapChallenge, "");
+
+ client->remoteState = iscsiAuthRemoteStateRecvResponse;
+ break;
+
+ case iscsiAuthRemoteStateRecvResponse:
+ chapResponseKeyValue = iscsiAuthClientGetKeyValue(
+ &client->recvKeyBlock, iscsiAuthKeyTypeChapResponse);
+
+ chapUsernameKeyValue = iscsiAuthClientGetKeyValue(
+ &client->recvKeyBlock, iscsiAuthKeyTypeChapUsername);
+
+ if (!chapResponseKeyValue) {
+ client->remoteState = iscsiAuthRemoteStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapResponseExpected;
+ break;
+ }
+
+ if (!chapUsernameKeyValue) {
+ client->remoteState = iscsiAuthRemoteStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapUsernameExpected;
+ break;
+ }
+
+ status = iscsiAuthClientTextToData(chapResponseKeyValue,
+ responseData, &responseLength);
+
+ if (status) {
+ client->remoteState = iscsiAuthRemoteStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusChapResponseBad;
+ break;
+ }
+
+ if (responseLength == iscsiAuthChapResponseLength) {
+ debugStatus = iscsiAuthClientChapComputeResponse(
+ client, TRUE, client->sendChapIdentifier,
+ client->sendChapChallenge.largeBinary,
+ client->sendChapChallenge.length, myResponseData);
+
+ /*
+ * Check if the same CHAP secret is being used for
+ * authentication in both directions.
+ */
+ if (debugStatus == iscsiAuthDebugStatusNotSet &&
+ bcmp(myResponseData, responseData,
+ iscsiAuthChapResponseLength) == 0) {
+
+ client->remoteState =
+ iscsiAuthRemoteStateError;
+ client->debugStatus =
+ iscsiAuthDebugStatusPasswordIdentical;
+ break;
+ }
+ }
+
+ (void) iscsiAuthClientStringCopy(client->chapUsername,
+ chapUsernameKeyValue, iscsiAuthStringMaxLength);
+
+ /* To verify the target's response. */
+ status = iscsiAuthClientChapAuthRequest(
+ client, client->chapUsername, client->sendChapIdentifier,
+ client->sendChapChallenge.largeBinary,
+ client->sendChapChallenge.length, responseData,
+ responseLength);
+
+ if (status == iscsiAuthStatusInProgress) {
+ iscsiAuthClientGlobalStats.requestSent++;
+ client->remoteState = iscsiAuthRemoteStateAuthRequest;
+ break;
+ }
+
+ client->remoteAuthStatus = (IscsiAuthStatus) status;
+ client->authResponseFlag = TRUE;
+
+ /* FALLTHRU */
+
+ case iscsiAuthRemoteStateAuthRequest:
+ /*
+ * client->remoteAuthStatus already set
+ */
+ if (client->authServerErrorFlag) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->debugStatus =
+ iscsiAuthDebugStatusAuthServerError;
+ } else if (client->remoteAuthStatus == iscsiAuthStatusPass) {
+ client->debugStatus = iscsiAuthDebugStatusAuthPass;
+ } else if (client->remoteAuthStatus == iscsiAuthStatusFail) {
+ client->debugStatus = iscsiAuthDebugStatusAuthFail;
+ } else {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->debugStatus = iscsiAuthDebugStatusAuthStatusBad;
+ }
+ client->remoteState = iscsiAuthRemoteStateDone;
+
+ /* FALLTHRU */
+
+ case iscsiAuthRemoteStateDone:
+ break;
+
+ case iscsiAuthRemoteStateError:
+ default:
+ client->phase = iscsiAuthPhaseError;
+ }
+}
+
+
+static void
+iscsiAuthClientHandshake(IscsiAuthClient * client)
+{
+ if (client->phase == iscsiAuthPhaseDone) {
+ /*
+ * Should only happen if authentication
+ * protocol error occured.
+ */
+ return;
+ }
+
+ if (client->remoteState == iscsiAuthRemoteStateAuthRequest) {
+ /*
+ * Defer until authentication response received
+ * from internal authentication service.
+ */
+ return;
+ }
+
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+
+ /*
+ * Target should only have set T bit on response if
+ * initiator set it on previous message.
+ */
+ if (client->recvKeyBlock.transitBit &&
+ client->transitBitSentFlag == 0) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus =
+ iscsiAuthDebugStatusTbitSetIllegal;
+ return;
+ }
+ }
+
+ if (client->phase == iscsiAuthPhaseNegotiate) {
+ /*
+ * Should only happen if waiting for peer
+ * to send AuthMethod key or set Transit Bit.
+ */
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ client->sendKeyBlock.transitBit = TRUE;
+ }
+ return;
+ }
+
+ if (client->remoteState == iscsiAuthRemoteStateRecvResponse ||
+ client->remoteState == iscsiAuthRemoteStateDone) {
+
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ if (client->recvKeyBlock.transitBit) {
+ if (client->remoteState !=
+ iscsiAuthRemoteStateDone) {
+ goto recvTransitBitError;
+ }
+ iscsiAuthClientNextPhase(client);
+ } else {
+ client->sendKeyBlock.transitBit = TRUE;
+ }
+ } else {
+ if (client->remoteState == iscsiAuthRemoteStateDone &&
+ client->remoteAuthStatus != iscsiAuthStatusPass) {
+
+ /*
+ * Authentication failed, don't
+ * do T bit handshake.
+ */
+ iscsiAuthClientNextPhase(client);
+ } else {
+
+ /*
+ * Target can only set T bit on response if
+ * initiator set it on current message.
+ */
+ if (client->recvKeyBlock.transitBit) {
+ client->sendKeyBlock.transitBit = TRUE;
+ iscsiAuthClientNextPhase(client);
+ }
+ }
+ }
+ } else {
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ if (client->recvKeyBlock.transitBit) {
+ goto recvTransitBitError;
+ }
+ }
+ }
+
+ return;
+
+recvTransitBitError:
+ /*
+ * Target set T bit on response but
+ * initiator was not done with authentication.
+ */
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus = iscsiAuthDebugStatusTbitSetPremature;
+}
+
+
+static int
+iscsiAuthClientRecvEndStatus(IscsiAuthClient * client)
+{
+ int authStatus;
+ int keyType;
+
+ if (client->phase == iscsiAuthPhaseError) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase == iscsiAuthPhaseDone) {
+
+ /*
+ * Perform sanity check against configured parameters.
+ */
+
+ if (client->authRemote && !client->authResponseFlag &&
+ client->remoteAuthStatus == iscsiAuthStatusPass) {
+
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->debugStatus =
+ iscsiAuthDebugStatusAuthPassNotValid;
+ }
+
+ authStatus = client->remoteAuthStatus;
+ } else if (client->remoteState == iscsiAuthRemoteStateAuthRequest) {
+ authStatus = iscsiAuthStatusInProgress;
+ } else {
+ authStatus = iscsiAuthStatusContinue;
+ }
+
+ if (authStatus != iscsiAuthStatusInProgress) {
+ client->recvInProgressFlag = FALSE;
+ }
+
+ if (authStatus == iscsiAuthStatusContinue ||
+ authStatus == iscsiAuthStatusPass) {
+ if (client->sendKeyBlock.duplicateSet) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus =
+ iscsiAuthDebugStatusSendDuplicateSetKeyValue;
+ authStatus = iscsiAuthStatusFail;
+ } else if (client->sendKeyBlock.stringTooLong) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus =
+ iscsiAuthDebugStatusSendStringTooLong;
+ authStatus = iscsiAuthStatusFail;
+ } else if (client->sendKeyBlock.tooMuchData) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus =
+ iscsiAuthDebugStatusSendTooMuchData;
+ authStatus = iscsiAuthStatusFail;
+ } else {
+ /*
+ * Check that all incoming keys have been processed.
+ */
+ for (keyType = iscsiAuthKeyTypeFirst;
+ keyType < iscsiAuthKeyTypeMaxCount; keyType++) {
+ if (client->recvKeyBlock.key[keyType].present &&
+ client->recvKeyBlock.key[keyType].
+ processed == 0) {
+ break;
+ }
+ }
+
+ if (keyType < iscsiAuthKeyTypeMaxCount) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus =
+ iscsiAuthDebugStatusUnexpectedKeyPresent;
+ authStatus = iscsiAuthStatusFail;
+ }
+ }
+ }
+
+ if (authStatus != iscsiAuthStatusPass &&
+ authStatus != iscsiAuthStatusContinue &&
+ authStatus != iscsiAuthStatusInProgress) {
+ int authMethodKeyPresent = FALSE;
+ int chapAlgorithmKeyPresent = FALSE;
+
+ /*
+ * Suppress send keys on error, except
+ * for AuthMethod and CHAP_A.
+ */
+ if (client->nodeType == iscsiAuthNodeTypeTarget) {
+ if (iscsiAuthClientGetKeyValue(&client->sendKeyBlock,
+ iscsiAuthKeyTypeAuthMethod)) {
+ authMethodKeyPresent = TRUE;
+ } else if (iscsiAuthClientGetKeyValue(
+ &client->sendKeyBlock,
+ iscsiAuthKeyTypeChapAlgorithm)) {
+ chapAlgorithmKeyPresent = TRUE;
+ }
+ }
+
+ iscsiAuthClientInitKeyBlock(&client->sendKeyBlock);
+
+ if (client->nodeType == iscsiAuthNodeTypeTarget) {
+ if (authMethodKeyPresent &&
+ client->negotiatedAuthMethod ==
+ iscsiAuthOptionReject) {
+ iscsiAuthClientSetKeyValue(
+ &client->sendKeyBlock,
+ iscsiAuthKeyTypeAuthMethod,
+ client->rejectOptionName);
+ } else if (chapAlgorithmKeyPresent &&
+ client->negotiatedChapAlgorithm ==
+ iscsiAuthOptionReject) {
+ iscsiAuthClientSetKeyValue(
+ &client->sendKeyBlock,
+ iscsiAuthKeyTypeChapAlgorithm,
+ client->rejectOptionName);
+ }
+ }
+ }
+
+ return (authStatus);
+}
+
+
+int
+iscsiAuthClientRecvBegin(IscsiAuthClient * client)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase == iscsiAuthPhaseError) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase == iscsiAuthPhaseDone) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->recvInProgressFlag) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ client->recvInProgressFlag = TRUE;
+
+ if (client->phase == iscsiAuthPhaseConfigure) {
+ iscsiAuthClientNextPhase(client);
+ }
+
+ client->transitBitSentFlag = client->sendKeyBlock.transitBit;
+
+ iscsiAuthClientInitKeyBlock(&client->recvKeyBlock);
+ iscsiAuthClientInitKeyBlock(&client->sendKeyBlock);
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientRecvEnd(IscsiAuthClient * client,
+ IscsiAuthClientCallback * callback, void *userHandle, void *messageHandle)
+{
+ int nextPhaseFlag = FALSE;
+
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase == iscsiAuthPhaseError) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (!callback || !client->recvInProgressFlag) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->recvEndCount > iscsiAuthRecvEndMaxCount) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus =
+ iscsiAuthDebugStatusRecvMessageCountLimit;
+ } else if (client->recvKeyBlock.duplicateSet) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus =
+ iscsiAuthDebugStatusRecvDuplicateSetKeyValue;
+ } else if (client->recvKeyBlock.stringTooLong) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus = iscsiAuthDebugStatusRecvStringTooLong;
+ } else if (client->recvKeyBlock.tooMuchData) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus = iscsiAuthDebugStatusRecvTooMuchData;
+ }
+
+ client->recvEndCount++;
+
+ client->callback = callback;
+ client->userHandle = userHandle;
+ client->messageHandle = messageHandle;
+
+ switch (client->phase) {
+ case iscsiAuthPhaseNegotiate:
+ iscsiAuthClientCheckAuthMethodKey(client);
+
+ if (client->authMethodValidNegRole ==
+ iscsiAuthNegRoleResponder) {
+ if (client->negotiatedAuthMethod ==
+ iscsiAuthOptionNotPresent) {
+ if (client->authRemote ||
+ client->recvKeyBlock.transitBit == 0) {
+ /*
+ * No AuthMethod key from peer
+ * on first message, try moving
+ * the process along by sending
+ * the AuthMethod key.
+ */
+
+ client->authMethodValidNegRole =
+ iscsiAuthNegRoleOriginator;
+
+ iscsiAuthClientSetAuthMethodKey(client,
+ client->authMethodValidCount,
+ client->authMethodValidList);
+ break;
+ }
+
+ /*
+ * Special case if peer sent no
+ * AuthMethod key, but did set Transit
+ * Bit, allowing this side to do a
+ * null authentication, and compelete
+ * the iSCSI security phase without
+ * either side sending the AuthMethod
+ * key.
+ */
+ } else {
+ /*
+ * Send response to AuthMethod key.
+ */
+
+ iscsiAuthClientSetAuthMethodKey(client, 1,
+ &client->negotiatedAuthMethod);
+ }
+
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ iscsiAuthClientNextPhase(client);
+ } else {
+ nextPhaseFlag = TRUE;
+ }
+
+ } else {
+ if (client->negotiatedAuthMethod ==
+ iscsiAuthOptionNotPresent) {
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ client->debugStatus =
+ iscsiAuthDebugStatusAuthMethodExpected;
+ break;
+ }
+
+ iscsiAuthClientNextPhase(client);
+ }
+ break;
+
+ case iscsiAuthPhaseAuthenticate:
+ case iscsiAuthPhaseDone:
+ break;
+
+ default:
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ switch (client->phase) {
+ case iscsiAuthPhaseNegotiate:
+ if (nextPhaseFlag) {
+ iscsiAuthClientNextPhase(client);
+ }
+ break;
+
+ case iscsiAuthPhaseAuthenticate:
+ /*
+ * Must call iscsiAuthClientLocalAuthentication()
+ * before iscsiAuthClientRemoteAuthentication()
+ * to insure processing of the CHAP algorithm key,
+ * and to avoid leaving an in progress request to the
+ * authentication service.
+ */
+ iscsiAuthClientLocalAuthentication(client);
+
+ if (client->localState != iscsiAuthLocalStateError) {
+ iscsiAuthClientRemoteAuthentication(client);
+ }
+
+ if (client->localState == iscsiAuthLocalStateError ||
+ client->remoteState == iscsiAuthRemoteStateError) {
+
+ client->remoteAuthStatus = iscsiAuthStatusFail;
+ client->phase = iscsiAuthPhaseDone;
+ /*
+ * client->debugStatus should already be set.
+ */
+ }
+ break;
+
+ case iscsiAuthPhaseDone:
+ break;
+
+ default:
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ iscsiAuthClientHandshake(client);
+
+ return (iscsiAuthClientRecvEndStatus(client));
+}
+
+
+#ifdef notused
+void
+iscsiAuthClientAuthResponse(IscsiAuthClient * client, int authStatus)
+{
+ iscsiAuthClientGlobalStats.responseReceived++;
+
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return;
+ }
+
+ if (!client->recvInProgressFlag ||
+ client->phase != iscsiAuthPhaseAuthenticate ||
+ client->remoteState != iscsiAuthRemoteStateAuthRequest) {
+
+ client->phase = iscsiAuthPhaseError;
+ return;
+ }
+
+ client->remoteAuthStatus = (IscsiAuthStatus) authStatus;
+ client->authResponseFlag = TRUE;
+
+ iscsiAuthClientRemoteAuthentication(client);
+
+ iscsiAuthClientHandshake(client);
+
+ authStatus = iscsiAuthClientRecvEndStatus(client);
+
+ client->callback(client->userHandle, client->messageHandle, authStatus);
+}
+#endif
+
+
+const char *
+iscsiAuthClientGetKeyName(int keyType)
+{
+ if (keyType < iscsiAuthKeyTypeFirst || keyType > iscsiAuthKeyTypeLast) {
+ return (0);
+ }
+ return (iscsiAuthClientKeyInfo[keyType].name);
+}
+
+
+int
+iscsiAuthClientGetNextKeyType(int *pKeyType)
+{
+ int keyType = *pKeyType;
+
+ if (keyType >= iscsiAuthKeyTypeLast) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (keyType < iscsiAuthKeyTypeFirst) {
+ keyType = iscsiAuthKeyTypeFirst;
+ } else {
+ keyType++;
+ }
+
+ *pKeyType = keyType;
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+#ifdef notused
+int
+iscsiAuthClientKeyNameToKeyType(const char *keyName)
+{
+ int keyType = iscsiAuthKeyTypeNone;
+
+ while (iscsiAuthClientGetNextKeyType(&keyType) ==
+ iscsiAuthStatusNoError) {
+ const char *keyName2 = iscsiAuthClientGetKeyName(keyType);
+
+ if (!keyName2) {
+ return (iscsiAuthKeyTypeNone);
+ }
+
+ if (strcmp(keyName, keyName2) == 0) {
+ return (keyType);
+ }
+ }
+
+ return (iscsiAuthKeyTypeNone);
+}
+#endif
+
+
+int
+iscsiAuthClientRecvKeyValue(IscsiAuthClient * client, int keyType,
+ const char *userKeyValue)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseNegotiate &&
+ client->phase != iscsiAuthPhaseAuthenticate) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (keyType < iscsiAuthKeyTypeFirst || keyType > iscsiAuthKeyTypeLast) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (keyType == iscsiAuthKeyTypeChapChallenge) {
+ client->recvChapChallenge.length =
+ iscsiAuthLargeBinaryMaxLength;
+ client->recvChapChallengeStatus =
+ iscsiAuthClientTextToData(userKeyValue,
+ client->recvChapChallenge.largeBinary,
+ &client->recvChapChallenge.length);
+ userKeyValue = "";
+ }
+
+ iscsiAuthClientSetKeyValue(&client->recvKeyBlock,
+ keyType, userKeyValue);
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientSendKeyValue(IscsiAuthClient * client, int keyType,
+ int *keyPresent, char *userKeyValue, unsigned int maxLength)
+{
+ const char *keyValue;
+
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure &&
+ client->phase != iscsiAuthPhaseNegotiate &&
+ client->phase != iscsiAuthPhaseAuthenticate &&
+ client->phase != iscsiAuthPhaseDone) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (keyType < iscsiAuthKeyTypeFirst || keyType > iscsiAuthKeyTypeLast) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ keyValue = iscsiAuthClientGetKeyValue(&client->sendKeyBlock, keyType);
+ if (keyValue) {
+ if (keyType == iscsiAuthKeyTypeChapChallenge) {
+ if (iscsiAuthClientDataToText(client->base64,
+ client->sendChapChallenge.largeBinary,
+ client->sendChapChallenge.length,
+ userKeyValue, maxLength)) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+ } else {
+ if (iscsiAuthClientStringCopy(userKeyValue,
+ keyValue, maxLength)) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+ }
+ *keyPresent = TRUE;
+ } else {
+ *keyPresent = FALSE;
+ }
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientRecvTransitBit(IscsiAuthClient * client, int value)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseNegotiate &&
+ client->phase != iscsiAuthPhaseAuthenticate) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (value) {
+ client->recvKeyBlock.transitBit = TRUE;
+ } else {
+ client->recvKeyBlock.transitBit = FALSE;
+ }
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientSendTransitBit(IscsiAuthClient * client, int *value)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure &&
+ client->phase != iscsiAuthPhaseNegotiate &&
+ client->phase != iscsiAuthPhaseAuthenticate &&
+ client->phase != iscsiAuthPhaseDone) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ *value = client->sendKeyBlock.transitBit;
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientInit(int nodeType, int bufferDescCount,
+ IscsiAuthBufferDesc * bufferDesc)
+{
+ IscsiAuthClient *client;
+ IscsiAuthStringBlock *recvStringBlock;
+ IscsiAuthStringBlock *sendStringBlock;
+ IscsiAuthLargeBinary *recvChapChallenge;
+ IscsiAuthLargeBinary *sendChapChallenge;
+ int valueList[2];
+
+ if (bufferDescCount != 5 ||
+ bufferDesc == 0) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (!bufferDesc[0].address ||
+ bufferDesc[0].length != sizeof (*client)) {
+ return (iscsiAuthStatusError);
+ }
+ client = (IscsiAuthClient *) bufferDesc[0].address;
+
+ if (bufferDesc[1].address == 0 ||
+ bufferDesc[1].length != sizeof (*recvStringBlock)) {
+ return (iscsiAuthStatusError);
+ }
+ recvStringBlock = (IscsiAuthStringBlock *) bufferDesc[1].address;
+
+ if (bufferDesc[2].address == 0 ||
+ bufferDesc[2].length != sizeof (*sendStringBlock)) {
+ return (iscsiAuthStatusError);
+ }
+ sendStringBlock = (IscsiAuthStringBlock *) bufferDesc[2].address;
+
+ if (bufferDesc[3].address == 0 ||
+ bufferDesc[3].length != sizeof (*recvChapChallenge)) {
+ return (iscsiAuthStatusError);
+ }
+ recvChapChallenge = (IscsiAuthLargeBinary *) bufferDesc[3].address;
+
+ if (bufferDesc[4].address == 0 ||
+ bufferDesc[4].length != sizeof (*sendChapChallenge)) {
+ return (iscsiAuthStatusError);
+ }
+ sendChapChallenge = (IscsiAuthLargeBinary *) bufferDesc[4].address;
+
+ bzero(client, sizeof (*client));
+ bzero(recvStringBlock, sizeof (*recvStringBlock));
+ bzero(sendStringBlock, sizeof (*sendStringBlock));
+ bzero(recvChapChallenge, sizeof (*recvChapChallenge));
+ bzero(sendChapChallenge, sizeof (*sendChapChallenge));
+
+ client->recvKeyBlock.stringBlock = recvStringBlock->stringBlock;
+ client->sendKeyBlock.stringBlock = sendStringBlock->stringBlock;
+ client->recvChapChallenge.largeBinary = recvChapChallenge->largeBinary;
+ client->sendChapChallenge.largeBinary = sendChapChallenge->largeBinary;
+
+ if (iscsiAuthClientCheckNodeType(nodeType)) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ client->signature = iscsiAuthClientSignature;
+ client->nodeType = (IscsiAuthNodeType) nodeType;
+ /* Assume bi-directional authentication enabled. */
+ client->authRemote = TRUE;
+ client->passwordPresent = FALSE;
+ client->version = iscsiAuthVersionRfc;
+ client->chapChallengeLength = iscsiAuthChapResponseLength;
+ client->ipSec = TRUE;
+ client->base64 = FALSE;
+
+ client->phase = iscsiAuthPhaseConfigure;
+ client->negotiatedAuthMethod = iscsiAuthOptionNotPresent;
+ client->negotiatedChapAlgorithm = iscsiAuthOptionNotPresent;
+
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ client->authMethodNegRole = iscsiAuthNegRoleOriginator;
+ } else {
+ /*
+ * Initial value ignored for Target.
+ */
+ client->authMethodNegRole = iscsiAuthNegRoleResponder;
+ }
+
+ /* All supported authentication methods */
+ valueList[0] = iscsiAuthMethodChap;
+ valueList[1] = iscsiAuthOptionNone;
+
+ /*
+ * Must call after setting authRemote, password,
+ * version and authMethodNegRole
+ */
+ if (iscsiAuthClientSetAuthMethodList(client, 2, valueList) !=
+ iscsiAuthStatusNoError) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ valueList[0] = iscsiAuthChapAlgorithmMd5;
+
+ if (iscsiAuthClientSetChapAlgorithmList(client, 1, valueList) !=
+ iscsiAuthStatusNoError) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+#ifdef notused
+int
+iscsiAuthClientFinish(IscsiAuthClient * client)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ iscsiAuthClientChapAuthCancel(client);
+
+ bzero(client, sizeof (*client));
+
+ return (iscsiAuthStatusNoError);
+}
+#endif
+
+
+static int
+iscsiAuthClientSetOptionList(IscsiAuthClient * client,
+ unsigned int optionCount,
+ const int *optionList,
+ unsigned int *clientOptionCount,
+ int *clientOptionList,
+ unsigned int optionMaxCount,
+ int (*checkOption) (int),
+ int (*checkList) (unsigned int optionCount, const int *optionList))
+{
+ unsigned int i;
+ unsigned int j;
+
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure ||
+ optionCount > optionMaxCount) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ for (i = 0; i < optionCount; i++) {
+ if ((*checkOption) (optionList[i])) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+ }
+
+ /*
+ * Check for duplicate entries.
+ */
+ for (i = 0; i < optionCount; i++) {
+ for (j = 0; j < optionCount; j++) {
+ if (j == i)
+ continue;
+ if (optionList[i] == optionList[j]) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+ }
+ }
+
+ /*
+ * Check for key specific constraints.
+ */
+ if (checkList) {
+ if ((*checkList) (optionCount, optionList)) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+ }
+
+ for (i = 0; i < optionCount; i++) {
+ clientOptionList[i] = optionList[i];
+ }
+
+ *clientOptionCount = optionCount;
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+static void
+iscsiAuthClientSetAuthMethodValid(IscsiAuthClient * client)
+{
+ static const char rejectOptionNameDraft8[] = "reject";
+ static const char rejectOptionNameRfc[] = "Reject";
+ static const char noneOptionNameDraft8[] = "none";
+ static const char noneOptionNameRfc[] = "None";
+ unsigned int i;
+ unsigned int j = 0;
+ int option = 0;
+
+ if (client->version == iscsiAuthVersionDraft8) {
+ client->rejectOptionName = rejectOptionNameDraft8;
+ client->noneOptionName = noneOptionNameDraft8;
+ } else {
+ client->rejectOptionName = rejectOptionNameRfc;
+ client->noneOptionName = noneOptionNameRfc;
+ }
+
+ /*
+ * Following checks may need to be revised if
+ * authentication options other than CHAP and none
+ * are supported.
+ */
+
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+
+ if (client->authRemote) {
+ /*
+ * If initiator doing authentication,
+ * don't offer authentication option none.
+ */
+ option = 1;
+ } else if (!client->passwordPresent) {
+ /*
+ * If initiator password not set,
+ * only offer authentication option none.
+ */
+ option = 2;
+ }
+ }
+
+ if (client->nodeType == iscsiAuthNodeTypeTarget) {
+
+ if (client->authRemote) {
+ /*
+ * If target doing authentication,
+ * don't accept authentication option none.
+ */
+ option = 1;
+ } else {
+ /*
+ * If target not doing authentication,
+ * only accept authentication option none.
+ */
+ option = 2;
+ }
+ }
+
+ for (i = 0; i < client->authMethodCount; i++) {
+
+ if (option == 1) {
+ if (client->authMethodList[i] == iscsiAuthOptionNone) {
+ continue;
+ }
+ } else if (option == 2) {
+ if (client->authMethodList[i] != iscsiAuthOptionNone) {
+ continue;
+ }
+ }
+
+ client->authMethodValidList[j++] = client->authMethodList[i];
+ }
+
+ client->authMethodValidCount = j;
+
+ iscsiAuthClientInitKeyBlock(&client->sendKeyBlock);
+
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ if (client->authRemote) {
+ /*
+ * Initiator wants to authenticate target,
+ * always send AuthMethod key.
+ */
+ client->sendKeyBlock.transitBit = FALSE;
+ client->authMethodValidNegRole =
+ iscsiAuthNegRoleOriginator;
+ } else {
+ client->sendKeyBlock.transitBit = TRUE;
+ client->authMethodValidNegRole =
+ client->authMethodNegRole;
+ }
+ } else {
+ client->sendKeyBlock.transitBit = FALSE;
+ client->authMethodValidNegRole = iscsiAuthNegRoleResponder;
+ }
+
+ if (client->authMethodValidNegRole == iscsiAuthNegRoleOriginator) {
+ iscsiAuthClientSetAuthMethodKey(client,
+ client->authMethodValidCount,
+ client->authMethodValidList);
+ } else {
+ int value = iscsiAuthOptionNotPresent;
+ iscsiAuthClientSetAuthMethodKey(client, 1, &value);
+ }
+}
+
+
+static int
+iscsiAuthClientCheckAuthMethodList(unsigned int optionCount,
+ const int *optionList)
+{
+ unsigned int i;
+
+ if (!optionList || optionCount < 2) {
+ return (TRUE);
+ }
+
+ if (optionList[optionCount - 1] != iscsiAuthOptionNone) {
+ return (TRUE);
+ }
+
+ for (i = 0; i < (optionCount - 1); i++) {
+ if (optionList[i] != iscsiAuthOptionNone) {
+ return (FALSE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+int
+iscsiAuthClientSetAuthMethodList(IscsiAuthClient * client,
+ unsigned int optionCount, const int *optionList)
+{
+ int status;
+
+ status = iscsiAuthClientSetOptionList(
+ client, optionCount, optionList, &client->authMethodCount,
+ client->authMethodList, iscsiAuthMethodMaxCount,
+ iscsiAuthClientCheckAuthMethodOption,
+ iscsiAuthClientCheckAuthMethodList);
+
+ if (status != iscsiAuthStatusNoError) {
+ return (status);
+ }
+
+ /*
+ * Setting authMethod affects authMethodValid.
+ */
+ iscsiAuthClientSetAuthMethodValid(client);
+
+ return (iscsiAuthStatusNoError);
+}
+
+#ifdef notused
+int
+iscsiAuthClientSetAuthMethodNegRole(IscsiAuthClient * client, int negRole)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure ||
+ iscsiAuthClientCheckNegRole(negRole) ||
+ client->nodeType != iscsiAuthNodeTypeInitiator) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ client->authMethodNegRole = (IscsiAuthNegRole) negRole;
+
+ /*
+ * Setting negRole affects authMethodValid.
+ */
+ iscsiAuthClientSetAuthMethodValid(client);
+
+ return (iscsiAuthStatusNoError);
+}
+#endif
+
+
+static int
+iscsiAuthClientCheckChapAlgorithmList(unsigned int optionCount,
+ const int *optionList)
+{
+ if (!optionList || optionCount < 1) {
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+
+int
+iscsiAuthClientSetChapAlgorithmList(IscsiAuthClient * client,
+ unsigned int optionCount, const int *optionList)
+{
+ return (iscsiAuthClientSetOptionList(client,
+ optionCount,
+ optionList,
+ &client->chapAlgorithmCount,
+ client->chapAlgorithmList,
+ iscsiAuthChapAlgorithmMaxCount,
+ iscsiAuthClientCheckChapAlgorithmOption,
+ iscsiAuthClientCheckChapAlgorithmList));
+}
+
+
+int
+iscsiAuthClientSetUsername(IscsiAuthClient * client, const char *username)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure ||
+ iscsiAuthClientCheckString(username, iscsiAuthStringMaxLength, 0)) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (iscsiAuthClientStringCopy(client->username, username,
+ iscsiAuthStringMaxLength)) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientSetPassword(IscsiAuthClient * client,
+ const unsigned char *passwordData, unsigned int passwordLength)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure ||
+ passwordLength > iscsiAuthStringMaxLength) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ bcopy(passwordData, client->passwordData, passwordLength);
+ client->passwordLength = passwordLength;
+ if (client->passwordLength > 0) {
+ client->passwordPresent = TRUE;
+ } else {
+ client->passwordPresent = FALSE;
+ }
+
+ /*
+ * Setting password may affect authMethodValid.
+ */
+ iscsiAuthClientSetAuthMethodValid(client);
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientSetAuthRemote(IscsiAuthClient * client, int authRemote)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ client->authRemote = authRemote;
+
+ /*
+ * Setting authRemote may affect authMethodValid.
+ */
+ iscsiAuthClientSetAuthMethodValid(client);
+
+ return (iscsiAuthStatusNoError);
+}
+
+#ifdef notused
+int
+iscsiAuthClientSetGlueHandle(IscsiAuthClient * client, void *glueHandle)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure &&
+ client->phase != iscsiAuthPhaseNegotiate &&
+ client->phase != iscsiAuthPhaseAuthenticate) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ client->glueHandle = glueHandle;
+
+ return (iscsiAuthStatusNoError);
+}
+
+int
+iscsiAuthClientSetMethodListName(IscsiAuthClient *client,
+ const char *methodListName)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure ||
+ iscsiAuthClientCheckString(methodListName,
+ iscsiAuthStringMaxLength, 0)) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (iscsiAuthClientStringCopy(client->methodListName, methodListName,
+ iscsiAuthStringMaxLength)) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ return (iscsiAuthStatusNoError);
+}
+#endif
+
+
+int
+iscsiAuthClientSetVersion(IscsiAuthClient * client, int version)
+{
+ if (client == 0 ||
+ client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure ||
+ iscsiAuthClientCheckVersion(version)) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ client->version = (IscsiAuthVersion) version;
+
+ iscsiAuthClientSetAuthMethodValid(client);
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientSetIpSec(IscsiAuthClient * client, int ipSec)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ client->ipSec = ipSec;
+
+ return (iscsiAuthStatusNoError);
+}
+
+#ifdef notused
+int
+iscsiAuthClientSetBase64(IscsiAuthClient * client, int base64)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ client->base64 = base64;
+
+ return (iscsiAuthStatusNoError);
+}
+
+int
+iscsiAuthClientSetChapChallengeLength(IscsiAuthClient * client,
+ unsigned int chapChallengeLength)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure ||
+ chapChallengeLength < iscsiAuthChapResponseLength ||
+ chapChallengeLength > iscsiAuthLargeBinaryMaxLength) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ client->chapChallengeLength = chapChallengeLength;
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientCheckPasswordNeeded(IscsiAuthClient *client, int *passwordNeeded)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ if (client->authRemote && !client->passwordPresent) {
+ *passwordNeeded = TRUE;
+ } else {
+ *passwordNeeded = FALSE;
+ }
+ } else {
+ *passwordNeeded = FALSE;
+ }
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientGetAuthPhase(IscsiAuthClient * client, int *value)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ *value = client->phase;
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientGetAuthStatus(IscsiAuthClient * client, int *value)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseDone) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ *value = client->remoteAuthStatus;
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientAuthStatusPass(int authStatus)
+{
+ if (authStatus == iscsiAuthStatusPass) {
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+
+int
+iscsiAuthClientGetAuthMethod(IscsiAuthClient * client, int *value)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseDone &&
+ client->phase != iscsiAuthPhaseAuthenticate) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ *value = client->negotiatedAuthMethod;
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientGetChapAlgorithm(IscsiAuthClient * client, int *value)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseDone) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ *value = client->negotiatedChapAlgorithm;
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientGetChapUsername(IscsiAuthClient * client,
+ char *value, unsigned int maxLength)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseDone) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (iscsiAuthClientStringCopy(value, client->chapUsername, maxLength)) {
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+int
+iscsiAuthClientSendStatusCode(IscsiAuthClient * client, int *statusCode)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseConfigure &&
+ client->phase != iscsiAuthPhaseNegotiate &&
+ client->phase != iscsiAuthPhaseAuthenticate &&
+ client->phase != iscsiAuthPhaseDone) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseDone) {
+ *statusCode = 0x0000;
+ return (iscsiAuthStatusNoError);
+ }
+
+ switch (client->remoteAuthStatus) {
+ case iscsiAuthStatusPass:
+ *statusCode = 0x0000; /* no error */
+ break;
+
+ case iscsiAuthStatusFail:
+ switch (client->debugStatus) {
+ case iscsiAuthDebugStatusAuthFail:
+ /*
+ * Authentication error with peer.
+ */
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ *statusCode = 0x0300;
+ /*
+ * iSCSI Target error
+ */
+ } else {
+ *statusCode = 0x0201;
+ /*
+ * iSCSI Initiator error
+ */
+ }
+ break;
+
+ case iscsiAuthDebugStatusAuthMethodExpected:
+ case iscsiAuthDebugStatusChapAlgorithmExpected:
+ case iscsiAuthDebugStatusChapIdentifierExpected:
+ case iscsiAuthDebugStatusChapChallengeExpected:
+ case iscsiAuthDebugStatusChapResponseExpected:
+ case iscsiAuthDebugStatusChapUsernameExpected:
+ /*
+ * Missing parameter with peer.
+ */
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ *statusCode = 0x0300;
+ /*
+ * iSCSI Target error
+ */
+ } else {
+ *statusCode = 0x0207;
+ /*
+ * iSCSI Initiator error
+ */
+ }
+ break;
+
+ case iscsiAuthDebugStatusAuthMethodNotPresent:
+ case iscsiAuthDebugStatusAuthMethodReject:
+ case iscsiAuthDebugStatusAuthMethodNone:
+ case iscsiAuthDebugStatusChapAlgorithmReject:
+ case iscsiAuthDebugStatusChapChallengeReflected:
+ case iscsiAuthDebugStatusPasswordIdentical:
+ /*
+ * Could not authenticate with peer.
+ */
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ *statusCode = 0x0300;
+ /*
+ * iSCSI Target error
+ */
+ } else {
+ *statusCode = 0x0201;
+ /*
+ * iSCSI Initiator error
+ */
+ }
+ break;
+
+ case iscsiAuthDebugStatusLocalPasswordNotSet:
+ /*
+ * Local password not set.
+ */
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ *statusCode = 0x0200;
+ /*
+ * iSCSI Initiator error
+ */
+ } else {
+ *statusCode = 0x0201;
+ /*
+ * iSCSI Target error
+ */
+ }
+ break;
+
+ case iscsiAuthDebugStatusChapIdentifierBad:
+ case iscsiAuthDebugStatusChapChallengeBad:
+ case iscsiAuthDebugStatusChapResponseBad:
+ case iscsiAuthDebugStatusUnexpectedKeyPresent:
+ case iscsiAuthDebugStatusTbitSetIllegal:
+ case iscsiAuthDebugStatusTbitSetPremature:
+ case iscsiAuthDebugStatusRecvMessageCountLimit:
+ case iscsiAuthDebugStatusRecvDuplicateSetKeyValue:
+ case iscsiAuthDebugStatusRecvStringTooLong:
+ case iscsiAuthDebugStatusRecvTooMuchData:
+ /*
+ * Other error with peer.
+ */
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ *statusCode = 0x0300;
+ /*
+ * iSCSI Target error
+ */
+ } else {
+ *statusCode = 0x0200;
+ /*
+ * iSCSI Initiator error
+ */
+ }
+ break;
+
+ case iscsiAuthDebugStatusNotSet:
+ case iscsiAuthDebugStatusAuthPass:
+ case iscsiAuthDebugStatusAuthRemoteFalse:
+ case iscsiAuthDebugStatusAuthMethodBad:
+ case iscsiAuthDebugStatusChapAlgorithmBad:
+ case iscsiAuthDebugStatusPasswordDecryptFailed:
+ case iscsiAuthDebugStatusPasswordTooShortWithNoIpSec:
+ case iscsiAuthDebugStatusAuthServerError:
+ case iscsiAuthDebugStatusAuthStatusBad:
+ case iscsiAuthDebugStatusAuthPassNotValid:
+ case iscsiAuthDebugStatusSendDuplicateSetKeyValue:
+ case iscsiAuthDebugStatusSendStringTooLong:
+ case iscsiAuthDebugStatusSendTooMuchData:
+ default:
+ /*
+ * Error on this side.
+ */
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ *statusCode = 0x0200;
+ /*
+ * iSCSI Initiator error
+ */
+ } else {
+ *statusCode = 0x0300;
+ /*
+ * iSCSI Target error
+ */
+ }
+
+ }
+ break;
+
+ case iscsiAuthStatusNoError:
+ case iscsiAuthStatusError:
+ case iscsiAuthStatusContinue:
+ case iscsiAuthStatusInProgress:
+ default:
+ /*
+ * Bad authStatus
+ */
+ if (client->nodeType == iscsiAuthNodeTypeInitiator) {
+ *statusCode = 0x0200;
+ /*
+ * iSCSI Initiator error
+ */
+ } else {
+ *statusCode = 0x0300;
+ /*
+ * iSCSI Target error
+ */
+ }
+ }
+
+ return (iscsiAuthStatusNoError);
+}
+#endif
+
+
+int
+iscsiAuthClientGetDebugStatus(IscsiAuthClient * client, int *value)
+{
+ if (!client || client->signature != iscsiAuthClientSignature) {
+ return (iscsiAuthStatusError);
+ }
+
+ if (client->phase != iscsiAuthPhaseDone) {
+
+ client->phase = iscsiAuthPhaseError;
+ return (iscsiAuthStatusError);
+ }
+
+ *value = client->debugStatus;
+
+ return (iscsiAuthStatusNoError);
+}
+
+
+const char *
+iscsiAuthClientDebugStatusToText(int debugStatus)
+{
+ const char *s;
+
+ switch (debugStatus) {
+ case iscsiAuthDebugStatusNotSet:
+ s = "Debug status not set";
+ break;
+
+ case iscsiAuthDebugStatusAuthPass:
+ s = "Authentication request passed";
+ break;
+
+ case iscsiAuthDebugStatusAuthRemoteFalse:
+ s = "Authentication not enabled";
+ break;
+
+ case iscsiAuthDebugStatusAuthFail:
+ s = "Authentication request failed";
+ break;
+
+ case iscsiAuthDebugStatusAuthMethodBad:
+ s = "AuthMethod bad";
+ break;
+
+ case iscsiAuthDebugStatusChapAlgorithmBad:
+ s = "CHAP algorithm bad";
+ break;
+
+ case iscsiAuthDebugStatusPasswordDecryptFailed:
+ s = "Decrypt password failed";
+ break;
+
+ case iscsiAuthDebugStatusPasswordTooShortWithNoIpSec:
+ s = "Local password too short with no IPSec";
+ break;
+
+ case iscsiAuthDebugStatusAuthServerError:
+ s = "Unexpected error from authentication server";
+ break;
+
+ case iscsiAuthDebugStatusAuthStatusBad:
+ s = "Authentication request status bad";
+ break;
+
+ case iscsiAuthDebugStatusAuthPassNotValid:
+ s = "Authentication pass status not valid";
+ break;
+
+ case iscsiAuthDebugStatusSendDuplicateSetKeyValue:
+ s = "Same key set more than once on send";
+ break;
+
+ case iscsiAuthDebugStatusSendStringTooLong:
+ s = "Key value too long on send";
+ break;
+
+ case iscsiAuthDebugStatusSendTooMuchData:
+ s = "Too much data on send";
+ break;
+
+ case iscsiAuthDebugStatusAuthMethodExpected:
+ s = "AuthMethod key expected";
+ break;
+
+ case iscsiAuthDebugStatusChapAlgorithmExpected:
+ s = "CHAP algorithm key expected";
+ break;
+
+ case iscsiAuthDebugStatusChapIdentifierExpected:
+ s = "CHAP identifier expected";
+ break;
+
+ case iscsiAuthDebugStatusChapChallengeExpected:
+ s = "CHAP challenge expected";
+ break;
+
+ case iscsiAuthDebugStatusChapResponseExpected:
+ s = "CHAP response expected";
+ break;
+
+ case iscsiAuthDebugStatusChapUsernameExpected:
+ s = "CHAP username expected";
+ break;
+
+ case iscsiAuthDebugStatusAuthMethodNotPresent:
+ s = "AuthMethod key not present";
+ break;
+
+ case iscsiAuthDebugStatusAuthMethodReject:
+ s = "AuthMethod negotiation failed";
+ break;
+
+ case iscsiAuthDebugStatusAuthMethodNone:
+ s = "AuthMethod negotiated to none";
+ break;
+
+ case iscsiAuthDebugStatusChapAlgorithmReject:
+ s = "CHAP algorithm negotiation failed";
+ break;
+
+ case iscsiAuthDebugStatusChapChallengeReflected:
+ s = "CHAP challange reflected";
+ break;
+
+ case iscsiAuthDebugStatusPasswordIdentical:
+ s = "Local password same as remote";
+ break;
+
+ case iscsiAuthDebugStatusLocalPasswordNotSet:
+ s = "Local password not set";
+ break;
+
+ case iscsiAuthDebugStatusChapIdentifierBad:
+ s = "CHAP identifier bad";
+ break;
+
+ case iscsiAuthDebugStatusChapChallengeBad:
+ s = "CHAP challenge bad";
+ break;
+
+ case iscsiAuthDebugStatusChapResponseBad:
+ s = "CHAP response bad";
+ break;
+
+ case iscsiAuthDebugStatusUnexpectedKeyPresent:
+ s = "Unexpected key present";
+ break;
+
+ case iscsiAuthDebugStatusTbitSetIllegal:
+ s = "T bit set on response, but not on previous message";
+ break;
+
+ case iscsiAuthDebugStatusTbitSetPremature:
+ s = "T bit set on response, but authenticaton not complete";
+ break;
+
+ case iscsiAuthDebugStatusRecvMessageCountLimit:
+ s = "Message count limit reached on receive";
+ break;
+
+ case iscsiAuthDebugStatusRecvDuplicateSetKeyValue:
+ s = "Same key set more than once on receive";
+ break;
+
+ case iscsiAuthDebugStatusRecvStringTooLong:
+ s = "Key value too long on receive";
+ break;
+
+ case iscsiAuthDebugStatusRecvTooMuchData:
+ s = "Too much data on receive";
+ break;
+
+ default:
+ s = "Unknown error";
+ }
+
+ return (s);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_authglue.c b/usr/src/cmd/iscsi/iscsitgtd/iscsi_authglue.c
new file mode 100644
index 0000000000..fe0f5b4188
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_authglue.c
@@ -0,0 +1,374 @@
+/*
+ * 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 2000 by Cisco Systems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * iSCSI Pseudo HBA Driver
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/random.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/iscsi_protocol.h>
+#include <sys/iscsi_authclient.h>
+#include <sys/types.h>
+#include "radius.h"
+#include "queue.h"
+#include "iscsi_sess.h"
+#include "xml.h"
+#include "target.h"
+
+#define DEFAULT_RADIUS_PORT 1812
+
+boolean_t
+persistent_radius_get(iscsi_radius_props_t *radius)
+{
+ Boolean_t bRadiusAccess = False;
+ char *szRadiusServer = NULL;
+ char *szRadiusSecret = NULL;
+ char *szRadiusPort = NULL;
+ int ret = 0;
+ struct addrinfo *res;
+
+ bzero(radius, sizeof (radius));
+ radius->r_radius_config_valid = B_FALSE;
+
+ /* Load RADIUS access option: enable/disable */
+ if (xml_find_value_boolean(main_config, XML_ELEMENT_RAD_ACCESS,
+ &bRadiusAccess) == False) {
+ return (B_FALSE);
+ }
+ if (bRadiusAccess == False) {
+ return (B_FALSE);
+ }
+
+ /* Load RADIUS server: ipaddr[:port] */
+ if (xml_find_value_str(main_config, XML_ELEMENT_RAD_SERV,
+ &szRadiusServer) == False) {
+ return (B_FALSE);
+ }
+
+ szRadiusPort = strchr(szRadiusServer, ':');
+ if (szRadiusPort == NULL) {
+ radius->r_port = DEFAULT_RADIUS_PORT;
+ } else {
+ radius->r_port = strtoul(szRadiusPort + 1, NULL, 0);
+ if (radius->r_port == 0) {
+ radius->r_port = DEFAULT_RADIUS_PORT;
+ }
+ *szRadiusPort = '\0';
+ }
+
+ ret = getaddrinfo(szRadiusServer, NULL, NULL, &res);
+ free(szRadiusServer);
+ if (ret != 0) {
+ return (B_FALSE);
+ }
+ if (res->ai_family == PF_INET) {
+ struct sockaddr_in sa_tmp;
+
+ bcopy(res->ai_addr, &sa_tmp, sizeof (sa_tmp));
+ radius->r_insize = sizeof (in_addr_t);
+ radius->r_addr.u_in4 = sa_tmp.sin_addr;
+ }
+ /*
+ * We don't handle IPV6 currently.
+ */
+
+ /* Load RADIUS shared secret */
+ if (xml_find_value_str(main_config, XML_ELEMENT_RAD_SECRET,
+ &szRadiusSecret) == False) {
+ freeaddrinfo(res);
+ return (B_FALSE);
+ }
+ (void) strncpy((char *)radius->r_shared_secret, szRadiusSecret,
+ MAX_RAD_SHARED_SECRET_LEN);
+ radius->r_shared_secret_len = strlen((char *)radius->r_shared_secret);
+ free(szRadiusSecret);
+ freeaddrinfo(res);
+
+ /* Set RADIUS config flag */
+ radius->r_radius_access = B_TRUE;
+ radius->r_radius_config_valid = B_TRUE;
+ return (B_TRUE);
+}
+
+/*
+ * Authenticate a target's CHAP response.
+ *
+ * username - Incoming username from the the target.
+ * responseData - Incoming response data from the target.
+ */
+int
+iscsiAuthClientChapAuthRequest(IscsiAuthClient *client,
+ char *username, unsigned int id, uchar_t *challengeData,
+ unsigned int challengeLength, uchar_t *responseData,
+ unsigned int responseLength)
+{
+ iscsi_sess_t *isp = (iscsi_sess_t *)client->userHandle;
+ IscsiAuthMd5Context context;
+ iscsi_radius_props_t p_radius_cfg;
+ uchar_t verifyData[iscsiAuthChapResponseLength];
+ char debug[128];
+
+ if (isp == NULL) {
+ return (iscsiAuthStatusFail);
+ }
+
+ /*
+ * the expected credentials are in the session
+ */
+ if (isp->sess_auth.username_in == NULL) {
+ (void) snprintf(debug, sizeof (debug),
+ "SES%x iscsi session(%u) failed authentication, "
+ "no incoming username configured to authenticate initiator",
+ isp->s_num);
+
+ queue_str(isp->s_mgmtq, Q_SESS_ERRS, msg_log, debug);
+ return (iscsiAuthStatusFail);
+ }
+ if (strcmp(username, isp->sess_auth.username_in) != 0) {
+ (void) snprintf(debug, sizeof (debug),
+ "SES%x iscsi session(%u) failed authentication, "
+ "received incorrect username from initiator",
+ isp->s_num);
+ queue_str(isp->s_mgmtq, Q_SESS_ERRS, msg_log, debug);
+ return (iscsiAuthStatusFail);
+ }
+
+ /* Check if RADIUS access is enabled */
+ if (persistent_radius_get(&p_radius_cfg) == B_TRUE &&
+ p_radius_cfg.r_radius_access == B_TRUE) {
+ chap_validation_status_type chap_valid_status;
+ int authStatus;
+ RADIUS_CONFIG radius_cfg;
+
+ if (p_radius_cfg.r_radius_config_valid == B_FALSE) {
+ /*
+ * Radius enabled but configuration invalid -
+ * invalid condition
+ */
+ return (iscsiAuthStatusFail);
+ }
+
+ /* Use RADIUS server to authentication target */
+ if (p_radius_cfg.r_insize == sizeof (in_addr_t)) {
+ /* IPv4 */
+ radius_cfg.rad_svr_addr.i_addr.in4.s_addr =
+ p_radius_cfg.r_addr.u_in4.s_addr;
+ radius_cfg.rad_svr_addr.i_insize
+ = sizeof (in_addr_t);
+ } else if (p_radius_cfg.r_insize == sizeof (in6_addr_t)) {
+ /* IPv6 */
+ bcopy(p_radius_cfg.r_addr.u_in6.s6_addr,
+ radius_cfg.rad_svr_addr.i_addr.in6.s6_addr,
+ 16);
+ radius_cfg.rad_svr_addr.i_insize = sizeof (in6_addr_t);
+ } else {
+ return (iscsiAuthStatusFail);
+ }
+
+ radius_cfg.rad_svr_port = p_radius_cfg.r_port;
+ bcopy(p_radius_cfg.r_shared_secret,
+ radius_cfg.rad_svr_shared_secret,
+ MAX_RAD_SHARED_SECRET_LEN);
+ radius_cfg.rad_svr_shared_secret_len =
+ p_radius_cfg.r_shared_secret_len;
+
+ chap_valid_status = radius_chap_validate(
+ isp->sess_auth.username_in,
+ isp->sess_auth.username,
+ challengeData,
+ challengeLength,
+ responseData,
+ responseLength,
+ id,
+ radius_cfg.rad_svr_addr,
+ radius_cfg.rad_svr_port,
+ radius_cfg.rad_svr_shared_secret,
+ radius_cfg.rad_svr_shared_secret_len);
+
+
+ switch (chap_valid_status) {
+ case CHAP_VALIDATION_PASSED:
+ authStatus = iscsiAuthStatusPass;
+ break;
+ case CHAP_VALIDATION_INVALID_RESPONSE:
+ authStatus = iscsiAuthStatusFail;
+ break;
+ case CHAP_VALIDATION_DUP_SECRET:
+ authStatus = iscsiAuthStatusFail;
+ break;
+ case CHAP_VALIDATION_RADIUS_ACCESS_ERROR:
+ authStatus = iscsiAuthStatusFail;
+ break;
+ case CHAP_VALIDATION_BAD_RADIUS_SECRET:
+ authStatus = iscsiAuthStatusFail;
+ break;
+ default:
+ authStatus = iscsiAuthStatusFail;
+ break;
+ }
+ return (authStatus);
+ } else {
+ /* Use target secret (if defined) to authenticate target */
+ if ((isp->sess_auth.password_length_in < 1) ||
+ (isp->sess_auth.password_in == NULL) ||
+ (isp->sess_auth.password_in[0] == '\0')) {
+ /* No target secret defined - invalid condition */
+ return (iscsiAuthStatusFail);
+ }
+
+ /*
+ * challenge length is I->T, and shouldn't need to
+ * be checked
+ */
+ if (responseLength != sizeof (verifyData)) {
+ (void) snprintf(debug, sizeof (debug),
+ "SES%x iscsi session(%u) failed "
+ "authentication, received incorrect CHAP response "
+ "from initiator", isp->s_num);
+ queue_str(isp->s_mgmtq, Q_SESS_ERRS, msg_log, debug);
+ return (iscsiAuthStatusFail);
+ }
+
+ iscsiAuthMd5Init(&context);
+
+ /*
+ * id byte
+ */
+ verifyData[0] = id;
+ iscsiAuthMd5Update(&context, verifyData, 1);
+
+ /*
+ * shared secret
+ */
+ iscsiAuthMd5Update(&context,
+ (uchar_t *)isp->sess_auth.password_in,
+ isp->sess_auth.password_length_in);
+
+ /*
+ * challenge value
+ */
+ iscsiAuthMd5Update(&context,
+ (uchar_t *)challengeData,
+ challengeLength);
+
+ iscsiAuthMd5Final(verifyData, &context);
+
+ if (bcmp(responseData, verifyData,
+ sizeof (verifyData)) == 0) {
+ return (iscsiAuthStatusPass);
+ }
+
+ (void) snprintf(debug, sizeof (debug),
+ "SES%x iscsi session(%u) failed authentication, "
+ "received incorrect CHAP response from initiator",
+ isp->s_num);
+ queue_str(isp->s_mgmtq, Q_SESS_ERRS, msg_log, debug);
+ }
+ return (iscsiAuthStatusFail);
+}
+
+int
+iscsiAuthClientTextToNumber(const char *text, unsigned long *pNumber)
+{
+ char *pEnd;
+ unsigned long number;
+
+ number = strtoul(text, &pEnd, 0);
+ if (*text != '\0' && *pEnd == '\0') {
+ *pNumber = number;
+ return (0); /* No error */
+ } else {
+ return (1); /* Error */
+ }
+}
+
+/* ARGSUSED */
+void
+iscsiAuthClientNumberToText(unsigned long number, char *text,
+ unsigned int length)
+{
+ (void) snprintf(text, length, "%lu", number);
+}
+
+
+void
+iscsiAuthRandomSetData(uchar_t *data, unsigned int length)
+{
+ int fd;
+ fd = open("/dev/random", O_RDONLY);
+ if (fd == -1)
+ return;
+ (void) read(fd, data, length);
+ (void) close(fd);
+}
+
+
+void
+iscsiAuthMd5Init(IscsiAuthMd5Context * context)
+{
+ MD5Init(context);
+}
+
+
+void
+iscsiAuthMd5Update(IscsiAuthMd5Context *context, uchar_t *data,
+ unsigned int length)
+{
+ MD5Update(context, data, length);
+}
+
+
+void
+iscsiAuthMd5Final(uchar_t *hash, IscsiAuthMd5Context *context)
+{
+ MD5Final(hash, context);
+}
+
+
+int
+iscsiAuthClientData(uchar_t *outData, unsigned int *outLength,
+ uchar_t *inData, unsigned int inLength)
+{
+ if (*outLength < inLength) {
+ return (1); /* error */
+ }
+ bcopy(inData, outData, inLength);
+ *outLength = inLength;
+ return (0); /* no error */
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_cmd.c b/usr/src/cmd/iscsi/iscsitgtd/iscsi_cmd.c
new file mode 100644
index 0000000000..e6822af1c2
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_cmd.c
@@ -0,0 +1,337 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <syslog.h>
+#include <synch.h>
+#include <sys/time.h>
+#include <sys/asynch.h>
+
+#include "iscsi_conn.h"
+#include "iscsi_cmd.h"
+#include "utility.h"
+
+static pthread_mutex_t cmd_mutex;
+static int cmd_ttt;
+
+/*
+ * []----
+ * | iscsi_cmd_init -- called at the beginning of time to initialize locks
+ * []----
+ */
+void
+iscsi_cmd_init()
+{
+ (void) pthread_mutex_init(&cmd_mutex, NULL);
+ cmd_ttt = 0;
+}
+
+/*
+ * []----
+ * | iscsi_cmd_alloc -- allocate space for new command
+ * []----
+ */
+iscsi_cmd_t *
+iscsi_cmd_alloc(iscsi_conn_t *c, int op)
+{
+ iscsi_cmd_t *cmd = (iscsi_cmd_t *)calloc(sizeof (iscsi_cmd_t), 1);
+
+ if (cmd == NULL)
+ return (NULL);
+
+ (void) pthread_mutex_lock(&cmd_mutex);
+ cmd->c_ttt = cmd_ttt++;
+ (void) pthread_mutex_unlock(&cmd_mutex);
+
+ (void) pthread_mutex_lock(&c->c_mutex);
+ cmd->c_opcode = op;
+ cmd->c_statsn = c->c_statsn;
+ cmd->c_state = CmdAlloc;
+ if (c->c_cmd_head == NULL) {
+ c->c_cmd_head = cmd;
+ assert(c->c_cmd_tail == NULL);
+ c->c_cmd_tail = cmd;
+ } else {
+ c->c_cmd_tail->c_next = cmd;
+ cmd->c_prev = c->c_cmd_tail;
+ c->c_cmd_tail = cmd;
+ }
+ cmd->c_allegiance = c;
+ cmd->c_t_start = gethrtime();
+ c->c_cmds_active++;
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ return (cmd);
+}
+
+/*
+ * []----
+ * | iscsi_cmd_find -- search for a specific command and return it
+ * |
+ * | XXX Need to switch to use an AVL tree.
+ * []----
+ */
+iscsi_cmd_t *
+iscsi_cmd_find(iscsi_conn_t *c, uint32_t val, find_type_t type)
+{
+ iscsi_cmd_t *cmd = NULL;
+
+ (void) pthread_mutex_lock(&c->c_mutex);
+ for (cmd = c->c_cmd_head; cmd; cmd = cmd->c_next) {
+
+ /*
+ * Depending on type determine correct matching value.
+ * Only return a hit if the command hasn't already been
+ * freed.
+ */
+ if ((((type == FindTTT) && (cmd->c_ttt == val)) ||
+ ((type == FindITT) && (cmd->c_itt == val))) &&
+ (cmd->c_state != CmdFree))
+ break;
+ }
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ return (cmd);
+}
+
+/*
+ * []----
+ * | iscsi_cmd_free -- mark a command as freed.
+ * []----
+ */
+void
+iscsi_cmd_free(iscsi_conn_t *c, iscsi_cmd_t *cmd)
+{
+ hrtime_t h = gethrtime();
+
+ cmd->c_state = CmdFree;
+ cmd->c_t_completion = h - cmd->c_t_start;
+ c->c_cmds_avg_sum += cmd->c_t_completion;
+ c->c_cmds_avg_cnt++;
+}
+
+/*
+ * []----
+ * | iscsi_cmd_cancel -- mark a command as canceled
+ * |
+ * | We don't actually stop commands in flight. We only prevent the canceled
+ * | commands from returning status and/or data to the initiator. At the
+ * | connection layer if a command is canceled nothing will be sent on the
+ * | wire and at that point the command is marked CmdFree so that future calls
+ * | to cmd_remove will actually free the space.
+ * []----
+ */
+void
+iscsi_cmd_cancel(iscsi_conn_t *c, iscsi_cmd_t *cmd)
+{
+ (void) pthread_mutex_lock(&c->c_mutex);
+ if (cmd->c_state == CmdAlloc) {
+ cmd->c_state = CmdCanceled;
+ if (cmd->c_t10_cmd != NULL)
+ t10_cmd_state(cmd->c_t10_cmd, T10_Cmd_Event_Canceled);
+ }
+ (void) pthread_mutex_unlock(&c->c_mutex);
+}
+
+/*
+ * []----
+ * | iscsi_cmd_remove -- actually free space allocated to commands
+ * |
+ * | According to the iSCSI specification the target must kept resources
+ * | around until the initiator sends a command with a status serial
+ * | number higher than the held resource. This is so that an initiator
+ * | can request data again if needed. During the processing of each new
+ * | command this routine is called to free old commands.
+ * []----
+ */
+void
+iscsi_cmd_remove(iscsi_conn_t *c, uint32_t statsn)
+{
+ iscsi_cmd_t *cmd,
+ *n;
+
+ (void) pthread_mutex_lock(&c->c_mutex);
+ for (cmd = c->c_cmd_head; cmd; ) {
+
+ /*
+ * If the StatusSN for this command is less than the incoming
+ * StatusSN and the command has been freed remove it from
+ * list. Don't bother with commands that are in the state of
+ * CmdCanceled. Once the I/O has been completed the command
+ * is passed back to the connection handler where the state
+ * will be noticed and then the command will be freed. At that
+ * point the next incoming command with a valid expected
+ * status serial number will free the memory.
+ */
+ if (sna_lt(cmd->c_statsn, statsn) &&
+ (cmd->c_state == CmdFree)) {
+
+ if (c->c_cmd_head == cmd) {
+ c->c_cmd_head = cmd->c_next;
+ if (c->c_cmd_head == NULL)
+ c->c_cmd_tail = NULL;
+ } else {
+ n = cmd->c_prev;
+ n->c_next = cmd->c_next;
+ if (cmd->c_next != NULL)
+ cmd->c_next->c_prev = n;
+ else {
+ assert(c->c_cmd_tail == cmd);
+ c->c_cmd_tail = n;
+ }
+ }
+
+ n = cmd->c_next;
+ if (cmd->c_scb_extended)
+ free(cmd->c_scb_extended);
+ if (cmd->c_data_alloc == True) {
+ free(cmd->c_data);
+ cmd->c_data = NULL;
+ }
+ c->c_cmds_active--;
+ free(cmd);
+ cmd = n;
+ } else if (sna_lte(statsn, cmd->c_statsn)) {
+ break;
+ } else {
+ cmd = cmd->c_next;
+ }
+ }
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+}
+
+/*
+ * []----
+ * | iscsi_cmd_window -- return the number of available commands
+ * |
+ * | There are currently 7 different places where this routine is called.
+ * | In some cases and command is allocated which will be freed shortly and
+ * | in others no command is held. This is why the number of commands found
+ * | will be decremented if larger than 0. Since the daemon doesn't have
+ * | any hard limits on the number of commands being supported this is more
+ * | arbitrary and the command window size is used for debugging other
+ * | initiators.
+ * |
+ * | NOTE: connection mutex must be held during this call.
+ * []----
+ */
+int
+iscsi_cmd_window(iscsi_conn_t *c)
+{
+ int cnt;
+
+ assert(pthread_mutex_trylock(&c->c_mutex) != 0);
+ if (c->c_cmds_avg_cnt == 0) {
+
+ /*
+ * If there are no outstanding commands clear the averages
+ * so that the initiator can start again.
+ */
+ c->c_cmds_avg_sum = 0;
+ c->c_cmds_avg_cnt = 0;
+ cnt = c->c_maxcmdsn - c->c_cmds_active;
+
+ } else if ((c->c_cmds_avg_sum / c->c_cmds_avg_cnt) >= NANOSEC) {
+
+ /*
+ * It would appear things are taking a real long time to
+ * complete on our end. Close down the command window to
+ * prevent the initiator from timing out commands.
+ */
+ cnt = (c->c_cmds_active > 5) ? 0 : 5 - c->c_cmds_active;
+
+ } else {
+ cnt = (c->c_cmds_active >= c->c_maxcmdsn) ? 0 :
+ c->c_maxcmdsn - c->c_cmds_active;
+ }
+
+ return (cnt);
+}
+
+void
+iscsi_cmd_delayed_store(iscsi_cmd_t *cmd, t10_cmd_t *t)
+{
+ iscsi_delayed_t *d,
+ *n,
+ *l = NULL;
+
+ if ((d = (iscsi_delayed_t *)calloc(1, sizeof (*d))) == NULL) {
+ syslog(LOG_ERR, "Failed to allocate space for delayed I/O");
+ queue_prt(cmd->c_allegiance->c_mgmtq, Q_CONN_ERRS,
+ "CON%x Failed calloc for delayed I/O",
+ cmd->c_allegiance->c_num);
+ t10_cmd_state(t, T10_Cmd_Event_Release);
+ return;
+ }
+
+ d->id_offset = T10_DATA_OFFSET(t);
+ d->id_t10_cmd = t;
+
+ for (n = cmd->c_t10_delayed; n; n = n->id_next) {
+ l = n;
+ if (d->id_offset < n->id_offset) {
+ if (n->id_prev == NULL) {
+ d->id_next = n;
+ n->id_prev = d;
+ cmd->c_t10_delayed = d;
+ } else {
+ d->id_prev = n->id_prev;
+ d->id_prev->id_next = d;
+ n->id_prev = d;
+ d->id_next = n;
+ }
+ return;
+ }
+ }
+
+ if (l == NULL) {
+ cmd->c_t10_delayed = d;
+ } else {
+ l->id_next = d;
+ d->id_prev = l;
+ }
+}
+
+void
+iscsi_cmd_delayed_remove(iscsi_cmd_t *cmd, iscsi_delayed_t *d)
+{
+ if (cmd->c_t10_delayed == d) {
+ cmd->c_t10_delayed = d->id_next;
+ if (d->id_next)
+ d->id_next->id_prev = NULL;
+ } else {
+ d->id_prev->id_next = d->id_next;
+ if (d->id_next != NULL)
+ d->id_next->id_prev = d->id_prev;
+ }
+ free(d);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_cmd.h b/usr/src/cmd/iscsi/iscsitgtd/iscsi_cmd.h
new file mode 100644
index 0000000000..f9c20c82ba
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_cmd.h
@@ -0,0 +1,167 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TARGET_CMD_H
+#define _TARGET_CMD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Block comment which describes the contents of this file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/avl.h>
+#include <aio.h>
+
+#include "local_types.h"
+#include "t10.h"
+
+#define CMD_MAXOUTSTANDING 64
+
+typedef enum {
+ FindTTT,
+ FindITT
+} find_type_t;
+
+typedef enum {
+ CmdAlloc,
+ CmdCanceled,
+ CmdFree
+} cmd_state_t;
+
+typedef struct iscsi_delayed {
+ struct iscsi_delayed *id_prev,
+ *id_next;
+ t10_cmd_t *id_t10_cmd;
+ size_t id_offset;
+} iscsi_delayed_t;
+
+typedef struct iscsi_cmd {
+ struct iscsi_cmd *c_next;
+ struct iscsi_cmd *c_prev;
+
+ /*
+ * Always kept in network byte order since we only
+ * store this field
+ */
+ uint32_t c_itt;
+
+ uint32_t c_opcode;
+ uint32_t c_ttt;
+ uint32_t c_cmdsn;
+ uint32_t c_datasn;
+ uint32_t c_statsn;
+
+ Boolean_t c_writeop;
+ uint32_t c_lun;
+
+ /*
+ * Default storage for SCB which is the most common size to day.
+ */
+ uint8_t c_scb_default[16];
+
+ /*
+ * If the CDB is larger than 16 bytes an Alternate Header Segment
+ * is used and memory allocated which is pointed to by the following
+ */
+ uint8_t *c_scb_extended;
+
+ /*
+ * Points at the appropriate memory area for the SCB
+ */
+ uint8_t *c_scb;
+ uint32_t c_scb_len;
+
+ cmd_state_t c_state;
+ uint32_t c_status;
+
+ /*
+ * When ImmediateData==Yes it'll be read in to a buffer
+ * allocated by the connection. This will be free'd when the
+ * callback is done which means the SAM-3 layer is finished with
+ * the data.
+ */
+ char *c_data;
+ size_t c_data_len;
+ off_t c_offset_out;
+
+ /*
+ * Arrange for the PDUs to always be sent in order. If DataPDUInOrder
+ * is True this is a *must* according to the specification. There's
+ * also a need that the final flag bit not be sent unless all other
+ * packets have been, regardless of order. Without keeping a complicated
+ * bitmap of which packets have been sent for this second case we
+ * just order things always.
+ */
+ off_t c_offset_in;
+ iscsi_delayed_t *c_t10_delayed;
+
+ Boolean_t c_data_alloc;
+
+ void (*c_dataout_cb)(t10_cmd_t *cmd, char *data,
+ size_t *xfer);
+
+ /*
+ * Used to hold the interface pointer when we've got an R2T
+ * for this command. This is needed because memory is allocated
+ * normally by the emulation layer and we can copy directly to that
+ * instead of allocating our own buffer.
+ */
+ t10_cmd_t *c_t10_cmd;
+
+ /*
+ * Used by the session layer to send packets out the same
+ * connection.
+ */
+ struct iscsi_conn *c_allegiance;
+
+ hrtime_t c_t_start,
+ c_t_completion;
+
+} iscsi_cmd_t;
+
+
+void iscsi_cmd_init();
+iscsi_cmd_t *iscsi_cmd_alloc(struct iscsi_conn *c, int opcode);
+iscsi_cmd_t *iscsi_cmd_find(struct iscsi_conn *c, uint32_t x,
+ find_type_t type);
+void iscsi_cmd_free(struct iscsi_conn *c, iscsi_cmd_t *cmd);
+void iscsi_cmd_cancel(struct iscsi_conn *c, iscsi_cmd_t *cmd);
+void iscsi_cmd_remove(struct iscsi_conn *c, uint32_t statsn);
+int iscsi_cmd_window(struct iscsi_conn *c);
+void iscsi_cmd_delayed_store(iscsi_cmd_t *cmd, t10_cmd_t *t);
+void iscsi_cmd_delayed_remove(iscsi_cmd_t *cmd, iscsi_delayed_t *d);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TARGET_CMD_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_conn.c b/usr/src/cmd/iscsi/iscsitgtd/iscsi_conn.c
new file mode 100644
index 0000000000..5252832d99
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_conn.c
@@ -0,0 +1,1226 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <signal.h>
+#include <pthread.h>
+#include <assert.h>
+#include <sys/select.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <strings.h>
+#include <sys/filio.h>
+#include <errno.h>
+#include <utility.h>
+#include <unistd.h>
+#include <sys/stropts.h>
+#include <syslog.h>
+#include <sys/iscsi_protocol.h>
+
+#include "local_types.h"
+#include "iscsi_conn.h"
+#include "iscsi_sess.h"
+#include "iscsi_login.h"
+#include "iscsi_ffp.h"
+#include "utility.h"
+#include "target.h"
+#include "port.h"
+#include "t10.h"
+
+/*
+ * defined here so that pad_text is initialized to zero's. It's
+ * never modified.
+ */
+static const char pad_text[ISCSI_PAD_WORD_LEN] = { 0 };
+
+static void iscsi_conn_data_rqst(t10_cmd_t *cmd);
+static void iscsi_conn_cmdcmplt(t10_cmd_t *cmd);
+static void iscsi_conn_data_in(t10_cmd_t *);
+static void iscsi_conn_pkt(iscsi_conn_t *c, iscsi_rsp_hdr_t *in);
+
+static void send_datain_pdu(iscsi_conn_t *c, t10_cmd_t *cmd,
+ uint8_t final_flag);
+static void send_scsi_rsp(iscsi_conn_t *c, t10_cmd_t *cmd);
+static void queue_noop_in(iscsi_conn_t *c);
+static char *state_to_str(iscsi_state_t s);
+static void send_async_logout(iscsi_conn_t *c);
+static void send_async_scsi(iscsi_conn_t *c, int key, int asc, int ascq);
+
+void *
+conn_poller(void *v)
+{
+ iscsi_conn_t *c = (iscsi_conn_t *)v;
+ int nbytes,
+ pval;
+ nfds_t nfds = 1;
+ struct pollfd fds[1];
+ iscsi_state_t state;
+ target_queue_t *mgmtq = c->c_mgmtq;
+
+ fds[0].fd = c->c_fd;
+ fds[0].events = POLLIN;
+
+ util_title(c->c_mgmtq, Q_CONN_LOGIN, c->c_num, "Start Poller");
+ while ((pval = poll(fds, nfds, 30 * 1000)) != -1) {
+
+ /*
+ * The true asynchronous events are when we're in S5_LOGGED_IN
+ * state. In the iscsi_full_feature() code the state is
+ * locked and checked before sending any messages along. The
+ * mutex is grabbed here only to prevent a collision between
+ * some thread setting the state and our reading of the value.
+ * There's no harm in us grabbing the state which might
+ * change right after we unlock the mutex.
+ */
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ state = c->c_state;
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+
+ switch (state) {
+ case S1_FREE:
+ /*
+ * If we moved to the free state. The session
+ * was sent a message to shutdown. Once it
+ * completes it will reply with a shutdown
+ * response which will close the main
+ * connection thread. So, this thread just
+ * returns a stop processing incoming packets.
+ */
+ goto error;
+
+ case S3_XPT_UP:
+ if (ioctl(c->c_fd, FIONREAD, &nbytes) < 0) {
+ queue_message_set(c->c_dataq, 0, msg_shutdown,
+ 0);
+ goto error;
+ }
+
+ /*
+ * To be fully compliant the code should use
+ * ioctl(fd, I_PEEK, (struct strpeek v)); and
+ * look to see if the header is indeed a login
+ * packet. If not, just close the connection.
+ */
+ if (nbytes < sizeof (iscsi_login_hdr_t)) {
+ queue_message_set(c->c_dataq, 0,
+ msg_shutdown, 0);
+ goto error;
+ } else {
+ /*
+ * Change the state to S4_IN_LOGIN.
+ * Since we haven't touched the data
+ * waiting on the stream when the
+ * sema_post() occurs below the poller
+ * will find data again and send
+ * another packet ready message at
+ * which point we deal with the
+ * login.
+ */
+ conn_state(c, T4);
+ }
+ break;
+
+ case S4_IN_LOGIN:
+ if (iscsi_handle_login_pkt(c) == False)
+ goto error;
+ break;
+
+ case S7_LOGOUT_REQUESTED:
+ case S5_LOGGED_IN:
+ if (fds[0].revents & POLLIN) {
+
+ if (iscsi_full_feature(c) == False)
+ goto error;
+
+ } else if (c->c_sess->s_type == SessionNormal) {
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO,
+ "CON%x Send a NOP request", c->c_num);
+ queue_noop_in(c);
+ }
+ break;
+
+ case S6_IN_LOGOUT:
+ goto error;
+
+ case S8_CLEANUP_WAIT:
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "Haven't handled state S8");
+ queue_message_set(c->c_dataq, 0,
+ msg_shutdown_rsp, 0);
+ goto error;
+ }
+
+ }
+
+error:
+ /*
+ * Only when we're logged in would we have an active session
+ * which needs to be shut down. In the case of S4_IN_LOGIN we could
+ * transition to either S1_FREE in which case a shutdown message
+ * was sent to the session which in turn will reply with a shutdown
+ * response causing the conn_process to exit.
+ */
+ if (c->c_state == S5_LOGGED_IN)
+ conn_state(c, T8);
+
+ /*
+ * If a msg_conn_lost was already sent it's invalid to reference
+ * the management queue from the connection structure at this point.
+ */
+ util_title(mgmtq, Q_CONN_LOGIN, c->c_num, "End Poller");
+
+ if (pval == -1)
+ queue_message_set(c->c_dataq, 0, msg_conn_lost, 0);
+ return (NULL);
+}
+
+/*
+ * conn_process -- thread which runs a connection
+ */
+void *
+conn_process(void *v)
+{
+ iscsi_conn_t *c = (iscsi_conn_t *)v;
+ iscsi_cmd_t *cmd;
+ Boolean_t process = True,
+ is_last = False;
+ msg_t *m;
+ void *thr_status;
+ int i;
+ mgmt_request_t *mgmt;
+ char debug[80];
+ time_t tval = time((time_t *)0);
+
+ c->c_dataq = queue_alloc();
+ c->c_maxcmdsn = CMD_MAXOUTSTANDING;
+ if (sema_init(&c->c_datain, 0, USYNC_THREAD, NULL) != 0) {
+ port_conn_remove(c);
+ free(c);
+ return (NULL);
+ }
+
+ util_title(c->c_mgmtq, Q_CONN_LOGIN, c->c_num, "Start Receiver");
+ util_title(c->c_mgmtq, Q_CONN_LOGIN, c->c_num,
+ ctime_r(&tval, debug, sizeof (debug)));
+
+ (void) pthread_create(&c->c_thr_id_poller, NULL,
+ conn_poller, (void *)c);
+ c->c_thr_id_process = pthread_self();
+
+ assert(c->c_state == S1_FREE);
+ conn_state(c, T3);
+
+ do {
+ m = queue_message_get(c->c_dataq);
+ switch (m->msg_type) {
+ case msg_mgmt_rqst:
+ mgmt = (mgmt_request_t *)m->msg_data;
+ if (c->c_state == S5_LOGGED_IN) {
+ if (mgmt->m_request == mgmt_logout) {
+ conn_state(c, T11);
+ queue_message_set(mgmt->m_q, 0,
+ msg_mgmt_rply, 0);
+ } else {
+ queue_message_set(c->c_sessq, 0,
+ msg_mgmt_rqst, m->msg_data);
+ }
+ } else {
+ /*
+ * Corner case which can occur when the
+ * connection has just started and is in the
+ * process of logging in and we get a
+ * mangement request. There's no session
+ * information or even a queue setup. Just
+ * show an empty connection.
+ *
+ * For the mgmt_logout, it's possible that
+ * we sent the T11 state change causing the
+ * connection to enter the S7 state. If we
+ * get a logout request again the specification
+ * says to just drop the connection.
+ */
+ if (mgmt->m_request == mgmt_logout)
+ conn_state(c, T18);
+ else {
+ (void) pthread_mutex_lock(
+ &mgmt->m_resp_mutex);
+ xml_add_tag(mgmt->m_u.m_resp,
+ "connection", NULL);
+ (void) pthread_mutex_unlock(
+ &mgmt->m_resp_mutex);
+ }
+ queue_message_set(mgmt->m_q, 0,
+ msg_mgmt_rply, 0);
+ }
+ m->msg_data = NULL;
+ break;
+
+ case msg_conn_lost:
+ queue_prt(c->c_mgmtq, Q_CONN_LOGIN,
+ "CON%x Shutdown: connection", c->c_num);
+
+ if (c->c_state == S5_LOGGED_IN)
+ conn_state(c, T8);
+ break;
+
+ case msg_shutdown_rsp:
+ if (c->c_state == S6_IN_LOGOUT)
+ conn_state(c, T13);
+ (void) pthread_join(c->c_thr_id_poller, &thr_status);
+ is_last = (Boolean_t)m->msg_data;
+ m->msg_data = NULL;
+ process = False;
+ break;
+
+ case msg_shutdown:
+ if (c->c_state == S5_LOGGED_IN) {
+ conn_state(c, T8);
+ } else if (c->c_state == S4_IN_LOGIN)
+ conn_state(c, T7);
+ else {
+ process = False;
+ }
+ break;
+
+ case msg_targ_inventory_change:
+ if (c->c_state == S5_LOGGED_IN) {
+ send_async_scsi(c, KEY_UNIT_ATTENTION, 0x3f,
+ 0x0e);
+ }
+ break;
+
+ case msg_send_pkt:
+ iscsi_conn_pkt(c, (iscsi_rsp_hdr_t *)m->msg_data);
+ break;
+
+ case msg_cmd_data_rqst:
+ /*
+ * The STE needs more data to complete
+ * the write command.
+ */
+ iscsi_conn_data_rqst((t10_cmd_t *)m->msg_data);
+ break;
+
+ case msg_cmd_data_in:
+ /*
+ * Data is available to satisfy the READ command
+ */
+ iscsi_conn_data_in((t10_cmd_t *)m->msg_data);
+ break;
+
+ case msg_cmd_cmplt:
+ /*
+ * Status is available for a previous STEOut.
+ * The status may be good and the previous STEOut data
+ * wasn't sent so we phase collapse.
+ */
+ iscsi_conn_cmdcmplt((t10_cmd_t *)m->msg_data);
+ break;
+
+ default:
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x Didn't handle msg_type %d", c->c_num,
+ m->msg_type);
+ break;
+ }
+
+ queue_message_free(m);
+ } while (process == True);
+
+ /*
+ * Free any resources used.
+ */
+ if (c->c_text_area)
+ free(c->c_text_area);
+ if (c->c_fd != -1)
+ (void) close(c->c_fd);
+
+ /*
+ * It's possible, but very unlikely that c_sessq is NULL at this
+ * point. I saw one case where the system had problems causing the
+ * poller routine to exit real early so that the session was never
+ * setup causing the daemon to get a SEGV in queue_free when a NULL
+ * was passed in.
+ */
+ if ((is_last == True) && (c->c_sessq != NULL))
+ queue_free(c->c_sessq, sess_queue_data_remove);
+
+ /*
+ * See if there are any commands outstanding and free them.
+ * NOTE: Should walk through the data_ptr list and find data structure
+ * who have alligence to this connection and free them as well.
+ */
+ (void) pthread_mutex_lock(&c->c_mutex);
+
+ for (i = 0, cmd = c->c_cmd_head; cmd; i++)
+ cmd = cmd->c_next; /* debug count of lost ttt's */
+
+ (void) snprintf(debug, sizeof (debug), "CON%x %d Lost TTTs: ",
+ c->c_num, i);
+
+ for (i = 0, cmd = c->c_cmd_head; cmd; i++) {
+ iscsi_cmd_t *n = cmd->c_next;
+ if (cmd->c_state != CmdCanceled) {
+ (void) snprintf(debug + strlen(debug),
+ sizeof (debug) - strlen(debug),
+ "0x%x ", cmd->c_ttt);
+ }
+ free(cmd);
+ cmd = n;
+ }
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ if (i) {
+ /*
+ * If there where any found send a message indicating
+ * which ones. This message is purely for information
+ * and is not indicative of an error.
+ */
+ queue_prt(c->c_mgmtq, Q_CONN_LOGIN, debug);
+ }
+
+ if (c->c_cmds_avg_cnt != 0)
+ queue_prt(c->c_mgmtq, Q_CONN_LOGIN,
+ "CON%x Average completion %lldms", c->c_num,
+ (c->c_cmds_avg_sum / c->c_cmds_avg_cnt) / (1000 * 1000));
+
+ (void) sema_destroy(&c->c_datain);
+ if (c->c_targ_alias)
+ free(c->c_targ_alias);
+
+ util_title(c->c_mgmtq, Q_CONN_LOGIN, c->c_num, "End Receiver");
+
+ /*
+ * Remove this connection from linked list of current connections.
+ * This will also free the connection queue. Must not hold the
+ * q here because port_conn_remove-->queue_free->conn_queue_data
+ * will possible grab the mutex.
+ */
+ port_conn_remove(c);
+ free(c);
+ return (NULL);
+}
+
+/*
+ * []----
+ * | iscsi_conn_pkt -- send out PDU from receive thread
+ * |
+ * | (1) This PDU could be either:
+ * | (a) A NOP request was sent from the initiator which the recieve
+ * | side processed and is requesting to be sent back.
+ * | (b) Nothing has been received in N seconds and we're looking
+ * | to see if the connection is still alive.
+ * | (c) A task management request was processed by the receive side
+ * | and the response must be sent.
+ * | (2) Fields to be filled in
+ * | Need to delay filling in several of the fields until
+ * | now to avoid using sequence number which would be out of
+ * | order.
+ * []----
+ */
+static void
+iscsi_conn_pkt(iscsi_conn_t *c, iscsi_rsp_hdr_t *in)
+{
+ if (c->c_state != S5_LOGGED_IN) {
+ free(in);
+ return;
+ }
+ (void) pthread_mutex_lock(&c->c_mutex);
+ in->statsn = htonl(c->c_statsn++);
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ in->expcmdsn = htonl(c->c_sess->s_seencmdsn + 1);
+ in->maxcmdsn = htonl(iscsi_cmd_window(c) + c->c_sess->s_seencmdsn);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ send_iscsi_pkt(c, (iscsi_hdr_t *)in, 0);
+ free(in);
+}
+
+/*
+ * []----
+ * | iscsi_conn_data_rqst -- request that data be sent from the initiator
+ * []----
+ */
+static void
+iscsi_conn_data_rqst(t10_cmd_t *t)
+{
+ iscsi_cmd_t *cmd = T10_TRANS_ID(t);
+ iscsi_conn_t *c;
+ iscsi_rtt_hdr_t rtt;
+
+ bzero(&rtt, sizeof (rtt));
+
+ c = cmd->c_allegiance;
+ (void) pthread_mutex_lock(&c->c_mutex);
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ if ((c->c_state != S5_LOGGED_IN) ||
+ (cmd->c_state == CmdCanceled)) {
+ t10_cmd_state(t, T10_Cmd_Event_Release);
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ return;
+ }
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+
+ /*
+ * Save the data pointer from the emulation code. It's their
+ * responsibility to allocate space for the data which the
+ * initiator will return. When we receive a DATAOUT packet
+ * we'll copy data from the socket directly to this buffer.
+ */
+ cmd->c_data = T10_DATA(t);
+
+ /*
+ * RFC3270.10.8.3
+ * The statsn field will contain the next statsn. The statsn for this
+ * connection is not advanced after this PDU is sent.
+ */
+ rtt.statsn = htonl(c->c_statsn);
+
+ rtt.opcode = ISCSI_OP_RTT_RSP;
+ rtt.flags = ISCSI_FLAG_FINAL;
+ rtt.itt = cmd->c_itt;
+ rtt.ttt = cmd->c_ttt;
+ rtt.data_offset = htonl(T10_DATA_OFFSET(t));
+ rtt.data_length = htonl(T10_DATA_LEN(t));
+
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ rtt.maxcmdsn = htonl(iscsi_cmd_window(c) + c->c_sess->s_seencmdsn);
+ rtt.expcmdsn = htonl(c->c_sess->s_seencmdsn + 1);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+
+#ifdef FULL_DEBUG
+ queue_prt(c->c_mgmtq, Q_CONN_IO,
+ "CON%x TTT 0x%x R2T offset 0x%x, len 0x%x",
+ c->c_num, cmd->c_ttt, T10_DATA_OFFSET(t), T10_DATA_LEN(t));
+#endif
+
+ t10_cmd_state(t, T10_Cmd_Event_DataOut_Sent);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ send_iscsi_pkt(c, (iscsi_hdr_t *)&rtt, 0);
+}
+
+/*
+ * []----
+ * | iscsi_conn_data_in -- Send data to initiator
+ * []----
+ */
+void
+iscsi_conn_data_in(t10_cmd_t *t)
+{
+ iscsi_cmd_t *cmd = (iscsi_cmd_t *)T10_TRANS_ID(t);
+ iscsi_conn_t *c;
+
+ c = cmd->c_allegiance;
+ (void) pthread_mutex_lock(&c->c_mutex);
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ if ((c->c_state != S5_LOGGED_IN) ||
+ (cmd->c_state == CmdCanceled)) {
+
+ t10_cmd_state(t, T10_Cmd_Event_Release);
+ while (cmd->c_t10_delayed) {
+ t10_cmd_state(cmd->c_t10_delayed->id_t10_cmd,
+ T10_Cmd_Event_Release);
+ iscsi_cmd_delayed_remove(cmd, cmd->c_t10_delayed);
+ }
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ return;
+ }
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+
+ /*
+ * Need to deal with out of order data PDUs. RFC3720 allows
+ * the initiator to indicate if it can handle out-of-order
+ * PDUs.
+ */
+ if (cmd->c_offset_in != T10_DATA_OFFSET(t)) {
+ iscsi_cmd_delayed_store(cmd, t);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ return;
+ }
+
+ while (t != NULL) {
+ cmd->c_offset_in += T10_DATA_LEN(t);
+ if (T10_CMD_LAST(t) == True) {
+ send_datain_pdu(c, t,
+ ISCSI_FLAG_FINAL | ISCSI_FLAG_DATA_STATUS);
+ iscsi_cmd_free(c, cmd);
+ } else {
+ send_datain_pdu(c, t, 0);
+ }
+ t10_cmd_state(t, T10_Cmd_Event_Release);
+ cmd->c_t10_cmd = NULL;
+
+ if (cmd->c_t10_delayed &&
+ (cmd->c_t10_delayed->id_offset == cmd->c_offset_in)) {
+ t = cmd->c_t10_delayed->id_t10_cmd;
+ iscsi_cmd_delayed_remove(cmd, cmd->c_t10_delayed);
+ } else {
+ t = NULL;
+ }
+ }
+ (void) pthread_mutex_unlock(&c->c_mutex);
+}
+
+/*
+ * []----
+ * | iscsi_conn_cmdcmplt -- Send out appropriate completion PDU
+ * []----
+ */
+void
+iscsi_conn_cmdcmplt(t10_cmd_t *t)
+{
+ iscsi_cmd_t *cmd = (iscsi_cmd_t *)T10_TRANS_ID(t);
+ iscsi_conn_t *c;
+
+ c = cmd->c_allegiance;
+ (void) pthread_mutex_lock(&c->c_mutex);
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ if ((c->c_state != S5_LOGGED_IN) ||
+ (cmd->c_state == CmdCanceled)) {
+
+ t10_cmd_state(t, T10_Cmd_Event_Release);
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ return;
+ }
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+
+ if (T10_SENSE_LEN(t) || (T10_DATA(t) == 0)) {
+
+ /*
+ * If d_sense_len is set there's a problem and we need to send
+ * a SCSI response packet. Or if there's no data buffer then
+ * this is an acknowledgement that a SCSI Write completed
+ * successfully.
+ */
+ send_scsi_rsp(c, t);
+
+ } else {
+
+ /*
+ * send data out with final bit. Last packet of a SCSI
+ * READ Op and we'll send it out with the final/status
+ * bits set.
+ */
+ send_datain_pdu(c, t,
+ ISCSI_FLAG_FINAL | ISCSI_FLAG_DATA_STATUS);
+
+ }
+
+ t10_cmd_state(t, T10_Cmd_Event_Release);
+ cmd->c_t10_cmd = NULL;
+
+ if (cmd->c_scb_extended != NULL)
+ free(cmd->c_scb_extended);
+ iscsi_cmd_free(c, cmd);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+}
+
+/*
+ * []----
+ * | send_datain_pdu -- Send DataIn PDU with READ data
+ * |
+ * | If this is the last read operation and it completed successfully
+ * | the final flag will be set along with the status bit which indicates
+ * | successful completion. This is known as a phase collapse for iSCSI.
+ * |
+ * | NOTE: connection mutex must be held.
+ * []----
+ */
+static void
+send_datain_pdu(iscsi_conn_t *c, t10_cmd_t *t, uint8_t final_flag)
+{
+ iscsi_cmd_t *cmd = (iscsi_cmd_t *)T10_TRANS_ID(t);
+ iscsi_data_rsp_hdr_t rsp;
+
+ assert(pthread_mutex_trylock(&c->c_mutex) != 0);
+ bzero(&rsp, sizeof (rsp));
+
+ rsp.opcode = ISCSI_OP_SCSI_DATA_RSP;
+ rsp.flags = final_flag;
+ rsp.cmd_status = cmd->c_status;
+ rsp.itt = cmd->c_itt;
+ rsp.ttt = ISCSI_RSVD_TASK_TAG;
+ rsp.datasn = htonl(cmd->c_datasn++);
+ rsp.offset = htonl(T10_DATA_OFFSET(t));
+ rsp.lun[1] = (uint8_t)cmd->c_lun;
+
+ hton24(rsp.dlength, T10_DATA_LEN(t));
+
+ rsp.statsn = htonl(c->c_statsn);
+ /*
+ * The statsn is only incremented when the Status bit is set
+ * for a DataIn PDU. This must be done *after* the value
+ * was stored in the PDU.
+ */
+ if (final_flag & ISCSI_FLAG_DATA_STATUS)
+ c->c_statsn++;
+
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ rsp.maxcmdsn = htonl(iscsi_cmd_window(c) + c->c_sess->s_seencmdsn);
+ rsp.expcmdsn = htonl(c->c_sess->s_seencmdsn);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+
+ send_iscsi_pkt(c, (iscsi_hdr_t *)&rsp, T10_DATA(t));
+}
+
+/*
+ * []----
+ * | send_scsi_rsp -- Send SCSI reponse PDU
+ * |
+ * | NOTE: connection mutex must be held.
+ * []----
+ */
+static void
+send_scsi_rsp(iscsi_conn_t *c, t10_cmd_t *t)
+{
+ iscsi_cmd_t *cmd = (iscsi_cmd_t *)T10_TRANS_ID(t);
+ iscsi_scsi_rsp_hdr_t rsp;
+ void *auto_sense = NULL;
+
+ assert(pthread_mutex_trylock(&c->c_mutex) != 0);
+ bzero(&rsp, sizeof (rsp));
+
+ rsp.opcode = ISCSI_OP_SCSI_RSP;
+ rsp.flags = ISCSI_FLAG_FINAL;
+ rsp.itt = cmd->c_itt;
+ rsp.statsn = htonl(c->c_statsn++);
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ rsp.expcmdsn = htonl(c->c_sess->s_seencmdsn + 1);
+ rsp.maxcmdsn = htonl(iscsi_cmd_window(c) + c->c_sess->s_seencmdsn);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+ rsp.cmd_status = T10_CMD_STATUS(t);
+
+ if (rsp.cmd_status) {
+ rsp.response = ISCSI_STATUS_CMD_COMPLETED;
+ rsp.residual_count = htonl(T10_CMD_RESID(t));
+ if (cmd->c_writeop == True)
+ rsp.flags |= ISCSI_FLAG_CMD_OVERFLOW;
+ else
+ rsp.flags |= ISCSI_FLAG_CMD_UNDERFLOW;
+
+ if (T10_SENSE_LEN(t) != 0) {
+ /*
+ * Need to handle autosense stuff. The data should
+ * be store in the d_sense area
+ */
+ auto_sense = (void *)T10_SENSE_DATA(t);
+ hton24(rsp.dlength, T10_SENSE_LEN(t));
+ }
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x LUN%d SCSI Error Status: %d",
+ c->c_num, t->c_lu->l_common->l_num, rsp.cmd_status);
+ } else {
+ rsp.response = ISCSI_STATUS_CMD_COMPLETED;
+ rsp.expdatasn = htonl(cmd->c_datasn);
+ }
+
+ send_iscsi_pkt(c, (iscsi_hdr_t *)&rsp, auto_sense);
+}
+
+static void
+send_async_scsi(iscsi_conn_t *c, int key, int asc, int ascq)
+{
+ iscsi_async_evt_hdr_t a;
+ struct scsi_extended_sense s;
+ char *buf;
+
+ bzero(&a, sizeof (a));
+ bzero(&s, sizeof (s));
+
+ s.es_class = CLASS_EXTENDED_SENSE;
+ s.es_code = CODE_FMT_FIXED_CURRENT;
+ s.es_key = key;
+ s.es_valid = 1;
+ s.es_add_code = asc;
+ s.es_qual_code = ascq;
+
+ if ((buf = malloc(sizeof (s) + 2)) == NULL)
+ return;
+
+ buf[0] = (sizeof (s) >> 8) & 0xff;
+ buf[1] = sizeof (s) & 0xff;
+ bcopy(&s, &buf[2], sizeof (s));
+
+ hton24(a.dlength, sizeof (s) + 2);
+ a.opcode = ISCSI_OP_ASYNC_EVENT;
+ a.flags = ISCSI_FLAG_FINAL;
+ a.async_event = ISCSI_ASYNC_EVENT_SCSI_EVENT;
+ (void) pthread_mutex_lock(&c->c_mutex);
+ a.statsn = htonl(c->c_statsn++);
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ a.expcmdsn = htonl(c->c_sess->s_seencmdsn + 1);
+ a.maxcmdsn = htonl(iscsi_cmd_window(c) + c->c_sess->s_seencmdsn);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO,
+ "CON%x Sending async scsi sense", c->c_num);
+
+ send_iscsi_pkt(c, (iscsi_hdr_t *)&a, buf);
+}
+
+/*
+ * []----
+ * | send_async_logout -- request logout from initiator
+ * []----
+ */
+static void
+send_async_logout(iscsi_conn_t *c)
+{
+ iscsi_async_evt_hdr_t a;
+
+ bzero(&a, sizeof (a));
+
+ a.opcode = ISCSI_OP_ASYNC_EVENT;
+ a.flags = ISCSI_FLAG_FINAL;
+ a.async_event = ISCSI_ASYNC_EVENT_REQUEST_LOGOUT;
+ (void) pthread_mutex_lock(&c->c_mutex);
+ a.statsn = htonl(c->c_statsn++);
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ a.expcmdsn = htonl(c->c_sess->s_seencmdsn + 1);
+ a.maxcmdsn = htonl(iscsi_cmd_window(c) + c->c_sess->s_seencmdsn);
+ a.param3 = htons(ASYNC_LOGOUT_TIMEOUT);
+ a.rsvd4[0] = 0xff;
+ a.rsvd4[1] = 0xff; /* According to the spec these four */
+ a.rsvd4[2] = 0xff; /* values must be 0xff */
+ a.rsvd4[3] = 0xff;
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO,
+ "CON%x Sending async logout request", c->c_num);
+
+ send_iscsi_pkt(c, (iscsi_hdr_t *)&a, 0);
+}
+
+/*
+ * []----
+ * | queue_noop_in -- generate a NOP request and queue it to be sent.
+ * []----
+ */
+static void
+queue_noop_in(iscsi_conn_t *c)
+{
+ iscsi_nop_in_hdr_t *in;
+ iscsi_cmd_t *cmd = iscsi_cmd_alloc(c, ISCSI_OP_NOOP_IN);
+
+ if (cmd == NULL)
+ return;
+
+ in = (iscsi_nop_in_hdr_t *)calloc(sizeof (*in), 1);
+ if (in == NULL)
+ return;
+
+ in->opcode = ISCSI_OP_NOOP_IN | ISCSI_OP_IMMEDIATE;
+ in->flags = ISCSI_FLAG_FINAL;
+ in->ttt = cmd->c_ttt;
+ in->itt = ISCSI_RSVD_TASK_TAG;
+
+ iscsi_cmd_free(c, cmd);
+ queue_message_set(c->c_dataq, 0, msg_send_pkt, (void *)in);
+}
+
+void
+iscsi_capacity_change(char *targ_name, int lun)
+{
+ iscsi_conn_t *conn;
+ extern pthread_mutex_t port_mutex;
+
+ /*
+ * SBC-2 revision 16, section 4.6 -- Initialization
+ * Any time the parameter data returned by the READ CAPACITY(10)
+ * (see 5.10) or the READ CAPACITY(16) command (see 5.11) changes,
+ * the device server should establish a unit attention condition for
+ * the initiator port associated with each I_T nexus.
+ * Since the transport knows which initiators are currently accessing
+ * the target the message will be sent from here.
+ */
+ (void) pthread_mutex_lock(&port_mutex);
+ for (conn = conn_head; conn; conn = conn->c_next) {
+ (void) pthread_mutex_lock(&conn->c_state_mutex);
+ if ((conn->c_state == S5_LOGGED_IN) &&
+ (conn->c_sess->s_type == SessionNormal) &&
+ (strcmp(conn->c_sess->s_t_name, targ_name) == 0)) {
+
+ queue_message_set(conn->c_sessq, 0,
+ msg_lu_capacity_change, (void *)(uintptr_t)lun);
+ }
+ (void) pthread_mutex_unlock(&conn->c_state_mutex);
+ }
+ (void) pthread_mutex_unlock(&port_mutex);
+}
+
+/*
+ * []----
+ * | iscsi_inventory_change -- Send notice to initiator that something changed.
+ * []----
+ */
+void
+iscsi_inventory_change(char *targ_name)
+{
+ iscsi_conn_t *c;
+ extern pthread_mutex_t port_mutex;
+
+ /*
+ * SPC-3 revision 21c, Section 6.21 REPORT_LUNS
+ * If the logical unit inventory changes for any reason
+ * (e.g. completion of initialization, removal of a logical unit,
+ * or create of a logical unit), then the device server shall generate
+ * a unit attention condition for all I_T nexuses, with the additional
+ * sense code set to REPORTED LUNS DATA HAS CHANGED.
+ */
+ (void) pthread_mutex_lock(&port_mutex);
+ for (c = conn_head; c; c = c->c_next) {
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ if ((c->c_state == S5_LOGGED_IN) &&
+ (c->c_sess->s_type == SessionNormal) &&
+ (strcmp(c->c_sess->s_t_name, targ_name) == 0)) {
+
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO,
+ "CON%x Sending Inventory change out", c->c_num);
+ /*
+ * Send a message indicating that the logical unit
+ * inventory has changed. 1) This message is sent
+ * to the session level which will pass it onto
+ * the SAM layer causing a UNIT_ATTENTION during
+ * the next command. 2) This message is also sent
+ * directly to the outgoing side of the connection
+ * which will send an asynchronous event message
+ * to the initiator.
+ */
+ queue_message_set(c->c_sessq, 0,
+ msg_targ_inventory_change, 0);
+ queue_message_set(c->c_dataq, 0,
+ msg_targ_inventory_change, 0);
+ }
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+ }
+ (void) pthread_mutex_unlock(&port_mutex);
+}
+
+/*
+ * []----
+ * | state_to_str -- return string for given state, used for debug
+ * []----
+ */
+static char *
+state_to_str(iscsi_state_t s)
+{
+ switch (s) {
+ case S1_FREE: return ("FREE");
+ case S3_XPT_UP: return ("XPT_UP");
+ case S4_IN_LOGIN: return ("IN_LOGIN");
+ case S5_LOGGED_IN: return ("LOGGED_IN");
+ case S6_IN_LOGOUT: return ("IN_LOGOUT");
+ case S7_LOGOUT_REQUESTED: return ("LOGOUT_REQUEST");
+ case S8_CLEANUP_WAIT: return ("CLEANUP_WAIT");
+ }
+ return ("Unknown");
+}
+
+/*
+ * []----
+ * | event_to_str -- return string for given event, used for debug
+ * []----
+ */
+static char *
+event_to_str(iscsi_transition_t t)
+{
+ switch (t) {
+ case T3: return ("T3");
+ case T4: return ("T4");
+ case T5: return ("T5");
+ case T6: return ("T6");
+ case T7: return ("T7");
+ case T8: return ("T8");
+ case T9: return ("T9");
+ case T10: return ("T10");
+ case T11: return ("T11");
+ case T12: return ("T12");
+ case T13: return ("T13");
+ case T15: return ("T15");
+ case T16: return ("T16");
+ case T17: return ("T17");
+ case T18: return ("T18");
+ }
+ return ("Unknown");
+}
+
+/*
+ * []----
+ * | conn_state -- Attempt to change from one state to the next
+ * []----
+ */
+void
+conn_state(iscsi_conn_t *c, iscsi_transition_t t)
+{
+ iscsi_state_t old_state = c->c_state;
+
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ switch (c->c_state) {
+ case S1_FREE:
+ switch (t) {
+ case T3:
+ c->c_state = S3_XPT_UP;
+ break;
+
+ }
+ break;
+
+ case S3_XPT_UP:
+ switch (t) {
+ case T6:
+ c->c_state = S1_FREE;
+ break;
+
+ case T4:
+ c->c_statsn = 1;
+ c->c_state = S4_IN_LOGIN;
+ break;
+
+ }
+ break;
+
+ case S4_IN_LOGIN:
+ switch (t) {
+ case T7:
+ /*
+ * When there's a session a shutdown messages is
+ * sent giving the opportunity to free resources
+ * used by the session code and STE. Very early
+ * on a session might not exist when a failure
+ * occurs like getting a bad opcode. The connection
+ * process routine is going to sit around waiting
+ * for a message which will never come so fake
+ * a completion message here if there's no session.
+ */
+ if (c->c_sessq == NULL) {
+ queue_message_set(c->c_dataq, 0,
+ msg_shutdown_rsp, (void *)True);
+ } else {
+ queue_message_set(c->c_sessq, 0, msg_shutdown,
+ (void *)c);
+ }
+ c->c_state = S1_FREE;
+ break;
+ case T5:
+ c->c_state = S5_LOGGED_IN;
+ if (strncmp(c->c_sess->s_i_name,
+ "iqn.1991-05.com.microsoft",
+ strlen("iqn.1991-05.com.microsoft")) == 0)
+ c->c_sess->s_cmdsn++;
+ break;
+ }
+ break;
+
+ case S5_LOGGED_IN:
+ switch (t) {
+ case T8:
+ queue_message_set(c->c_sessq, 0, msg_shutdown,
+ (void *)c);
+ c->c_state = S1_FREE;
+
+ break;
+ case T9:
+ queue_message_set(c->c_sessq, 0, msg_shutdown,
+ (void *)c);
+ c->c_state = S6_IN_LOGOUT;
+ break;
+ case T11:
+ c->c_state = S7_LOGOUT_REQUESTED;
+ send_async_logout(c);
+ break;
+ case T15:
+ c->c_state = S8_CLEANUP_WAIT;
+ break;
+ }
+ break;
+
+ case S6_IN_LOGOUT:
+ switch (t) {
+ case T13:
+ if (c->c_last_pkg) {
+ send_iscsi_pkt(c, c->c_last_pkg, NULL);
+ free(c->c_last_pkg);
+ }
+ c->c_state = S1_FREE;
+ break;
+ case T17:
+ c->c_state = S8_CLEANUP_WAIT;
+ break;
+ }
+ break;
+
+ case S7_LOGOUT_REQUESTED:
+ switch (t) {
+ case T18:
+ queue_message_set(c->c_sessq, 0, msg_shutdown,
+ (void *)c);
+ c->c_state = S1_FREE;
+ break;
+ case T10:
+ queue_message_set(c->c_sessq, 0, msg_shutdown,
+ (void *)c);
+ c->c_state = S6_IN_LOGOUT;
+ break;
+ case T12:
+ c->c_state = S7_LOGOUT_REQUESTED;
+ break;
+ case T16:
+ c->c_state = S8_CLEANUP_WAIT;
+ break;
+ }
+ break;
+
+ case S8_CLEANUP_WAIT:
+ default:
+ break;
+ }
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO, "CON%x ---- %s(%s) -> %s",
+ c->c_num, state_to_str(old_state), event_to_str(t),
+ state_to_str(c->c_state));
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+}
+
+/*
+ * []----
+ * | send_iscsi_pkt -- output PDU header, data, and alignment bytes if needed
+ * |
+ * | NOTE: This routine may be called with the connection mutex held. This
+ * | is done to prevent a state change being made to a command pointer. This
+ * | routine is currently written so that it doesn't need to have this mutex
+ * | held or calls a routine which needs it to be held.
+ * []----
+ */
+void
+send_iscsi_pkt(iscsi_conn_t *c, iscsi_hdr_t *h, char *opt_text)
+{
+ int dlen = ntoh24(h->dlength),
+ pad_len;
+ uint32_t crc;
+
+ /*
+ * Sanity check. If there's a length in the header we must
+ * have text to send or if the length is zero there better not
+ * be any text.
+ */
+ if (((dlen == 0) && (opt_text != NULL)) ||
+ ((dlen != 0) && (opt_text == NULL)))
+ return;
+
+ if (write(c->c_fd, h, sizeof (*h)) < 0) {
+ if (errno == EPIPE) {
+
+ /*
+ * For some reason the initiator has closed the
+ * socket on us. This is most likely caused because
+ * of some network related condition
+ * (e.g. broken cable). We'll shutdown our side and
+ * wait for a reconnect from the initiator.
+ */
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x iscsi_pkt -- initiator closed socket",
+ c->c_num);
+ } else {
+
+ /*
+ * This is not good.
+ */
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x iscsi_pkt write failed, errno %d",
+ c->c_num, errno);
+ }
+ conn_state(c, T8);
+ return;
+ }
+
+ /*
+ * Only start generating digest values once we've completed the
+ * login phase. If the state is not checked here and during login
+ * header or data digests have been enabled we would generate
+ * a digest value during the Login RSP PDU which the initiator
+ * is not expecting.
+ */
+ if ((c->c_state == S5_LOGGED_IN) && (c->c_header_digest == True)) {
+ crc = iscsi_crc32c((void *)h, sizeof (*h));
+ if (write(c->c_fd, &crc, sizeof (crc)) != sizeof (crc)) {
+ conn_state(c, T8);
+ return;
+ }
+ }
+
+ if (dlen) {
+ if (write(c->c_fd, opt_text, dlen) != dlen) {
+ conn_state(c, T8);
+ return;
+ }
+
+ /*
+ * Find out how many pad bytes we need to send out.
+ */
+ pad_len = (ISCSI_PAD_WORD_LEN -
+ (dlen & (ISCSI_PAD_WORD_LEN - 1))) &
+ (ISCSI_PAD_WORD_LEN - 1);
+ if (pad_len) {
+ if (write(c->c_fd, pad_text, pad_len) != pad_len) {
+ conn_state(c, T8);
+ return;
+ }
+ }
+
+ if ((c->c_state == S5_LOGGED_IN) &&
+ (c->c_data_digest == True)) {
+
+ crc = iscsi_crc32c((void *)opt_text,
+ (unsigned long)dlen);
+
+ /*
+ * Include the pad information in the calculation of
+ * the CRC for the data.
+ */
+ crc = iscsi_crc32c_continued((void *)pad_text,
+ (unsigned long)pad_len, crc);
+
+ if (write(c->c_fd, &crc, sizeof (crc)) !=
+ sizeof (crc)) {
+ conn_state(c, T8);
+ return;
+ }
+ }
+ }
+#ifdef FULL_DEBUG
+ if (dlen != 0) {
+ queue_prt(c->c_mgmtq, Q_CONN_IO,
+ "CON%x Response(0x%x), Data: len=%d addr=0x%llx",
+ c->c_num, h->opcode, dlen, opt_text);
+ }
+#endif
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_conn.h b/usr/src/cmd/iscsi/iscsitgtd/iscsi_conn.h
new file mode 100644
index 0000000000..851757200d
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_conn.h
@@ -0,0 +1,216 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TARGET_CONN_H
+#define _TARGET_CONN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Block comment which describes the contents of this file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/iscsi_protocol.h>
+#include <sys/socket.h>
+#include "queue.h"
+#include "iscsi_sess.h"
+#include "iscsi_cmd.h"
+
+#define LBUFSIZE 80
+
+#define TARGET_LOCATION "targets"
+/*
+ * Currently I'm having some problems with network reads/write when the
+ * data size is larger than 8k. To work around this problem I set the
+ * various negotiation parameters during login to limit things to 8k.
+ */
+
+#define NETWORK_SNDRCV 65536
+#define NETWORK_SNDRCV_STR "65536"
+
+typedef enum iscsi_state {
+ S1_FREE,
+ /* S2_XPT_WAIT, Not possible for target */
+ S3_XPT_UP,
+ S4_IN_LOGIN,
+ S5_LOGGED_IN,
+ S6_IN_LOGOUT,
+ S7_LOGOUT_REQUESTED,
+ S8_CLEANUP_WAIT
+} iscsi_state_t;
+
+typedef enum iscsi_transition {
+ T3, T4, T5, T6, T7, T8,
+ T9, T10, T11, T12, T13, T15,
+ T16, T17, T18
+} iscsi_transition_t;
+
+/*
+ * When grabbing mutex's make sure to grab c_mutex before c_mutex_state
+ * if you need to grab both.
+ */
+typedef struct iscsi_conn {
+ int c_fd;
+
+ /*
+ * This is a linked list of all connections. Not just the connections
+ * associated with a particular session.
+ */
+ struct iscsi_conn *c_next,
+ *c_prev;
+
+ target_queue_t *c_mgmtq;
+
+ /*
+ * Time as reported by time(2) when this connection was started.
+ */
+ time_t c_up_at;
+
+ /*
+ * This queue is used to accept notification that incoming packets
+ * are available and command completion status from Session.
+ */
+ target_queue_t *c_dataq;
+
+ /*
+ * Messages are sent to Session, and from there onto STE, using
+ * this queue.
+ */
+ target_queue_t *c_sessq;
+
+ iscsi_sess_t *c_sess;
+
+ pthread_mutex_t c_state_mutex;
+ iscsi_state_t c_state;
+
+ /*
+ * Protected by c_mutex
+ */
+ int c_statsn;
+
+ int c_cid;
+
+ /*
+ * Pointer to data buffer used to store text messages which have
+ * the 'C' bit set. Since the text data separates name/value pairs
+ * with a '\0' strlen can't be used to determine the amount of space
+ * used so we keep the length in c_text_len;
+ */
+ char *c_text_area;
+ int c_text_len;
+ int c_text_sent;
+
+ sema_t c_datain;
+ pthread_t c_thr_id_poller,
+ c_thr_id_process;
+
+ pthread_mutex_t c_mutex;
+ iscsi_cmd_t *c_cmd_head;
+ iscsi_cmd_t *c_cmd_tail;
+
+ struct sockaddr_storage c_initiator_sockaddr;
+ struct sockaddr_storage c_target_sockaddr;
+
+ int c_num;
+
+ int c_auth_pass : 1;
+
+ int c_cmds_active;
+
+ /*
+ * A performance issue has been found when the backing store
+ * is UFS. Because of the indirect blocks used by UFS large files
+ * (many GBs in size) perform poorly. This in turn can cause the
+ * initiator to issue commands which don't complete in time. So,
+ * we'll monitor the completion times for commands if if it's
+ * increasing the command window will be reduced.
+ * The avg_sum is in nanoseconds. This will wrap once every 584
+ * years.
+ */
+ uint64_t c_cmds_avg_cnt;
+ hrtime_t c_cmds_avg_sum;
+
+ /*
+ * During an orderly shutdown the logout response is created when
+ * we receive the logout request. We must however wait for all I/O
+ * to complete before processing the data else we'll loose data
+ * which the initiator believes was successfully transferred.
+ * Once the STE and sessions have closed they will send a shutdown
+ * complete message. At that point the transmit side of the connection
+ * will set the state to T13 which pushes this message out.
+ * Unfortunately we need information from the Logout Request PDU
+ * to create the Logout Response PDU. Otherwise the response could
+ * be generated on the fly in the T13 state handler. By creating
+ * the response PDU and saving a pointer gives us some flexibility
+ * in the future if the final outgoing packet needs to change.
+ * Otherwise, storing that one bit of information from the request
+ * PDU might become dated.
+ */
+ iscsi_hdr_t *c_last_pkg;
+
+ /*
+ * Connection parameters
+ */
+ Boolean_t c_header_digest,
+ c_data_digest,
+ c_ifmarker,
+ c_ofmarker,
+ c_initialR2T,
+ c_immediate_data,
+ c_data_pdu_in_order,
+ c_data_sequence_in_order;
+ int c_tpgt,
+ c_maxcmdsn,
+ c_max_recv_data,
+ c_default_time_2_wait,
+ c_default_time_2_retain,
+ c_erl,
+ c_max_burst_len,
+ c_first_burst_len,
+ c_max_outstanding_r2t,
+ c_max_connections;
+ char *c_targ_alias,
+ *auth_text;
+ int auth_text_length;
+
+} iscsi_conn_t;
+
+void *conn_process(void *v);
+void conn_state(iscsi_conn_t *c, iscsi_transition_t t);
+void send_iscsi_pkt(iscsi_conn_t *c, iscsi_hdr_t *h, char *opt_text);
+int read_retry(int fd, char *buf, int count);
+void iscsi_inventory_change(char *targ_name);
+void iscsi_capacity_change(char *targ_name, int lun);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TARGET_CONN_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_crc.c b/usr/src/cmd/iscsi/iscsitgtd/iscsi_crc.c
new file mode 100644
index 0000000000..417839d918
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_crc.c
@@ -0,0 +1,178 @@
+/*
+ * 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 2000 by Cisco Systems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * This file contains CRC-32C code use to verify
+ * iSCSI HeaderDigests and DataDigests.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h> /* standard types */
+#include <sys/iscsi_protocol.h> /* contains prototypes */
+
+/*
+ * This is the CRC-32C table
+ * Generated with:
+ * width = 32 bits
+ * poly = 0x1EDC6F41
+ * reflect input bytes = true
+ * reflect output bytes = true
+ */
+
+uint32_t iscsi_crc32c_table[256] =
+{
+ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
+ 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
+ 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
+ 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
+ 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
+ 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
+ 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
+ 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
+ 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
+ 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
+ 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
+ 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
+ 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
+ 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
+ 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
+ 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
+ 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
+ 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
+ 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
+ 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
+ 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
+ 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
+ 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
+ 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
+ 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
+ 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
+ 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
+ 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
+ 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
+ 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
+ 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
+ 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
+ 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
+ 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
+ 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
+ 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
+ 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
+ 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
+ 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
+ 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
+ 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
+ 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
+ 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
+ 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
+ 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
+ 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
+ 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
+ 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
+ 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
+ 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
+ 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
+ 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
+ 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
+ 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
+ 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
+ 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
+ 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
+ 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
+ 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
+ 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
+ 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
+ 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
+ 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
+ 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
+};
+
+/*
+ * iscsi_crc32c - Steps through buffer one byte at at time, calculates
+ * reflected crc using table.
+ */
+uint32_t
+iscsi_crc32c(void *address, unsigned long length)
+{
+ uint8_t *buffer = address;
+ uint32_t crc = 0xffffffff, result;
+#ifdef _BIG_ENDIAN
+ uint8_t byte0, byte1, byte2, byte3;
+#endif
+
+ while (length--) {
+ crc = iscsi_crc32c_table[(crc ^ *buffer++) & 0xFFL] ^
+ (crc >> 8);
+ }
+ result = crc ^ 0xffffffff;
+
+#ifdef _BIG_ENDIAN
+ byte0 = (uint8_t)(result & 0xFF);
+ byte1 = (uint8_t)((result >> 8) & 0xFF);
+ byte2 = (uint8_t)((result >> 16) & 0xFF);
+ byte3 = (uint8_t)((result >> 24) & 0xFF);
+ result = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
+#endif /* _BIG_ENDIAN */
+
+ return (result);
+}
+
+
+/*
+ * iscsi_crc32c_continued - Continues stepping through buffer one
+ * byte at at time, calculates reflected crc using table.
+ */
+uint32_t
+iscsi_crc32c_continued(void *address, unsigned long length, uint32_t crc)
+{
+ uint8_t *buffer = address;
+ uint32_t result;
+#ifdef _BIG_ENDIAN
+ uint8_t byte0, byte1, byte2, byte3;
+#endif
+
+#ifdef _BIG_ENDIAN
+ byte0 = (uint8_t)((crc >> 24) & 0xFF);
+ byte1 = (uint8_t)((crc >> 16) & 0xFF);
+ byte2 = (uint8_t)((crc >> 8) & 0xFF);
+ byte3 = (uint8_t)(crc & 0xFF);
+ crc = ((byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0);
+#endif
+
+ crc = crc ^ 0xffffffff;
+ while (length--) {
+ crc = iscsi_crc32c_table[(crc ^ *buffer++) & 0xFFL] ^
+ (crc >> 8);
+ }
+ result = crc ^ 0xffffffff;
+
+#ifdef _BIG_ENDIAN
+ byte0 = (uint8_t)(result & 0xFF);
+ byte1 = (uint8_t)((result >> 8) & 0xFF);
+ byte2 = (uint8_t)((result >> 16) & 0xFF);
+ byte3 = (uint8_t)((result >> 24) & 0xFF);
+ result = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
+#endif
+ return (result);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_ffp.c b/usr/src/cmd/iscsi/iscsitgtd/iscsi_ffp.c
new file mode 100644
index 0000000000..3eaaea7fde
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_ffp.c
@@ -0,0 +1,953 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains methods to handle the iSCSI Full Feature Phase aspects
+ * of the protocol.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <unistd.h>
+#include <poll.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <utility.h>
+#include <netinet/in.h>
+#include <inttypes.h>
+#include <sys/iscsi_protocol.h>
+
+#include "iscsi_ffp.h"
+#include "iscsi_cmd.h"
+#include "t10_spc.h"
+#include "utility.h"
+
+static Boolean_t handle_text_msg(iscsi_conn_t *, iscsi_hdr_t *, char *, int);
+static Boolean_t handle_logout_msg(iscsi_conn_t *, iscsi_hdr_t *, char *, int);
+static Boolean_t handle_scsi_cmd(iscsi_conn_t *, iscsi_hdr_t *, char *, int);
+static Boolean_t handle_noop_cmd(iscsi_conn_t *, iscsi_hdr_t *, char *, int);
+static Boolean_t handle_scsi_data(iscsi_conn_t *, iscsi_hdr_t *, char *, int);
+static Boolean_t handle_task_mgt(iscsi_conn_t *, iscsi_hdr_t *, char *, int);
+static Boolean_t dataout_delayed(iscsi_cmd_t *cmd, msg_type_t type);
+void dataout_callback(t10_cmd_t *t, char *data, size_t *xfer);
+
+Boolean_t
+iscsi_full_feature(iscsi_conn_t *c)
+{
+ iscsi_hdr_t h;
+ Boolean_t rval = False;
+ char debug[128],
+ *ahs = NULL;
+ int cc,
+ ahslen;
+
+ if ((cc = read_retry(c->c_fd, (char *)&h,
+ sizeof (h))) != sizeof (h)) {
+ if (errno == ECONNRESET) {
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x full_feature -- initiator reset socket",
+ c->c_num);
+ } else {
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x full_feature(got-%d, expect-%d), errno=%d",
+ c->c_num, cc, sizeof (h), errno);
+ }
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ conn_state(c, T8);
+ return (False);
+ }
+
+ /*
+ * Look to see if there's an Additional Header Segment available.
+ * If so, read it in.
+ */
+ if ((ahslen = (h.hlength * sizeof (uint32_t))) != 0) {
+ if ((ahs = malloc(ahslen)) == NULL)
+ return (False);
+ if (read_retry(c->c_fd, ahs, ahslen) != ahslen) {
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Failed to read in AHS", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ return (False);
+ }
+ }
+
+ if ((c->c_state == S5_LOGGED_IN) && (c->c_header_digest == True)) {
+ uint32_t crc_actual,
+ crc_calculated;
+
+ (void) read_retry(c->c_fd, (char *)&crc_actual,
+ sizeof (crc_actual));
+ crc_calculated = iscsi_crc32c((void *)&h, sizeof (h));
+ if (ahslen)
+ crc_calculated = iscsi_crc32c_continued(ahs,
+ ahslen, crc_calculated);
+ if (crc_actual != crc_calculated) {
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x CRC error: actual 0x%x v. calc 0x%x",
+ c->c_num, crc_actual, crc_calculated);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ return (False);
+ }
+ }
+
+ if (c->c_sess->s_type == SessionDiscovery) {
+ switch (h.opcode & ISCSI_OPCODE_MASK) {
+ default:
+ /*
+ * Need to handle the error case here.
+ */
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Wrong opcode for Discovery, %d",
+ c->c_num, h.opcode & ISCSI_OPCODE_MASK);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ rval = False;
+ break;
+
+ case ISCSI_OP_LOGOUT_CMD:
+ /*
+ * This will transition from S5_LOGGED_IN
+ * to S6_IN_LOGOUT to S1_FREE;
+ */
+ rval = handle_logout_msg(c, &h, ahs, ahslen);
+ break;
+
+ case ISCSI_OP_TEXT_CMD:
+ rval = handle_text_msg(c, &h, ahs, ahslen);
+ break;
+ }
+ } else {
+ iscsi_cmd_remove(c, htonl(h.expstatsn));
+ switch (h.opcode & ISCSI_OPCODE_MASK) {
+ case ISCSI_OP_NOOP_OUT:
+ rval = handle_noop_cmd(c, &h, ahs, ahslen);
+ break;
+
+ case ISCSI_OP_SCSI_CMD:
+ rval = handle_scsi_cmd(c, &h, ahs, ahslen);
+ break;
+
+ case ISCSI_OP_SCSI_TASK_MGT_MSG:
+ rval = handle_task_mgt(c, &h, ahs, ahslen);
+ break;
+
+ case ISCSI_OP_LOGIN_CMD:
+ /*
+ * This is an illegal state transition. Should
+ * we drop the connection?
+ */
+ break;
+
+ case ISCSI_OP_TEXT_CMD:
+ rval = handle_text_msg(c, &h, ahs, ahslen);
+ break;
+
+ case ISCSI_OP_SCSI_DATA:
+ rval = handle_scsi_data(c, &h, ahs, ahslen);
+ break;
+
+ case ISCSI_OP_LOGOUT_CMD:
+ /*
+ * This will transition from S5_LOGGED_IN
+ * to S6_IN_LOGOUT.
+ */
+ rval = handle_logout_msg(c, &h, ahs, ahslen);
+ break;
+
+ case ISCSI_OP_SNACK_CMD:
+ default:
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Opcode: %d not handled",
+ c->c_num, h.opcode & ISCSI_OPCODE_MASK);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ conn_state(c, T8);
+ rval = True;
+ break;
+ }
+ }
+
+ if (ahs != NULL)
+ free(ahs);
+ return (rval);
+}
+
+/*ARGSUSED*/
+static Boolean_t
+handle_task_mgt(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen)
+{
+ iscsi_scsi_task_mgt_hdr_t *hp = (iscsi_scsi_task_mgt_hdr_t *)p;
+ iscsi_scsi_task_mgt_rsp_hdr_t *rsp;
+ iscsi_cmd_t *cmd;
+ uint32_t lun;
+ Boolean_t lu_reset = False;
+
+ rsp = (iscsi_scsi_task_mgt_rsp_hdr_t *)calloc(sizeof (*rsp), 1);
+ if (rsp == NULL)
+ return (False);
+
+ if (spc_decode_lu_addr(&hp->lun[0], 8, &lun) == False)
+ return (False);
+
+ rsp->opcode = ISCSI_OP_SCSI_TASK_MGT_RSP;
+ rsp->flags = ISCSI_FLAG_FINAL;
+ rsp->itt = hp->itt;
+
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ if (ntohl(hp->cmdsn) > c->c_sess->s_seencmdsn)
+ c->c_sess->s_seencmdsn = ntohl(hp->cmdsn);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO,
+ "CON%x PDU(Task Mgt): %s, cmdsn 0x%x",
+ c->c_num,
+ task_to_str(hp->function & ISCSI_FLAG_TASK_MGMT_FUNCTION_MASK),
+ ntohl(hp->cmdsn));
+
+ switch (hp->function & ISCSI_FLAG_TASK_MGMT_FUNCTION_MASK) {
+ case ISCSI_TM_FUNC_ABORT_TASK:
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO,
+ "CON%x Abort ITT 0x%x", c->c_num, hp->rtt);
+ if ((cmd = iscsi_cmd_find(c, hp->rtt, FindITT)) == NULL) {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x Invalid AbortTask rtt 0x%x\n",
+ c->c_num, hp->rtt);
+ rsp->response = SCSI_TCP_TM_RESP_NO_TASK;
+ } else {
+ iscsi_cmd_cancel(c, cmd);
+ rsp->response = SCSI_TCP_TM_RESP_COMPLETE;
+ }
+ break;
+
+ case ISCSI_TM_FUNC_ABORT_TASK_SET:
+ /* ---- This is actually "Function not support" ---- */
+ rsp->response = SCSI_TCP_TM_RESP_IN_PRGRESS;
+ break;
+
+ case ISCSI_TM_FUNC_CLEAR_ACA:
+ /* ---- This is actually "Function not support" ---- */
+ rsp->response = SCSI_TCP_TM_RESP_IN_PRGRESS;
+ break;
+
+ case ISCSI_TM_FUNC_CLEAR_TASK_SET:
+ /* ---- This is actually "Function not support" ---- */
+ rsp->response = SCSI_TCP_TM_RESP_IN_PRGRESS;
+ break;
+
+ case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
+ lu_reset = True;
+ /*FALLTHRU*/
+ case ISCSI_TM_FUNC_TARGET_WARM_RESET:
+ (void) pthread_mutex_lock(&c->c_mutex);
+ for (cmd = c->c_cmd_head; cmd; cmd = cmd->c_next) {
+ if (((hp->function &
+ ISCSI_FLAG_TASK_MGMT_FUNCTION_MASK) ==
+ ISCSI_TM_FUNC_TARGET_WARM_RESET) ||
+ (lun == cmd->c_lun)) {
+
+ /*
+ * Can't call cmd_cancel() here because it
+ * will attempt to grab the lock which we
+ * already have held.
+ */
+ if (cmd->c_state == CmdAlloc) {
+ cmd->c_state = CmdCanceled;
+
+ /*
+ * It's possible that the session
+ * queue hasn't had a chance to run
+ * yet and get a T10 command structure.
+ */
+ if (cmd->c_t10_cmd != NULL) {
+ t10_cmd_state(cmd->c_t10_cmd,
+ T10_Cmd_Event_Canceled);
+ }
+ }
+
+ }
+ }
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ if (lu_reset == True)
+ queue_message_set(c->c_sessq, 0, msg_reset_lu,
+ (void *)(uintptr_t)lun);
+ else
+ queue_message_set(c->c_sessq, 0, msg_reset_targ, 0);
+ rsp->response = SCSI_TCP_TM_RESP_COMPLETE;
+ break;
+
+ case ISCSI_TM_FUNC_TARGET_COLD_RESET:
+ /*
+ * According to the specification a cold reset should
+ * close *all* connections on the target, not just those
+ * for this current session.
+ */
+ queue_message_set(c->c_sessq, 0, msg_reset_targ, (void *)1);
+ conn_state(c, T8);
+ break;
+
+ case ISCSI_TM_FUNC_TASK_REASSIGN:
+ default:
+ /* ---- This is actually "Function not support" ---- */
+ rsp->response = SCSI_TCP_TM_RESP_IN_PRGRESS;
+ break;
+ }
+
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ if (c->c_state == S5_LOGGED_IN)
+ queue_message_set(c->c_dataq,
+ hp->opcode & ISCSI_OP_IMMEDIATE ? Q_HIGH : 0,
+ msg_send_pkt, rsp);
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+ return (True);
+}
+
+/*ARGSUSED*/
+static Boolean_t
+handle_noop_cmd(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen)
+{
+ iscsi_nop_out_hdr_t *hp = (iscsi_nop_out_hdr_t *)p;
+ iscsi_nop_in_hdr_t *in;
+
+ in = (iscsi_nop_in_hdr_t *)calloc(sizeof (*in), 1);
+ if (in == NULL) {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x NopIn -- failed to malloc space for header",
+ c->c_num);
+ return (False);
+ }
+
+ /*
+ * Just an answer to our ping
+ */
+ if (hp->ttt != ISCSI_RSVD_TASK_TAG) {
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO,
+ "CON%x NopIn -- answer to our call", c->c_num);
+ return (True);
+ }
+
+ in->opcode = ISCSI_OP_NOOP_IN;
+ in->flags = ISCSI_FLAG_FINAL;
+ /*
+ * Need to handle possible data associated with NOP-Out
+ */
+ bcopy(hp->lun, in->lun, 8);
+ in->itt = hp->itt;
+ in->ttt = ISCSI_RSVD_TASK_TAG;
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ if (ntohl(hp->cmdsn) > c->c_sess->s_seencmdsn)
+ c->c_sess->s_seencmdsn = ntohl(hp->cmdsn);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ if (c->c_state == S5_LOGGED_IN)
+ queue_message_set(c->c_dataq,
+ hp->opcode & ISCSI_OP_IMMEDIATE ? Q_HIGH : 0,
+ msg_send_pkt, in);
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+ return (True);
+}
+
+/*ARGSUSED*/
+static Boolean_t
+handle_scsi_data(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen)
+{
+ iscsi_data_hdr_t *hp = (iscsi_data_hdr_t *)p;
+ int dlen = ntoh24(hp->dlength);
+ iscsi_cmd_t *cmd;
+
+ if ((cmd = iscsi_cmd_find(c, hp->ttt, FindTTT)) == NULL) {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x failed to find ttt 0x%x", c->c_num, hp->ttt);
+ /*
+ * Need to handle error case.
+ */
+ return (False);
+ }
+ cmd->c_opcode = hp->opcode & ISCSI_OPCODE_MASK;
+
+ /*
+ * assert(cmd->c_lun == hp->lun[1]);
+ * Previously this check was done, but is caused a problem with
+ * the RedHat initiator. There was a discussion on the IPS alias
+ * around this very topic. Even though section 10.7.4 states:
+ * "If the Target Transfer Tag is provided, then the LUN field
+ * MUST hold a valid value and be consistent with whatever was
+ * specified with the command; otherwise, the LUN field is
+ * reserved."
+ * Everyone agreed though that for a DataOut command the LUN field
+ * wasn't required to be valid because the TTT gives the Target
+ * enough information to complete the command.
+ */
+ assert(cmd->c_allegiance == c);
+ assert(cmd->c_itt == hp->itt);
+
+ cmd->c_offset_out = ntohl(hp->offset);
+ cmd->c_data_len = dlen;
+ (void) pthread_mutex_lock(&c->c_mutex);
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ if (c->c_state == S5_LOGGED_IN) {
+ if (cmd->c_state != CmdCanceled) {
+ t10_cmd_state(cmd->c_t10_cmd,
+ T10_Cmd_Event_DataIn_Recv);
+ }
+ }
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+#ifdef FULL_DEBUG
+ queue_prt(c->c_mgmtq, Q_CONN_IO,
+ "CON%x PDU(DataOut) TTT 0x%x, offset=0x%x, len=0x%x",
+ c->c_num, cmd->c_ttt, cmd->c_t10_cmd->c_offset, dlen);
+#endif
+
+ return (dataout_delayed(cmd, msg_cmd_data_out));
+}
+
+static Boolean_t
+handle_scsi_cmd(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen)
+{
+ iscsi_scsi_cmd_hdr_t *hp = (iscsi_scsi_cmd_hdr_t *)p;
+ int dlen = ntoh24(hp->dlength);
+ iscsi_cmd_t *cmd;
+
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ if (ntohl(hp->cmdsn) > c->c_sess->s_seencmdsn)
+ c->c_sess->s_seencmdsn = ntohl(hp->cmdsn);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+
+ if ((cmd = iscsi_cmd_alloc(c, hp->opcode & ISCSI_OPCODE_MASK)) == NULL)
+ return (False);
+
+ bcopy(hp->scb, cmd->c_scb_default, sizeof (cmd->c_scb_default));
+ cmd->c_scb = cmd->c_scb_default;
+ cmd->c_scb_len = sizeof (cmd->c_scb_default);
+ cmd->c_data_len = dlen;
+
+ if (ahslen) {
+
+ /*
+ * Additional Header Section ----
+ *
+ * For Object Storage Devices the SCB is quite large. On
+ * the order of 140 bytes which means the data must be
+ * found in the AHS.
+ */
+ uint16_t hslen,
+ next_seg;
+ uint8_t hstyp;
+
+ do {
+ /*
+ * Find this header segment's length and type
+ */
+ bcopy(ahs, &hslen, sizeof (hslen));
+ hslen = ntohs(hslen);
+ hstyp = ahs[2];
+
+ switch (hstyp) {
+ /* ---- Extended CDB ---- */
+ case 1:
+ /*
+ * The hslen accounts for the reserved
+ * data byte in the segment. So the first
+ * sixteen bytes are in hp->scb with the
+ * remainder here. By only adding 15 bytes
+ * we allocate the correct amount of space
+ */
+ cmd->c_scb_extended = malloc(hslen + 15);
+ cmd->c_scb_len = hslen + 15;
+ if (cmd->c_scb_extended == NULL)
+ return (False);
+
+ /*
+ * First 16 bytes of extended SCB are
+ * found in the normal location.
+ */
+ bcopy(hp->scb, cmd->c_scb_extended, 16);
+ bcopy(&ahs[4], &cmd->c_scb_extended[16],
+ hslen - 16);
+ cmd->c_scb = cmd->c_scb_extended;
+ break;
+
+ /* ---- Expected bidirectional read data len ---- */
+ case 2:
+ /*
+ * We shouldn't need this since we're
+ * not prealloc'ing resources. If that should
+ * change or the need for error checking
+ * here's the spot to locate the data.
+ */
+ break;
+ }
+
+ /*
+ * hslen contains the effective length in bytes of
+ * segment, excluding type and length (not including
+ * padding). Each segment is padded to a 4 byte
+ * boundary.
+ */
+ next_seg = ((hslen + sizeof (hslen) +
+ sizeof (hstyp) + 3) & ~3);
+ ahs += next_seg;
+ ahslen -= next_seg;
+
+ } while (ahslen);
+ }
+
+ /*
+ * XXX Need to handle error case better.
+ */
+ if (spc_decode_lu_addr(&hp->lun[0], sizeof (hp->lun), &cmd->c_lun) ==
+ False) {
+ return (False);
+ }
+
+ cmd->c_itt = hp->itt;
+ cmd->c_cmdsn = ntohl(hp->cmdsn);
+ cmd->c_writeop = hp->flags & ISCSI_FLAG_CMD_WRITE ?
+ True : False;
+
+#ifdef FULL_DEBUG
+ queue_prt(c->c_mgmtq, Q_CONN_IO,
+ "CON%x PDU(SCSI Cmd) CmdSN 0x%x ITT 0x%x TTT 0x%x LUN[%02x] "
+ "SCSI Op", c->c_num, cmd->c_cmdsn, cmd->c_itt, cmd->c_ttt,
+ cmd->c_lun);
+#endif
+ if (dlen && (hp->flags & ISCSI_FLAG_CMD_WRITE)) {
+ /*
+ * NOTE: This should only occur if ImmediateData==Yes.
+ * We can handle this even if the initiator violates
+ * the specification so no need to worry. Use the rule
+ * of "Be strict in what is sent, but lenient in what
+ * is accepted."
+ */
+ return (dataout_delayed(cmd, msg_cmd_send));
+ } else {
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ if (c->c_state == S5_LOGGED_IN)
+ queue_message_set(c->c_sessq, 0,
+ msg_cmd_send, (void *)cmd);
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+ return (True);
+ }
+}
+
+/*
+ * []----
+ * | handle_text_msg -- process incoming test parameters
+ * |
+ * | NOTE: Need to handle continuation packets sent by the initiator.
+ * []----
+ */
+/*ARGSUSED*/
+static Boolean_t
+handle_text_msg(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen)
+{
+ iscsi_text_rsp_hdr_t rsp;
+ iscsi_text_hdr_t *hp = (iscsi_text_hdr_t *)p;
+ char *text = NULL;
+ int text_length = 0;
+ Boolean_t release_at_end = False;
+
+ bzero(&rsp, sizeof (rsp));
+ rsp.opcode = ISCSI_OP_TEXT_RSP;
+ rsp.itt = hp->itt;
+
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO, "CON%x PDU(Text Message)",
+ c->c_num);
+
+ /*
+ * Need to determine if this incoming text PDU is an initial message
+ * or a continuation.
+ */
+ if (hp->ttt == ISCSI_RSVD_TASK_TAG) {
+
+ /* ---- Initial text PDU, so parse the incoming data ---- */
+ if (parse_text(c, ntoh24(hp->dlength), &text, &text_length,
+ NULL) == False) {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "Failed to parse Text");
+ if (text) {
+ /*
+ * It's possible that we started to create
+ * a response, but yet an error occurred.
+ * Release the partial text response if that
+ * occurred.
+ */
+ free(text);
+ }
+ return (False);
+ }
+
+ /*
+ * 10.11.4 --
+ * When the target receives a Text Request with the Target
+ * Transfer Tag set to the reserved value of 0xffffffff, it
+ * resets its internal information (resets state) associated
+ * with the given Initiator Task Tag (restarts the negotiation).
+ */
+ if (c->c_text_area != NULL)
+ free(c->c_text_area);
+
+ c->c_text_area = text;
+ if (text_length > c->c_max_recv_data) {
+
+ /*
+ * Too much data to send at once, break it up into
+ * multiple transfers.
+ */
+ rsp.flags = ISCSI_FLAG_TEXT_CONTINUE;
+ rsp.ttt = 1;
+ c->c_text_len = text_length;
+ text_length = c->c_max_recv_data;
+ c->c_text_sent = text_length;
+
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO,
+ "CON%x Text PDU: %d PDUs required (len=%d)",
+ c->c_num,
+ (c->c_text_len + c->c_max_recv_data - 1) /
+ c->c_max_recv_data, c->c_text_len);
+
+ } else {
+ rsp.flags = ISCSI_FLAG_FINAL;
+ rsp.ttt = ISCSI_RSVD_TASK_TAG;
+ release_at_end = True;
+ }
+ } else {
+
+ /* ---- Continuation of previous text request ---- */
+ text_length = c->c_text_len - c->c_text_sent;
+ text = c->c_text_area + c->c_text_sent;
+ if (text_length > c->c_max_recv_data) {
+ rsp.flags = ISCSI_FLAG_TEXT_CONTINUE;
+ rsp.ttt = 1;
+ text_length = c->c_max_recv_data;
+ c->c_text_sent += text_length;
+ } else {
+ rsp.flags = ISCSI_FLAG_FINAL;
+ rsp.ttt = ISCSI_RSVD_TASK_TAG;
+ release_at_end = True;
+ }
+ }
+
+ queue_prt(c->c_mgmtq, Q_CONN_NONIO,
+ "CON%x Text PDU: flags=0x%02x, ttt=0x%08x, len=%d",
+ c->c_num, rsp.flags, rsp.ttt, text_length);
+
+ hton24(rsp.dlength, text_length);
+ (void) pthread_mutex_lock(&c->c_mutex);
+ rsp.statsn = htonl(c->c_statsn++);
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ if (ntohl(hp->cmdsn) > c->c_sess->s_seencmdsn)
+ c->c_sess->s_seencmdsn = ntohl(hp->cmdsn);
+ rsp.maxcmdsn = htonl(iscsi_cmd_window(c) + c->c_sess->s_seencmdsn);
+ rsp.expcmdsn = htonl(c->c_sess->s_seencmdsn + 1);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ send_iscsi_pkt(c, (iscsi_hdr_t *)&rsp, text);
+
+ if (release_at_end == True) {
+ free(c->c_text_area);
+ c->c_text_area = NULL;
+ }
+ return (True);
+}
+
+/*ARGSUSED*/
+static Boolean_t
+handle_logout_msg(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen)
+{
+ iscsi_logout_rsp_hdr_t *rsp;
+ iscsi_logout_hdr_t *hp = (iscsi_logout_hdr_t *)p;
+ char debug[80];
+
+ rsp = (iscsi_logout_rsp_hdr_t *)calloc(sizeof (*rsp), 1);
+ if (rsp) {
+
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x PDU(Logout Request)", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_NONIO, msg_log, debug);
+
+ (void) pthread_mutex_lock(&c->c_mutex);
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ if (hp->cmdsn > c->c_sess->s_seencmdsn)
+ c->c_sess->s_seencmdsn = hp->cmdsn;
+ rsp->expcmdsn = htonl(c->c_sess->s_seencmdsn + 1);
+ rsp->maxcmdsn = htonl(iscsi_cmd_window(c) +
+ c->c_sess->s_seencmdsn);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ rsp->opcode = ISCSI_OP_LOGOUT_RSP;
+ rsp->flags = ISCSI_FLAG_FINAL;
+ rsp->itt = hp->itt;
+ (void) pthread_mutex_lock(&c->c_mutex);
+ rsp->statsn = htonl(c->c_statsn++);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ c->c_last_pkg = (iscsi_hdr_t *)rsp;
+
+ /*
+ * Call the state transition last. This will send out
+ * an asynchronous message to shutdown the session and STE.
+ * Once that's complete a shutdown reply will be sent to
+ * the transmit connection thread. That will cause another
+ * transition to T13 which expects to send out this logout
+ * response.
+ */
+ if (c->c_state == S7_LOGOUT_REQUESTED)
+ conn_state(c, T10);
+ else
+ conn_state(c, T9);
+
+ return (True);
+ } else
+ return (False);
+}
+
+/*
+ * dataou_delayed -- possibly copy data from initiator
+ *
+ * If DataDigests are enabled copy the data from the socket into a buffer
+ * and perform the CRC check now.
+ *
+ * If MaxConnections==1 don't copy the data now and wait until the STE is
+ * ready to copy the data directly from the socket to it's final location.
+ * This is extremely beneficial when using mmap'd data.
+ * NOTE:
+ * (1) For this to work we must not use the queues and instead
+ * call the STE functions directly. If the queues are used
+ * this routine must pause until STE processes the data to
+ * prevent this thread from attempting to read data from
+ * the socket as if it's the next PDU header.
+ * (2) Currently we don't call STE directly. To prevent a performance
+ * issue we'll have the code in place to support calling
+ * STE directly, but any time MaxConnections is greater than 0
+ * we'll copy the buffer. This will be removed at some future
+ * point.
+ */
+static Boolean_t
+dataout_delayed(iscsi_cmd_t *cmd, msg_type_t type)
+{
+ iscsi_conn_t *c = cmd->c_allegiance;
+ int dlen = cmd->c_data_len,
+ cc;
+ uint32_t crc_calc,
+ crc_actual;
+ char pad_buf[ISCSI_PAD_WORD_LEN - 1];
+ char pad_len;
+ char debug[80];
+
+ cmd->c_dataout_cb = dataout_callback;
+
+ if (cmd->c_data == NULL) {
+ if ((cmd->c_data = (char *)malloc(dlen)) == NULL) {
+ (void) pthread_mutex_lock(&c->c_mutex);
+ iscsi_cmd_free(c, cmd);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ return (False);
+ }
+ cmd->c_data_alloc = True;
+ }
+
+ if ((cc = read_retry(c->c_fd, cmd->c_data, dlen)) != dlen) {
+ if (errno == ECONNRESET) {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x dataout_delayed -- "
+ "initiator reset socket", c->c_num);
+ } else {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x read_retry(got-%d, expect-%d), "
+ "errno=%d", c->c_num, cc, dlen, errno);
+ }
+
+ (void) pthread_mutex_lock(&c->c_mutex);
+ iscsi_cmd_free(c, cmd);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ conn_state(c, T8);
+ return (True);
+ }
+
+ pad_len = ((ISCSI_PAD_WORD_LEN -
+ (dlen & (ISCSI_PAD_WORD_LEN - 1))) & (ISCSI_PAD_WORD_LEN - 1));
+
+ if (pad_len) {
+ if (read_retry(c->c_fd, pad_buf, pad_len) != pad_len) {
+ if (errno == ECONNRESET) {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x dataout_delayed -- "
+ "initiator reset socket", c->c_num);
+ } else {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x Pad Word read errno=%d", c->c_num,
+ errno);
+ }
+
+ (void) pthread_mutex_lock(&c->c_mutex);
+ iscsi_cmd_free(c, cmd);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ conn_state(c, T8);
+ return (True);
+ }
+ }
+
+ if (c->c_data_digest == True) {
+ if (read_retry(c->c_fd, (char *)&crc_actual,
+ sizeof (crc_actual)) != sizeof (crc_actual)) {
+ if (errno == ECONNRESET) {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x dataout_delayed -- "
+ "initiator reset socket", c->c_num);
+ } else {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x CRC32 read errno=%d", c->c_num,
+ errno);
+ }
+
+ (void) pthread_mutex_lock(&c->c_mutex);
+ iscsi_cmd_free(c, cmd);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ conn_state(c, T8);
+ return (True);
+ }
+ crc_calc = iscsi_crc32c((void *)cmd->c_data, dlen);
+ if (crc_calc != crc_actual) {
+
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x CRC Error: actual %x vs. calc 0x%x",
+ c->c_num, crc_actual, crc_calc);
+
+ /*
+ * NOTE: Need to think about this one some more.
+ * Just because we get a data error doesn't mean
+ * we should drop the connection. Look at the
+ * spec and determine what's the appropriate
+ * error recovery for this issue.
+ */
+ (void) pthread_mutex_lock(&c->c_mutex);
+ iscsi_cmd_free(c, cmd);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ conn_state(c, T8);
+ return (True);
+ }
+ }
+
+ (void) pthread_mutex_lock(&c->c_mutex);
+ (void) pthread_mutex_lock(&c->c_state_mutex);
+ if (c->c_state == S5_LOGGED_IN) {
+ if ((cmd->c_state == CmdCanceled) &&
+ (type == msg_cmd_data_out)) {
+ t10_cmd_state(cmd->c_t10_cmd, T10_Cmd_Event_Release);
+ cmd->c_t10_cmd = NULL;
+ } else
+ queue_message_set(c->c_sessq, 0, type, (void *)cmd);
+ } else if (cmd->c_state == CmdCanceled) {
+ t10_cmd_state(cmd->c_t10_cmd, T10_Cmd_Event_Release);
+ cmd->c_t10_cmd = NULL;
+ }
+ (void) pthread_mutex_unlock(&c->c_state_mutex);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+
+ /*
+ * The else case here is if we're calling STE directly and the data
+ * will be read from the socket when STE is ready for it.
+ */
+
+ return (True);
+}
+
+/*
+ * []----
+ * | dataout_callback -- copy data from socket to emulation buffer
+ * []----
+ */
+void
+dataout_callback(t10_cmd_t *t, char *data, size_t *xfer)
+{
+ iscsi_cmd_t *cmd = (iscsi_cmd_t *)T10_TRANS_ID(t);
+ iscsi_conn_t *c = cmd->c_allegiance;
+ int dlen = cmd->c_data_len,
+ cc;
+ char pad_buf[ISCSI_PAD_WORD_LEN - 1];
+ char pad_len = 0;
+
+ pad_len = ((ISCSI_PAD_WORD_LEN -
+ (dlen & (ISCSI_PAD_WORD_LEN - 1))) &
+ (ISCSI_PAD_WORD_LEN - 1));
+
+
+ if (T10_DATA(t) != NULL) {
+ assert(T10_DATA(t) == cmd->c_data);
+ assert(cmd->c_data_alloc == True);
+ free(T10_DATA(t));
+ T10_DATA(t) = NULL;
+ cmd->c_data = NULL;
+ cmd->c_data_alloc = False;
+ return;
+ }
+
+ if ((cc = read_retry(c->c_fd, data, dlen)) != dlen) {
+ if (errno == ECONNRESET) {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x data_callback -- initiator reset socket",
+ c->c_num);
+ } else {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x read_retry(got-%d, expect-%d) errno=%d",
+ c->c_num, cc, dlen, errno);
+ }
+
+ conn_state(c, T8);
+ goto finish;
+ }
+
+ if (pad_len) {
+ if (read_retry(c->c_fd, pad_buf, pad_len)
+ != pad_len) {
+ if (errno == ECONNRESET) {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x data_callback -- "
+ "initiator reset socket", c->c_num);
+ } else {
+ queue_prt(c->c_mgmtq, Q_CONN_ERRS,
+ "CON%x data_callback -- "
+ "pad read errno=%d", c->c_num, errno);
+ }
+ conn_state(c, T8);
+ goto finish;
+ }
+ }
+
+finish:
+ *xfer = cc;
+ /* ---- Send msg that receive side of the connection can go ---- */
+ (void) sema_post(&c->c_datain);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_ffp.h b/usr/src/cmd/iscsi/iscsitgtd/iscsi_ffp.h
new file mode 100644
index 0000000000..ae84910a99
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_ffp.h
@@ -0,0 +1,43 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _FEATURE_H
+#define _FEATURE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include "iscsi_conn.h"
+
+/*
+ * The number of seconds that an initiator has to respond to our
+ * asynchronous logout request before we just drop the connection.
+ */
+#define ASYNC_LOGOUT_TIMEOUT 10
+
+Boolean_t iscsi_full_feature(iscsi_conn_t *c);
+
+#endif /* _FEATURE_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_login.c b/usr/src/cmd/iscsi/iscsitgtd/iscsi_login.c
new file mode 100644
index 0000000000..7e7fd6a808
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_login.c
@@ -0,0 +1,843 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/iscsi_protocol.h>
+
+#include "queue.h"
+#include "iscsi_conn.h"
+#include "iscsi_sess.h"
+#include "iscsi_login.h"
+#include "utility.h"
+#include "xml.h"
+#include "target.h"
+
+typedef enum auth_action {
+ LOGIN_NO_AUTH,
+ LOGIN_AUTH,
+ LOGIN_DROP
+} auth_action_t;
+
+/*
+ * Forward declarations
+ */
+static iscsi_login_rsp_hdr_t *make_login_response(iscsi_conn_t *c,
+ iscsi_login_hdr_t *lhp);
+static void send_login_reject(iscsi_conn_t *c, iscsi_login_hdr_t *lhp,
+ int err_code);
+static Boolean_t check_for_valid_I_T(iscsi_conn_t *c);
+static auth_action_t login_set_auth(iscsi_sess_t *s);
+
+/*
+ * iscsi_null_callback - This callback may be used under certain
+ * conditions when authenticating a target, but I'm not sure what
+ * we need to do here.
+ */
+/* ARGSUSED */
+static void
+iscsi_null_callback(void *user_handle, void *message_handle, int auth_status)
+{
+}
+
+/*
+ * iscsi_find_key_value -
+ */
+static int
+iscsi_find_key_value(char *param, char *ihp, char *pdu_end,
+ char **value_start, char **value_end)
+{
+ char *str = param;
+ char *text = ihp;
+ char *value = NULL;
+
+ if (value_start)
+ *value_start = NULL;
+ if (value_end)
+ *value_end = NULL;
+
+ /*
+ * make sure they contain the same bytes
+ */
+ while (*str) {
+ if (text >= pdu_end) {
+ return (0);
+ }
+ if (*text == '\0') {
+ return (0);
+ }
+ if (*str != *text) {
+ return (0);
+ }
+ str++;
+ text++;
+ }
+
+ if ((text >= pdu_end) ||
+ (*text == '\0') ||
+ (*text != ISCSI_TEXT_SEPARATOR)) {
+ return (0);
+ }
+
+ /*
+ * find the value
+ */
+ value = text + 1;
+
+ /*
+ * find the end of the value
+ */
+ while ((text < pdu_end) && (*text))
+ text++;
+
+ if (value_start)
+ *value_start = value;
+ if (value_end)
+ *value_end = text;
+
+ return (1);
+}
+
+Boolean_t
+iscsi_handle_login_pkt(iscsi_conn_t *c)
+{
+ iscsi_login_hdr_t lh;
+ iscsi_login_rsp_hdr_t *rsp = NULL;
+ Boolean_t rval = False;
+ IscsiAuthClient *auth_client = NULL;
+ char *text = NULL,
+ *end = NULL,
+ *text_rsp = NULL,
+ debug[128];
+ int debug_status = 0,
+ errcode = 0,
+ text_length = 0,
+ keytype = 0,
+ transit = 0,
+ tpgt,
+ rc = 0;
+ auth_action_t auth_action = LOGIN_DROP;
+ xml_node_t *tnode = NULL;
+
+ if (read(c->c_fd, &lh, sizeof (lh)) != sizeof (lh)) {
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log,
+ "Header to small");
+ return (False);
+ }
+
+ if ((lh.opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_LOGIN_CMD) {
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Wrong OP code for state (Got 0x%x, Expected 0x%x)",
+ c->c_num, lh.opcode, ISCSI_OP_LOGIN_CMD);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
+ ISCSI_LOGIN_STATUS_INVALID_REQUEST);
+ conn_state(c, T7);
+ return (True);
+ }
+
+ if ((rval = session_alloc(c, lh.isid)) == False) {
+ conn_state(c, T7);
+ return (True);
+ }
+
+ connection_parameters_default(c);
+
+ c->c_cid = ntohl(lh.cid);
+
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ c->c_sess->s_cmdsn = ntohl(lh.cmdsn);
+ c->c_sess->s_seencmdsn = ntohl(lh.cmdsn);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+
+ /*
+ * Is this a new session or an attempt to add a connection to
+ * an existing session.
+ */
+ if (ntohs(lh.tsid) != 0) {
+
+ /* Multiple connections per session not handled right now */
+ conn_state(c, T7);
+ return (True);
+ }
+
+ if ((rsp = make_login_response(c, &lh)) == NULL) {
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Failed make_login_response", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ return (False);
+ }
+ /* default is ISCSI_FLAG_LOGIN_TRANSIT, not good for login */
+ rsp->flags = 0;
+
+ if ((rsp->active_version < lh.min_version) ||
+ (rsp->active_version > lh.max_version)) {
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Version: Active %d, min %d, max %d", c->c_num,
+ rsp->active_version, lh.min_version, lh.max_version);
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
+ ISCSI_LOGIN_STATUS_NO_VERSION);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ conn_state(c, T7);
+ return (True);
+ }
+
+ if (lh.flags & ISCSI_FLAG_LOGIN_CONTINUE) {
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Continuation pkt", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_LOGIN, msg_log, debug);
+ }
+
+ auth_client =
+ (c->c_sess->sess_auth.auth_buffers &&
+ c->c_sess->sess_auth.num_auth_buffers) ?
+ (IscsiAuthClient *) c->c_sess->sess_auth.auth_buffers[0].address :
+ NULL;
+
+ if (c->auth_text != NULL)
+ free(c->auth_text);
+ c->auth_text_length = 0;
+
+ transit = lh.flags & ISCSI_FLAG_LOGIN_TRANSIT;
+
+ switch (ISCSI_LOGIN_CURRENT_STAGE(lh.flags)) {
+ case ISCSI_SECURITY_NEGOTIATION_STAGE:
+
+ /*
+ * Grab the parameters and create the response
+ * text.
+ */
+ rval = parse_text(c, ntoh24(lh.dlength),
+ &text_rsp, &text_length, &errcode);
+ if (rval == False) {
+ send_login_reject(c, &lh, errcode);
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x SecurityNegotiation: parse_text"
+ " failed", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ conn_state(c, T7);
+ break;
+ }
+
+ if ((rval = check_for_valid_I_T(c)) == False) {
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x SecurityNegotiation: invalid I "
+ "or T", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ conn_state(c, T7);
+ break;
+ }
+
+ auth_action = login_set_auth(c->c_sess);
+
+ if (auth_action == LOGIN_NO_AUTH) {
+ rsp->flags |= ISCSI_FLAG_LOGIN_TRANSIT;
+ rsp->flags |= ISCSI_OP_PARMS_NEGOTIATION_STAGE;
+ rval = add_text(&text_rsp, &text_length, "AuthMethod",
+ "None");
+ if (rval == False) {
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_TARGET_ERR << 8) |
+ ISCSI_LOGIN_STATUS_TARGET_ERROR);
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Norm: Failed to add AuthMethod=None",
+ c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log,
+ debug);
+ conn_state(c, T7);
+ }
+ break;
+ }
+
+ if (auth_action == LOGIN_DROP) {
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
+ ISCSI_LOGIN_STATUS_TGT_FORBIDDEN);
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x SecurityNegotiation: access denied",
+ c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ conn_state(c, T7);
+ rval = False;
+ break;
+ }
+
+ if (iscsiAuthClientRecvBegin(auth_client) !=
+ iscsiAuthStatusNoError) {
+ (void) snprintf(debug, sizeof (debug), "CON%x "
+ "login failed - authentication receive failed",
+ c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ break;
+ }
+
+ if (iscsiAuthClientRecvTransitBit(auth_client,
+ transit) != iscsiAuthStatusNoError) {
+ (void) snprintf(debug, sizeof (debug),
+ "iscsi connection(%u) login failed - "
+ "authentication transmit failed", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ break;
+ }
+
+ /*
+ * scan the text data
+ */
+ text = c->auth_text;
+ end = text + c->auth_text_length;
+more_text:
+ while (text && (text < end)) {
+ char *value = NULL;
+ char *value_end = NULL;
+ keytype = iscsiAuthKeyTypeNone;
+
+ /*
+ * skip any NULs separating each text key=value pair
+ */
+ while ((text < end) && (*text == '\0')) {
+ text++;
+ }
+ if (text >= end) {
+ break;
+ }
+
+ while (iscsiAuthClientGetNextKeyType(&keytype) ==
+ iscsiAuthStatusNoError) {
+ char *key =
+ (char *)iscsiAuthClientGetKeyName(keytype);
+ if ((key) &&
+ (iscsi_find_key_value(key, text, end,
+ &value, &value_end))) {
+ (void) snprintf(debug, sizeof (debug),
+ "%s=%s", key, value);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS,
+ msg_log, debug);
+ if (iscsiAuthClientRecvKeyValue(
+ auth_client, keytype, value)
+ != iscsiAuthStatusNoError) {
+ (void) snprintf(debug,
+ sizeof (debug),
+ "iscsi connection(%u) login"
+ "failed - can't accept "
+ "%s in security stage",
+ c->c_num, text);
+ queue_str(c->c_mgmtq,
+ Q_CONN_ERRS,
+ msg_log, debug);
+ }
+ text = value_end;
+ goto more_text;
+ }
+ }
+ }
+
+ switch (iscsiAuthClientRecvEnd(auth_client, iscsi_null_callback,
+ (void *)c->c_sess, NULL)) {
+ case iscsiAuthStatusContinue:
+ /*
+ * continue sending PDUs
+ */
+ break;
+
+ case iscsiAuthStatusPass:
+ c->c_auth_pass = 1;
+ break;
+
+ case iscsiAuthStatusInProgress:
+ /*
+ * this should only occur if we were authenticating the
+ * target, which we don't do yet, so treat this as an
+ * error.
+ */
+ case iscsiAuthStatusNoError:
+ /*
+ * treat this as an error, since we should get a
+ * different code
+ */
+ case iscsiAuthStatusError:
+ case iscsiAuthStatusFail:
+ default:
+ debug_status = 0;
+
+ (void) iscsiAuthClientGetDebugStatus(auth_client,
+ &debug_status);
+ (void) snprintf(debug, sizeof (debug),
+ "iscsi connection(%u) authentication failed (%s)",
+ c->c_num,
+ iscsiAuthClientDebugStatusToText(
+ debug_status));
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log,
+ debug);
+
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
+ ISCSI_LOGIN_STATUS_AUTH_FAILED);
+ conn_state(c, T7);
+ rval = False;
+ break;
+ }
+
+ if (rval == False)
+ break;
+
+ keytype = iscsiAuthKeyTypeNone;
+ rc = iscsiAuthClientSendTransitBit(auth_client, &transit);
+
+ /*
+ * see if we're ready for a stage change
+ */
+ if (rc == iscsiAuthStatusNoError) {
+ if (transit) {
+ rsp->flags = lh.flags;
+ }
+
+ } else {
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
+ ISCSI_LOGIN_STATUS_AUTH_FAILED);
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x SecurityNegotiation: wants", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ conn_state(c, T7);
+ rval = False;
+ }
+
+ /*
+ * enumerate all the keys the auth code might want to send
+ */
+ while (iscsiAuthClientGetNextKeyType(&keytype) ==
+ iscsiAuthStatusNoError) {
+ int present = 0;
+ char *key = (char *)iscsiAuthClientGetKeyName(keytype);
+ int key_length = key ? strlen(key) : 0;
+ int pdu_length = ntoh24(rsp->dlength);
+ char *auth_value = NULL;
+ unsigned int max_length = ISCSI_DEFAULT_MAX_XMIT_SEG_LEN
+ - (pdu_length + key_length + 1); /* FIXME: check */
+
+ /*
+ * add the key/value pairs the auth code wants to
+ * send directly to the PDU, since they could in
+ * theory be large.
+ */
+ if ((auth_value = (char *)malloc(max_length)) ==
+ NULL) {
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_TARGET_ERR << 8) |
+ ISCSI_LOGIN_STATUS_TARGET_ERROR);
+
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Norm: Failed alloc auth_key %S=%s",
+ c->c_num, key, auth_value);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log,
+ debug);
+ conn_state(c, T7);
+ rval = False;
+ break;
+ }
+ rc = iscsiAuthClientSendKeyValue(auth_client, keytype,
+ &present, auth_value, max_length);
+ if ((rc == iscsiAuthStatusNoError) && present) {
+ (void) snprintf(debug, sizeof (debug),
+ "key:%s, auth_value:%s\n", key, auth_value);
+ queue_str(c->c_mgmtq, Q_CONN_LOGIN, msg_log,
+ debug);
+
+ rval = add_text(&text_rsp, &text_length,
+ key, auth_value);
+ if (rval == False) {
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_TARGET_ERR <<
+ 8) |
+ ISCSI_LOGIN_STATUS_TARGET_ERROR);
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Norm: Failed to add %S=%s",
+ c->c_num, key, auth_value);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS,
+ msg_log, debug);
+ conn_state(c, T7);
+ }
+ }
+ if (auth_value != NULL)
+ free(auth_value);
+ }
+
+ break;
+
+ case ISCSI_OP_PARMS_NEGOTIATION_STAGE:
+
+ /*
+ * Gather up the parameters sent across and build a response
+ * based on any selection required.
+ */
+ if ((rval = parse_text(c, ntoh24(lh.dlength), &text_rsp,
+ &text_length, &errcode)) == False) {
+
+ send_login_reject(c, &lh, errcode);
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Norm: parse_text failed", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ conn_state(c, T7);
+ break;
+ }
+
+ /*
+ * If the connection hasn't passed authentication and
+ * it's a normal session see if this connection MUST
+ * have gone through authentication first. If the
+ * initiator has a CHAP secret stored that means we
+ * want to validate.
+ */
+ if ((c->c_auth_pass == 0) &&
+ (c->c_sess->s_type == SessionNormal)) {
+ if ((tnode = find_target_node(c->c_sess->s_t_name)) ==
+ NULL) {
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_TARGET_ERR << 8) |
+ ISCSI_LOGIN_STATUS_TARGET_ERROR);
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x No target node in login",
+ c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log,
+ debug);
+ conn_state(c, T7);
+ }
+
+ /*
+ * check_access will return True if the initiator
+ * is required to use CHAP authentication. So if
+ * true and we're here it means that the initiator
+ * is trying to skip the authentication step.
+ */
+ if (check_access(tnode, c->c_sess->s_i_name, True) ==
+ False) {
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
+ ISCSI_LOGIN_STATUS_AUTH_FAILED);
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Authentication required for %s",
+ c->c_num, c->c_sess->s_i_name);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log,
+ debug);
+ conn_state(c, T7);
+ }
+ }
+
+ if ((rval = check_for_valid_I_T(c)) == False) {
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Norm: bad I or T", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ conn_state(c, T7);
+ break;
+ }
+
+ /*
+ * We accept transition and stage information as is
+ * and echo it back because at this point there's no need
+ * to send a parameter to the Initiator and expect a
+ * reply.
+ */
+ rsp->flags = lh.flags;
+
+ break;
+
+ case ISCSI_FULL_FEATURE_PHASE:
+ /* can't jump directly to full feature phase */
+ (void) snprintf(debug, sizeof (debug),
+ "CON%x Protocol error: wrong stage", c->c_num);
+ queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug);
+ send_login_reject(c, &lh,
+ (ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ conn_state(c, T7);
+ rval = False;
+ break;
+
+ default:
+ /* just drop the connection since we don't know what's up */
+ rval = False;
+ break;
+ }
+
+ hton24(rsp->dlength, text_length);
+ if ((rval == True) && (session_validate(c->c_sess) == True)) {
+
+ send_iscsi_pkt(c, (iscsi_hdr_t *)rsp, text_rsp);
+
+ if ((lh.flags & ISCSI_FLAG_LOGIN_TRANSIT) &&
+ (ISCSI_LOGIN_NEXT_STAGE(lh.flags) ==
+ ISCSI_FULL_FEATURE_PHASE)) {
+
+ conn_state(c, T5);
+
+ /*
+ * At this point we've completed the negotiation
+ * of all login parameters. Now we need to perform
+ * some quick boundary checks and then send a couple
+ * pieces of information to STE for it's use.
+ */
+ c->c_max_burst_len = MIN(c->c_max_burst_len,
+ c->c_max_recv_data);
+ /*
+ * XXX Need to get this information passed up
+ * to the SAM-3 layer so that the SCSI TPG stuff
+ * works again.
+ */
+ tpgt = find_main_tpgt(&(c->c_target_sockaddr));
+ if (tpgt == 0)
+ tpgt = T10_DEFAULT_TPG;
+ c->c_tpgt = tpgt;
+ }
+ }
+
+ if (text_rsp != NULL)
+ free(text_rsp);
+ if (rsp != NULL)
+ free(rsp);
+
+ return (rval);
+}
+
+/*
+ * check_for_valid_I_T -- check to see if we have valid names
+ *
+ * This routine checks to see if we have received a valid InitiatorName
+ * and TargetName which is the bare minimum which an Initiator must send
+ * across during the login phase.
+ */
+static Boolean_t
+check_for_valid_I_T(iscsi_conn_t *c)
+{
+ iscsi_sess_t *s = c->c_sess;
+ if (s->s_type == SessionDiscovery)
+ return ((s->s_i_name == NULL) ? False : True);
+ else
+ return (((s->s_t_name == NULL) ||
+ (s->s_i_name == NULL)) ? False : True);
+}
+
+static iscsi_login_rsp_hdr_t *
+make_login_response(iscsi_conn_t *c, iscsi_login_hdr_t *lhp)
+{
+ iscsi_login_rsp_hdr_t *r;
+
+ if (lhp->tsid != 0)
+ /* don't except existing sessions for now */
+ return (NULL);
+
+ r = (iscsi_login_rsp_hdr_t *)calloc(sizeof (*r), sizeof (char));
+ if (r == NULL)
+ return (NULL);
+
+ bcopy(lhp->isid, r->isid, 6); /* 6 is defined by protocol */
+ r->opcode = ISCSI_OP_LOGIN_RSP;
+ r->flags = ISCSI_FLAG_LOGIN_TRANSIT;
+ r->max_version = ISCSI_MAX_VERSION;
+ r->active_version = ISCSI_MIN_VERSION;
+ r->itt = lhp->itt;
+ r->tsid = htons(c->c_sess->s_tsid);
+ (void) pthread_mutex_lock(&c->c_mutex);
+ r->statsn = htonl(c->c_statsn++);
+ (void) pthread_mutex_unlock(&c->c_mutex);
+ /* ---- cmdsn is not advanced during login ---- */
+ (void) pthread_mutex_lock(&c->c_sess->s_mutex);
+ r->expcmdsn = htonl(c->c_sess->s_seencmdsn + 1);
+ r->maxcmdsn = htonl(c->c_sess->s_seencmdsn + 1);
+ (void) pthread_mutex_unlock(&c->c_sess->s_mutex);
+
+
+ return (r);
+}
+
+static void
+send_login_reject(iscsi_conn_t *c, iscsi_login_hdr_t *lhp, int err_code)
+{
+ iscsi_login_rsp_hdr_t *r;
+
+ if ((r = make_login_response(c, lhp)) == NULL)
+ return;
+
+ r->status_class = (err_code >> 8) & 0xff;
+ r->status_detail = err_code & 0xff;
+
+ (void) write(c->c_fd, r, sizeof (*r));
+ free(r);
+}
+
+static auth_action_t
+login_set_auth(iscsi_sess_t *s)
+{
+ xml_node_t *xnInitiator = NULL;
+ xml_node_t *xnTarget = NULL;
+ xml_node_t *xnAcl = NULL;
+ char *szIniAlias = NULL;
+ char *szIscsiName = NULL;
+ char *szChapName = NULL;
+ char *szChapSecret = NULL;
+ char *possible = NULL;
+ iscsi_auth_t *sess_auth = &(s->sess_auth);
+ int comp = 0;
+
+ bzero(sess_auth->username_in, sizeof (sess_auth->username_in));
+ bzero(sess_auth->password_in, sizeof (sess_auth->password_in));
+ sess_auth->password_length_in = 0;
+
+ /* Load alias, iscsi-name, chap-name, chap-secret from config file */
+ while ((xnInitiator = xml_node_next(main_config, XML_ELEMENT_INIT,
+ xnInitiator)) != NULL) {
+
+ (void) xml_find_value_str(xnInitiator, XML_ELEMENT_INIT,
+ &szIniAlias);
+
+ if (xml_find_value_str(xnInitiator, XML_ELEMENT_INAME,
+ &szIscsiName) == True) {
+
+ comp = strcmp(s->s_i_name, szIscsiName);
+ free(szIscsiName);
+ szIscsiName = NULL;
+
+ if (comp == 0) {
+
+ if (xml_find_value_str(xnInitiator,
+ XML_ELEMENT_CHAPNAME,
+ &szChapName) == True) {
+ /*CSTYLED*/
+ (void) strcpy((char *)sess_auth->username_in,
+ szChapName);
+ free(szChapName);
+ }
+
+ if (xml_find_value_str(xnInitiator,
+ XML_ELEMENT_CHAPSECRET,
+ &szChapSecret) == True) {
+ /*CSTYLED*/
+ (void) strcpy((char *)sess_auth->password_in,
+ szChapSecret);
+ sess_auth->password_length_in =
+ strlen(szChapSecret);
+ free(szChapSecret);
+ }
+ break;
+ }
+ }
+ }
+
+ if (s->s_type == SessionDiscovery) {
+ return (LOGIN_NO_AUTH);
+ }
+
+ if (s->s_t_name == NULL) {
+ /*
+ * Should not happen for non-discovery session
+ */
+ return (LOGIN_DROP);
+ }
+
+ /*
+ * If no acc_list for current target, transit.
+ * If acc_list exists for the target, and
+ * If the initiator not in the list, drop it.
+ * If the initiator in the list, and
+ * If no CHAP secret for the initiator, transit.
+ * If a CHAP secret exists for the initiator, it must be authed.
+ */
+
+ while ((xnTarget = xml_node_next(targets_config, XML_ELEMENT_TARG,
+ xnTarget)) != NULL) {
+
+ if ((xml_find_value_str(xnTarget, XML_ELEMENT_INAME,
+ &szIscsiName) == False) || (szIscsiName == NULL)) {
+ return (LOGIN_DROP);
+ }
+
+ comp = strcmp(szIscsiName, s->s_t_name);
+ free(szIscsiName);
+ szIscsiName = NULL;
+
+ if (comp == 0) {
+
+ if ((xnAcl = xml_node_next(xnTarget,
+ XML_ELEMENT_ACLLIST, 0)) == NULL) {
+ /*
+ * No acl_list found, return True for no auth
+ */
+ return (LOGIN_NO_AUTH);
+ }
+
+ /*
+ * This target has an access_list. Now compare
+ * those entries against the initiator who started
+ * this session.
+ */
+ xnInitiator = NULL;
+ while ((xnInitiator = xml_node_next(xnAcl,
+ XML_ELEMENT_INIT, xnInitiator)) != NULL) {
+
+ if ((xml_find_value_str(xnInitiator,
+ XML_ELEMENT_INIT, &possible) == False) ||
+ (possible == NULL))
+ continue;
+
+ if (strcmp(szIniAlias, possible) == 0) {
+ /*
+ * Found the initiator in acl-list,
+ * authentication needed
+ */
+ free(possible);
+ if (sess_auth->password_length_in == 0)
+ return (LOGIN_NO_AUTH);
+ else
+ return (LOGIN_AUTH);
+ }
+
+ free(possible);
+ possible = NULL;
+ }
+ /*
+ * Acl-list exists, while the initiator is not found in
+ * the list, we should drop the connection
+ */
+ return (LOGIN_DROP);
+ }
+ }
+
+ /* False means Need authentication */
+ return (LOGIN_AUTH);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_login.h b/usr/src/cmd/iscsi/iscsitgtd/iscsi_login.h
new file mode 100644
index 0000000000..846b23521e
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_login.h
@@ -0,0 +1,42 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LOGIN_H
+#define _LOGIN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Boolean_t iscsi_handle_login_pkt(iscsi_conn_t *c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LOGIN_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c b/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c
new file mode 100644
index 0000000000..a2d119886a
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c
@@ -0,0 +1,713 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <syslog.h>
+
+#include "iscsi_conn.h"
+#include "iscsi_sess.h"
+#include "t10.h"
+#include "utility.h"
+#include "xml.h"
+#include "target.h"
+
+pthread_mutex_t sess_mutex;
+int sess_num;
+iscsi_sess_t *sess_head;
+
+static void session_free(struct iscsi_sess *s);
+static void sess_set_auth(iscsi_sess_t *isp);
+static void *sess_from_t10(void *v);
+static void *sess_process(void *v);
+
+/*
+ * []----
+ * | session_init -- initialize global variables and mutexs
+ * []----
+ */
+void
+session_init()
+{
+ (void) pthread_mutex_init(&sess_mutex, NULL);
+ sess_num = 0;
+ sess_head = 0;
+}
+
+/*
+ * []----
+ * | session_alloc -- create a new session attached to the lead connection
+ * []----
+ */
+Boolean_t
+session_alloc(iscsi_conn_t *c, uint8_t *isid)
+{
+ iscsi_sess_t *s,
+ *n;
+
+ if (c->c_sess != NULL)
+ return (True);
+
+ s = (iscsi_sess_t *)calloc(sizeof (iscsi_sess_t), 1);
+ if (s == NULL)
+ return (False);
+
+ (void) pthread_mutex_lock(&sess_mutex);
+ s->s_num = sess_num++;
+ s->s_state = SS_STARTED;
+
+ if (sess_head == NULL)
+ sess_head = s;
+ else {
+ for (n = sess_head; n->s_next; n = n->s_next)
+ ;
+ n->s_next = s;
+ }
+ (void) pthread_mutex_unlock(&sess_mutex);
+
+ bcopy(isid, s->s_isid, 6);
+
+ (void) pthread_mutex_init(&s->s_mutex, NULL);
+ c->c_sess = s;
+ s->s_conn_head = c;
+ s->s_sessq = queue_alloc();
+ s->s_t10q = queue_alloc();
+ c->c_sessq = s->s_sessq;
+ s->s_mgmtq = c->c_mgmtq;
+ s->s_type = SessionNormal;
+ s->s_tsid = s->s_num;
+
+ sess_set_auth(s);
+
+ (void) pthread_create(&s->s_thr_id_t10, NULL, sess_from_t10, s);
+ (void) pthread_create(&s->s_thr_id_conn, NULL, sess_process, s);
+
+ util_title(s->s_mgmtq, Q_SESS_LOGIN, s->s_num, "Start Session");
+
+ return (True);
+}
+
+/*
+ * []----
+ * | session_free -- remove connection from session
+ * []----
+ */
+static void
+session_free(iscsi_sess_t *s)
+{
+ iscsi_sess_t *n;
+
+ /*
+ * Early errors in connection setup can still call this routine
+ * which means the session hasn't been called.
+ */
+ if (s == NULL)
+ return;
+
+ if (s->s_i_name)
+ free(s->s_i_name);
+ if (s->s_t_name)
+ free(s->s_t_name);
+ if (s->s_i_alias)
+ free(s->s_i_alias);
+
+ (void) pthread_mutex_lock(&sess_mutex);
+ if (sess_head == s)
+ sess_head = s->s_next;
+ else {
+ for (n = sess_head; n; n = n->s_next) {
+ if (n->s_next == s) {
+ n->s_next = s->s_next;
+ break;
+ }
+ }
+ if (n == NULL) {
+ queue_prt(s->s_mgmtq, Q_SESS_ERRS,
+ "SES%x NOT IN SESSION LIST!", s->s_num);
+ }
+ }
+ (void) pthread_mutex_unlock(&sess_mutex);
+}
+
+/*
+ * []----
+ * | session_remove_connection -- removes conn from sess list
+ * |
+ * | Returns True if this was the last connection which is always the case
+ * | for now. In the future with multiple connections per session it'll be
+ * | different.
+ * []----
+ */
+/*ARGSUSED*/
+static Boolean_t
+session_remove_connection(iscsi_sess_t *s, iscsi_conn_t *c)
+{
+ bzero(s->s_isid, 6);
+ return (True);
+}
+
+/*
+ * []----
+ * | convert_i_local -- Return a local name for the initiator if avilable.
+ * |
+ * | NOTE: If this routine returns true, it's the callers responsibility
+ * | to free the memory.
+ * []----
+ */
+Boolean_t
+convert_i_local(char *ip, char **rtn)
+{
+ xml_node_t *inode = NULL;
+ char *iname,
+ *name;
+
+ while ((inode = xml_node_next(main_config, XML_ELEMENT_INIT, inode)) !=
+ NULL) {
+ if (xml_find_value_str(inode, XML_ELEMENT_INAME, &iname) ==
+ False) {
+ continue;
+ }
+ if (strcmp(iname, ip) == 0) {
+ if (xml_find_value_str(inode, XML_ELEMENT_INIT,
+ &name) == False) {
+ free(iname);
+ return (False);
+ } else
+ free(iname);
+ *rtn = name;
+ return (True);
+ } else
+ free(iname);
+ }
+ return (False);
+}
+
+/*
+ * []----
+ * | sess_from_t10 -- handle messages from the T10 layer
+ * []----
+ */
+void *
+sess_from_t10(void *v)
+{
+ iscsi_sess_t *s = (iscsi_sess_t *)v;
+ msg_t *m;
+ Boolean_t process = True;
+
+ while (process == True) {
+ m = queue_message_get(s->s_t10q);
+ switch (m->msg_type) {
+ case msg_cmd_data_rqst:
+ queue_message_set(s->s_conn_head->c_dataq, 0,
+ msg_cmd_data_rqst, m->msg_data);
+ break;
+
+ case msg_cmd_data_in:
+ queue_message_set(s->s_conn_head->c_dataq, 0,
+ msg_cmd_data_in, m->msg_data);
+ break;
+
+ case msg_cmd_cmplt:
+ queue_message_set(s->s_conn_head->c_dataq, 0,
+ msg_cmd_cmplt, m->msg_data);
+ break;
+
+ case msg_shutdown_rsp:
+
+ if (s->s_t10) {
+ t10_handle_destroy(s->s_t10);
+ s->s_t10 = NULL;
+ }
+
+ (void) pthread_mutex_lock(&s->s_mutex);
+ s->s_state = SS_SHUTDOWN_CMPLT;
+ (void) pthread_mutex_unlock(&s->s_mutex);
+
+ session_free(s);
+
+ /*
+ * Let the connection, which is the last, know
+ * about our completion of the shutdown.
+ */
+ queue_message_set(s->s_conn_head->c_dataq, 0,
+ msg_shutdown_rsp, (void *)True);
+ process = False;
+ s->s_state = SS_FREE;
+ break;
+
+ default:
+ queue_prt(s->s_mgmtq, Q_SESS_ERRS,
+ "SES%x Unknown msg type (%d) from T10 ",
+ s->s_num, m->msg_type);
+ queue_message_set(s->s_conn_head->c_dataq, 0,
+ m->msg_type, m->msg_data);
+ break;
+ }
+ queue_message_free(m);
+ }
+ queue_free(s->s_t10q, NULL);
+ util_title(s->s_mgmtq, Q_SESS_LOGIN, s->s_num, "End Session");
+ free(s);
+ return (NULL);
+}
+
+/*
+ * []----
+ * | sess_process -- handle messages from the connection(s)
+ * []----
+ */
+static void *
+sess_process(void *v)
+{
+ iscsi_sess_t *s = (iscsi_sess_t *)v;
+ iscsi_conn_t *c;
+ iscsi_cmd_t *cmd;
+ msg_t *m;
+ Boolean_t process = True;
+ mgmt_request_t *mgmt;
+ name_request_t *nr;
+ t10_cmd_t *t10_cmd;
+ char **buf,
+ local_buf[16];
+ int lun;
+ extern void dataout_callback(t10_cmd_t *t, char *data, size_t *xfer);
+
+ (void) pthread_mutex_lock(&s->s_mutex);
+ s->s_state = SS_RUNNING;
+ (void) pthread_mutex_unlock(&s->s_mutex);
+ do {
+ m = queue_message_get(s->s_sessq);
+ switch (m->msg_type) {
+ case msg_cmd_send:
+ cmd = (iscsi_cmd_t *)m->msg_data;
+ if (s->s_t10 == NULL) {
+
+ /*
+ * The value of 0x960 comes from T10.
+ * See SPC-4, revision 1a, section 6.4.2,
+ * table 87
+ *
+ * XXX Need to rethink how I should do
+ * the callback.
+ */
+ s->s_t10 = t10_handle_create(s->s_t_name,
+ T10_TRANS_ISCSI, s->s_conn_head->c_tpgt,
+ s->s_conn_head->c_max_burst_len,
+ s->s_t10q, dataout_callback);
+ }
+ if (t10_cmd_create(s->s_t10, cmd->c_lun, cmd->c_scb,
+ cmd->c_scb_len, (transport_t)cmd,
+ &t10_cmd) == False) {
+
+ queue_prt(s->s_mgmtq, Q_SESS_ERRS,
+ "SES%x FAILED to create cmd", s->s_num);
+ /*
+ * If the command create failed, the T10 layer
+ * will attempt to create a sense buffer
+ * telling the initiator what went wrong. If
+ * that layer was unable to accomplish that
+ * things are really bad and we need to just
+ * close the connection.
+ */
+ if (cmd->c_t10_cmd != NULL) {
+ queue_message_set(
+ cmd->c_allegiance->c_dataq,
+ 0, msg_cmd_cmplt, t10_cmd);
+ } else
+ conn_state(cmd->c_allegiance, T11);
+ } else {
+ (void) pthread_mutex_lock(
+ &cmd->c_allegiance->c_mutex);
+ if (cmd->c_state != CmdCanceled) {
+ cmd->c_t10_cmd = t10_cmd;
+ (void) t10_cmd_send(s->s_t10,
+ cmd->c_t10_cmd, cmd->c_data,
+ cmd->c_data_len);
+ } else {
+ t10_cmd_state(t10_cmd,
+ T10_Cmd_Event_Canceled);
+ }
+ (void) pthread_mutex_unlock(
+ &cmd->c_allegiance->c_mutex);
+ }
+ break;
+
+ case msg_cmd_data_out:
+ cmd = (iscsi_cmd_t *)m->msg_data;
+ if (s->s_t10 != NULL)
+ (void) t10_cmd_data(s->s_t10, cmd->c_t10_cmd,
+ cmd->c_offset_out, cmd->c_data,
+ cmd->c_data_len);
+ break;
+
+ case msg_targ_inventory_change:
+ if (s->s_t10 != NULL)
+ (void) t10_task_mgmt(s->s_t10, InventoryChange,
+ 0, 0);
+ break;
+
+ case msg_lu_capacity_change:
+ lun = (int)(uintptr_t)m->msg_data;
+ if (s->s_t10 != NULL)
+ (void) t10_task_mgmt(s->s_t10, CapacityChange,
+ lun, 0);
+ break;
+
+ case msg_reset_targ:
+ if (s->s_t10 != NULL)
+ (void) t10_task_mgmt(s->s_t10, ResetTarget,
+ 0, 0);
+ break;
+
+ case msg_reset_lu:
+ if (s->s_t10 != NULL)
+ (void) t10_task_mgmt(s->s_t10, ResetLun,
+ (int)(uintptr_t)m->msg_data, 0);
+ break;
+
+ case msg_shutdown:
+ (void) pthread_mutex_lock(&s->s_mutex);
+ s->s_state = SS_SHUTDOWN_START;
+ (void) pthread_mutex_unlock(&s->s_mutex);
+
+ /*
+ * Shutdown rquest comming from a connection. Only
+ * shutdown the STE if this is the last connection
+ * for this session.
+ */
+ c = (iscsi_conn_t *)m->msg_data;
+ if (session_remove_connection(s, c) == True) {
+ queue_prt(s->s_mgmtq, Q_SESS_NONIO,
+ "SES%x Starting shutdown", s->s_num);
+
+ /*
+ * If this is the last connection for this
+ * session send a message to the SAM-3 layer to
+ * shutdown.
+ */
+ if (s->s_t10 != NULL) {
+ t10_handle_disable(s->s_t10);
+ }
+ queue_message_set(s->s_t10q, 0,
+ msg_shutdown_rsp, 0);
+ process = False;
+ } else {
+
+ /*
+ * Since this isn't the last connection for
+ * the session, acknowledge the connection
+ * request now since it's references from
+ * this session have been removed.
+ */
+ queue_message_set(c->c_dataq, 0,
+ msg_shutdown_rsp, (void *)False);
+ }
+ break;
+
+ case msg_initiator_name:
+ nr = (name_request_t *)m->msg_data;
+ s->s_i_name = strdup(nr->nr_name);
+
+ /*
+ * Acknowledge the request by sending back an empty
+ * message.
+ */
+ queue_message_set(nr->nr_q, 0, msg_initiator_name, 0);
+ break;
+
+ case msg_initiator_alias:
+ nr = (name_request_t *)m->msg_data;
+ s->s_i_alias = strdup(nr->nr_name);
+
+ /*
+ * Acknowledge the request by sending back an empty
+ * message.
+ */
+ queue_message_set(nr->nr_q, 0, msg_initiator_alias, 0);
+ break;
+
+ case msg_target_name:
+ nr = (name_request_t *)m->msg_data;
+ s->s_t_name = strdup(nr->nr_name);
+
+ /*
+ * Acknowledge the request by sending back an empty
+ * message.
+ */
+ queue_message_set(nr->nr_q, 0, msg_target_name, 0);
+ break;
+
+ case msg_mgmt_rqst:
+ mgmt = (mgmt_request_t *)m->msg_data;
+ m->msg_data = NULL;
+
+ (void) pthread_mutex_lock(&mgmt->m_resp_mutex);
+ buf = mgmt->m_u.m_resp;
+
+ if ((s->s_type == SessionNormal) &&
+ (mgmt->m_request == mgmt_full_phase_statistics) &&
+ (strcmp(s->s_t_name, mgmt->m_targ_name) == 0)) {
+
+ buf_add_tag(buf, XML_ELEMENT_CONN, Tag_Start);
+ buf_add_tag(buf, s->s_i_name, Tag_String);
+ if (s->s_i_alias != NULL) {
+ xml_add_tag(buf, XML_ELEMENT_ALIAS,
+ s->s_i_alias);
+ }
+
+ /*
+ * Need to loop through the connections
+ * and create one time_connected tag for
+ * each. This will be needed once MC/S support
+ * is added.
+ */
+ (void) snprintf(local_buf, sizeof (local_buf),
+ "%d",
+ mgmt->m_time - s->s_conn_head->c_up_at);
+ xml_add_tag(buf, XML_ELEMENT_TIMECON,
+ local_buf);
+
+ buf_add_tag(buf, XML_ELEMENT_STATS, Tag_Start);
+
+ t10_targ_stat(s->s_t10, buf);
+
+ buf_add_tag(buf, XML_ELEMENT_STATS, Tag_End);
+ buf_add_tag(buf, XML_ELEMENT_CONN, Tag_End);
+ }
+
+ (void) pthread_mutex_unlock(&mgmt->m_resp_mutex);
+
+ queue_message_set(mgmt->m_q, 0, msg_mgmt_rply, 0);
+
+ break;
+
+ default:
+ queue_prt(s->s_mgmtq, Q_SESS_ERRS,
+ "SES%x Unknown msg type (%d) from Connection",
+ s->s_num, m->msg_type);
+ break;
+ }
+ queue_message_free(m);
+ } while (process == True);
+
+ return (NULL);
+}
+
+/*
+ * []----
+ * | session_validate -- do what the name says
+ * |
+ * | At this point the connection has processed the login command so that
+ * | we have InitiatorName and ISID at a minimum. Check to see if there
+ * | are other sessions which match. If so, log that one out and proceed with
+ * | this session. If nothing matches, then link this into a global list.
+ * |
+ * | Once we support multiple connections per session need to scan list
+ * | to see if other connection have the same CID. If so, log out that
+ * | connection.
+ * []----
+ */
+Boolean_t
+session_validate(iscsi_sess_t *s)
+{
+ iscsi_sess_t *check;
+
+ queue_prt(s->s_mgmtq, Q_SESS_NONIO,
+ "SES%x %s ISID[%02x%02x%02x%02x%02x%02x]",
+ s->s_num, s->s_i_alias == NULL ? s->s_i_name : s->s_i_alias,
+ s->s_isid[0], s->s_isid[1], s->s_isid[2],
+ s->s_isid[3], s->s_isid[4], s->s_isid[5]);
+
+
+ /*
+ * SessionType=Discovery which means no target name and therefore
+ * this is okay.
+ */
+ if (s->s_t_name == NULL)
+ return (True);
+
+ (void) pthread_mutex_lock(&sess_mutex);
+ for (check = sess_head; check; check = check->s_next) {
+ /*
+ * Ignore ourselves in this check.
+ */
+ if (check == s)
+ continue;
+ if ((check->s_t_name == NULL) ||
+ (strcmp(check->s_t_name, s->s_t_name) != 0))
+ continue;
+ if (strcmp(check->s_i_name, s->s_i_name) != 0)
+ continue;
+ if (check->s_conn_head->c_tpgt != s->s_conn_head->c_tpgt)
+ continue;
+ /*
+ * Section 5.3.5
+ * Session reinstatement is the process of the initiator
+ * logging in with an ISID that is possible active from
+ * the target's perspective. Thus implicitly logging out
+ * the session that corresponds to the ISID and
+ * reinstating a new iSCSI session in its place (with the
+ * same ISID).
+ */
+ if (bcmp(check->s_isid, s->s_isid, 6) == 0) {
+ queue_prt(s->s_mgmtq, Q_SESS_NONIO,
+ "SES%x Implicit shutdown", check->s_num);
+ if (check->s_conn_head->c_state == S5_LOGGED_IN)
+ conn_state(check->s_conn_head, T8);
+ else
+ conn_state(check->s_conn_head, T7);
+ break;
+ }
+ }
+ (void) pthread_mutex_unlock(&sess_mutex);
+
+ return (True);
+}
+
+/*
+ * []----
+ * | static iscsi_sess_set_auth -
+ * []----
+ */
+static void
+sess_set_auth(iscsi_sess_t *isp)
+{
+ IscsiAuthClient *auth_client = NULL;
+ xml_node_t *node = NULL;
+
+ if (isp == (iscsi_sess_t *)NULL)
+ return;
+
+ /* Zero out the session authentication structure */
+ bzero(&isp->sess_auth, sizeof (iscsi_auth_t));
+ isp->sess_auth.auth_enabled = B_TRUE;
+
+ /* Load CHAP name */
+ node = xml_node_next_child(main_config, XML_ELEMENT_CHAPNAME, NULL);
+ if (node == NULL)
+ return;
+ (void) strcpy(isp->sess_auth.username, node->x_value);
+
+ /* Load CHAP secret */
+ node = xml_node_next_child(main_config, XML_ELEMENT_CHAPSECRET, NULL);
+ if (node == NULL)
+ return;
+ (void) strcpy((char *)isp->sess_auth.password, node->x_value);
+ isp->sess_auth.password_length = strlen(node->x_value);
+
+ /* Set up authentication buffers only if configured */
+ if ((isp->sess_auth.password_length != 0) ||
+ (isp->sess_auth.password_length_in != 0)) {
+ isp->sess_auth.num_auth_buffers = 5;
+ isp->sess_auth.auth_buffers[0].address =
+ &(isp->sess_auth.auth_client_block);
+ isp->sess_auth.auth_buffers[0].length =
+ sizeof (isp->sess_auth.auth_client_block);
+ isp->sess_auth.auth_buffers[1].address =
+ &(isp->sess_auth.auth_recv_string_block);
+ isp->sess_auth.auth_buffers[1].length =
+ sizeof (isp->sess_auth.auth_recv_string_block);
+ isp->sess_auth.auth_buffers[2].address =
+ &(isp->sess_auth.auth_send_string_block);
+ isp->sess_auth.auth_buffers[2].length =
+ sizeof (isp->sess_auth.auth_send_string_block);
+ isp->sess_auth.auth_buffers[3].address =
+ &(isp->sess_auth.auth_recv_binary_block);
+ isp->sess_auth.auth_buffers[3].length =
+ sizeof (isp->sess_auth.auth_recv_binary_block);
+ isp->sess_auth.auth_buffers[4].address =
+ &(isp->sess_auth.auth_send_binary_block);
+ isp->sess_auth.auth_buffers[4].length =
+ sizeof (isp->sess_auth.auth_send_binary_block);
+ }
+
+ if (isp->sess_auth.auth_buffers &&
+ isp->sess_auth.num_auth_buffers) {
+
+ auth_client = (IscsiAuthClient *)isp->
+ sess_auth.auth_buffers[0].address;
+
+ /*
+ * prepare for authentication
+ */
+ if (iscsiAuthClientInit(iscsiAuthNodeTypeTarget,
+ isp->sess_auth.num_auth_buffers,
+ isp->sess_auth.auth_buffers) !=
+ iscsiAuthStatusNoError) {
+ syslog(LOG_ERR, "iscsi connection login failed - "
+ "unable to initialize authentication\n");
+ return;
+ }
+
+ if (iscsiAuthClientSetVersion(auth_client,
+ iscsiAuthVersionRfc) != iscsiAuthStatusNoError) {
+ syslog(LOG_ERR, "iscsi connection login failed - "
+ "unable to set version\n");
+ return;
+ }
+
+ if (isp->sess_auth.username &&
+ (iscsiAuthClientSetUsername(auth_client,
+ isp->sess_auth.username) !=
+ iscsiAuthStatusNoError)) {
+ syslog(LOG_ERR, "iscsi connection login failed - "
+ "unable to set username\n");
+ return;
+ }
+
+ if (isp->sess_auth.password &&
+ (iscsiAuthClientSetPassword(auth_client,
+ isp->sess_auth.password, isp->sess_auth.password_length) !=
+ iscsiAuthStatusNoError)) {
+ syslog(LOG_ERR, "iscsi connection login failed - "
+ "unable to set password\n");
+ return;
+ }
+
+ /*
+ * FIXME: we disable the minimum size check for now
+ */
+ if (iscsiAuthClientSetIpSec(auth_client, 1) !=
+ iscsiAuthStatusNoError) {
+ syslog(LOG_ERR, "iscsi connection login failed - "
+ "unable to set ipsec\n");
+ return;
+ }
+
+ if (iscsiAuthClientSetAuthRemote(auth_client,
+ isp->sess_auth.auth_enabled) !=
+ iscsiAuthStatusNoError) {
+ syslog(LOG_ERR, "iscsi connection login failed - "
+ "unable to set remote authentication\n");
+ return;
+ }
+ }
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.h b/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.h
new file mode 100644
index 0000000000..4426046117
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.h
@@ -0,0 +1,142 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SESSION_H
+#define _SESSION_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Block comment which describes the contents of this file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/iscsi_authclient.h>
+#include "iscsi_cmd.h"
+#include "t10.h"
+
+/*
+ * iSCSI Auth Information
+ */
+typedef struct iscsi_auth {
+ IscsiAuthStringBlock auth_recv_string_block;
+ IscsiAuthStringBlock auth_send_string_block;
+ IscsiAuthLargeBinary auth_recv_binary_block;
+ IscsiAuthLargeBinary auth_send_binary_block;
+ IscsiAuthClient auth_client_block;
+ int num_auth_buffers;
+ IscsiAuthBufferDesc auth_buffers[5];
+
+ /*
+ * To indicate if authentication is enabled.
+ * 0 means authentication disabled.
+ * 1 means authentication enabled.
+ */
+ int auth_enabled;
+
+ /* Initiator's authentication information. */
+ char username[iscsiAuthStringMaxLength];
+ uint8_t password[iscsiAuthStringMaxLength];
+ int password_length;
+
+ /* Target's authentication information. */
+ char username_in[iscsiAuthStringMaxLength];
+ uint8_t password_in[iscsiAuthStringMaxLength];
+ int password_length_in;
+} iscsi_auth_t;
+
+typedef enum iscsi_session_type {
+ SessionDiscovery, SessionNormal
+} iscsi_session_type_t;
+
+typedef enum iscsi_session_state {
+ SS_FREE, SS_STARTED, SS_RUNNING, SS_SHUTDOWN_START, SS_SHUTDOWN_CMPLT
+} iscsi_sess_state_t;
+
+typedef struct iscsi_sess {
+ struct iscsi_sess *s_next;
+
+ iscsi_sess_state_t s_state;
+
+ /*
+ * Set during login
+ * mutex isn't held.
+ */
+ char *s_i_name,
+ *s_i_alias,
+ *s_t_name;
+ uint8_t s_isid[6];
+ /*
+ * This is the highest packet number we've seen and is
+ * used during replies.
+ */
+ int s_seencmdsn;
+
+ /*
+ * To keep the correct order of PDU's submitted to the SCSI
+ * layer we check that the incoming cmdsn matches this value.
+ * Otherwise, we're missing a packet and need to wait. This
+ * is particularly important with multiple connections per
+ * session.
+ */
+ int s_cmdsn;
+
+ iscsi_session_type_t s_type;
+
+ /*
+ * Set during allocation of this struct and only referenced
+ */
+ int s_tsid;
+
+ target_queue_t *s_sessq,
+ *s_t10q,
+ *s_mgmtq;
+
+ t10_targ_handle_t s_t10;
+
+ struct iscsi_conn *s_conn_head;
+
+ int s_num;
+
+ pthread_mutex_t s_mutex;
+ iscsi_auth_t sess_auth;
+ pthread_t s_thr_id_conn,
+ s_thr_id_t10;
+} iscsi_sess_t;
+
+void session_init();
+Boolean_t session_alloc(struct iscsi_conn *c, uint8_t *isid);
+Boolean_t session_validate(struct iscsi_sess *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SESSION_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_target.xml b/usr/src/cmd/iscsi/iscsitgtd/iscsi_target.xml
new file mode 100644
index 0000000000..e0bd20b7a5
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_target.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+
+ ident "%Z%%M% %I% %E% SMI"
+
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWiscsitgtu:iscsitgt'>
+
+<service
+ name='system/iscsitgt'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <single_instance/>
+
+ <!-- We need name resolution and full filesystem access -->
+ <dependency
+ name='milestone'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/milestone/multi-user' />
+ </dependency>
+
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/sbin/iscsitgtd'
+ timeout_seconds='60'>
+ <method_context>
+ <method_credential
+ user='root'
+ group='sys'
+ privileges='basic,net_rawaccess,sys_mount,file_dac_write,sys_devices' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='60'>
+ <method_context>
+ <method_credential
+ user='root'
+ group='sys'
+ privileges='basic,net_rawaccess,sys_mount,file_dac_write,sys_devices' />
+ </method_context>
+ </exec_method>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'> Solaris iSCSI Target
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='iscsitgtd' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='iscsitadm' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/iscsi/iscsitgtd/main.c b/usr/src/cmd/iscsi/iscsitgtd/main.c
new file mode 100644
index 0000000000..3769262ea2
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/main.c
@@ -0,0 +1,975 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#define FD_SETSIZE 65536
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/conf.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <door.h>
+#include <iscsi_door.h>
+#include <signal.h>
+#include <siginfo.h>
+#include <sys/ethernet.h>
+#include <libscf.h>
+#include <syslog.h>
+#include <synch.h>
+#include <libxml/xmlreader.h>
+#include <sys/resource.h>
+#include <syslog.h>
+#include <sys/select.h>
+
+#include "queue.h"
+#include "port.h"
+#include "iscsi_conn.h"
+#include "target.h"
+#include "xml.h"
+#include "utility.h"
+#include "iscsi_ffp.h"
+#include "errcode.h"
+#include "t10.h"
+
+#define EMPTY_CONFIG "<config version='1.0'>\n</config>\n"
+
+/* ---- Forward declarations ---- */
+static void variable_handler(xml_node_t *, target_queue_t *, target_queue_t *);
+
+
+/* ---- Global configuration data. ---- */
+char *target_basedir = NULL;
+char *target_log = DEFAULT_TARGET_LOG;
+char *config_file = DEFAULT_CONFIG_LOCATION;
+int iscsi_port = 3260; /* defined by the spec */
+xml_node_t *main_config,
+ *targets_config;
+Boolean_t enforce_strict_guid = True,
+ thin_provisioning = False,
+ disable_tpgs = False,
+ dbg_timestamps = False;
+int targets_vers_maj,
+ targets_vers_min,
+ main_vers_maj,
+ main_vers_min;
+pthread_mutex_t targ_config_mutex;
+
+int dbg_lvl = 0;
+
+/*
+ * door_return doesn't have a means to free the memory that's passed in
+ * which means the program must either use the buffer space provided as
+ * the argument to the service routine or allocate it's own and create
+ * a leak. Since the argument to the service routine is likely to be a
+ * fairly small buffer it may not be able to hold the results. So, the
+ * daemon which already has the results supplies that value to the door_return
+ * and sets up a request to collect that memory later. This structure
+ * empty_garbage(), and delayed_free() are used to recoupe the memory.
+ */
+typedef struct garbage_can {
+ char *g_buf;
+ int g_timo;
+} garbage_can_t;
+
+typedef struct var_table {
+ char *v_name;
+ int *v_value;
+} var_table_t;
+
+typedef struct cmd_table {
+ char *c_name;
+ void (*c_func)(xml_node_t *, target_queue_t *, target_queue_t *);
+} cmd_table_t;
+
+admin_table_t admin_prop_list[] = {
+ {XML_ELEMENT_BASEDIR, update_basedir},
+ {XML_ELEMENT_CHAPSECRET, 0},
+ {XML_ELEMENT_CHAPNAME, 0},
+ {XML_ELEMENT_RAD_ACCESS, 0},
+ {XML_ELEMENT_RAD_SERV, valid_radius_srv},
+ {XML_ELEMENT_RAD_SECRET, 0},
+ {XML_ELEMENT_ISNS_ACCESS, 0},
+ {XML_ELEMENT_FAST, 0},
+ {0, 0}
+};
+
+/*
+ * Global variables which can be set via the management XML interface
+ * with the syntax of "<variable><dbg_lvl>0x033</dbg_lvl></variable>"
+ */
+var_table_t var_table[] = {
+ { "dbg_lvl", &dbg_lvl },
+ { "qlog_lvl", &qlog_lvl },
+ /* ---- End of Table marker ---- */
+ { NULL, 0 }
+};
+
+/*
+ * Commands which are run via the management XML interface
+ */
+cmd_table_t cmd_table[] = {
+ { "variable", variable_handler },
+ { "create", create_func },
+ { "modify", modify_func },
+ { "delete", remove_func },
+ { "list", list_func },
+ /* ---- End of Table marker ---- */
+ { NULL, NULL }
+};
+
+/*
+ * []----
+ * | scan_for_luns -- scan target for missing luns
+ * |
+ * | Checks to see if there are any LUs not in the target node which
+ * | exist in the target directory and adds them. Also checks to see
+ * | if there are LUs in the target node which are not present in the
+ * | directory and removes them.
+ * |
+ * | NOTE: This routine expects that the targ_dir has already been
+ * | verified to exist and is a directory
+ * []----
+ */
+static void
+scan_for_luns(xml_node_t *targ, char *targ_dir)
+{
+ xml_node_t *lun_list = NULL,
+ *lun = NULL;
+ Boolean_t changes_made = False;
+ DIR *dp;
+ struct dirent *de;
+ fd_set lu_bits;
+ int l;
+
+ FD_ZERO(&lu_bits);
+
+ if ((lun_list = xml_node_next(targ, XML_ELEMENT_LUNLIST, NULL)) ==
+ NULL) {
+ /*
+ * Most likely have an older configuration file that
+ * didn't list the LUs assocated with each target. No problem.
+ * Create a node and add it to the target.
+ */
+ lun_list = xml_alloc_node(XML_ELEMENT_LUNLIST, String, "");
+ (void) xml_add_child(targ, lun_list);
+ changes_made = True;
+ } else {
+ /*
+ * Build up a bit mask of the current LU set. This will be
+ * used to see if a LU already exists or not.
+ * When a LU is found the bit will be checked. If it's
+ * set then we know the current tree is correct for that
+ * LU and the bit will be cleared. If it's not set we're
+ * missing that LU and it will be added.
+ * Once we've got through the list any bits that are still
+ * set means that LU node in the tree should be removed.
+ */
+ while ((lun = xml_node_next(lun_list, XML_ELEMENT_LUN, lun)) !=
+ NULL) {
+ (void) xml_find_value_int(lun, XML_ELEMENT_LUN, &l);
+ FD_SET(l, &lu_bits);
+ }
+ }
+
+ dp = opendir(targ_dir);
+ assert(dp != NULL);
+ while ((de = readdir(dp)) != NULL) {
+ if ((strcmp(de->d_name, ".") == 0) ||
+ (strcmp(de->d_name, "..") == 0))
+ continue;
+ /* ---- Look for the LU backing store files ---- */
+ if (strncmp(de->d_name, LUNBASE, sizeof (LUNBASE) - 1) != 0)
+ continue;
+
+ l = atoi(&de->d_name[sizeof (LUNBASE) - 1]);
+ if (FD_ISSET(l, &lu_bits) == 0) {
+ lun = xml_alloc_node(XML_ELEMENT_LUN, Int, &l);
+ (void) xml_add_child(lun_list, lun);
+ changes_made = True;
+ } else
+ FD_CLR(l, &lu_bits);
+ }
+ closedir(dp);
+ for (l = 0; l < FD_SETSIZE; l++) {
+ if (FD_ISSET(l, &lu_bits)) {
+ lun = xml_alloc_node(XML_ELEMENT_LUN, Int, &l);
+ (void) xml_remove_child(lun_list, lun, MatchBoth);
+ xml_tree_free(lun);
+ changes_made = True;
+ }
+ }
+ if (changes_made == True)
+ (void) update_config_targets(NULL);
+}
+/*
+ * []----
+ * | process_target_config -- Load up the targets into memory
+ * []----
+ */
+Boolean_t
+process_target_config()
+{
+ xmlTextReaderPtr r = NULL;
+ char path[MAXPATHLEN],
+ *target = NULL;
+ struct stat ss;
+ xml_node_t *node = NULL,
+ *next = NULL;
+ int xml_fd = -1;
+
+ if (target_basedir != NULL) {
+ if (access(target_basedir, R_OK) != 0) {
+
+ /*
+ * The target base directory has been set, but no
+ * longer exists which means someone has removed it
+ * behind our back. Obviously something bad has
+ * occurred, but we should attempt to start anyway and
+ * do so with an empty configuration.
+ */
+ r = (xmlTextReaderPtr)xmlReaderForMemory(EMPTY_CONFIG,
+ strlen(EMPTY_CONFIG), NULL, NULL, 0);
+ syslog(LOG_WARNING,
+ "Previous target directory (%s) has been removed",
+ target_basedir);
+
+ } else {
+
+ (void) snprintf(path, MAXPATHLEN, "%s/%s",
+ target_basedir, "config.xml");
+ if (access(path, R_OK) != 0) {
+
+ /*
+ * No existing configuration, but we have
+ * the target directory so attempt to create
+ * an empty configuration file.
+ * If the open or write fail there's a
+ * serious problem which needs attention.
+ */
+ if ((xml_fd = open(path,
+ O_RDWR | O_CREAT, 0600)) < 0) {
+
+ syslog(LOG_ERR,
+ "Can not create empty "
+ "configuration file in %s",
+ target_basedir);
+ return (False);
+ }
+
+ if (write(xml_fd, EMPTY_CONFIG,
+ strlen(EMPTY_CONFIG)) !=
+ strlen(EMPTY_CONFIG)) {
+ syslog(LOG_ERR, "Failed to write empty "
+ "configuration file in %s", path);
+ return (False);
+ }
+
+ (void) close(xml_fd);
+ }
+
+ if ((xml_fd = open(path, O_RDONLY)) >= 0)
+ r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd,
+ NULL, NULL, 0);
+ }
+ } else {
+ r = (xmlTextReaderPtr)xmlReaderForMemory(EMPTY_CONFIG,
+ strlen(EMPTY_CONFIG), NULL, NULL, 0);
+ }
+
+ if (r != NULL) {
+ while (xmlTextReaderRead(r) == 1) {
+ if (xml_process_node(r, &node) == False)
+ break;
+ }
+
+ /*
+ * Validate the configuration file has the appropriate
+ * version number.
+ */
+ targets_vers_maj = XML_VERS_TARG_MAJ;
+ targets_vers_min = XML_VERS_TARG_MIN;
+ if (validate_version(node, &targets_vers_maj,
+ &targets_vers_min) == False) {
+ syslog(LOG_ERR, "Target config(%s/config.xml) invalid",
+ target_basedir);
+ return (False);
+ }
+
+ targets_config = node;
+ while ((next = xml_node_next(node, XML_ELEMENT_TARG,
+ next)) != NULL) {
+ if (xml_find_value_str(next, XML_ELEMENT_INAME,
+ &target) == False) {
+ continue;
+ }
+ (void) snprintf(path, MAXPATHLEN, "%s/%s",
+ target_basedir, target);
+ if (stat(path, &ss) < 0) {
+ continue;
+ }
+ if ((ss.st_mode & S_IFDIR) == 0) {
+ continue;
+ }
+ scan_for_luns(next, path);
+ free(target);
+ }
+ if (xml_fd != -1)
+ (void) close(xml_fd);
+ xmlTextReaderClose(r);
+ xmlFreeTextReader(r);
+ xmlCleanupParser();
+ return (True);
+ } else {
+
+ if (xml_fd != -1)
+ (void) close(xml_fd);
+ /*
+ * NOTE: Look at sending a syslog message or maybe
+ * something to FMA.
+ */
+ return (False);
+ }
+}
+
+/*
+ * []----
+ * | process_config -- parse the main configuration file
+ * |
+ * | Everything in the configuratin file is optional. That's because
+ * | the management CLI can set the value to everything and update
+ * | the configuration.
+ * []----
+ */
+static Boolean_t
+process_config(char *file)
+{
+ xmlTextReaderPtr r;
+ int ret,
+ xml_fd = -1;
+ xml_node_t *node = NULL;
+
+#ifndef lint
+ LIBXML_TEST_VERSION;
+#endif
+
+ if (access(file, R_OK) != 0) {
+ if ((xml_fd = open(file, O_RDWR | O_CREAT, 0600)) < 0)
+ return (False);
+ if (write(xml_fd, EMPTY_CONFIG, strlen(EMPTY_CONFIG)) !=
+ strlen(EMPTY_CONFIG))
+ return (False);
+ (void) close(xml_fd);
+ }
+
+ if ((xml_fd = open(file, O_RDONLY)) >= 0)
+ r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd, NULL, NULL, 0);
+
+ if (r != NULL) {
+ ret = xmlTextReaderRead(r);
+ while (ret == 1) {
+ if (xml_process_node(r, &node) == False) {
+ break;
+ }
+ ret = xmlTextReaderRead(r);
+ }
+
+ /*
+ * Validate the configuration file has the appropriate
+ * version number.
+ */
+ main_vers_maj = XML_VERS_MAIN_MAJ;
+ main_vers_min = XML_VERS_MAIN_MIN;
+ if (validate_version(node, &main_vers_maj, &main_vers_min) ==
+ False) {
+ syslog(LOG_ERR, "Target main config invalid");
+ return (False);
+ }
+
+ /*
+ * The base directory is optional in the sense that the daemon
+ * can start without it, but the daemon can't really do
+ * anything until the administrator sets the value.
+ */
+ (void) xml_find_value_str(node, XML_ELEMENT_BASEDIR,
+ &target_basedir);
+
+ /*
+ * These are optional settings for the target. Each of
+ * these has a default value which can be overwritten in
+ * the configuration file.
+ */
+ (void) xml_find_value_str(node, XML_ELEMENT_TARGLOG,
+ &target_log);
+ (void) xml_find_value_int(node, XML_ELEMENT_ISCSIPORT,
+ &iscsi_port);
+ (void) xml_find_value_int(node, XML_ELEMENT_DBGLVL, &dbg_lvl);
+ (void) xml_find_value_boolean(node, XML_ELEMENT_ENFORCE,
+ &enforce_strict_guid);
+ (void) xml_find_value_boolean(node, XML_ELEMENT_THIN_PROVO,
+ &thin_provisioning);
+ (void) xml_find_value_boolean(node, XML_ELEMENT_DISABLE_TPGS,
+ &disable_tpgs);
+ (void) xml_find_value_boolean(node, XML_ELEMENT_TIMESTAMPS,
+ &dbg_timestamps);
+ if (xml_find_value_int(node, XML_ELEMENT_LOGLVL,
+ &qlog_lvl) == True)
+ queue_log(True);
+
+ main_config = node;
+ if (xml_fd != -1)
+ (void) close(xml_fd);
+ xmlTextReaderClose(r);
+ xmlFreeTextReader(r);
+ xmlCleanupParser();
+
+ return (True);
+ } else {
+ if (xml_fd != -1)
+ (void) close(xml_fd);
+ return (False);
+ }
+}
+
+/*
+ * []----
+ * | logout_cleanup -- see if the initiator did what was requested
+ * |
+ * | When a target issues an asynchrouns event with the code set to
+ * | "logout requested" the initiator is supposed to respond with
+ * | a LogoutRequested PDU within a certain amount of time. If it
+ * | fails to do so, it's the targets responsibility to clean up.
+ * | After ASYNC_LOGOUT_TIMEOUT seconds (currently 10) we look to
+ * | see if any connections are still in the S7_LOGOUT_REQUESTED
+ * | state. If so, reissue the management request to logout which
+ * | will cause the connections to close.
+ * []----
+ */
+static void *
+logout_cleanup(void *v)
+{
+ int msg_sent,
+ i;
+ char *targ = (char *)v;
+ mgmt_request_t m;
+ iscsi_conn_t *conn;
+ extern pthread_mutex_t port_mutex;
+
+ bzero(&m, sizeof (m));
+ m.m_request = mgmt_logout;
+ m.m_q = queue_alloc();
+ msg_sent = 0;
+
+ (void) sleep(ASYNC_LOGOUT_TIMEOUT);
+ (void) pthread_mutex_lock(&port_mutex);
+ for (conn = conn_head; conn; conn = conn->c_next) {
+ if ((conn->c_state == S7_LOGOUT_REQUESTED) &&
+ (strcmp(conn->c_sess->s_t_name, targ) == 0)) {
+
+ queue_message_set(conn->c_dataq, 0,
+ msg_mgmt_rqst, &m);
+ msg_sent++;
+ }
+ }
+ (void) pthread_mutex_unlock(&port_mutex);
+
+ /*
+ * Wait to see if they received the message.
+ */
+ for (i = 0; i < msg_sent; i++)
+ queue_message_free(queue_message_get(m.m_q));
+ queue_free(m.m_q, NULL);
+ free(targ);
+
+ return ((void *)0);
+}
+
+void
+logout_targ(char *targ)
+{
+ mgmt_request_t m;
+ iscsi_conn_t *conn;
+ int i,
+ msg_sent;
+ pthread_t junk;
+ extern pthread_mutex_t port_mutex;
+
+ /*
+ * Now we look for connections to this target and issue
+ * a request to asynchronously logout.
+ */
+ bzero(&m, sizeof (m));
+ m.m_request = mgmt_logout;
+ m.m_q = queue_alloc();
+ msg_sent = 0;
+
+ (void) pthread_mutex_lock(&port_mutex);
+ for (conn = conn_head; conn; conn = conn->c_next) {
+ if ((conn->c_state == S5_LOGGED_IN) &&
+ (strcmp(conn->c_sess->s_t_name, targ) == 0)) {
+
+ queue_message_set(conn->c_dataq, 0, msg_mgmt_rqst, &m);
+ msg_sent++;
+ }
+ }
+ (void) pthread_mutex_unlock(&port_mutex);
+
+ /* ---- Wait to see if they received the message. ---- */
+ for (i = 0; i < msg_sent; i++)
+ queue_message_free(queue_message_get(m.m_q));
+
+ queue_free(m.m_q, NULL);
+
+ /* ---- Start housecleaning thread ---- */
+ (void) pthread_create(&junk, NULL, logout_cleanup,
+ (void *)strdup(targ));
+}
+
+/*
+ * [] ---- XML Management Handlers ---- []
+ */
+
+/*
+ * []----
+ * | variable_handler -- used to set a couple of internal global variables
+ * []----
+ */
+/*ARGSUSED*/
+void
+variable_handler(xml_node_t *x, target_queue_t *reply, target_queue_t *mgmt)
+{
+ char *reply_buf = NULL;
+ var_table_t *v;
+ xml_node_t *c;
+
+ for (c = x->x_child; c; c = c->x_sibling) {
+
+ for (v = var_table; v->v_name; v++) {
+ if (strcmp(c->x_name, v->v_name) == 0) {
+ *v->v_value = strtol(c->x_value, NULL, 0);
+ if (strcmp(v->v_name, "qlog_lvl") == 0)
+ queue_log(True);
+ xml_rtn_msg(&reply_buf, ERR_SUCCESS);
+ break;
+ }
+ }
+ if (v->v_name == NULL)
+ xml_rtn_msg(&reply_buf, ERR_NO_MATCH);
+
+ queue_str(reply, 0, msg_mgmt_rply, reply_buf);
+ }
+}
+
+/*
+ * []----
+ * | parse_xml -- incoming management requests are sent here for processing
+ * []----
+ */
+static void
+parse_xml(xml_node_t *x, target_queue_t *reply, target_queue_t *mgmt)
+{
+ char *reply_msg = NULL;
+ cmd_table_t *c;
+
+ if ((x->x_name == NULL) || (x->x_state == NodeFree)) {
+ xml_rtn_msg(&reply_msg, ERR_SYNTAX_EMPTY);
+ queue_message_set(reply, 0, msg_mgmt_rply, reply_msg);
+ return;
+ }
+
+ for (c = cmd_table; c->c_name != NULL; c++)
+ if (strcmp(c->c_name, x->x_name) == 0)
+ break;
+ if (c->c_name == NULL) {
+ xml_rtn_msg(&reply_msg, ERR_INVALID_COMMAND);
+ queue_message_set(reply, 0, msg_mgmt_rply, reply_msg);
+ } else {
+ (c->c_func)(x, reply, mgmt);
+ }
+}
+
+/*
+ * []----
+ * | empty_garbage -- sleep for the requested time and then release the memory
+ * []----
+ */
+static void *
+empty_garbage(void *v)
+{
+ garbage_can_t *g = (garbage_can_t *)v;
+
+ (void) sleep(g->g_timo);
+ free(g->g_buf);
+ free(g);
+ return (NULL);
+}
+
+/*
+ * []----
+ * | delayed_free -- set things up to free a chunk of memory later
+ * []----
+ */
+static void
+delayed_free(char *buf, int timeout)
+{
+ garbage_can_t *g;
+ pthread_t junk;
+
+ /*
+ * if we can't get memory we're pretty much sunk.
+ */
+ if ((g = (garbage_can_t *)calloc(1, sizeof (*g))) == NULL)
+ return;
+
+ g->g_buf = buf;
+ g->g_timo = timeout;
+
+ (void) pthread_create(&junk, NULL, empty_garbage, g);
+}
+
+/*ARGSUSED*/
+static void
+server_for_door(void *cookie, char *argp, size_t arg_size, door_desc_t *dp,
+ uint_t n_desc)
+{
+ target_queue_t *mgmtq = (target_queue_t *)cookie;
+ mgmt_request_t m;
+ msg_t *msg = NULL;
+ xml_node_t *node = NULL;
+ xmlTextReaderPtr r;
+ char *err_rply = NULL;
+
+ bzero(&m, sizeof (m));
+
+ if ((r = (xmlTextReaderPtr)xmlReaderForMemory(argp, strlen(argp),
+ NULL, NULL, 0)) != NULL) {
+ while (xmlTextReaderRead(r)) {
+ if (xml_process_node(r, &node) == False)
+ break;
+ }
+ if (node != NULL) {
+ m.m_q = queue_alloc();
+ m.m_request = mgmt_parse_xml;
+ m.m_time = time(NULL);
+ m.m_targ_name = NULL;
+ m.m_u.m_node = node;
+
+ queue_message_set(mgmtq, 0, msg_mgmt_rqst, &m);
+ if ((msg = queue_message_get(m.m_q)) == NULL) {
+ xmlFreeTextReader(r);
+ xmlCleanupParser();
+ door_return("", 1, NULL, 0);
+ }
+
+ /*
+ * Check to see if the response can fit into the
+ * incoming argument buffer. If so, copy the response
+ * to that buffer so that we can free the data.
+ * If it's not big enough we'll return the pointer to
+ * the message response and have to free the data
+ * at another time.
+ */
+ if (strlen(msg->msg_data) < arg_size) {
+ (void) strcpy(argp, msg->msg_data);
+ free(msg->msg_data);
+ } else {
+ delayed_free(msg->msg_data, 2);
+ argp = msg->msg_data;
+ }
+ queue_message_free(msg);
+ } else {
+ xml_rtn_msg(&err_rply, ERR_NULL_XML_MESSAGE);
+ }
+
+ xmlFreeTextReader(r);
+ xmlCleanupParser();
+
+ } else {
+ xml_rtn_msg(&err_rply, ERR_INIT_XML_READER_FAILED);
+ }
+
+ if (node != NULL)
+ xml_tree_free(node);
+ if (err_rply != NULL) {
+ strcpy(argp, err_rply);
+ free(err_rply);
+ }
+ if (m.m_q != NULL)
+ queue_free(m.m_q, NULL);
+
+ (void) door_return(argp, strlen(argp) + 1, NULL, 0);
+}
+
+/*
+ * []----
+ * | setup_door -- Create a door portal for management requests
+ * |
+ * | First check to see if another daemon is already running by attempting
+ * | to send an empty request to the door. If successful it means this
+ * | daemon should exit.
+ * []----
+ */
+static void
+setup_door(target_queue_t *q, char *door_name)
+{
+ int did,
+ fd;
+ struct stat s;
+ door_arg_t d;
+
+ if ((fd = open(door_name, 0)) >= 0) {
+
+ /*
+ * There's at least a file with the same name as our
+ * door. Let's see if someone is currently answering
+ * by sending an empty XML request.
+ */
+ d.data_ptr = "<config></config>";
+ d.data_size = strlen(d.data_ptr) + 1;
+ d.desc_ptr = NULL;
+ d.desc_num = 0;
+ d.rbuf = NULL;
+ d.rsize = 0;
+
+ if (door_call(fd, &d) == 0) {
+
+ /*
+ * If the door_call succeeds that means another
+ * daemon is already running so let's just exit.
+ */
+ exit(0);
+ }
+ (void) close(fd);
+ }
+
+ if ((did = door_create(server_for_door, (void *)q, 0)) < 0) {
+ syslog(LOG_ERR, "door_create");
+ exit(1);
+ }
+
+ if (stat(door_name, &s) < 0) {
+ int newfd;
+ if ((newfd = creat(door_name, 0400)) < 0) {
+ syslog(LOG_ERR, "creat failed");
+ exit(1);
+ }
+ (void) close(newfd);
+ }
+
+ (void) fdetach(door_name);
+ if (fattach(did, door_name) < 0) {
+ syslog(LOG_ERR, "fattach failed errno=%d", errno);
+ exit(2);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ char c,
+ *p,
+ *door_name;
+ msg_t *msg;
+ target_queue_t *q;
+ port_args_t port1,
+ port2;
+ Boolean_t mgmt_up = False,
+ daemonize = True,
+ console_output = True;
+ pthread_t junk;
+ mgmt_request_t *mgmt;
+ struct sigaction act;
+ struct rlimit rl;
+
+ door_name = ISCSI_TARGET_MGMT_DOOR;
+
+ while ((c = getopt(argc, argv, "c:d:")) != EOF) {
+ switch (c) {
+ case 'c':
+ config_file = optarg;
+ break;
+ case 'd':
+ door_name = optarg;
+ break;
+ }
+ }
+
+ /*
+ * If the initiator closes the socket because of a protocol error
+ * or bad digest on the header packet we'll receive a SIGPIPE if we're
+ * in the middle of a write operation. There's no need to receive
+ * a signal when a -1 from the write will handle things correctly.
+ * So, ignore SIGPIPE's.
+ */
+ (void) sigignore(SIGPIPE);
+
+ /*
+ * Look at the function lu_buserr_handler() above to see the details
+ * of why we need to handle segmentation violations.
+ */
+ bzero(&act, sizeof (act));
+ act.sa_sigaction = lu_buserr_handler;
+ act.sa_flags = SA_SIGINFO;
+
+ if (sigaction(SIGBUS, &act, NULL) == -1) {
+ perror("sigaction");
+ exit(SMF_EXIT_ERR_CONFIG);
+ }
+
+ if (process_config(config_file) == False)
+ exit(SMF_EXIT_ERR_CONFIG);
+
+ if (process_target_config() == False)
+ exit(SMF_EXIT_ERR_CONFIG);
+
+ (void) xml_find_value_boolean(main_config, XML_ELEMENT_DBGDAEMON,
+ &daemonize);
+ if (daemonize == True) {
+ switch (fork()) {
+ case 0:
+ /* ---- I'm the daemon, continue running ---- */
+ break;
+
+ case -1:
+ /* ---- Failed to fork!. Trouble ---- */
+ exit(SMF_EXIT_ERR_CONFIG);
+
+ default:
+ exit(SMF_EXIT_OK);
+ }
+ closefrom(0);
+ }
+
+ q = queue_alloc();
+
+ /*
+ * Initialize the various subsystems. In most cases these are
+ * just initializing mutexs.
+ */
+ (void) pthread_mutex_init(&targ_config_mutex, NULL);
+ iscsi_cmd_init();
+ session_init();
+ t10_init(q);
+ port_init();
+ queue_init();
+ util_init();
+
+ /*
+ * If there's no MAC address currently available don't worry about
+ * it. The first time an initiator connects the SAM-3 layer will
+ * attempt to create a GUID and force another look for a MAC address.
+ */
+ if (if_find_mac(q) == False)
+ queue_prt(q, Q_GEN_DETAILS, "MAIN: No MAC address available");
+
+ /*
+ * At a minimum we need two file descriptors for each target, one for
+ * the socket and one for the backing store. If there's more than one
+ * initiator attached to a given target than that number goes up by 1.
+ * Once we have multiple sessions per connection that to will cause
+ * an increase.
+ */
+ if ((getrlimit(RLIMIT_NOFILE, &rl) == 0) &&
+ (rl.rlim_cur < TARGET_NOFILE)) {
+ rl.rlim_cur = TARGET_NOFILE;
+ if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
+ syslog(LOG_NOTICE,
+ "Can't set new limit for open files");
+ }
+
+ port1.port_mgmtq = q;
+ port1.port_num = iscsi_port;
+ (void) pthread_create(&junk, NULL, port_watcher, &port1);
+
+ setup_door(q, door_name);
+ if ((xml_find_value_int(main_config, XML_ELEMENT_MGMTPORT,
+ &port2.port_num) == True) && (port2.port_num != -1)) {
+ port2.port_mgmtq = q;
+ port2.port_dataq = queue_alloc();
+ (void) pthread_create(&junk, NULL, port_management, &port2);
+ }
+
+ do {
+ msg = queue_message_get(q);
+
+ switch (msg->msg_type) {
+ case msg_log:
+ if ((p = strchr(msg->msg_data, '\n')) != NULL)
+ *p = '\0';
+ p = (char *)msg->msg_data;
+ if ((msg->msg_pri_level & dbg_lvl) == 0)
+ break;
+
+ if (mgmt_up == True)
+ queue_str(port2.port_dataq, Q_GEN_DETAILS,
+ msg_log, p);
+ if (console_output == True)
+ (void) printf("%s\n", p);
+ break;
+
+ case msg_mgmt_rqst:
+ mgmt = (mgmt_request_t *)msg->msg_data;
+ if (mgmt->m_request == mgmt_parse_xml)
+ parse_xml(mgmt->m_u.m_node, mgmt->m_q, q);
+ msg->msg_data = NULL;
+ break;
+
+ case msg_status:
+ p = (char *)msg->msg_data;
+ /*
+ * NOTE:
+ * These are real error conditons being sent from
+ * the other threads and should be logged in
+ * some manner, either syslog() or using a FMA
+ * interface.
+ */
+ (void) printf("STATUS: %s\n", p);
+ break;
+
+ default:
+ break;
+ }
+
+ if (msg->msg_data != NULL)
+ free(msg->msg_data);
+ queue_message_free(msg);
+ /*CONSTANTCONDITION*/
+ } while (1);
+
+ return (0);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/mgmt.c b/usr/src/cmd/iscsi/iscsitgtd/mgmt.c
new file mode 100644
index 0000000000..13bcc97105
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/mgmt.c
@@ -0,0 +1,259 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <sys/filio.h>
+#include <utility.h>
+#include <synch.h>
+#include <sys/stropts.h>
+#include <libxml/xmlreader.h>
+
+#include "queue.h"
+#include "port.h"
+#include "utility.h"
+#include "xml.h"
+
+static void
+mgmt_monitor_queue(port_args_t *p)
+{
+ target_queue_t *in = p->port_dataq;
+ msg_t *m;
+ int process = True;
+ char *data,
+ *output;
+
+ do {
+ m = queue_message_get(in);
+ switch (m->msg_type) {
+ case msg_conn_lost:
+ process = False;
+ break;
+
+ case msg_log:
+ data = (char *)m->msg_data;
+ output = NULL;
+ xml_add_tag(&output, "log", data);
+ (void) write(p->port_socket, output, strlen(output));
+ free(output);
+ break;
+
+ case msg_mgmt_rply:
+ data = (char *)m->msg_data;
+ output = NULL;
+ xml_add_tag(&output, "mgmt", data);
+ (void) write(p->port_socket, output, strlen(output));
+ free(output);
+ free(data);
+ m->msg_data = NULL;
+ break;
+
+ default:
+ break;
+ }
+
+ if (m->msg_data)
+ free(m->msg_data);
+ queue_message_free(m);
+
+ } while (process == True);
+}
+
+static void *
+mgmt_process(void *v)
+{
+ port_args_t *p = (port_args_t *)v;
+ int nbytes,
+ nmsgs,
+ pval,
+ ret;
+ char *buf;
+ nfds_t nfds = 1;
+ struct pollfd fds[1];
+ xmlTextReaderPtr r;
+ xml_node_t *node = NULL;
+ mgmt_request_t m;
+
+ fds[0].fd = p->port_socket;
+ fds[0].events = POLLIN;
+
+ m.m_q = p->port_dataq;
+ m.m_request = mgmt_parse_xml;
+ m.m_time = time(NULL);
+ m.m_targ_name = NULL;
+
+ while ((pval = poll(fds, nfds, -1)) != -1) {
+ if ((nmsgs = ioctl(p->port_socket, FIONREAD, &nbytes)) == -1) {
+
+ queue_message_set(p->port_dataq, 0, msg_conn_lost, 0);
+ break;
+
+ } else if ((nmsgs == 0) && (nbytes == 0)) {
+
+ queue_message_set(p->port_dataq, 0, msg_conn_lost, 0);
+ break;
+
+ } else if ((buf = malloc(nbytes)) == NULL) {
+
+ queue_message_set(p->port_dataq, 0, msg_conn_lost, 0);
+ break;
+
+ } else if (read(p->port_socket, buf, nbytes) != nbytes) {
+
+ queue_message_set(p->port_dataq, 0, msg_conn_lost, 0);
+ break;
+
+ }
+
+ buf[nbytes] = '\0';
+ r = (xmlTextReaderPtr)xmlReaderForMemory(buf, nbytes,
+ NULL, NULL, 0);
+ if (r != NULL) {
+ ret = xmlTextReaderRead(r);
+ while (ret == 1) {
+ if (xml_process_node(r, &node) == False)
+ break;
+ ret = xmlTextReaderRead(r);
+ }
+ if (node != NULL) {
+ m.m_u.m_node = node;
+ queue_message_set(p->port_mgmtq, 0,
+ msg_mgmt_rqst, &m);
+ }
+ xmlFreeTextReader(r);
+ node = NULL;
+ }
+
+ }
+
+ if (pval == -1)
+ queue_message_set(p->port_dataq, 0, msg_conn_lost, 0);
+ (void) close(p->port_socket);
+ p->port_socket = -1;
+ return (NULL);
+}
+
+void *
+port_management(void *v)
+{
+ int s,
+ fd,
+ on = 1;
+ struct sockaddr_in sin_ip;
+ struct sockaddr_in6 sin6_ip;
+ socklen_t fromlen;
+ struct sockaddr_storage from;
+ port_args_t *p = (port_args_t *)v;
+ target_queue_t *q = p->port_mgmtq;
+ int l;
+ pthread_t junk;
+ char debug[80];
+
+ if ((s = socket(PF_INET6, SOCK_STREAM, 0)) == -1) {
+ if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
+ queue_str(q, Q_GEN_ERRS, msg_status,
+ "Can't open socket");
+ return (NULL);
+ } else {
+
+ bzero(&sin_ip, sizeof (sin_ip));
+ sin_ip.sin_family = AF_INET;
+ sin_ip.sin_port = htons(p->port_num);
+ sin_ip.sin_addr.s_addr = INADDR_ANY;
+
+ (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof (on));
+
+ if ((bind(s, (struct sockaddr *)&sin_ip,
+ sizeof (sin_ip))) < 0) {
+ (void) snprintf(debug, sizeof (debug),
+ "bind on port %d failed\n", p->port_num);
+ queue_str(q, Q_GEN_ERRS, msg_status, debug);
+ return (NULL);
+ }
+ }
+ } else {
+
+ bzero(&sin6_ip, sizeof (sin6_ip));
+ sin6_ip.sin6_family = AF_INET6;
+ sin6_ip.sin6_port = htons(p->port_num);
+ sin6_ip.sin6_addr = in6addr_any;
+
+ (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
+ sizeof (on));
+
+ if ((bind(s, (struct sockaddr *)&sin6_ip, sizeof (sin6_ip)))
+ < 0) {
+ (void) snprintf(debug, sizeof (debug),
+ "bind on port %d failed\n",
+ p->port_num);
+ queue_str(q, Q_GEN_ERRS, msg_status, debug);
+ return (NULL);
+ }
+ }
+
+ if (listen(s, 5) < 0) {
+ queue_str(q, Q_GEN_ERRS, msg_status, "listen failed");
+ return (NULL);
+ }
+
+ /*CONSTANTCONDITION*/
+ while (1) {
+ fromlen = sizeof (from);
+ if ((fd = accept(s, (struct sockaddr *)&from,
+ &fromlen)) < 0) {
+ queue_str(q, Q_GEN_ERRS, msg_status, "accept failed");
+ return (NULL);
+ }
+
+ l = 128 * 1024;
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&l,
+ sizeof (l)) < 0)
+ queue_str(q, Q_GEN_ERRS, msg_status,
+ "setsockopt failed");
+
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&l,
+ sizeof (l)) < 0)
+ queue_str(q, Q_GEN_ERRS, msg_status,
+ "setsockopt failed");
+
+
+ p->port_socket = fd;
+ (void) pthread_create(&junk, NULL, mgmt_process, p);
+
+ mgmt_monitor_queue(p);
+ }
+ return (NULL);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/mgmt_create.c b/usr/src/cmd/iscsi/iscsitgtd/mgmt_create.c
new file mode 100644
index 0000000000..e492c2b853
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/mgmt_create.c
@@ -0,0 +1,1000 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <time.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <errno.h>
+#include <strings.h>
+#include <sys/vtoc.h>
+#include <sys/efi_partition.h>
+#include <uuid/uuid.h>
+#include <sys/scsi/impl/uscsi.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/impl/commands.h>
+
+#include "xml.h"
+#include "queue.h"
+#include "target.h"
+#include "iscsi_cmd.h"
+#include "utility.h"
+#include "errcode.h"
+#include "t10_spc.h"
+
+extern char *getfullrawname();
+
+static char *create_target(xml_node_t *);
+static char *create_initiator(xml_node_t *);
+static char *create_tpgt(xml_node_t *);
+static Boolean_t create_target_dir(char *targ_name, char *local_name);
+static char *create_node_name(char *local_nick, char *alias);
+static Boolean_t create_lun(char *targ_name, char *type, int lun,
+ char *size_str, char *backing, err_code_t *code);
+static Boolean_t create_lun_common(char *targ_name, int lun, uint64_t size,
+ err_code_t *code);
+static Boolean_t setup_disk_backing(err_code_t *code, char *path, char *backing,
+ FILE *fp, uint64_t *size);
+static Boolean_t setup_raw_backing(err_code_t *code, char *path, char *backing,
+ uint64_t *size);
+
+
+/*
+ * []----
+ * | create_func -- Branch out to appropriate object create function
+ * []----
+ */
+/*ARGSUSED*/
+void
+create_func(xml_node_t *p, target_queue_t *reply, target_queue_t *mgmt)
+{
+ xml_node_t *x;
+ char msgbuf[80],
+ *reply_msg = NULL;
+
+ if (p->x_child == NULL) {
+ xml_rtn_msg(&reply_msg, ERR_SYNTAX_MISSING_OBJECT);
+ } else {
+ x = p->x_child;
+
+ if (x->x_name == NULL) {
+ xml_rtn_msg(&reply_msg, ERR_SYNTAX_MISSING_OBJECT);
+ } else if (strcmp(x->x_name, XML_ELEMENT_TARG) == 0) {
+ reply_msg = create_target(x);
+ } else if (strcmp(x->x_name, XML_ELEMENT_INIT) == 0) {
+ reply_msg = create_initiator(x);
+ } else if (strcmp(x->x_name, XML_ELEMENT_TPGT) == 0) {
+ reply_msg = create_tpgt(x);
+ } else {
+ (void) snprintf(msgbuf, sizeof (msgbuf),
+ "Unknown object '%s' for create element",
+ x->x_name);
+ xml_rtn_msg(&reply_msg, ERR_INVALID_OBJECT);
+ }
+ }
+ queue_message_set(reply, 0, msg_mgmt_rply, reply_msg);
+}
+
+static char *
+create_target(xml_node_t *x)
+{
+ char *msg = NULL,
+ *name = NULL,
+ *alias = NULL,
+ *size = NULL,
+ *type = NULL,
+ *backing = NULL,
+ *node_name = NULL,
+ path[MAXPATHLEN];
+ int lun = 0, /* default to LUN 0 */
+ i;
+ xml_node_t *n,
+ *c,
+ *l;
+ err_code_t code;
+
+ (void) xml_find_value_str(x, XML_ELEMENT_BACK, &backing);
+ (void) xml_find_value_str(x, XML_ELEMENT_ALIAS, &alias);
+ if (xml_find_value_intchk(x, XML_ELEMENT_LUN, &lun) == False) {
+ xml_rtn_msg(&msg, ERR_LUN_INVALID_RANGE);
+ goto error;
+ }
+
+ /*
+ * We've got to have a name element or all bets are off.
+ */
+ if (xml_find_value_str(x, XML_ELEMENT_NAME, &name) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ goto error;
+ }
+
+ /*
+ * RFC3722 states that names must be one of:
+ * (1) a..z
+ * (2) A..Z
+ * (3) 0-9
+ * or
+ * (4) ':', '.', '-'
+ * If it's an upper case character is must be made lower
+ * case.
+ */
+ for (i = 0; i < strlen(name); i++) {
+ if (!isalnum(name[i]) &&
+ (name[i] != ':') && (name[i] != '.') && (name[i] != '-')) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_INVALID_NAME);
+ goto error;
+ } else if (isupper(name[i]))
+ name[i] = tolower(name[i]);
+ }
+
+ if (xml_find_value_str(x, XML_ELEMENT_TYPE, &type) == False) {
+ /*
+ * If a type hasn't been specified default to disk emulation.
+ * We use strdup() since at the end of this routine the code
+ * is expecting to free 'type' along with other strings.
+ */
+ type = strdup(TGT_TYPE_DISK);
+ }
+
+ if ((strcmp(type, "raw") == 0) && (backing == NULL)) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_BACKING_STORE);
+ goto error;
+ }
+
+ if (xml_find_value_str(x, XML_ELEMENT_SIZE, &size) == False) {
+ if (backing != NULL) {
+
+ /*
+ * If a backing store has been provided we don't
+ * need the size since we can determine that from
+ * a READ_CAPACITY command which everyone issues.
+ *
+ * NOTE: strdup is used here, since at the end
+ * of this routine any of the string pointers which
+ * are non-NULL get freed.
+ */
+ size = strdup("0");
+ } else {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_SIZE);
+ goto error;
+ }
+ }
+ if ((lun < 0) || (lun > T10_MAX_LUNS)) {
+ xml_rtn_msg(&msg, ERR_LUN_INVALID_RANGE);
+ goto error;
+ }
+
+ /*
+ * See if we already have a local target name created. If so,
+ * the user is most likely wanting to create another LUN for this
+ * target. Checking to see if there's a duplicate LUN will be
+ * done later.
+ */
+ for (n = targets_config->x_child; n; n = n->x_sibling) {
+ if (strcmp(n->x_value, name) == 0)
+ break;
+ }
+
+ if (n == NULL) {
+ if (lun != 0) {
+ xml_rtn_msg(&msg, ERR_LUN_ZERO_NOT_FIRST);
+ goto error;
+ }
+ if ((node_name = create_node_name(name, alias)) == NULL) {
+ xml_rtn_msg(&msg, ERR_CREATE_NAME_TO_LONG);
+ goto error;
+ }
+ if (create_target_dir(node_name, name) == False) {
+ xml_rtn_msg(&msg, ERR_CREATE_TARGET_DIR_FAILED);
+ goto error;
+ }
+ n = xml_alloc_node(XML_ELEMENT_TARG, String, name);
+ c = xml_alloc_node(XML_ELEMENT_INAME, String, node_name);
+ (void) xml_add_child(n, c);
+ c = xml_alloc_node(XML_ELEMENT_LUNLIST, String, "");
+ l = xml_alloc_node(XML_ELEMENT_LUN, Int, &lun);
+ (void) xml_add_child(c, l);
+ (void) xml_add_child(n, c);
+ if (alias != NULL) {
+ c = xml_alloc_node(XML_ELEMENT_ALIAS, String, alias);
+ (void) xml_add_child(n, c);
+ }
+ (void) xml_add_child(targets_config, n);
+
+ } else {
+ if (xml_find_value_str(n, XML_ELEMENT_INAME,
+ &node_name) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_INAME);
+ goto error;
+ }
+ if ((c = xml_node_next(n, XML_ELEMENT_LUNLIST, NULL)) == NULL) {
+ xml_rtn_msg(&msg, ERR_INTERNAL_ERROR);
+ goto error;
+ }
+ l = xml_alloc_node(XML_ELEMENT_LUN, Int, &lun);
+ (void) xml_add_child(c, l);
+ }
+
+ if (create_lun(node_name, type, lun, size, backing, &code) == True) {
+
+ if (update_config_targets(&msg) == False)
+ goto error;
+
+ } else if ((lun == 0) && (code != ERR_LUN_EXISTS)) {
+
+ /*
+ * The first LU will have created the directory and
+ * symbolic link. Remove those on error.
+ */
+ (void) snprintf(path, sizeof (path), "%s/%s",
+ target_basedir, node_name);
+ rmdir(path);
+ (void) snprintf(path, sizeof (path), "%s/%s",
+ target_basedir, name);
+ unlink(path);
+ xml_remove_child(targets_config, n, MatchBoth);
+ xml_tree_free(n);
+ } else
+ xml_remove_child(c, l, MatchBoth);
+
+ xml_rtn_msg(&msg, code);
+
+error:
+ if (name != NULL)
+ free(name);
+ if (size != NULL)
+ free(size);
+ if (alias != NULL)
+ free(name);
+ if (backing != NULL)
+ free(backing);
+ if (node_name != NULL)
+ free(node_name);
+ if (type != NULL)
+ free(type);
+
+ return (msg);
+}
+
+static char *
+create_initiator(xml_node_t *x)
+{
+ char *msg = NULL,
+ *name = NULL,
+ *iscsi_name = NULL;
+ xml_node_t *inode = NULL,
+ *n,
+ *c;
+
+ if (xml_find_value_str(x, XML_ELEMENT_NAME, &name) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ goto error;
+ }
+ if (xml_find_value_str(x, XML_ELEMENT_INAME, &iscsi_name) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_INAME);
+ goto error;
+ }
+ if (strlen(iscsi_name) >= ISCSI_MAX_NAME_LEN) {
+ xml_rtn_msg(&msg, ERR_NAME_TO_LONG);
+ goto error;
+ }
+
+ while ((inode = xml_node_next(main_config, XML_ELEMENT_INIT,
+ inode)) != NULL) {
+ if (strcmp(inode->x_value, name) == 0) {
+ xml_rtn_msg(&msg, ERR_INIT_EXISTS);
+ goto error;
+ }
+ }
+
+ n = xml_alloc_node(XML_ELEMENT_INIT, String, name);
+ c = xml_alloc_node(XML_ELEMENT_INAME, String, iscsi_name);
+ (void) xml_add_child(n, c);
+ (void) xml_add_child(main_config, n);
+
+ if (update_config_main(&msg) == True)
+ xml_rtn_msg(&msg, ERR_SUCCESS);
+
+error:
+ if (name)
+ free(name);
+ if (iscsi_name)
+ free(iscsi_name);
+
+ return (msg);
+}
+
+static char *
+create_tpgt(xml_node_t *x)
+{
+ char *msg = NULL,
+ *tpgt = NULL,
+ *extra = NULL;
+ xml_node_t *tnode = NULL,
+ *n;
+ int tpgt_val;
+
+ if (xml_find_value_str(x, XML_ELEMENT_NAME, &tpgt) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ goto error;
+ }
+
+ /* ---- Validation checks ---- */
+ tpgt_val = strtol(tpgt, &extra, 0);
+ if ((extra && (*extra != '\0')) ||
+ (tpgt_val < TPGT_MIN) || (tpgt_val > TPGT_MAX)) {
+ xml_rtn_msg(&msg, ERR_INVALID_TPGT);
+ goto error;
+ }
+
+ while ((tnode = xml_node_next(main_config, XML_ELEMENT_TPGT,
+ tnode)) != NULL) {
+ if (strcmp(tnode->x_value, tpgt) == 0) {
+ xml_rtn_msg(&msg, ERR_TPGT_EXISTS);
+ goto error;
+ }
+ }
+
+ n = xml_alloc_node(XML_ELEMENT_TPGT, String, tpgt);
+ (void) xml_add_child(main_config, n);
+
+ if (update_config_main(&msg) == True)
+ xml_rtn_msg(&msg, ERR_SUCCESS);
+
+error:
+ if (tpgt)
+ free(tpgt);
+
+ return (msg);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Utility functions used by the routines above. |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * []----
+ * | create_node_name -- Creates the IQN that adhears to RFC3270
+ * []----
+ */
+static char *
+create_node_name(char *local_nick, char *alias)
+{
+ uuid_t id;
+ char id_str[37],
+ *p;
+
+ if ((p = (char *)malloc(ISCSI_MAX_NAME_LEN)) == NULL)
+ return (NULL);
+
+ /*
+ * Originally we we going to use the machines MAC address and
+ * timestamp in hex format. This would be consistent with the
+ * Solaris iSCSI initiator and NAS5310. Unfortunately, someone
+ * pointed out that there's no requirement that the network
+ * interfaces be plumbed before someone attempts to create
+ * targets. If the networks aren't plumbed there are no MAC
+ * addresses available and we can't use 0 for the MAC address
+ * since that would introduce the probability of non-unique
+ * IQN names.
+ */
+ uuid_generate(id);
+ uuid_unparse(id, id_str);
+ if (snprintf(p, ISCSI_MAX_NAME_LEN, "iqn.1986-03.com.sun:%02d:%s.%s",
+ TARGET_NAME_VERS, id_str, alias != NULL ? alias : local_nick) >
+ ISCSI_MAX_NAME_LEN) {
+ free(p);
+ return (NULL);
+ }
+
+ return (p);
+}
+
+/*
+ * []----
+ * | create_target_dir -- create the target directory
+ * []----
+ */
+static Boolean_t
+create_target_dir(char *targ_name, char *local_name)
+{
+ char path[MAXPATHLEN],
+ sympath[MAXPATHLEN];
+
+ if ((mkdir(target_basedir, 0777) == -1) && (errno != EEXIST))
+ return (False);
+
+ (void) snprintf(path, sizeof (path), "%s/%s", target_basedir,
+ targ_name);
+ (void) snprintf(sympath, sizeof (sympath), "%s/%s", target_basedir,
+ local_name);
+
+ if ((mkdir(path, 0777) == -1) && (errno != EEXIST))
+ return (False);
+
+ /*
+ * This symbolic link is here for convenience and nothing more, so if
+ * if fails. Oh well.
+ */
+ (void) symlink(path, sympath);
+ return (True);
+}
+
+/*
+ * []----
+ * | create_lun -- given type, lun, size, backing create LU and params
+ * []----
+ */
+static Boolean_t
+create_lun(char *targ_name, char *type, int lun, char *size_str, char *backing,
+ err_code_t *code)
+{
+ uint64_t size;
+ int fd = -1,
+ rpm = DEFAULT_RPM,
+ heads = DEFAULT_HEADS,
+ cylinders = DEFAULT_CYLINDERS,
+ spt = DEFAULT_SPT,
+ bytes_sect = DEFAULT_BYTES_PER,
+ interleave = DEFAULT_INTERLEAVE;
+ char *vid = DEFAULT_VID,
+ *pid = DEFAULT_PID,
+ path[MAXPATHLEN];
+ FILE *fp = NULL;
+
+ /*
+ * after calling stroll_multipler it's an error for size to be
+ * 0, if and only if, the size_str doesn't equal "0". The administrator
+ * may want the code to determine the size. This would be the case
+ * when the administrator has provide a backing store which exists.
+ */
+ if ((strtoll_multiplier(size_str, &size) == False) ||
+ ((size == 0) && (size_str != NULL) && strcmp(size_str, "0"))) {
+ *code = ERR_INVALID_SIZE;
+ return (False);
+ }
+
+ if ((size % 512) != 0) {
+ *code = ERR_SIZE_MOD_BLOCK;
+ return (False);
+ }
+
+ /*
+ * Make sure we're not trying to recreate an existing LU.
+ */
+ (void) snprintf(path, sizeof (path), "%s/%s/%s%d", target_basedir,
+ targ_name, PARAMBASE, lun);
+ if (access(path, F_OK) == 0) {
+ *code = ERR_LUN_EXISTS;
+ return (False);
+ }
+
+ if ((fp = fopen(path, "w+")) == NULL) {
+ *code = ERR_OPEN_PARAM_FILE_FAILED;
+ return (False);
+ }
+
+ (void) fprintf(fp, "<%s version='1.0'>\n", XML_ELEMENT_PARAMS);
+ (void) fprintf(fp, "\t<%s>0</%s>\n", XML_ELEMENT_GUID,
+ XML_ELEMENT_GUID);
+ (void) fprintf(fp, "\t<%s>%s</%s>\n", XML_ELEMENT_PID, pid,
+ XML_ELEMENT_PID);
+ (void) fprintf(fp, "\t<%s>%s</%s>\n", XML_ELEMENT_VID, vid,
+ XML_ELEMENT_VID);
+ (void) fprintf(fp, "\t<%s>%s</%s>\n", XML_ELEMENT_DTYPE, type,
+ XML_ELEMENT_DTYPE);
+
+ if (strcmp(type, TGT_TYPE_DISK) == 0) {
+
+ (void) snprintf(path, sizeof (path), "%s/%s/lun.%d",
+ target_basedir, targ_name, lun);
+ if (setup_disk_backing(code, path, backing, fp, &size) == False)
+ goto error;
+
+ create_geom(size, &cylinders, &heads, &spt);
+
+ (void) fprintf(fp, "\t<%s>%d</%s>\n", XML_ELEMENT_RPM, rpm,
+ XML_ELEMENT_RPM);
+ (void) fprintf(fp, "\t<%s>%d</%s>\n", XML_ELEMENT_HEADS, heads,
+ XML_ELEMENT_HEADS);
+ (void) fprintf(fp, "\t<%s>%d</%s>\n", XML_ELEMENT_CYLINDERS,
+ cylinders, XML_ELEMENT_CYLINDERS);
+ (void) fprintf(fp, "\t<%s>%d</%s>\n", XML_ELEMENT_SPT, spt,
+ XML_ELEMENT_SPT);
+ (void) fprintf(fp, "\t<%s>%d</%s>\n", XML_ELEMENT_BPS,
+ bytes_sect, XML_ELEMENT_BPS);
+ (void) fprintf(fp, "\t<%s>%d</%s>\n", XML_ELEMENT_INTERLEAVE,
+ interleave, XML_ELEMENT_INTERLEAVE);
+ (void) fprintf(fp, "\t<%s>%s</%s>\n", XML_ELEMENT_STATUS,
+ TGT_STATUS_OFFLINE, XML_ELEMENT_STATUS);
+
+ } else if (strcmp(type, TGT_TYPE_TAPE) == 0) {
+#ifndef _LP64
+ *code = ERR_TAPE_NOT_SUPPORTED_IN_32BIT;
+ goto error;
+#else
+ (void) fprintf(fp, "\t<%s>%s</%s>\n", XML_ELEMENT_STATUS,
+ TGT_STATUS_OFFLINE, XML_ELEMENT_STATUS);
+ (void) snprintf(path, sizeof (path), "%s/%s/lun.%d",
+ target_basedir, targ_name, lun);
+ if (setup_disk_backing(code, path, backing, fp, &size) == False)
+ goto error;
+#endif
+
+ } else if (strcmp(type, TGT_TYPE_RAW) == 0) {
+
+ (void) fprintf(fp, "\t<%s>%s</%s>\n", XML_ELEMENT_STATUS,
+ TGT_STATUS_ONLINE, XML_ELEMENT_STATUS);
+ backing = getfullrawname(backing);
+ if (setup_raw_backing(code, path, backing, &size) == False)
+ goto error;
+
+ (void) fprintf(fp, "\t<%s>false</%s>\n", XML_ELEMENT_MMAP_LUN,
+ XML_ELEMENT_MMAP_LUN);
+
+ (void) snprintf(path, sizeof (path), "%s/%s/lun.%d",
+ target_basedir, targ_name, lun);
+ if (symlink(backing, path)) {
+ *code = ERR_CREATE_SYMLINK_FAILED;
+ goto error;
+ }
+
+ iscsi_inventory_change(targ_name);
+
+ } else if (strcmp(type, TGT_TYPE_OSD) == 0) {
+
+ (void) snprintf(path, sizeof (path), "%s/%s/%s%d",
+ target_basedir, targ_name, OSDBASE, lun);
+ if (mkdir(path, 0700) != 0)
+ goto error;
+ }
+
+
+ /*
+ * Wait to set the size until here because it may be unknown until
+ * the possible backing store has been setup.
+ */
+ (void) fprintf(fp, "\t<%s>0x%llx</%s>\n", XML_ELEMENT_SIZE,
+ size / 512LL, XML_ELEMENT_SIZE);
+ if (backing != NULL) {
+ (void) fprintf(fp, "\t<%s>%s</%s>\n", XML_ELEMENT_BACK,
+ backing, XML_ELEMENT_BACK);
+ }
+
+ (void) fprintf(fp, "</%s>\n", XML_ELEMENT_PARAMS);
+ (void) fclose(fp);
+ fp = NULL;
+
+ if ((strcmp(type, TGT_TYPE_DISK) == 0) ||
+ (strcmp(type, TGT_TYPE_TAPE) == 0)) {
+ if (create_lun_common(targ_name, lun, size, code) == False)
+ goto error;
+ }
+
+ *code = ERR_SUCCESS;
+ return (True);
+
+error:
+ (void) snprintf(path, sizeof (path), "%s/%s/%s%d", target_basedir,
+ targ_name, PARAMBASE, lun);
+ (void) unlink(path);
+ if (fd == -1)
+ (void) close(fd);
+ if (fp != NULL)
+ (void) fclose(fp);
+ return (False);
+}
+
+/*
+ * []----
+ * | create_lun_common -- create LU and start provisioning if needed
+ * |
+ * | This function is common to both the tape and disk emulation
+ * | code.
+ * []----
+ */
+static Boolean_t
+create_lun_common(char *targ_name, int lun, uint64_t size, err_code_t *code)
+{
+ struct stat s;
+ int fd = -1;
+ char path[MAXPATHLEN],
+ buf[512];
+ struct statvfs fs;
+ xml_node_t *node = NULL,
+ *c;
+ xmlTextReaderPtr r;
+
+ /*
+ * Touch the last block of the file which will cause file systems
+ * to understand the intent of the file to be a certain size. The
+ * space isn't allocated, but the daemon can then mmap in this file
+ * and start writing to it.
+ */
+ (void) snprintf(path, sizeof (path), "%s/%s/%s%d",
+ target_basedir, targ_name, LUNBASE, lun);
+ if ((fd = open(path, O_RDWR|O_CREAT|O_LARGEFILE, 0600)) < 0)
+ goto error;
+
+ (void) lseek(fd, size - 512LL, 0);
+ if (write(fd, buf, sizeof (buf)) != sizeof (buf)) {
+ unlink(path);
+ if (errno == EFBIG)
+ *code = ERR_FILE_TO_BIG;
+ else
+ *code = ERR_FAILED_TO_CREATE_LU;
+ goto error;
+ }
+ (void) close(fd);
+
+ /*
+ * Set the fd back to -1 so that if an error occurs we don't
+ * attempt to close this device twice. This could be an issue
+ * if another thread opened a file right after we closed this
+ * one and the system reused the file descriptor. During an
+ * error we would then close another threads file which would
+ * be ugly, not to mention difficult to track down.
+ */
+ fd = -1;
+
+ if (stat(path, &s) != 0) {
+ *code = ERR_FAILED_TO_CREATE_LU;
+ goto error;
+ }
+
+ /*
+ * If the backing store is a regular file and the default is
+ * used which initializes the file instead of sparse allocation
+ * go ahead a set things up.
+ */
+ if ((thin_provisioning == False) && ((s.st_mode & S_IFMT) == S_IFREG)) {
+ thick_provo_t *tp;
+ pthread_t junk;
+
+ /*
+ * Attempt to see if there is enough space currently
+ * for the LU. The initialization might still fail
+ * with "out of space" because someone else is
+ * consuming space while the initialization is occuring.
+ * Nothing we can do about that.
+ */
+ if (statvfs(path, &fs) != 0) {
+ queue_prt(mgmtq, Q_GEN_ERRS,
+ "GEN statvfs failed for %s", path);
+ *code = ERR_FAILED_TO_CREATE_LU;
+ goto error;
+ } else if ((fs.f_frsize * fs.f_bfree) < size) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "GEN Not enough space for LU");
+ *code = ERR_FILE_TO_BIG;
+ goto error;
+ }
+
+ /*
+ * Run the initialization thread in the background so that
+ * the administrator doesn't have to wait which for UFS could
+ * be a long time on a large LU.
+ */
+ if ((tp = calloc(1, sizeof (*tp))) != NULL) {
+ tp->targ_name = strdup(targ_name);
+ tp->lun = lun;
+ tp->q = queue_alloc();
+ (void) pthread_create(&junk, NULL,
+ thick_provo_start, tp);
+
+ /*
+ * As soon as the thread starts it will send a simple
+ * ACK to it's own queue that we can look for. When
+ * we see this message we know that the thread has
+ * started and it's been added to the provisioning
+ * list. If this were not done it's possible for someone
+ * to create and delete a target within a script and
+ * have the delete run and fail to find the provision
+ * thread in the list.
+ */
+ queue_message_free(queue_message_get(tp->q));
+ }
+ } else {
+
+ (void) snprintf(path, sizeof (path), "%s/%s/%s%d",
+ target_basedir, targ_name, PARAMBASE, lun);
+ if (r = (xmlTextReaderPtr)xmlReaderForFile(path, NULL, 0)) {
+ while (xmlTextReaderRead(r) == 1)
+ if (xml_process_node(r, &node) == False)
+ break;
+ c = xml_alloc_node(XML_ELEMENT_STATUS, String,
+ TGT_STATUS_ONLINE);
+ xml_replace_child(node, c, MatchName);
+ xml_tree_free(c);
+ if (xml_dump2file(node, path) == False) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "GEN%d failed to dump out params", lun);
+ goto error;
+ }
+ xml_tree_free(node);
+ xmlTextReaderClose(r);
+ xmlFreeTextReader(r);
+ xmlCleanupParser();
+
+ /*
+ * The thick_provo_start will issue an inventory
+ * change once it's finished.
+ */
+ iscsi_inventory_change(targ_name);
+ } else
+ return (False);
+ }
+
+ return (True);
+
+error:
+ if (fd != -1)
+ (void) close(fd);
+ return (False);
+}
+
+static Boolean_t
+readvtoc(int fd, struct vtoc *v, int *slice)
+{
+ if ((*slice = read_vtoc(fd, v)) >= 0)
+ return (True);
+ else
+ return (False);
+}
+
+static Boolean_t
+readefi(int fd, struct dk_gpt **efi, int *slice)
+{
+ if ((*slice = efi_alloc_and_read(fd, efi)) >= 0)
+ return (True);
+ else
+ return (False);
+}
+
+/*
+ * []----
+ * | setup_alt_backing -- use backing store link for regular file lun
+ * |
+ * | If the size is zero, then the administrator MUST have
+ * | specified a backing store to use.
+ * | If the size is non-zero and the backing store doesn't exist it will
+ * | be created. Also a tag will be added indicating that during removal
+ * | the backing store should be deleted as well.
+ * []----
+ */
+static Boolean_t
+setup_disk_backing(err_code_t *code, char *path, char *backing, FILE *fp,
+ uint64_t *size)
+{
+ struct stat s;
+ char *raw_name,
+ buf[512];
+ struct vtoc vtoc;
+ struct dk_gpt *efi;
+ int slice,
+ fd;
+
+ /*
+ * Error checking regarding size and backing store has already
+ * been done. If the backing store is null at this point everything
+ * is okay so just return True.
+ */
+ if (backing == NULL)
+ return (True);
+
+ if (stat(backing, &s) == -1) {
+ if (*size == 0) {
+ *code = ERR_STAT_BACKING_FAILED;
+ return (False);
+ } else {
+ (void) fprintf(fp, "\t<%s>true</%s>\n",
+ XML_ELEMENT_DELETE_BACK, XML_ELEMENT_DELETE_BACK);
+ if ((fd = open(backing, O_RDWR|O_CREAT|O_LARGEFILE,
+ 0600)) < 0) {
+ *code = ERR_FAILED_TO_CREATE_LU;
+ return (False);
+ }
+ lseek(fd, *size - 512LL, 0);
+ (void) write(fd, buf, sizeof (buf));
+ close(fd);
+ }
+ } else if (*size != 0) {
+ *code = ERR_DISK_BACKING_SIZE_OR_FILE;
+ return (False);
+ } else if (((s.st_mode & S_IFMT) == S_IFCHR) ||
+ ((s.st_mode & S_IFMT) == S_IFBLK)) {
+ raw_name = getfullrawname(backing);
+ if ((raw_name == NULL) ||
+ ((fd = open(raw_name, O_NONBLOCK|O_RDONLY)) < 0)) {
+ *code = ERR_DISK_BACKING_NOT_VALID_RAW;
+ (void) close(fd);
+ return (False);
+ }
+ if (readvtoc(fd, &vtoc, &slice) == True) {
+ *size = (long long)vtoc.v_part[slice].p_size * 512;
+
+ } else if (readefi(fd, &efi, &slice) == True) {
+ *size = efi->efi_parts[slice].p_size * 512;
+ efi_free(efi);
+ } else {
+ *code = ERR_DISK_BACKING_NOT_VALID_RAW;
+ (void) close(fd);
+ return (False);
+ }
+ (void) close(fd);
+
+ } else if ((s.st_mode & S_IFMT) == S_IFREG) {
+ *size = s.st_size;
+ } else {
+ *code = ERR_DISK_BACKING_MUST_BE_REGULAR_FILE;
+ return (False);
+ }
+
+ if (symlink(backing, path)) {
+ *code = ERR_CREATE_SYMLINK_FAILED;
+ return (False);
+ }
+
+ return (True);
+}
+
+/*
+ * []----
+ * | validate_raw_backing -- check that device is full partition
+ * |
+ * | The size of the device will be returned in rtn_size in bytes.
+ * |
+ * | Need to guarantee that the backing store for a raw device is:
+ * | (a) character device
+ * | (b) Not buffered
+ * | Don't want this host to have data which is not flushed
+ * | out during a write since a multiple path access to
+ * | the backing store would be possible meaning we'd have
+ * | cache issue.
+ * | (c) read/write will access entire device.
+ * | To speed things up we use asynchronous I/O which means
+ * | the path has to have access to the entire device through
+ * | the partition table. If not, some client will issue a
+ * | READ_CAPACITY command, but not be able to access all of
+ * | the data.
+ * []----
+ */
+static Boolean_t
+setup_raw_backing(err_code_t *code, char *path, char *backing,
+ uint64_t *rtn_size)
+{
+ struct stat s;
+ char buf[512];
+ int fd;
+ uint64_t size;
+ struct uscsi_cmd u;
+ struct scsi_extended_sense sense;
+ union scsi_cdb cdb;
+ struct scsi_capacity cap;
+ struct scsi_capacity_16 cap16;
+ int cap_len = sizeof (cap16);
+ size_t cc;
+ Boolean_t rval = False;
+
+ if (stat(backing, &s) == -1) {
+ *code = ERR_ACCESS_RAW_DEVICE_FAILED;
+ return (False);
+ } else if (((s.st_mode & S_IFMT) != S_IFCHR) &&
+ ((s.st_mode & S_IFMT) != S_IFBLK)) {
+ *code = ERR_DISK_BACKING_NOT_VALID_RAW;
+ return (False);
+ }
+
+ if ((backing == NULL) ||
+ ((fd = open(backing, O_NDELAY|O_RDONLY|O_LARGEFILE)) < 0)) {
+ *code = ERR_DISK_BACKING_NOT_VALID_RAW;
+ (void) close(fd);
+ return (False);
+ }
+
+ bzero(&u, sizeof (u));
+ bzero(&cdb, sizeof (cdb));
+ bzero(&cap, sizeof (cap));
+ bzero(&sense, sizeof (sense));
+
+ cdb.scc_cmd = 0x25; /* ---- READ_CAPACITY(10) ---- */
+
+ u.uscsi_cdb = (caddr_t)&cdb;
+ u.uscsi_cdblen = CDB_GROUP1;
+ u.uscsi_bufaddr = (caddr_t)&cap;
+ u.uscsi_buflen = sizeof (cap);
+ u.uscsi_flags = USCSI_READ | USCSI_RQENABLE;
+ u.uscsi_rqbuf = (char *)&sense;
+ u.uscsi_rqlen = sizeof (sense);
+
+ if ((ioctl(fd, USCSICMD, &u) != 0) || (u.uscsi_status != 0)) {
+ queue_prt(mgmtq, Q_GEN_DETAILS, "GEN0 uscsi(READ_CAP) failed");
+ *code = ERR_DISK_BACKING_NOT_VALID_RAW;
+ rval = False;
+ goto error;
+ }
+
+ if (cap.capacity == 0xffffffff) {
+
+ bzero(&u, sizeof (u));
+ bzero(&cdb, sizeof (cdb));
+ bzero(&sense, sizeof (sense));
+ /*
+ * The device is to large for the 10byte CDB.
+ * Using the larger 16byte read capacity command
+ */
+ cdb.scc_cmd = 0x9E;
+ cdb.g4_reladdr = 0x10;
+ cdb.g4_count3 = hibyte(hiword(cap_len));
+ cdb.g4_count2 = lobyte(hiword(cap_len));
+ cdb.g4_count1 = hibyte(loword(cap_len));
+ cdb.g4_count0 = lobyte(loword(cap_len));
+
+ u.uscsi_cdb = (caddr_t)&cdb;
+ u.uscsi_cdblen = CDB_GROUP4;
+ u.uscsi_bufaddr = (caddr_t)&cap16;
+ u.uscsi_buflen = sizeof (cap16);
+ u.uscsi_flags = USCSI_READ | USCSI_RQENABLE;
+ u.uscsi_rqbuf = (char *)&sense;
+ u.uscsi_rqlen = sizeof (sense);
+
+ if ((ioctl(fd, USCSICMD, &u) != 0) || (u.uscsi_status != 0)) {
+ queue_prt(mgmtq, Q_GEN_DETAILS,
+ "GEN0 uscsi(READ_CAP16) failed");
+ *code = ERR_DISK_BACKING_NOT_VALID_RAW;
+ rval = False;
+ goto error;
+ }
+
+ size = ntohll(cap16.sc_capacity) - 1;
+ } else
+ size = (uint64_t)ntohl(cap.capacity) - 1;
+
+ if ((cc = pread(fd, buf, sizeof (buf), size * 512LL)) != sizeof (buf)) {
+ queue_prt(mgmtq, Q_GEN_DETAILS,
+ "GEN0 Partition size != capacity(0x%llx), cc=%d, errno=%d",
+ size, cc, errno);
+ *code = ERR_RAW_PART_NOT_CAP;
+ rval = False;
+ goto error;
+ } else {
+ *rtn_size = size * 512LL;
+ rval = True;
+ }
+
+error:
+ (void) close(fd);
+ return (rval);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/mgmt_list.c b/usr/src/cmd/iscsi/iscsitgtd/mgmt_list.c
new file mode 100644
index 0000000000..fef771b514
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/mgmt_list.c
@@ -0,0 +1,447 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <time.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <strings.h>
+#include <dirent.h>
+
+#include "utility.h"
+#include <xml.h>
+#include "queue.h"
+#include "target.h"
+#include "iscsi_cmd.h"
+#include "iscsi_conn.h"
+#include "port.h"
+#include "errcode.h"
+
+static char *list_targets(xml_node_t *x);
+static char *list_initiator(xml_node_t *x);
+static char *list_tpgt(xml_node_t *x);
+static char *list_admin(xml_node_t *x);
+static void target_info(char **msg, char *targ_name, xml_node_t *tnode);
+static void target_stat(char **msg, char *iname, mgmt_type_t type);
+
+/*ARGSUSED*/
+void
+list_func(xml_node_t *p, target_queue_t *reply, target_queue_t *mgmt)
+{
+ xml_node_t *x;
+ char msgbuf[80],
+ *reply_msg = NULL;
+
+ if (p->x_child == NULL) {
+ xml_rtn_msg(&reply_msg, ERR_SYNTAX_MISSING_OBJECT);
+ } else {
+ x = p->x_child;
+
+ if (x->x_name == NULL) {
+ xml_rtn_msg(&reply_msg, ERR_SYNTAX_MISSING_OBJECT);
+ } else if (strcmp(x->x_name, XML_ELEMENT_TARG) == 0) {
+ reply_msg = list_targets(x);
+ } else if (strcmp(x->x_name, XML_ELEMENT_INIT) == 0) {
+ reply_msg = list_initiator(x);
+ } else if (strcmp(x->x_name, XML_ELEMENT_TPGT) == 0) {
+ reply_msg = list_tpgt(x);
+ } else if (strcmp(x->x_name, XML_ELEMENT_ADMIN) == 0) {
+ reply_msg = list_admin(x);
+ } else {
+ (void) snprintf(msgbuf, sizeof (msgbuf),
+ "Unknown object '%s' for list element",
+ x->x_name);
+ xml_rtn_msg(&reply_msg, ERR_INVALID_OBJECT);
+ }
+ }
+ queue_message_set(reply, 0, msg_mgmt_rply, reply_msg);
+}
+
+static char *
+list_targets(xml_node_t *x)
+{
+ char *msg = NULL,
+ *prop = NULL,
+ *iname = NULL;
+ xml_node_t *targ = NULL;
+ Boolean_t luninfo = False,
+ dostat = False;
+
+ /*
+ * It's okay to not supply a "name" element. That just means the
+ * administrator wants a complete list of targets. However if a
+ * "name" is supplied then there must be a value for that element.
+ */
+ if ((xml_find_value_str(x, XML_ELEMENT_NAME, &prop) == True) &&
+ (prop == NULL)) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ return (msg);
+ }
+
+ /* ---- optional arguments ---- */
+ (void) xml_find_value_boolean(x, XML_ELEMENT_LUNINFO, &luninfo);
+ (void) xml_find_value_boolean(x, XML_ELEMENT_IOSTAT, &dostat);
+
+ buf_add_tag_and_attr(&msg, XML_ELEMENT_RESULT, "version='1.0'");
+ while ((targ = xml_node_next(targets_config, XML_ELEMENT_TARG,
+ targ)) != NULL) {
+ if (targ->x_value == NULL) {
+ xml_add_tag(&msg, XML_ELEMENT_TARG,
+ "bogus entry");
+ continue;
+ }
+ if (xml_find_value_str(targ, XML_ELEMENT_INAME, &iname) ==
+ False) {
+ xml_add_tag(&msg, XML_ELEMENT_TARG,
+ "missing iscsi-name");
+ continue;
+ }
+ if (prop != NULL) {
+ if (strcmp(prop, targ->x_value) == 0) {
+ buf_add_tag(&msg, XML_ELEMENT_TARG, Tag_Start);
+ buf_add_tag(&msg, targ->x_value, Tag_String);
+ xml_add_tag(&msg, XML_ELEMENT_INAME, iname);
+ if (luninfo == True)
+ target_info(&msg, iname, targ);
+ if (dostat == True)
+ target_stat(&msg, iname,
+ mgmt_full_phase_statistics);
+ buf_add_tag(&msg, XML_ELEMENT_TARG, Tag_End);
+ }
+ } else {
+ buf_add_tag(&msg, XML_ELEMENT_TARG, Tag_Start);
+ buf_add_tag(&msg, targ->x_value, Tag_String);
+ xml_add_tag(&msg, XML_ELEMENT_INAME, iname);
+ if (dostat == True)
+ target_stat(&msg, iname,
+ mgmt_full_phase_statistics);
+ if (luninfo == True)
+ target_info(&msg, iname, targ);
+ buf_add_tag(&msg, XML_ELEMENT_TARG, Tag_End);
+ }
+ free(iname);
+ }
+ buf_add_tag(&msg, XML_ELEMENT_RESULT, Tag_End);
+ free(prop);
+
+ return (msg);
+}
+
+static char *
+list_initiator(xml_node_t *x)
+{
+ char *msg = NULL,
+ *attr,
+ *prop = NULL;
+ Boolean_t verbose = False;
+ xml_node_t *init = NULL;
+
+ /* ---- Optional arguments ---- */
+ if ((xml_find_value_str(x, XML_ELEMENT_NAME, &prop) == True) &&
+ (prop == NULL)) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ return (msg);
+ }
+ (void) xml_find_value_boolean(x, XML_ELEMENT_VERBOSE, &verbose);
+
+ buf_add_tag_and_attr(&msg, XML_ELEMENT_RESULT, "version='1.0'");
+ while ((init = xml_node_next(main_config, XML_ELEMENT_INIT, init)) !=
+ NULL) {
+ if ((prop == NULL) ||
+ ((prop != NULL) && (strcmp(prop, init->x_value) == 0))) {
+
+ buf_add_tag(&msg, XML_ELEMENT_INIT, Tag_Start);
+ buf_add_tag(&msg, init->x_value, Tag_String);
+
+ if (xml_find_value_str(init, XML_ELEMENT_INAME,
+ &attr) == True) {
+ xml_add_tag(&msg, XML_ELEMENT_INAME, attr);
+ free(attr);
+ }
+
+ if (xml_find_value_str(init, XML_ELEMENT_CHAPSECRET,
+ &attr) == True) {
+ xml_add_tag(&msg, XML_ELEMENT_CHAPSECRET,
+ attr);
+ free(attr);
+ }
+
+ if (xml_find_value_str(init, XML_ELEMENT_CHAPNAME,
+ &attr) == True) {
+ xml_add_tag(&msg, XML_ELEMENT_CHAPNAME, attr);
+ free(attr);
+ }
+
+ buf_add_tag(&msg, XML_ELEMENT_INIT, Tag_End);
+ }
+ }
+
+ if (prop != NULL)
+ free(prop);
+
+ buf_add_tag(&msg, XML_ELEMENT_RESULT, Tag_End);
+
+ return (msg);
+}
+
+static char *
+list_tpgt(xml_node_t *x)
+{
+ char *msg = NULL,
+ *prop = NULL;
+ Boolean_t verbose = False;
+ xml_node_t *tpgt = NULL,
+ *ip = NULL;
+
+ /* ---- Optional arguments ---- */
+ if ((xml_find_value_str(x, XML_ELEMENT_NAME, &prop) == True) &&
+ (prop == NULL)) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ return (msg);
+ }
+ (void) xml_find_value_boolean(x, XML_ELEMENT_VERBOSE, &verbose);
+
+ buf_add_tag_and_attr(&msg, XML_ELEMENT_RESULT, "version='1.0'");
+ while ((tpgt = xml_node_next(main_config, XML_ELEMENT_TPGT, tpgt)) !=
+ NULL) {
+ if ((prop == NULL) ||
+ ((prop != NULL) && (strcmp(prop, tpgt->x_value) == 0))) {
+
+ buf_add_tag(&msg, XML_ELEMENT_TPGT, Tag_Start);
+ buf_add_tag(&msg, tpgt->x_value, Tag_String);
+
+ while ((ip = xml_node_next(tpgt, XML_ELEMENT_IPADDR,
+ ip)) != NULL) {
+ xml_add_tag(&msg, ip->x_name, ip->x_value);
+ }
+
+ buf_add_tag(&msg, XML_ELEMENT_TPGT, Tag_End);
+ }
+ }
+ buf_add_tag(&msg, XML_ELEMENT_RESULT, Tag_End);
+
+ if (prop != NULL)
+ free(prop);
+ return (msg);
+}
+
+/*ARGSUSED*/
+static char *
+list_admin(xml_node_t *x)
+{
+ char *msg = NULL;
+ admin_table_t *p;
+ xml_node_t *node = NULL;
+
+ buf_add_tag_and_attr(&msg, XML_ELEMENT_RESULT, "version='1.0'");
+ buf_add_tag(&msg, XML_ELEMENT_ADMIN, Tag_Start);
+
+ node = NULL;
+ for (p = admin_prop_list; p->name != NULL; p++) {
+ node = xml_node_next_child(main_config, p->name, NULL);
+ if (node) {
+ xml_add_tag(&msg, p->name, node->x_value);
+ }
+ }
+
+ buf_add_tag(&msg, XML_ELEMENT_ADMIN, Tag_End);
+ buf_add_tag(&msg, XML_ELEMENT_RESULT, Tag_End);
+
+ return (msg);
+}
+
+static void
+target_stat(char **msg, char *targ_name, mgmt_type_t type)
+{
+ iscsi_conn_t *c;
+ msg_t *m;
+ target_queue_t *q = queue_alloc();
+ mgmt_request_t mgmt_rqst;
+ int msg_sent,
+ i;
+ extern pthread_mutex_t port_mutex;
+
+ mgmt_rqst.m_q = q;
+ mgmt_rqst.m_u.m_resp = msg;
+ mgmt_rqst.m_time = time(NULL);
+ mgmt_rqst.m_request = type;
+ (void) pthread_mutex_init(&mgmt_rqst.m_resp_mutex, NULL);
+
+ (void) pthread_mutex_lock(&port_mutex);
+ mgmt_rqst.m_targ_name = targ_name;
+ msg_sent = 0;
+ for (c = conn_head; c; c = c->c_next) {
+ if (c->c_state == S5_LOGGED_IN) {
+ /*
+ * Only send requests for statistics to
+ * connections that are up. Could even
+ * go further and only look at connections
+ * which are S5_LOGGED_IN, but there may
+ * be statistics, such as connection time,
+ * which we'd like to have.
+ */
+ queue_message_set(c->c_dataq, 0, msg_mgmt_rqst,
+ &mgmt_rqst);
+ msg_sent++;
+ }
+ }
+ (void) pthread_mutex_unlock(&port_mutex);
+
+ /*
+ * Comment: main.c:list_targets:1
+ * We wait for the responses without the port_mutex
+ * being held. There is a small window between when the
+ * connection last listens for a message and when the
+ * queue is freed. During that time the connection will
+ * attempt to grab the port_mutex lock so that it
+ * can unlink itself and call queueu_free(). If we sent
+ * the message with the lock held and then wait for a response
+ * it's possible that the connection will deadlock waiting
+ * to get the port_mutex.
+ */
+ for (i = 0; i < msg_sent; i++) {
+ m = queue_message_get(q);
+ queue_message_free(m);
+ }
+ queue_free(q, NULL);
+}
+
+static void
+target_info(char **msg, char *targ_name, xml_node_t *tnode)
+{
+ char path[MAXPATHLEN],
+ lun_buf[16],
+ *prop;
+ xmlTextReaderPtr r;
+ xml_node_t *lnode, /* list node */
+ *lnp, /* list node pointer */
+ *lun,
+ *params;
+ int xml_fd,
+ lun_num;
+
+ if ((lnode = xml_node_next(tnode, XML_ELEMENT_ACLLIST, NULL)) !=
+ NULL) {
+ lnp = NULL;
+ buf_add_tag(msg, XML_ELEMENT_ACLLIST, Tag_Start);
+ while ((lnp = xml_node_next(lnode, XML_ELEMENT_INIT, lnp)) !=
+ NULL)
+ xml_add_tag(msg, XML_ELEMENT_INIT, lnp->x_value);
+ buf_add_tag(msg, XML_ELEMENT_ACLLIST, Tag_End);
+ }
+
+ if ((lnode = xml_node_next(tnode, XML_ELEMENT_TPGTLIST, NULL)) !=
+ NULL) {
+ lnp = NULL;
+ buf_add_tag(msg, XML_ELEMENT_TPGTLIST, Tag_Start);
+ while ((lnp = xml_node_next(lnode, XML_ELEMENT_TPGT, lnp)) !=
+ NULL)
+ xml_add_tag(msg, XML_ELEMENT_TPGT, lnp->x_value);
+ buf_add_tag(msg, XML_ELEMENT_TPGTLIST, Tag_End);
+ }
+
+ if ((lnode = xml_node_next(tnode, XML_ELEMENT_ALIAS, NULL)) != NULL)
+ xml_add_tag(msg, XML_ELEMENT_ALIAS, lnode->x_value);
+
+ if ((lnode = xml_node_next(tnode, XML_ELEMENT_MAXRECV, NULL)) != NULL)
+ xml_add_tag(msg, XML_ELEMENT_MAXRECV, lnode->x_value);
+
+ if ((lnode = xml_node_next(tnode, XML_ELEMENT_LUNLIST, NULL)) == NULL)
+ return;
+
+ buf_add_tag(msg, XML_ELEMENT_LUNINFO, Tag_Start);
+ lun = NULL;
+ while ((lun = xml_node_next(lnode, XML_ELEMENT_LUN, lun)) != NULL) {
+ if ((xml_find_value_int(lun, XML_ELEMENT_LUN, &lun_num)) ==
+ False)
+ continue;
+ (void) snprintf(path, sizeof (path), "%s/%s/%s%d",
+ target_basedir, targ_name, PARAMBASE, lun_num);
+ if ((xml_fd = open(path, O_RDONLY)) < 0)
+ continue;
+ if ((r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd,
+ NULL, NULL, 0)) == NULL)
+ continue;
+
+ params = NULL;
+ while (xmlTextReaderRead(r) == 1) {
+ if (xml_process_node(r, &params) == False)
+ break;
+ }
+ (void) close(xml_fd);
+ xmlTextReaderClose(r);
+ xmlFreeTextReader(r);
+
+ buf_add_tag(msg, XML_ELEMENT_LUN, Tag_Start);
+ snprintf(lun_buf, sizeof (lun_buf), "%d", lun_num);
+ buf_add_tag(msg, lun_buf, Tag_String);
+
+ if (xml_find_value_str(params, XML_ELEMENT_GUID, &prop) ==
+ True) {
+ xml_add_tag(msg, XML_ELEMENT_GUID, prop);
+ free(prop);
+ }
+ if (xml_find_value_str(params, XML_ELEMENT_VID, &prop) ==
+ True) {
+ xml_add_tag(msg, XML_ELEMENT_VID, prop);
+ free(prop);
+ }
+ if (xml_find_value_str(params, XML_ELEMENT_PID, &prop) ==
+ True) {
+ xml_add_tag(msg, XML_ELEMENT_PID, prop);
+ free(prop);
+ }
+ if (xml_find_value_str(params, XML_ELEMENT_DTYPE, &prop) ==
+ True) {
+ xml_add_tag(msg, XML_ELEMENT_DTYPE, prop);
+ free(prop);
+ }
+ if (xml_find_value_str(params, XML_ELEMENT_SIZE, &prop) ==
+ True) {
+ xml_add_tag(msg, XML_ELEMENT_SIZE, prop);
+ free(prop);
+ }
+ if (xml_find_value_str(params, XML_ELEMENT_STATUS, &prop) ==
+ True) {
+ xml_add_tag(msg, XML_ELEMENT_STATUS, prop);
+ free(prop);
+ }
+ if (xml_find_value_str(params, XML_ELEMENT_BACK, &prop) ==
+ True) {
+ xml_add_tag(msg, XML_ELEMENT_BACK, prop);
+ free(prop);
+ }
+ buf_add_tag(msg, XML_ELEMENT_LUN, Tag_End);
+
+ xml_tree_free(params);
+ }
+ buf_add_tag(msg, XML_ELEMENT_LUNINFO, Tag_End);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/mgmt_modify.c b/usr/src/cmd/iscsi/iscsitgtd/mgmt_modify.c
new file mode 100644
index 0000000000..ffc9384f11
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/mgmt_modify.c
@@ -0,0 +1,692 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <time.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <strings.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "xml.h"
+#include "queue.h"
+#include "utility.h"
+#include "iscsi_cmd.h"
+#include "target.h"
+#include "errcode.h"
+
+static char *modify_target(xml_node_t *x);
+static char *modify_initiator(xml_node_t *x);
+static char *modify_admin(xml_node_t *x);
+static char *modify_tpgt(xml_node_t *x);
+static Boolean_t modify_element(char *, char *, xml_node_t *, match_type_t);
+
+/*
+ * []----
+ * | modify_func -- dispatch routine for objects
+ * []----
+ */
+/*ARGSUSED*/
+void
+modify_func(xml_node_t *p, target_queue_t *reply, target_queue_t *mgmt)
+{
+ xml_node_t *x;
+ char *reply_msg = NULL;
+
+ if (p->x_child == NULL) {
+ xml_rtn_msg(&reply_msg, ERR_SYNTAX_MISSING_OBJECT);
+
+ } else {
+ x = p->x_child;
+
+ if (x->x_name == NULL) {
+ xml_rtn_msg(&reply_msg, ERR_SYNTAX_MISSING_OBJECT);
+ } else if (strcmp(x->x_name, XML_ELEMENT_TARG) == 0) {
+ reply_msg = modify_target(x);
+ } else if (strcmp(x->x_name, XML_ELEMENT_INIT) == 0) {
+ reply_msg = modify_initiator(x);
+ } else if (strcmp(x->x_name, XML_ELEMENT_ADMIN) == 0) {
+ reply_msg = modify_admin(x);
+ } else if (strcmp(x->x_name, XML_ELEMENT_TPGT) == 0) {
+ reply_msg = modify_tpgt(x);
+ } else {
+ xml_rtn_msg(&reply_msg, ERR_INVALID_OBJECT);
+ }
+ }
+ queue_message_set(reply, 0, msg_mgmt_rply, reply_msg);
+}
+
+/*
+ * []----
+ * | modify_target -- updates one or more properties for a target
+ * []----
+ */
+static char *
+modify_target(xml_node_t *x)
+{
+ char *msg = NULL,
+ *name = NULL,
+ *iscsi,
+ *prop = NULL,
+ size_str[16],
+ path[MAXPATHLEN],
+ *m,
+ buf[512]; /* one sector size block */
+ xml_node_t *t = NULL,
+ *list = NULL,
+ *c = NULL,
+ *node;
+ Boolean_t change_made = False;
+ int lun = 0,
+ fd,
+ xml_fd;
+ uint64_t val,
+ new_lu_size,
+ cur_lu_size;
+ struct stat st;
+ xmlTextReaderPtr r;
+
+ if (xml_find_value_str(x, XML_ELEMENT_NAME, &name) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ return (msg);
+ }
+
+ while ((t = xml_node_next(targets_config, XML_ELEMENT_TARG,
+ t)) != NULL) {
+ if (strcmp(t->x_value, name) == 0) {
+ break;
+ }
+ }
+
+ /* ---- Finished with these so go ahead and release the memory ---- */
+ free(name);
+
+ if (t == NULL) {
+ xml_rtn_msg(&msg, ERR_TARG_NOT_FOUND);
+ return (msg);
+ }
+
+ /*
+ * Grow the LU. We currently do not support shrinking the LU and
+ * that is only because it's unknown if any applications could support
+ * that type of data loss. To support shrinking all that would be
+ * needed is to remove the new/old size check and perform a truncation.
+ * The actually truncation request should be shipped off to the T10
+ * layer so that the LU thread can remap the smaller size without
+ * anyone accessing the data.
+ */
+ if (xml_find_value_str(x, XML_ELEMENT_SIZE, &prop) == True) {
+ if (prop == NULL) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_TPGT);
+ return (msg);
+ }
+ if (strtoll_multiplier(prop, &new_lu_size) == False) {
+ free(prop);
+ xml_rtn_msg(&msg, ERR_INVALID_SIZE);
+ return (msg);
+ }
+ free(prop);
+ if ((new_lu_size % 512LL) != 0) {
+ xml_rtn_msg(&msg, ERR_SIZE_MOD_BLOCK);
+ return (msg);
+ }
+ new_lu_size /= 512LL;
+
+ if (xml_find_value_str(x, XML_ELEMENT_INAME, &iscsi) == False) {
+ xml_rtn_msg(&msg, ERR_TARGCFG_MISSING_INAME);
+ return (msg);
+ }
+
+ /* ---- default to LUN 0 ---- */
+ (void) xml_find_value_int(x, XML_ELEMENT_LUN, &lun);
+
+ /* ---- read in current paramaters ---- */
+ snprintf(path, sizeof (path), "%s/%s/%s%d", target_basedir,
+ iscsi, PARAMBASE, lun);
+ if ((xml_fd = open(path, O_RDONLY)) < 0) {
+ xml_rtn_msg(&msg, ERR_OPEN_PARAM_FILE_FAILED);
+ return (msg);
+ }
+ if ((r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd, NULL, NULL,
+ 0)) != NULL) {
+ node = NULL;
+ while (xmlTextReaderRead(r) == 1)
+ if (xml_process_node(r, &node) == False)
+ break;
+ } else {
+ xml_rtn_msg(&msg, ERR_INIT_XML_READER_FAILED);
+ return (msg);
+ }
+
+ (void) close(xml_fd);
+ xmlTextReaderClose(r);
+ xmlFreeTextReader(r);
+
+ /* ---- validate that we're indeed growing the LU ---- */
+ if (xml_find_value_str(node, XML_ELEMENT_SIZE, &prop) ==
+ False) {
+ xml_rtn_msg(&msg, ERR_INIT_XML_READER_FAILED);
+ return (msg);
+ }
+ if (strtoll_multiplier(prop, &cur_lu_size) == False) {
+ free(prop);
+ xml_rtn_msg(&msg, ERR_INVALID_SIZE);
+ return (msg);
+ }
+ free(prop);
+
+ if (new_lu_size < cur_lu_size) {
+ xml_rtn_msg(&msg, ERR_CANT_SHRINK_LU);
+ return (msg);
+ }
+
+ /* ---- check that this LU is of type 'disk' or 'tape' ---- */
+ if (xml_find_value_str(node, XML_ELEMENT_DTYPE, &prop) ==
+ False) {
+ xml_rtn_msg(&msg, ERR_INIT_XML_READER_FAILED);
+ return (msg);
+ }
+ if ((strcmp(prop, TGT_TYPE_DISK) != 0) &&
+ (strcmp(prop, TGT_TYPE_TAPE) != 0)) {
+ xml_rtn_msg(&msg, ERR_RESIZE_WRONG_DTYPE);
+ return (msg);
+ }
+ free(prop);
+
+ /* ---- validate the backing store is a regular file ---- */
+ snprintf(path, sizeof (path), "%s/%s/%s%d", target_basedir,
+ iscsi, LUNBASE, lun);
+ if (stat(path, &st) == -1) {
+ xml_rtn_msg(&msg, ERR_STAT_BACKING_FAILED);
+ return (msg);
+ }
+ if ((st.st_mode & S_IFMT) != S_IFREG) {
+ xml_rtn_msg(&msg,
+ ERR_DISK_BACKING_MUST_BE_REGULAR_FILE);
+ return (msg);
+ }
+
+ /* ---- update the parameter node with new size ---- */
+ snprintf(size_str, sizeof (size_str), "0x%llx", new_lu_size);
+ if ((c = xml_alloc_node(XML_ELEMENT_SIZE, Uint64, size_str)) ==
+ False) {
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+ xml_replace_child(node, c, MatchName);
+ xml_tree_free(c);
+
+ /* ---- now update params file ---- */
+ snprintf(path, sizeof (path), "%s/%s/%s%d", target_basedir,
+ iscsi, PARAMBASE, lun);
+ if (xml_dump2file(node, path) == False) {
+ xml_rtn_msg(&msg, ERR_UPDATE_TARGCFG_FAILED);
+ return (msg);
+ }
+
+ /* ---- grow lu backing store ---- */
+ snprintf(path, sizeof (path), "%s/%s/%s%d", target_basedir,
+ iscsi, LUNBASE, lun);
+ if ((fd = open(path, O_RDWR|O_CREAT|O_LARGEFILE, 0600)) < 0) {
+ xml_rtn_msg(&msg, ERR_LUN_NOT_FOUND);
+ return (msg);
+ }
+ (void) lseek(fd, (new_lu_size * 512LL) - 512LL, 0);
+ bzero(buf, sizeof (buf));
+ if (write(fd, buf, sizeof (buf)) != sizeof (buf)) {
+ xml_rtn_msg(&msg, ERR_LUN_NOT_GROWN);
+ return (msg);
+ }
+ (void) close(fd);
+
+ /* ---- send updates to current initiators via ASC/ASCQ ---- */
+ iscsi_capacity_change(iscsi, lun);
+
+ free(iscsi);
+ prop = NULL;
+ xml_tree_free(node);
+ }
+
+ if (xml_find_value_str(x, XML_ELEMENT_TPGT, &prop) == True) {
+ if (prop == NULL) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_ACL);
+ return (msg);
+ }
+
+ /*
+ * Validate that the Target Portal Group Tag is reasonable.
+ */
+ val = strtoll(prop, &m, 0);
+ if ((val < TPGT_MIN) || (val > TPGT_MAX) ||
+ ((m != NULL) && (*m != '\0'))) {
+ xml_rtn_msg(&msg, ERR_INVALID_TPGT);
+ free(prop);
+ return (msg);
+ }
+
+ if ((c = xml_alloc_node(XML_ELEMENT_TPGT, String, prop)) ==
+ NULL) {
+ free(prop);
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+
+ if ((list = xml_node_next(t, XML_ELEMENT_TPGTLIST,
+ NULL)) != NULL) {
+ xml_replace_child(list, c, MatchBoth);
+ /*
+ * xml_replace_child will duplicate the child node
+ * xml_add_child which is used below just links it
+ * into the tree.
+ */
+ xml_tree_free(c);
+ } else {
+ list = xml_alloc_node(XML_ELEMENT_TPGTLIST, String, "");
+ if (list == NULL) {
+ free(prop);
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+ (void) xml_add_child(list, c);
+ (void) xml_add_child(t, list);
+ }
+ free(prop);
+ prop = NULL;
+ change_made = True;
+ }
+
+ if (xml_find_value_str(x, XML_ELEMENT_ACL, &prop) == True) {
+ if (prop == NULL) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_ACL);
+ return (msg);
+ }
+
+ c = xml_alloc_node(XML_ELEMENT_INIT, String, prop);
+ if (c == NULL) {
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+ if ((list = xml_node_next(t, XML_ELEMENT_ACLLIST,
+ NULL)) != NULL) {
+ xml_replace_child(list, c, MatchBoth);
+ /* ---- See above usage ---- */
+ xml_tree_free(c);
+ } else {
+ list = xml_alloc_node(XML_ELEMENT_ACLLIST, String, "");
+ if (list == NULL) {
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+ (void) xml_add_child(list, c);
+ (void) xml_add_child(t, list);
+ }
+ free(prop);
+ prop = NULL;
+ change_made = True;
+ }
+
+ if (xml_find_value_str(x, XML_ELEMENT_ALIAS, &prop) == True) {
+ if (prop == NULL) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_ALIAS);
+ return (msg);
+ }
+
+ if (modify_element(XML_ELEMENT_ALIAS, prop, t, MatchName) ==
+ False) {
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+ free(prop);
+ prop = NULL;
+ change_made = True;
+ }
+
+ if (xml_find_value_str(x, XML_ELEMENT_MAXRECV, &prop) == True) {
+ if (prop == NULL) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_MAXRECV);
+ return (msg);
+ }
+
+ if ((strtoll_multiplier(prop, &val) == False) ||
+ (val < MAXRCVDATA_MIN) || (val > MAXRCVDATA_MAX)) {
+ free(prop);
+ xml_rtn_msg(&msg, ERR_INVALID_MAXRECV);
+ return (msg);
+ }
+ free(prop);
+ if ((prop = malloc(32)) == NULL) {
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+ snprintf(prop, 32, "%d", val);
+
+ if (modify_element(XML_ELEMENT_MAXRECV, prop, t, MatchName) ==
+ False) {
+ free(prop);
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+ free(prop);
+ prop = NULL;
+ change_made = True;
+ }
+
+ if (change_made == True) {
+ if (update_config_targets(&msg) == True)
+ xml_rtn_msg(&msg, ERR_SUCCESS);
+ } else {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_OPERAND);
+ }
+
+ return (msg);
+}
+
+/*
+ * []----
+ * | modify_initiator -- store the CHAP information for an initiator
+ * []----
+ */
+static char *
+modify_initiator(xml_node_t *x)
+{
+ char *msg = NULL,
+ *name = NULL,
+ *prop = NULL;
+ xml_node_t *inode = NULL;
+ Boolean_t changes_made = False;
+
+ if (xml_find_value_str(x, XML_ELEMENT_NAME, &name) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ return (msg);
+ }
+
+ while ((inode = xml_node_next(main_config, XML_ELEMENT_INIT,
+ inode)) != NULL) {
+ if (strcmp(inode->x_value, name) == 0)
+ break;
+ }
+
+ /*
+ * We no longer need the name since we should have found the node
+ * it refers to and this way we don't have to worry about freeing
+ * the storage later.
+ */
+ free(name);
+
+ if (inode == NULL) {
+ xml_rtn_msg(&msg, ERR_INIT_NOT_FOUND);
+ return (msg);
+ }
+
+ if (xml_find_value_str(x, XML_ELEMENT_CHAPSECRET, &prop) == True) {
+ if (prop == NULL) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_CHAPSECRET);
+ return (msg);
+ }
+
+ if (modify_element(XML_ELEMENT_CHAPSECRET, prop, inode,
+ MatchName) == False) {
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+ free(prop);
+ changes_made = True;
+ }
+
+ if (xml_find_value_str(x, XML_ELEMENT_CHAPNAME, &prop) == True) {
+ if (prop == NULL) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_CHAPNAME);
+ return (msg);
+ }
+
+ if (modify_element(XML_ELEMENT_CHAPNAME, prop, inode,
+ MatchName) == False) {
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+ free(prop);
+ changes_made = True;
+ }
+
+ if (changes_made == True) {
+ if (update_config_main(&msg) == True)
+ xml_rtn_msg(&msg, ERR_SUCCESS);
+ } else {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_OPERAND);
+ }
+
+ return (msg);
+}
+
+/*
+ * []----
+ * | modify_admin -- modify one or more of the admin related props
+ * []----
+ */
+static char *
+modify_admin(xml_node_t *x)
+{
+ char *msg = NULL,
+ *prop;
+ Boolean_t changes_made = False;
+ admin_table_t *ap;
+
+ for (ap = admin_prop_list; ap->name; ap++) {
+ if (xml_find_value_str(x, ap->name, &prop) == True) {
+
+ if ((prop == NULL) || (strlen(prop) == 0))
+ break;
+
+ /*
+ * Do the function call first if it exists which
+ * will allow possible checking to be done first.
+ */
+ if (ap->func) {
+ if ((msg = (*ap->func)(ap->name, prop)) != NULL)
+ return (msg);
+ }
+ if (modify_element(ap->name, prop, main_config,
+ MatchName) == False) {
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+ free(prop);
+ changes_made = True;
+ }
+ }
+
+ if (changes_made == True) {
+ if (update_config_main(&msg) == True)
+ xml_rtn_msg(&msg, ERR_SUCCESS);
+ } else {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_OPERAND);
+ }
+
+ return (msg);
+}
+
+/*
+ * []----
+ * | modify_tpgt -- add an IP-address to a target portal group
+ * []----
+ */
+static char *
+modify_tpgt(xml_node_t *x)
+{
+ struct addrinfo *res = NULL;
+ char *msg = NULL,
+ *name = NULL,
+ *ip_str = NULL;
+ xml_node_t *tnode = NULL;
+
+ if (xml_find_value_str(x, XML_ELEMENT_NAME, &name) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ goto error;
+ }
+ if (xml_find_value_str(x, XML_ELEMENT_IPADDR, &ip_str) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_IPADDR);
+ goto error;
+ }
+ if ((getaddrinfo(ip_str, NULL, NULL, &res) != 0) || (res == NULL)) {
+ xml_rtn_msg(&msg, ERR_INVALID_IP);
+ goto error;
+ }
+ while ((tnode = xml_node_next(main_config, XML_ELEMENT_TPGT,
+ tnode)) != NULL) {
+ if (strcmp(tnode->x_value, name) == 0)
+ break;
+ }
+ if (tnode == NULL) {
+ xml_rtn_msg(&msg, ERR_TPGT_NOT_FOUND);
+ goto error;
+ }
+ if (modify_element(XML_ELEMENT_IPADDR, ip_str, tnode, MatchBoth) ==
+ False) {
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ }
+
+ if (update_config_main(&msg) == True)
+ xml_rtn_msg(&msg, ERR_SUCCESS);
+
+error:
+ if (name)
+ free(name);
+ if (ip_str)
+ free(ip_str);
+ return (msg);
+}
+
+/*
+ * []----
+ * | modify_element -- helper function to create node and add it to parent
+ * |
+ * | A False return value indicates a failure to allocate enough memory.
+ * []----
+ */
+static Boolean_t
+modify_element(char *name, char *value, xml_node_t *p, match_type_t m)
+{
+ xml_node_t *c;
+
+ if ((c = xml_alloc_node(name, String, value)) == NULL)
+ return (False);
+ else {
+ xml_replace_child(p, c, m);
+ xml_tree_free(c);
+ return (True);
+ }
+}
+
+/*
+ * []----
+ * | update_basedir -- update the global target directory
+ * |
+ * | Most of the properties when updated require no futher processing. The
+ * | target base directory however must be updated if it hasn't been set.
+ * | On a new system the daemon will not have any location to place the
+ * | backing store and target configuration files. On a live system we would
+ * | screw things up if we changed the global variable if it was already
+ * | in use, so we only allow the updating to occur if there are no targets.
+ * []----
+ */
+/*ARGSUSED*/
+char *
+update_basedir(char *name, char *prop)
+{
+ xml_node_t *targ = NULL;
+ int count = 0;
+ char *msg = NULL;
+
+ if ((prop == NULL) || (strlen(prop) == 0) || (prop[0] != '/')) {
+ xml_rtn_msg(&msg, ERR_INVALID_BASEDIR);
+ return (msg);
+ }
+
+ while ((targ = xml_node_next(targets_config, XML_ELEMENT_TARG,
+ targ)) != NULL) {
+ count++;
+ }
+
+ if (target_basedir == NULL) {
+ target_basedir = strdup(prop);
+ } else if (count == 0) {
+ free(target_basedir);
+ target_basedir = strdup(prop);
+ if ((mkdir(target_basedir, 0700) != 0) && (errno != EEXIST)) {
+ xml_rtn_msg(&msg, ERR_CREATE_TARGET_DIR_FAILED);
+ } else {
+ if (process_target_config() == False) {
+ xml_rtn_msg(&msg, ERR_CREATE_TARGET_DIR_FAILED);
+ }
+ }
+ } else {
+ xml_rtn_msg(&msg, ERR_VALID_TARG_EXIST);
+ }
+ return (msg);
+}
+
+/*
+ * []----
+ * | validate_radius -- validate that server[:port] are valid
+ * []----
+ */
+char *
+valid_radius_srv(char *name, char *prop)
+{
+ struct addrinfo *res = NULL;
+ char *msg = NULL,
+ *sp,
+ *p;
+ int port;
+
+ if ((sp = strdup(prop)) == NULL) {
+ xml_rtn_msg(&msg, ERR_NO_MEM);
+ return (msg);
+ } else if ((p = strrchr(sp, ':')) != NULL) {
+ *p++ = '\0';
+ port = atoi(p);
+ if ((port < 1) || (port > 65535)) {
+ xml_rtn_msg(&msg, ERR_INVALID_RADSRV);
+ free(sp);
+ return (msg);
+ }
+ }
+ if ((getaddrinfo(sp, NULL, NULL, &res) != 0) || (res == NULL))
+ xml_rtn_msg(&msg, ERR_INVALID_RADSRV);
+ else
+ freeaddrinfo(res);
+ free(sp);
+ return (msg);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/mgmt_remove.c b/usr/src/cmd/iscsi/iscsitgtd/mgmt_remove.c
new file mode 100644
index 0000000000..47a1e269b3
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/mgmt_remove.c
@@ -0,0 +1,288 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file deals with XML data for removing various configuration data.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "utility.h"
+#include "xml.h"
+#include "queue.h"
+#include "target.h"
+#include "iscsi_cmd.h"
+#include "errcode.h"
+
+static char *remove_target(xml_node_t *x);
+static char *remove_initiator(xml_node_t *x);
+static char *remove_tpgt(xml_node_t *x);
+
+/*ARGSUSED*/
+void
+remove_func(xml_node_t *p, target_queue_t *reply, target_queue_t *mgmt)
+{
+ xml_node_t *x;
+ char msgbuf[80],
+ *reply_msg = NULL;
+
+ if (p->x_child == NULL) {
+ xml_rtn_msg(&reply_msg, ERR_SYNTAX_MISSING_OBJECT);
+ } else {
+ x = p->x_child;
+
+ if (x->x_name == NULL) {
+ xml_rtn_msg(&reply_msg, ERR_SYNTAX_MISSING_OBJECT);
+ } else if (strcmp(x->x_name, XML_ELEMENT_TARG) == 0) {
+ reply_msg = remove_target(x);
+ } else if (strcmp(x->x_name, XML_ELEMENT_INIT) == 0) {
+ reply_msg = remove_initiator(x);
+ } else if (strcmp(x->x_name, XML_ELEMENT_TPGT) == 0) {
+ reply_msg = remove_tpgt(x);
+ } else {
+ (void) snprintf(msgbuf, sizeof (msgbuf),
+ "Unknown object '%s' for delete element",
+ x->x_name);
+ xml_rtn_msg(&reply_msg, ERR_INVALID_OBJECT);
+ }
+ }
+ queue_message_set(reply, 0, msg_mgmt_rply, reply_msg);
+}
+
+static char *
+remove_target(xml_node_t *x)
+{
+ char *msg = NULL,
+ *prop = NULL;
+ xml_node_t *targ = NULL,
+ *list,
+ *c = NULL;
+ Boolean_t change_made = False;
+ int lun_num;
+
+ if (xml_find_value_str(x, XML_ELEMENT_NAME, &prop) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ return (msg);
+ }
+
+ while ((targ = xml_node_next(targets_config, XML_ELEMENT_TARG, targ)) !=
+ NULL) {
+ if (strcmp(targ->x_value, prop) == 0)
+ break;
+ }
+ free(prop);
+ if (targ == NULL) {
+ xml_rtn_msg(&msg, ERR_TARG_NOT_FOUND);
+ return (msg);
+ }
+ if (xml_find_value_str(x, XML_ELEMENT_ACL, &prop) == True) {
+ if (prop == NULL) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_ACL);
+ return (msg);
+ }
+ if ((list = xml_node_next(targ, XML_ELEMENT_ACLLIST, NULL)) ==
+ NULL) {
+ free(prop);
+ xml_rtn_msg(&msg, ERR_ACL_NOT_FOUND);
+ return (msg);
+ }
+ c = xml_alloc_node(XML_ELEMENT_INIT, String, prop);
+ if (xml_remove_child(list, c, MatchBoth) == False) {
+ xml_rtn_msg(&msg, ERR_INIT_NOT_FOUND);
+ goto error;
+ }
+ xml_free_node(c);
+ if (list->x_child == NULL)
+ (void) xml_remove_child(targ, list, MatchName);
+ free(prop);
+ change_made = True;
+ }
+ if (xml_find_value_str(x, XML_ELEMENT_TPGT, &prop) == True) {
+ if (prop == NULL) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_TPGT);
+ return (msg);
+ }
+ if ((list = xml_node_next(targ, XML_ELEMENT_TPGTLIST, NULL)) ==
+ NULL) {
+ free(prop);
+ xml_rtn_msg(&msg, ERR_ACL_NOT_FOUND);
+ return (msg);
+ }
+ c = xml_alloc_node(XML_ELEMENT_TPGT, String, prop);
+ if (xml_remove_child(list, c, MatchBoth) == False) {
+ xml_rtn_msg(&msg, ERR_TPGT_NOT_FOUND);
+ goto error;
+ }
+ xml_free_node(c);
+ if (list->x_child == NULL)
+ (void) xml_remove_child(targ, list, MatchName);
+ free(prop);
+ change_made = True;
+ }
+ if (xml_find_value_int(x, XML_ELEMENT_LUN, &lun_num) == True) {
+
+ if (xml_find_value_intchk(x, XML_ELEMENT_LUN, &lun_num) ==
+ False) {
+ xml_rtn_msg(&msg, ERR_LUN_INVALID_RANGE);
+ return (msg);
+ }
+
+ /*
+ * Save the iscsi-name which we'll need to remove LUNs.
+ */
+ if (xml_find_value_str(targ, XML_ELEMENT_INAME, &prop) ==
+ False) {
+ xml_rtn_msg(&msg, ERR_TARGCFG_MISSING_INAME);
+ return (msg);
+ }
+
+ logout_targ(prop);
+ thick_provo_stop(prop, lun_num);
+
+ remove_target_common(targ->x_value, lun_num, &msg);
+ if (msg != NULL)
+ goto error;
+
+ iscsi_inventory_change(prop);
+ free(prop);
+ change_made = True;
+ }
+
+ if (change_made == True) {
+ if (update_config_targets(&msg) == True)
+ xml_rtn_msg(&msg, ERR_SUCCESS);
+ } else {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_OPERAND);
+ }
+
+ return (msg);
+
+error:
+ if (c != NULL)
+ xml_free_node(c);
+ if (prop != NULL)
+ free(prop);
+ return (msg);
+}
+
+static char *
+remove_initiator(xml_node_t *x)
+{
+ char *msg = NULL,
+ *name;
+ xml_node_t *node = NULL;
+
+ if (xml_find_value_str(x, XML_ELEMENT_NAME, &name) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ return (msg);
+ }
+ while ((node = xml_node_next(main_config, XML_ELEMENT_INIT, node)) !=
+ NULL) {
+ if (strcmp(node->x_value, name) == 0)
+ break;
+ }
+ free(name);
+ if (node == NULL) {
+ xml_rtn_msg(&msg, ERR_INIT_NOT_FOUND);
+ return (msg);
+ }
+ if (xml_find_value_str(x, XML_ELEMENT_ALL, &name) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_ALL);
+ return (msg);
+ }
+ (void) xml_remove_child(main_config, node, MatchBoth);
+
+ if (update_config_main(&msg) == True)
+ xml_rtn_msg(&msg, ERR_SUCCESS);
+
+ return (msg);
+}
+
+static char *
+remove_tpgt(xml_node_t *x)
+{
+ char *msg = NULL,
+ *prop = NULL;
+ xml_node_t *node = NULL,
+ *c = NULL;
+ Boolean_t change_made = False;
+
+ if (xml_find_value_str(x, XML_ELEMENT_NAME, &prop) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ return (msg);
+ }
+ while ((node = xml_node_next(main_config, XML_ELEMENT_TPGT, node)) !=
+ NULL) {
+ if (strcmp(node->x_value, prop) == 0)
+ break;
+ }
+ free(prop);
+ if (node == NULL) {
+ xml_rtn_msg(&msg, ERR_TPGT_NOT_FOUND);
+ return (msg);
+ }
+ if (xml_find_value_str(x, XML_ELEMENT_IPADDR, &prop) == True) {
+ if (prop == NULL) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_IPADDR);
+ return (msg);
+ }
+ c = xml_alloc_node(XML_ELEMENT_IPADDR, String, prop);
+ if (xml_remove_child(node, c, MatchBoth) == False) {
+ xml_rtn_msg(&msg, ERR_INVALID_IP);
+ goto error;
+ }
+ xml_free_node(c);
+ free(prop);
+ change_made = True;
+ }
+ if ((change_made != True) &&
+ (xml_find_value_str(x, XML_ELEMENT_ALL, &prop) == True)) {
+ xml_remove_child(main_config, node, MatchBoth);
+ change_made = True;
+ }
+
+ if (change_made == True) {
+ if (update_config_main(&msg) == True)
+ xml_rtn_msg(&msg, ERR_SUCCESS);
+ } else {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_OPERAND);
+ }
+
+ return (msg);
+
+error:
+ if (c != NULL)
+ xml_free_node(c);
+ if (prop != NULL)
+ free(prop);
+ return (msg);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/port.h b/usr/src/cmd/iscsi/iscsitgtd/port.h
new file mode 100644
index 0000000000..e1cc377fee
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/port.h
@@ -0,0 +1,48 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef PORT_H
+#define PORT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "iscsi_conn.h"
+
+typedef struct port_args {
+ target_queue_t *port_mgmtq, /* management queue */
+ *port_dataq; /* incoming data for thread */
+ int port_num, /* port number to monitor */
+ port_socket;
+} port_args_t;
+
+void port_init();
+void *port_watcher(void *v);
+void *port_management(void *v);
+void port_conn_remove(iscsi_conn_t *c);
+
+extern iscsi_conn_t *conn_head;
+
+#endif /* PORT_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/queue.h b/usr/src/cmd/iscsi/iscsitgtd/queue.h
new file mode 100644
index 0000000000..92e823fc80
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/queue.h
@@ -0,0 +1,268 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TARGET_QUEUE_H
+#define _TARGET_QUEUE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <pthread.h>
+#include <sys/time.h>
+#include <stdarg.h>
+#include <xml.h>
+#include <synch.h>
+
+#include "local_types.h"
+
+#define Q_CONN_ERRS 0x00001
+#define Q_CONN_LOGIN 0x00002
+#define Q_CONN_NONIO 0x00004
+#define Q_CONN_IO 0x00008
+
+#define Q_SESS_ERRS 0x00010
+#define Q_SESS_LOGIN 0x00020
+#define Q_SESS_NONIO 0x00040
+#define Q_SESS_IO 0x00080
+
+#define Q_STE_ERRS 0x00100
+#define Q_STE_NONIO 0x00200
+#define Q_STE_IO 0x00400
+
+#define Q_GEN_ERRS 0x01000
+#define Q_GEN_DETAILS 0x02000
+
+/*
+ * When used the queue request will be place at the head of the queue.
+ */
+#define Q_HIGH 0x10000
+
+extern int qlog_lvl;
+
+typedef enum {
+ /*
+ * []----------------------------------------------------------------
+ * | Messages internal to the SAM-3 portion. When the transport calls
+ * | the SAM-3 interfaces messages are enqueued to the LU. The LU
+ * | thread then dequeues these messages and calls the appropriate
+ * | function for the emulator.
+ */
+
+ /* ---- from transport ---- */
+ msg_cmd_send,
+ msg_cmd_data_out,
+
+ /* ---- from emulation ---- */
+ msg_cmd_data_in,
+ msg_cmd_data_rqst,
+ msg_cmd_cmplt,
+
+ /* ---- Internal SAM-3 messages ---- */
+ msg_lu_add,
+ msg_lu_remove,
+ msg_lu_online,
+
+ /*
+ * | End of SAM-3 messages
+ * []----------------------------------------------------------------
+ */
+
+ msg_reset_lu,
+ msg_reset_targ,
+ msg_targ_inventory_change,
+ msg_lu_capacity_change,
+
+ /*
+ * The ConnectionReader will send packet ready messages when
+ * data is available. If the socket has an error or is closed
+ * a conn_lost message will be sent. Packet ready will have the
+ * number of bytes currently available on the connection. Don't
+ * free.
+ */
+ msg_conn_lost,
+ msg_packet_ready,
+
+ /*
+ * Shutdowns happen from the bottom up. The replies are in place
+ * so that threads can wait for the top end to disappear, at least
+ * they must no longer reference any common structures such as
+ * message queues.
+ */
+ msg_drain_complete,
+ msg_shutdown,
+ msg_shutdown_rsp,
+
+ /*
+ * Here's a special error condition for STE. When using mmap
+ * to access the backing store of a LUN which is larger than
+ * the underlying storage it's possible to run out of room
+ * on the device (no duh). When that happens the OS will send
+ * the daemon a SIGSBUS. The STE thread catches that signal,
+ * sends a UNIT ATTENTION to the other side, and closes down
+ * the STE thread in a special manner. The transport layer
+ * can then restart another STE thread with the same queues
+ * which mean outstanding I/O restarts.
+ */
+ msg_ste_media_error,
+
+ /*
+ * A NopIn request could be sent on the connection receive thread
+ * except for one little issue. Since both the receive and transmit
+ * threads could be issuing packets and data to the socket at the
+ * same time we must protect those writes so that all of the data
+ * for a single PDU (hdr, checksum, data, checksum) go out together.
+ * It's possible for the socket to receive so much incoming data
+ * that writes will be blocked until some of that data has been
+ * read. If the transmit grabs the lock, attempts to write, and is
+ * blocked we find a condition where the receiver is also blocked
+ * processing a nop command because it can't get the lock. So, instead
+ * we build up the packet and queue it.
+ *
+ * This will also occur with Task Management Requests.
+ */
+ msg_send_pkt,
+
+ /*
+ * During login when the TargetName name/value pair is processed
+ * the value will be sent to STE through the session layer.
+ * STE can use the information however it sees fit.
+ * The InitiatorName will also be sent which STE can use to
+ * validate login properties.
+ */
+ msg_target_name,
+ msg_initiator_name,
+ msg_initiator_alias,
+
+ /*
+ * Issued when causing full allocation of backing store.
+ * This is an internal message used by t10_sam.c
+ */
+ msg_thick_provo,
+
+ /*
+ * ---------------- Debug/Management type messages ----------------
+ */
+ /*
+ * Requests from and replys to the management host will be done using
+ * these messages.
+ */
+ msg_mgmt_rqst,
+ msg_mgmt_rply,
+
+ /*
+ * General debug messages.
+ */
+ msg_log,
+
+ /*
+ * Problem message by some of the auxiliary threads indication
+ * problems.
+ */
+ msg_status
+
+} msg_type_t;
+
+typedef struct msg {
+ struct msg *msg_next,
+ *msg_prev;
+ struct msg *msg_all_next;
+
+ msg_type_t msg_type;
+ void *msg_data;
+
+ /*
+ * This can be used either to insert a message higher into the queue
+ * or as debug level flags.
+ */
+ uint32_t msg_pri_level;
+} msg_t;
+
+typedef struct target_queue {
+ msg_t *q_head,
+ *q_tail;
+ pthread_mutex_t q_mutex;
+ sema_t q_sema;
+ int q_num;
+} target_queue_t;
+
+typedef enum mgmt_type {
+ mgmt_full_phase_statistics,
+ mgmt_discovery_statistics,
+ mgmt_lun_information,
+ mgmt_parse_xml,
+ mgmt_logout
+} mgmt_type_t;
+
+typedef struct mgmt_request {
+ target_queue_t *m_q;
+ mgmt_type_t m_request;
+ time_t m_time;
+ char *m_targ_name;
+
+ /*
+ * This mutex protects the m_buf pointer from multiple connections
+ * attempting to update the response at the same time. One management
+ * request structure is sent to possible multiple connections when
+ * gathering statistics. The connections/sessions will lock access
+ * to the buffer.
+ */
+ pthread_mutex_t m_resp_mutex;
+ union {
+ char **m_resp;
+ xml_node_t *m_node;
+ } m_u;
+} mgmt_request_t;
+
+typedef struct name_request {
+ target_queue_t *nr_q;
+ char *nr_name;
+} name_request_t;
+
+void queue_init();
+target_queue_t *queue_alloc();
+void queue_message_set(target_queue_t *, uint32_t lvl, msg_type_t, void *);
+msg_t *queue_message_get(target_queue_t *);
+msg_t *queue_message_try_get(target_queue_t *q);
+void queue_message_free(msg_t *);
+void queue_walker_free(target_queue_t *q,
+ Boolean_t (*func)(msg_t *, void *v), void *v1);
+void queue_free(target_queue_t *, void (*free_func)(msg_t *));
+void queue_reset(target_queue_t *q);
+void queue_prt(target_queue_t *q, int type, char *fmt, ...);
+void queue_str(target_queue_t *, uint32_t lvl, msg_type_t, char *);
+void queue_log(Boolean_t on_off);
+void ste_queue_data_remove(msg_t *m);
+void conn_queue_data_remove(msg_t *m);
+void sess_queue_data_remove(msg_t *m);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TARGET_QUEUE_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/radius.c b/usr/src/cmd/iscsi/iscsitgtd/radius.c
new file mode 100644
index 0000000000..a6432ae451
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/radius.c
@@ -0,0 +1,514 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/random.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <md5.h>
+#include "target.h"
+#include "radius.h"
+
+/* Forward declaration */
+
+/*
+ * Encode a CHAP-Password attribute. This function basically prepends
+ * the identifier in front of chap_passwd and copy the results to
+ * *result.
+ */
+static
+void
+encode_chap_password(int identifier,
+ int chap_passwd_len,
+ uint8_t *chap_passwd,
+ uint8_t *result);
+
+int
+snd_radius_request(int sd,
+ iscsi_ipaddr_t rsvr_ip_addr,
+ uint32_t rsvr_port,
+ radius_packet_data_t *req_data);
+
+int
+rcv_radius_response(int sd,
+ uint8_t *shared_secret,
+ uint32_t shared_secret_len,
+ uint8_t *req_authenticator,
+ radius_packet_data_t *resp_data);
+
+/*
+ * Annotate the radius_attr_t objects with authentication data.
+ */
+static
+void
+set_radius_attrs(radius_packet_data_t *req,
+ char *target_chap_name,
+ unsigned char *target_response,
+ uint32_t responseLength,
+ uint8_t *challenge,
+uint32_t challengeLength);
+
+/*
+ * See radius_auth.h.
+ */
+/* ARGSUSED */
+chap_validation_status_type
+radius_chap_validate(char *target_chap_name,
+ char *initiator_chap_name,
+ uint8_t *challenge,
+ uint32_t challengeLength,
+ uint8_t *target_response,
+ uint32_t responseLength,
+ uint8_t identifier,
+ iscsi_ipaddr_t rad_svr_ip_addr,
+ uint32_t rad_svr_port,
+ uint8_t *rad_svr_shared_secret,
+ uint32_t rad_svr_shared_secret_len)
+{
+ chap_validation_status_type validation_status;
+ int rcv_status;
+ int sd;
+ int rc;
+ struct sockaddr_in sockaddr;
+ radius_packet_data_t req;
+ radius_packet_data_t resp;
+ MD5_CTX context;
+ uint8_t md5_digest[16]; /* MD5 digest length 16 */
+ uint8_t random_number[16];
+ int fd;
+
+ if (rad_svr_shared_secret_len == 0) {
+ /* The secret must not be empty (section 3, RFC 2865) */
+ return (CHAP_VALIDATION_BAD_RADIUS_SECRET);
+ }
+
+ bzero(&req, sizeof (radius_packet_data_t));
+
+ req.identifier = identifier;
+ req.code = RAD_ACCESS_REQ;
+ set_radius_attrs(&req,
+ target_chap_name,
+ target_response,
+ responseLength,
+ challenge,
+ challengeLength);
+
+ /* Prepare the request authenticator */
+ MD5Init(&context);
+ bzero(&md5_digest, 16);
+ /* First, the shared secret */
+ MD5Update(&context, rad_svr_shared_secret, rad_svr_shared_secret_len);
+ /* Then a unique number - use a random number */
+ fd = open("/dev/random", O_RDONLY);
+ if (fd == -1)
+ return (CHAP_VALIDATION_INTERNAL_ERROR);
+ (void) read(fd, &random_number, sizeof (random_number));
+ (void) close(fd);
+ MD5Update(&context, random_number, sizeof (random_number));
+ MD5Final(md5_digest, &context);
+ bcopy(md5_digest, &req.authenticator, RAD_AUTHENTICATOR_LEN);
+
+ /* Create UDP socket */
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0) {
+ return (CHAP_VALIDATION_RADIUS_ACCESS_ERROR);
+ }
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ sockaddr.sin_port = htons(0);
+ rc = bind(sd, (struct sockaddr *)&sockaddr, sizeof (sockaddr));
+ if (rc < 0) {
+ return (CHAP_VALIDATION_RADIUS_ACCESS_ERROR);
+ }
+
+ /* Send the authentication access request to the RADIUS server */
+ if (snd_radius_request(sd,
+ rad_svr_ip_addr,
+ rad_svr_port,
+ &req) == -1) {
+ (void) close(sd);
+ return (CHAP_VALIDATION_RADIUS_ACCESS_ERROR);
+ }
+
+ bzero(&resp, sizeof (radius_packet_data_t));
+ /* Analyze the response coming through from the same socket. */
+ rcv_status = rcv_radius_response(sd,
+ rad_svr_shared_secret,
+ rad_svr_shared_secret_len,
+ req.authenticator, &resp);
+ if (rcv_status == RAD_RSP_RCVD_SUCCESS) {
+ if (resp.code == RAD_ACCESS_ACPT) {
+ validation_status = CHAP_VALIDATION_PASSED;
+ } else if (resp.code == RAD_ACCESS_REJ) {
+ validation_status = CHAP_VALIDATION_INVALID_RESPONSE;
+ } else {
+ validation_status =
+ CHAP_VALIDATION_UNKNOWN_RADIUS_CODE;
+ }
+ } else if (rcv_status == RAD_RSP_RCVD_AUTH_FAILED) {
+ validation_status = CHAP_VALIDATION_BAD_RADIUS_SECRET;
+ } else {
+ validation_status = CHAP_VALIDATION_RADIUS_ACCESS_ERROR;
+ }
+
+ (void) close(sd);
+ return (validation_status);
+}
+
+/* See forward declaration. */
+static void
+set_radius_attrs(radius_packet_data_t *req,
+ char *target_chap_name,
+ unsigned char *target_response,
+ uint32_t responseLength,
+ uint8_t *challenge,
+ uint32_t challengeLength)
+{
+ req->attrs[0].attr_type_code = RAD_USER_NAME;
+ (void) strncpy((char *)req->attrs[0].attr_value,
+ (const char *)target_chap_name,
+ strlen(target_chap_name));
+ req->attrs[0].attr_value_len = strlen(target_chap_name);
+
+ req->attrs[1].attr_type_code = RAD_CHAP_PASSWORD;
+ bcopy(target_response,
+ (char *)req->attrs[1].attr_value,
+ min(responseLength, sizeof (req->attrs[1].attr_value)));
+ /* A target response is an MD5 hash thus its length has to be 16. */
+ req->attrs[1].attr_value_len = responseLength;
+
+ req->attrs[2].attr_type_code = RAD_CHAP_CHALLENGE;
+ bcopy(challenge,
+ (char *)req->attrs[2].attr_value,
+ min(challengeLength, sizeof (req->attrs[2].attr_value)));
+ req->attrs[2].attr_value_len = challengeLength;
+
+ /* 3 attributes associated with each RADIUS packet. */
+ req->num_of_attrs = 3;
+}
+
+/*
+ * See radius_packet.h.
+ */
+int
+snd_radius_request(int sd,
+ iscsi_ipaddr_t rsvr_ip_addr,
+ uint32_t rsvr_port,
+ radius_packet_data_t *req_data)
+{
+ int i; /* Loop counter. */
+ int data_len;
+ int len;
+ ushort_t total_length; /* Has to be 2 octets in size */
+ uint8_t *ptr; /* Pointer to RADIUS packet data */
+ uint8_t *length_ptr; /* Points to the Length field of the */
+ /* packet. */
+ uint8_t *data; /* RADIUS data to be sent */
+ radius_attr_t *req_attr; /* Request attributes */
+ radius_packet_t *packet; /* Outbound RADIUS packet */
+ union {
+ struct sockaddr_in s_in4;
+ struct sockaddr_in6 s_in6;
+ } sa_rsvr; /* Socket address of the server */
+
+ /*
+ * Create a RADIUS packet with minimal length for now.
+ */
+ total_length = MIN_RAD_PACKET_LEN;
+ data = (uint8_t *)malloc(MAX_RAD_PACKET_LEN);
+ packet = (radius_packet_t *)data;
+ packet->code = req_data->code;
+ packet->identifier = req_data->identifier;
+ bcopy(req_data->authenticator, packet->authenticator,
+ RAD_AUTHENTICATOR_LEN);
+ ptr = packet->data;
+
+ /* Loop over all attributes of the request. */
+ for (i = 0; i < req_data->num_of_attrs; i++) {
+ if (total_length > MAX_RAD_PACKET_LEN) {
+ /* The packet has exceed its maximum size. */
+ free(data);
+ return (-1);
+ }
+
+ req_attr = &req_data->attrs[i];
+ *ptr++ = (req_attr->attr_type_code & 0xFF);
+ length_ptr = ptr;
+ /* Length is 2 octets - RFC 2865 section 3 */
+ *ptr++ = 2;
+ total_length += 2;
+
+ /* If the attribute is CHAP-Password, encode it. */
+ if (req_attr->attr_type_code == RAD_CHAP_PASSWORD) {
+ /*
+ * Identifier plus CHAP response. RFC 2865
+ * section 5.3.
+ */
+ uint8_t encoded_chap_passwd[RAD_CHAP_PASSWD_STR_LEN +
+ RAD_IDENTIFIER_LEN +
+ 1];
+ encode_chap_password
+ (req_data->identifier,
+ req_attr->attr_value_len,
+ req_attr->attr_value,
+ encoded_chap_passwd);
+
+ req_attr->attr_value_len = RAD_CHAP_PASSWD_STR_LEN +
+ RAD_IDENTIFIER_LEN;
+
+ bcopy(encoded_chap_passwd,
+ req_attr->attr_value,
+ req_attr->attr_value_len);
+ }
+
+ len = req_attr->attr_value_len;
+ *length_ptr += len;
+
+ bcopy(req_attr->attr_value, ptr, req_attr->attr_value_len);
+ ptr += req_attr->attr_value_len;
+
+ total_length += len;
+ } /* Done looping over all attributes */
+
+ data_len = total_length;
+ total_length = htons(total_length);
+ bcopy(&total_length, packet->length, sizeof (ushort_t));
+
+ /*
+ * Send the packet to the RADIUS server.
+ */
+ bzero((char *)&sa_rsvr, sizeof (sa_rsvr));
+ if (rsvr_ip_addr.i_insize == sizeof (in_addr_t)) {
+ int ret;
+
+ /* IPv4 */
+ sa_rsvr.s_in4.sin_family = AF_INET;
+ sa_rsvr.s_in4.sin_addr.s_addr =
+ rsvr_ip_addr.i_addr.in4.s_addr;
+ /*
+ * sin_port is of type u_short (or ushort_t - POSIX compliant).
+ */
+ sa_rsvr.s_in4.sin_port = htons((ushort_t)rsvr_port);
+
+ ret = sendto(sd, data, data_len, 0,
+ (struct sockaddr *)&sa_rsvr.s_in4,
+ sizeof (struct sockaddr_in));
+ free(data);
+ return (ret);
+ } else if (rsvr_ip_addr.i_insize == sizeof (in6_addr_t)) {
+ /* IPv6 */
+ sa_rsvr.s_in6.sin6_family = AF_INET6;
+ bcopy(sa_rsvr.s_in6.sin6_addr.s6_addr,
+ rsvr_ip_addr.i_addr.in6.s6_addr, 16);
+ /*
+ * sin6_port is of type in_port_t (i.e., uint16_t).
+ */
+ sa_rsvr.s_in6.sin6_port = htons((in_port_t)rsvr_port);
+
+ free(data);
+ /* No IPv6 support for now. */
+ return (-1);
+ } else {
+ /* Invalid IP address for RADIUS server. */
+ free(data);
+ return (-1);
+ }
+}
+
+/*
+ * See radius_packet.h.
+ */
+int
+rcv_radius_response(int sd,
+ uint8_t *shared_secret,
+ uint32_t shared_secret_len,
+ uint8_t *req_authenticator,
+ radius_packet_data_t *resp_data)
+{
+ int poll_cnt = 0;
+ int rcv_len = 0;
+ radius_packet_t *packet;
+ MD5_CTX context;
+ uint8_t *tmp_data;
+ uint8_t md5_digest[16]; /* MD5 Digest Length 16 */
+ uint16_t declared_len = 0;
+ ushort_t len;
+
+ fd_set fdset;
+ struct timeval timeout;
+
+ tmp_data = (uint8_t *)malloc(MAX_RAD_PACKET_LEN);
+
+ /*
+ * Poll and receive RADIUS packet.
+ */
+ poll_cnt = 0;
+ do {
+ timeout.tv_sec = RAD_RCV_TIMEOUT;
+ timeout.tv_usec = 0;
+
+ FD_ZERO(&fdset);
+ FD_SET(sd, &fdset);
+
+ if (select(sd+1, &fdset, NULL, NULL, &timeout) < 0) {
+ free(tmp_data);
+ return (RAD_RSP_RCVD_PROTOCOL_ERR);
+ }
+
+ if (FD_ISSET(sd, &fdset)) {
+ rcv_len = recv(sd, tmp_data, MAX_RAD_PACKET_LEN, 0);
+ break;
+ } else {
+ poll_cnt++;
+ }
+
+ } while (poll_cnt < RAD_RETRY_MAX);
+
+ if (poll_cnt >= RAD_RETRY_MAX) {
+ free(tmp_data);
+ return (RAD_RSP_RCVD_TIMEOUT);
+ }
+
+ if (rcv_len < 0) {
+ /* Socket error. */
+ free(tmp_data);
+ return (RAD_RSP_RCVD_PROTOCOL_ERR);
+ }
+
+ packet = (radius_packet_t *)tmp_data;
+ bcopy(packet->length, &len, sizeof (ushort_t));
+ declared_len = ntohs(len);
+
+ /*
+ * Check if the received packet length is within allowable range.
+ * RFC 2865 section 3.
+ */
+ if (rcv_len < MIN_RAD_PACKET_LEN) {
+ free(tmp_data);
+ return (RAD_RSP_RCVD_PROTOCOL_ERR);
+ } else if (rcv_len > MAX_RAD_PACKET_LEN) {
+ free(tmp_data);
+ return (RAD_RSP_RCVD_PROTOCOL_ERR);
+ }
+
+ /*
+ * Check if the declared packet length is within allowable range.
+ * RFC 2865 section 3.
+ */
+ if (declared_len < MIN_RAD_PACKET_LEN) {
+ free(tmp_data);
+ return (RAD_RSP_RCVD_PROTOCOL_ERR);
+ } else if (declared_len > MAX_RAD_PACKET_LEN) {
+ free(tmp_data);
+ return (RAD_RSP_RCVD_PROTOCOL_ERR);
+ }
+
+ /*
+ * Discard packet with received length shorter than declared
+ * length. RFC 2865 section 3.
+ */
+ if (rcv_len < declared_len) {
+ free(tmp_data);
+ return (RAD_RSP_RCVD_PROTOCOL_ERR);
+ }
+
+ /*
+ * Authenticate the incoming packet, using the following algorithm
+ * (RFC 2865 section 3):
+ *
+ * MD5(Code+ID+Length+RequestAuth+Attributes+Secret)
+ *
+ * Code = RADIUS packet code
+ * ID = RADIUS packet identifier
+ * Length = Declared length of the packet
+ * RequestAuth = The request authenticator
+ * Attributes = The response attributes
+ * Secret = The shared secret
+ */
+ MD5Init(&context);
+ bzero(&md5_digest, 16);
+ MD5Update(&context, &packet->code, 1);
+ MD5Update(&context, &packet->identifier, 1);
+ MD5Update(&context, packet->length, 2);
+ MD5Update(&context, req_authenticator, RAD_AUTHENTICATOR_LEN);
+ /* Include response attributes only if there is a payload */
+ if (declared_len > RAD_PACKET_HDR_LEN) {
+ /* Response Attributes */
+ MD5Update(&context, packet->data,
+ declared_len - RAD_PACKET_HDR_LEN);
+ }
+ MD5Update(&context, shared_secret, shared_secret_len);
+ MD5Final(md5_digest, &context);
+
+ if (bcmp(md5_digest, packet->authenticator, RAD_AUTHENTICATOR_LEN)
+ != 0) {
+ free(tmp_data);
+ return (RAD_RSP_RCVD_AUTH_FAILED);
+ }
+
+ /*
+ * If the received length is greater than the declared length,
+ * trust the declared length and shorten the packet (i.e., to
+ * treat the octets outside the range of the Length field as
+ * padding - RFC 2865 section 3).
+ */
+ if (rcv_len > declared_len) {
+ /* Clear the padding data. */
+ bzero(tmp_data + declared_len, rcv_len - declared_len);
+ rcv_len = declared_len;
+ }
+
+ /*
+ * Annotate the RADIUS packet data with the data we received from
+ * the server.
+ */
+ resp_data->code = packet->code;
+ resp_data->identifier = packet->identifier;
+
+ free(tmp_data);
+ return (RAD_RSP_RCVD_SUCCESS);
+}
+
+static
+void
+encode_chap_password(int identifier,
+ int chap_passwd_len,
+ uint8_t *chap_passwd,
+ uint8_t *result)
+{
+ result[0] = (uint8_t)identifier;
+ bcopy(chap_passwd, &result[1], chap_passwd_len);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/radius.h b/usr/src/cmd/iscsi/iscsitgtd/radius.h
new file mode 100644
index 0000000000..fdb9684475
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/radius.h
@@ -0,0 +1,243 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _RADIUS_H
+#define _RADIUS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <netinet/in.h>
+#include <sys/int_types.h>
+
+/* Packet type. RFC 2865 section 4. */
+#define RAD_ACCESS_REQ 1 /* Authentication Request */
+#define RAD_ACCESS_ACPT 2 /* Authentication Accepted */
+#define RAD_ACCESS_REJ 3 /* Authentication Rejected */
+
+/* RADIUS Attribute Types. RFC 2865 section 5. */
+#define RAD_USER_NAME 1
+#define RAD_CHAP_PASSWORD 3
+#define RAD_CHAP_CHALLENGE 60
+
+/* RFC 2865 Section 3. The Identifier field is one octet. */
+#define RAD_IDENTIFIER_LEN 1
+
+/* RFC 2865 Section 5.3. The String field is 16 octets. */
+#define RAD_CHAP_PASSWD_STR_LEN 16
+
+/* RFC 2865 Section 3. Authenticator field is 16 octets. */
+#define RAD_AUTHENTICATOR_LEN 16
+
+/* RFC 2865 Section 5: 1-253 octets */
+#define MAX_RAD_ATTR_VALUE_LEN 253
+
+/* RFC 2865 Section 3. Minimum length 20 octets. */
+#define MIN_RAD_PACKET_LEN 20
+
+/* RFC 2865 Section 3. Maximum length 4096 octets. */
+#define MAX_RAD_PACKET_LEN 4096
+
+/* Maximum RADIUS shared secret length (in fact there is no defined limit) */
+#define MAX_RAD_SHARED_SECRET_LEN 128
+
+/* RFC 2865 Section 3. Minimum RADIUS shared secret length */
+#define MIN_RAD_SHARED_SECRET_LEN 16
+
+/* Raw RADIUS packet. RFC 2865 section 3. */
+typedef struct radius_packet {
+ uint8_t code; /* RADIUS code, section 3, RFC 2865 */
+ uint8_t identifier; /* 1 octet in length. RFC 2865 section 3 */
+ uint8_t length[2]; /* 2 octets, or sizeof (u_short) */
+ uint8_t authenticator[RAD_AUTHENTICATOR_LEN];
+ uint8_t data[1];
+} radius_packet_t;
+
+/* Length of a RADIUS packet minus the payload */
+#define RAD_PACKET_HDR_LEN 20
+
+
+typedef enum chap_validation_status_type {
+ CHAP_VALIDATION_PASSED, /* CHAP validation passed */
+ CHAP_VALIDATION_INVALID_RESPONSE, /* Invalid CHAP response */
+ CHAP_VALIDATION_DUP_SECRET, /* Same CHAP secret used */
+ /* for authentication in the */
+ /* other direction */
+ CHAP_VALIDATION_UNKNOWN_AUTH_METHOD, /* Unknown authentication */
+ /* method */
+ CHAP_VALIDATION_INTERNAL_ERROR, /* MISC internal error */
+ CHAP_VALIDATION_RADIUS_ACCESS_ERROR, /* Problem accessing RADIUS */
+ CHAP_VALIDATION_BAD_RADIUS_SECRET, /* Invalid RADIUS shared */
+ /* secret */
+ CHAP_VALIDATION_UNKNOWN_RADIUS_CODE /* Irrelevant or unknown */
+ /* RADIUS packet code */
+ /* returned */
+} chap_validation_status_type;
+
+typedef enum authentication_method_type {
+ RADIUS_AUTHENTICATION,
+ DIRECT_AUTHENTICATION
+} authentication_method_type;
+
+typedef struct _IPAddress {
+ union {
+ struct in_addr in4;
+ struct in6_addr in6;
+ } i_addr;
+ /* i_insize determines which is valid in the union above */
+ int i_insize;
+} iscsi_ipaddr_t;
+
+typedef struct radius_config {
+ iscsi_ipaddr_t rad_svr_addr; /* IPv6 enabled */
+ uint32_t rad_svr_port;
+ uint8_t rad_svr_shared_secret[MAX_RAD_SHARED_SECRET_LEN];
+ uint32_t rad_svr_shared_secret_len;
+} RADIUS_CONFIG;
+
+/* A total of RAD_RCV_TIMEOUT * RAD_RETRY_MAX seconds timeout. */
+#define RAD_RCV_TIMEOUT 5 /* Timeout for receiving RADIUS packet in */
+ /* sec. */
+#define RAD_RETRY_MAX 2 /* Max. # of times to retry receiving */
+ /* packet. */
+
+/* Describes a RADIUS attribute */
+typedef struct radius_attr {
+ int attr_type_code; /* RADIUS attribute type code, */
+ /* e.g. RAD_USER_PASSWORD, etc. */
+ int attr_value_len;
+ uint8_t attr_value[MAX_RAD_ATTR_VALUE_LEN];
+} radius_attr_t;
+
+/* Describes data fields of a RADIUS packet. */
+typedef struct radius_packet_data {
+ uint8_t code; /* RADIUS code, section 3, RFC 2865. */
+ uint8_t identifier;
+ uint8_t authenticator[RAD_AUTHENTICATOR_LEN];
+ int num_of_attrs;
+ radius_attr_t attrs[4]; /* For this implementation each */
+ /* outbound RADIUS packet will only */
+ /* have 3 attributes associated with */
+ /* it thus the chosen size should be */
+ /* good enough. */
+} radius_packet_data_t;
+
+/*
+ * Data in this structure is set by the user agent and consumed by
+ * the driver.
+ */
+#define MAX_RAD_SHARED_SECRET_LEN 128
+typedef struct radius_props {
+ uint32_t r_vers;
+ uint32_t r_oid;
+ union {
+ struct in_addr u_in4;
+ struct in6_addr u_in6;
+ } r_addr;
+ /*
+ * r_insize indicates which of the previous structs is valid.
+ */
+ int r_insize;
+
+ uint32_t r_port;
+ uint8_t r_shared_secret[MAX_RAD_SHARED_SECRET_LEN];
+ boolean_t r_radius_access;
+ boolean_t r_radius_config_valid;
+ uint32_t r_shared_secret_len;
+} iscsi_radius_props_t;
+
+/*
+ * Send a request to a RADIUS server.
+ *
+ * Returns > 0 on success, <= 0 on failure .
+ *
+ */
+int
+snd_radius_request(int sd,
+ iscsi_ipaddr_t rsvr_ip_addr,
+ uint32_t rsvr_port,
+ radius_packet_data_t *packet_data);
+
+#define RAD_RSP_RCVD_SUCCESS 0
+#define RAD_RSP_RCVD_NO_DATA 1
+#define RAD_RSP_RCVD_TIMEOUT 2
+#define RAD_RSP_RCVD_PROTOCOL_ERR 3
+#define RAD_RSP_RCVD_AUTH_FAILED 4
+/*
+ * Receives a response from a RADIUS server.
+ *
+ * Return receive status.
+ */
+int
+rcv_radius_response(int sd,
+ uint8_t *shared_secret,
+ uint32_t shared_secret_len,
+ uint8_t *req_authenticator,
+ radius_packet_data_t *resp_data);
+
+/*
+ * Function: radius_chap_validate
+ *
+ * Description: To validate a target response given the
+ * associated challenge via the specified
+ * RADIUS server.
+ *
+ * Arguments:
+ * target_chap_name - The CHAP name of the target being authenticated.
+ * initiator_chap_name - The CHAP name of the authenticating initiator.
+ * challenge - The CHAP challenge to which the target responded.
+ * target_response - The target's CHAP response to be validated.
+ * identifier - The identifier associated with the CHAP challenge.
+ * radius_server_ip_address - The IP address of the RADIUS server.
+ * radius_server_port - The port number of the RADIUS server.
+ * radius_shared_secret - The shared secret for accessing the RADIUS server.
+ * radius_shared_secret_len - The length of the shared secret.
+ *
+ * Return: See chap_validation_status_type.
+ */
+chap_validation_status_type
+radius_chap_validate(char *target_chap_name,
+ char *initiator_chap_name,
+ uint8_t *challenge,
+ uint32_t challengeLength,
+ uint8_t *target_response,
+ uint32_t responseLength,
+ uint8_t identifier,
+ iscsi_ipaddr_t rad_svr_ip_addr,
+ uint32_t rad_svr_port,
+ uint8_t *rad_svr_shared_secret,
+ uint32_t rad_svr_shared_secret_len);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RADIUS_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/sparcv9/Makefile b/usr/src/cmd/iscsi/iscsitgtd/sparcv9/Makefile
new file mode 100644
index 0000000000..69b08dbca6
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/sparcv9/Makefile
@@ -0,0 +1,32 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/iscsi/iscsitgt/sparcv9/Makefile
+
+include ../Makefile.com
+include ../../../Makefile.cmd.64
+
+install: all $(ROOTUSRSBINPROG64)
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10.h b/usr/src/cmd/iscsi/iscsitgtd/t10.h
new file mode 100644
index 0000000000..bbcb97e00c
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10.h
@@ -0,0 +1,623 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _T10_H
+#define _T10_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This header file describes the service level between the transport
+ * layer and the emulation portion. These procedure calls can be thought
+ * of as part of the T10 SAM-3 specification.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Here are the header files which are required to define references found
+ * in this file. No other header files are to be included.
+ */
+#include <pthread.h>
+#include <sys/avl.h>
+#include <signal.h>
+#include <sys/scsi/generic/sense.h>
+
+#include "queue.h"
+
+#ifdef lint
+/*
+ * lints sees aio_return64, but can't find it in the aio structure. To keep
+ * lint happy this define is used.
+ */
+#define aio_return64 aio_return
+#endif
+
+typedef void *transport_t;
+typedef void *t10_targ_handle_t;
+
+typedef void *t10_lun_handle_t;
+
+typedef void *emul_handle_t;
+typedef void *emul_cmd_t;
+
+typedef enum {
+ ClearSet,
+ ResetTarget,
+ ResetLun,
+ InventoryChange,
+ CapacityChange,
+ DeviceOnline,
+ DeviceOffline
+} TaskOp_t;
+
+typedef enum {
+ T10_Cmd_Free = 1,
+ T10_Cmd_Alloc,
+ T10_Cmd_DataIn,
+ T10_Cmd_DataOut,
+ T10_Cmd_DataOut_Sent,
+ T10_Cmd_Complete,
+ T10_Cmd_Canceled,
+ T10_Cmd_Errored
+} t10_cmd_state_t;
+
+typedef enum {
+ T10_Cmd_Event_DataOut_Sent,
+ T10_Cmd_Event_DataIn_Recv,
+ T10_Cmd_Event_Release,
+ T10_Cmd_Event_Canceled
+} t10_cmd_event_t;
+
+typedef enum {
+ lu_online,
+ lu_offline,
+ lu_errored
+} t10_lu_state_t;
+
+/*
+ * The t10_cmd_t structure bridges the gap between the transport and
+ * emulation services. At certain times either the transport or emulation
+ * service needs to access the data stored within this structure.
+ * For now we'll just use macros which hide the reference, but in the
+ * future when the transport and emulation services are loadable modules
+ * these macros will become functions so that the structure can change
+ * inside of the T10 space and not cause compatibility issues.
+ */
+#define T10_MAX_OUT(cmd) (cmd->c_lu->l_targ->s_maxout)
+#define T10_MMAP_AREA(cmd) (cmd->c_lu->l_common->l_mmap)
+#define T10_PARAMS_AREA(cmd) trans_params_area(cmd)
+#define T10_TRANS_ID(cmd) (cmd->c_trans_id)
+#define T10_DATA(cmd) (cmd->c_data)
+#define T10_DATA_LEN(cmd) (cmd->c_data_len)
+#define T10_DATA_OFFSET(cmd) (cmd->c_offset)
+#define T10_CMD_LAST(cmd) (cmd->c_last)
+#define T10_CMD_STATUS(cmd) (cmd->c_cmd_status)
+#define T10_CMD_RESID(cmd) (cmd->c_resid)
+#define T10_SENSE_LEN(cmd) (cmd->c_cmd_sense_len)
+#define T10_SENSE_DATA(cmd) (cmd->c_cmd_sense)
+
+#define T10_DEFAULT_TPG 1
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SAM-3 revision 14, section 4.9 -- Logical Unit Numbers |
+ * | The specification allows for 64-bit LUNs, but at this point |
+ * | most OSes don't support that many. Section 4.9.7, table 9 gives |
+ * | the Flat Space Addressing Method which allows for 16,383 LUNs. |
+ * | This will be the imposed maximum even though the code can support |
+ * | more. Raise this number if needed. |
+ * []------------------------------------------------------------------[]
+ */
+#define T10_MAX_LUNS 16383
+
+/*
+ * SPC-3 Revision 21c, Section 6.4.2 Table 85
+ * Version Descriptor Values
+ */
+#define T10_TRANS_ISCSI 0x960 /* iSCSI (no version claimed) */
+#define T10_TRANS_FC 0x8c0 /* FCP (no version claimed) */
+
+typedef struct t10_aio {
+ /*
+ * This must be the first member of the structure. aioread/aiowrite
+ * take as one of the arguments an pointer to a aio_result_t
+ * structure. When the operation is complete the aio_return and
+ * aio_errno of that structure are updated. When aiowait() is
+ * called the address of that aio_result_t is returned. By having
+ * this structure at the beginning we can pass in the data_ptr
+ * structure address. The ste_aio_process thread will get everything
+ * it needs from the aiowait to send a message to the correct
+ * STE thread. Clear as mud?
+ */
+ aio_result_t a_aio;
+
+ void (*a_aio_cmplt)(emul_cmd_t id);
+ emul_cmd_t a_id;
+} t10_aio_t;
+
+/*
+ * Bidirectional structure used to track requests from the transport
+ * and send reponse data from the emulation.
+ *
+ * The glue logic for t10_send_cmd will allocate this structure, fill in
+ * in with the provided data and put it on the LUN queue. The LUN thread
+ * will dequeue this request and call the appropriate LUN command interpreter.
+ */
+typedef struct t10_cmd {
+ /*
+ * Transport specific tracking value. If this value is non-zero it
+ * means this command was part of a previous command that wasn't
+ * completed. Currently this is only used for DATA_OUT (SCSI write op)
+ * commands.
+ */
+ transport_t c_trans_id;
+
+ t10_cmd_state_t c_state;
+
+ /*
+ * Emulation specific tracking value.
+ */
+ emul_cmd_t c_emul_id;
+
+ /*
+ * Per I_T_L structure used to determine which command
+ * interpreter to call and which transport queue to send the response.
+ */
+ struct t10_lu_impl *c_lu;
+
+ /*
+ * Pointer to command buffer. No interpretation of data is
+ * done by the glue logic. Interpretation is done by the LUN
+ * emulation code.
+ */
+ uint8_t *c_cdb;
+ size_t c_cdb_len;
+
+ /*
+ * Optional offset into the command. If more than one response
+ * is required this value indicates where the data belongs.
+ */
+ off_t c_offset;
+
+ /*
+ * Data for transfer.
+ */
+ char *c_data;
+ size_t c_data_len;
+ size_t c_resid;
+
+ /*
+ * Indicates if this response is the last to be sent
+ * and will be followed closely by a complete message. Enables
+ * transports to phase collapse the final READ data PDU with
+ * completion PDU if possible.
+ */
+ Boolean_t c_last;
+
+ /*
+ * When the transport is finished sending the data it will
+ * call t10_cmd_destroy() which will cause the SAM-3 layer to
+ * call the emulation function stored here with this command
+ * pointer. The emulation code is responsible for freeing any
+ * memory it allocated.
+ */
+ void (*c_emul_complete)(emul_handle_t id);
+
+ /*
+ * SCSI sense information.
+ */
+ int c_cmd_status;
+ char *c_cmd_sense;
+ size_t c_cmd_sense_len;
+
+ /*
+ * List of active commands at the ITL level.
+ */
+ avl_tree_t c_cmd_avl;
+} t10_cmd_t;
+
+/*
+ * Each LU has a structure which contains common data for all I_T's who
+ * access this LU.
+ */
+typedef struct t10_lu_common {
+ /*
+ * Logic Unit Number
+ */
+ int l_num;
+
+ /*
+ * state of device
+ */
+ t10_lu_state_t l_state;
+
+ /*
+ * Internal ID which will be unique for all LUs. This will be
+ * used for log messages to help tracking details.
+ */
+ int l_internal_num;
+
+ /*
+ * Thread ID which is running this logical unit. This is currently
+ * used for only one purpose which is to locate this structure
+ * in case of a SIGBUS. It's possible for the underlying file system
+ * to run out of space for an mmap'd LU. The only means of notification
+ * the OS has is to send a SIGBUS. The thread only receives the memory
+ * address, so we look for our thread ID amongst all of the LU
+ * available.
+ */
+ pthread_t l_thr_id;
+
+ /*
+ * If we receive a SIGBUS the initiator needs to be notified that
+ * something bad has occurred. This means we need to know which
+ * command was being emulated so that we can find the appropriate
+ * transport.
+ * Special handling needs to be done if the thread is initializing
+ * the LU so we need a flag to indicate that fact.
+ */
+ t10_cmd_t *l_curr;
+ Boolean_t l_curr_provo;
+
+ /*
+ * The implementation uses a 16 byte EUI value for the GUID.
+ * Not only is this value used for SCSI INQUIRY data, but it
+ * is used to distinquish this common LUN from other LUNs in
+ * the AVL tree.
+ */
+ uint8_t *l_guid;
+ size_t l_guid_len;
+
+ /*
+ * Other common information which is needed for ever device
+ * type.
+ */
+ int l_dtype;
+ char *l_pid,
+ *l_vid;
+
+ /*
+ * Each dtype has different parameters that it uses. This
+ * is a place holder for storing a pointer to some structure which
+ * contains that information.
+ */
+ void *l_dtype_params;
+
+ /*
+ * Parameter information in XML format.
+ */
+ xml_node_t *l_root;
+
+ /*
+ * File descriptor for the open file which is the backing store
+ * for this device. This can be a regular file or a character
+ * special device if we're acting as a bridge between transports.
+ */
+ int l_fd;
+
+ void *l_mmap;
+ off64_t l_size;
+
+ Boolean_t l_fast_write_ack;
+
+ /*
+ * AVL tree containing all I_T_Q nexus' which are actively using
+ * this LUN.
+ */
+ avl_tree_t l_all_open;
+
+ /*
+ * Each I_T will place requests for command emulation on this
+ * queue. Common requests are msg_ste_cmd and msg_ste_shutdown
+ */
+ target_queue_t *l_from_transports;
+
+ /*
+ * Mutex used to lock access to the AVL tree.
+ */
+ pthread_mutex_t l_common_mutex;
+
+ /*
+ * When a target is looking to see if an existing LUN is opened
+ * a search of all LUNs needs to be done and will use this
+ * AVL node. This field is modified only by the AVL code.
+ */
+ avl_node_t l_all_luns;
+} t10_lu_common_t;
+
+/*
+ * Each I_T_Q has a LU structure associated with it.
+ */
+typedef struct t10_lu_impl {
+ /*
+ * pointer to common area of LUN.
+ */
+ t10_lu_common_t *l_common;
+ pthread_mutex_t l_mutex;
+
+ /*
+ * Mutex to protect access to active commands
+ */
+ pthread_mutex_t l_cmd_mutex;
+ pthread_cond_t l_cmd_cond;
+ Boolean_t l_wait_for_drain;
+
+ avl_tree_t l_cmds;
+
+ /*
+ * Queue for sending command results and R2T results back
+ * to the transport.
+ */
+ target_queue_t *l_to_transport;
+
+ /*
+ * Back pointer to target structure who created this LUN reference.
+ */
+ struct t10_targ_impl *l_targ;
+
+ struct scsi_cmd_table *l_cmd_table;
+
+ /*
+ * Per LU methods for issuing commands and data to the
+ * DTYPE emulator.
+ */
+ void (*l_cmd)(t10_cmd_t *cmd, uint8_t *cdb,
+ size_t cdb_len);
+ void (*l_data)(t10_cmd_t *cmd, emul_handle_t e,
+ size_t offset, char *data, size_t data_len);
+
+ /*
+ * AVL node information for all other I_T nexus' who are referencing
+ * this LUN. This is used by the AVL code and *not* modified by
+ * this daemon directly.
+ */
+ avl_node_t l_open_lu_node;
+
+ /*
+ * AVL node information for all LUN's being access by this I_T nexus.
+ * This is used by the AVL code and *not* modified by this daemon
+ * directly.
+ */
+ avl_node_t l_open_targ_node;
+
+ /*
+ * Logical Unit Number. This value is used as the comparision value
+ * for the AVL search at the per target level.
+ */
+ int l_targ_lun;
+
+ Boolean_t l_dsense_enabled;
+
+ /*
+ * Statistics on a per ITL basis
+ */
+ uint64_t l_cmds_read,
+ l_cmds_write,
+ l_sects_read,
+ l_sects_write;
+
+ /*
+ * Each time a command is run the value of l_status is checked.
+ * If non-zero the command isn't executed and instead a transport
+ * complete message is sent with these values. This is commonly
+ * used to send UNIT ATTENTION for things like power on.
+ * -- Do we need some sort of stack to push and pop these values?
+ */
+ int l_status,
+ l_asc,
+ l_ascq;
+} t10_lu_impl_t;
+
+typedef struct t10_targ_impl {
+ char *s_targ_base;
+ int s_targ_num; /* used in log messages */
+ avl_tree_t s_open_lu;
+ pthread_mutex_t s_mutex;
+
+ /*
+ * The transport layer will set the maximum output size
+ * it's able to deal with during a call to set_create_handle()
+ */
+ size_t s_maxout;
+
+ /*
+ * Target Port Set
+ */
+ int s_tp_grp;
+
+ /*
+ * transport version number to use in standard inquiry data
+ */
+ int s_trans_vers;
+
+ /*
+ * Transport response queue. This queue will be stored in each
+ * lun that gets created.
+ */
+ target_queue_t *s_to_transport;
+
+ /*
+ * During a SCSI WRITE the emulation will call trans_rqst_datain.
+ * If the transport indicated data was available by using non-zero
+ * values for the optional data and length when t10_send_cmd was
+ * called this callback is used when the emulation requests data.
+ */
+ void (*s_dataout_cb)(t10_cmd_t *, char *data,
+ size_t *data_len);
+
+} t10_targ_impl_t;
+
+typedef struct t10_shutdown {
+ t10_lu_impl_t *t_lu;
+ target_queue_t *t_q;
+} t10_shutdown_t;
+
+typedef struct scsi_cmd_table {
+ void (*cmd_start)(struct t10_cmd *, uint8_t *, size_t);
+ void (*cmd_data)(struct t10_cmd *, emul_handle_t e,
+ size_t offset, char *data, size_t data_len);
+ void (*cmd_end)(emul_handle_t e);
+ char *cmd_name;
+} scsi_cmd_table_t;
+
+/*
+ * []----
+ * | Interfaces
+ * []----
+ */
+
+extern target_queue_t *mgmtq;
+void t10_init(target_queue_t *q);
+void lu_buserr_handler(int sig, siginfo_t *sip, void *v);
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Methods called by the transports |
+ * []------------------------------------------------------------------[]
+ */
+/*
+ * t10_handle_create -- create target handle to be used by transports
+ */
+t10_targ_handle_t
+t10_handle_create(char *targ_name, int trans_version, int tpg, int max_out,
+ target_queue_t *transq, void (*datain_cb)(t10_cmd_t *, char *, size_t *));
+
+/*
+ * t10_handle_disable -- drains commands from emulation queues
+ */
+void
+t10_handle_disable(t10_targ_handle_t t);
+
+/*
+ * t10_handle_destroy -- free resources used by handle
+ */
+void
+t10_handle_destroy(t10_targ_handle_t t);
+
+Boolean_t
+t10_cmd_create(t10_targ_handle_t t, int lun_number, uint8_t *cdb,
+ size_t cdb_len, transport_t trans_id, t10_cmd_t **);
+
+/*
+ * t10_send_cmd -- send a command block to an target/LUN for emulation
+ */
+Boolean_t
+t10_cmd_send(t10_targ_handle_t t, t10_cmd_t *cmd,
+ char *opt_data, size_t opt_data_len);
+
+Boolean_t
+t10_cmd_data(t10_targ_handle_t t, t10_cmd_t *cmd, size_t offset,
+ char *data, size_t data_len);
+
+Boolean_t
+t10_task_mgmt(t10_targ_handle_t t, TaskOp_t op, int opt_lun, void *tag);
+
+/*
+ * t10_cmd_state -- issue an event to change the state of a T10 layer command
+ */
+void t10_cmd_state(t10_cmd_t *c, t10_cmd_event_t e);
+
+void t10_targ_stat(t10_targ_handle_t t, char **buf);
+
+/*
+ * t10_thick_provision -- management function used when creating a new lun
+ */
+Boolean_t t10_thick_provision(char *target, int lun, target_queue_t *q);
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Methods called by the emulation routines |
+ * []------------------------------------------------------------------[]
+ */
+/*
+ * trans_send_datain -- Emulation layer sending data to initiator
+ */
+Boolean_t
+trans_send_datain(t10_cmd_t *cmd, char *data, size_t data_len, size_t offset,
+ void (*callback)(emul_handle_t t), Boolean_t last, emul_handle_t id);
+
+/*
+ * trans_rqst_dataout -- Emulation needs more data to complete request
+ */
+Boolean_t
+trans_rqst_dataout(t10_cmd_t *cmd, char *data, size_t data_len, size_t offset,
+ emul_cmd_t emul_id);
+
+/*
+ * trans_send_complete -- Emulation has completed request w/ opt. sense data
+ */
+void
+trans_send_complete(t10_cmd_t *cmd, int t10_status);
+
+/*
+ * trans_aiowrite -- asynchronous write and kicks the aio wait thread
+ */
+void trans_aiowrite(t10_cmd_t *cmd, char *data, size_t data_len, off_t offset,
+ aio_result_t *aiop);
+
+/*
+ * trans_aioread -- asynchronous read and kicks the aio wait thread
+ */
+void trans_aioread(t10_cmd_t *cmd, char *data, size_t data_len, off_t offset,
+ aio_result_t *aiop);
+
+/*
+ * trans_params_area -- given a t10_cmd return the dtype params
+ */
+void *trans_params_area(t10_cmd_t *cmd);
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Declaration of emulation entry points |
+ * []------------------------------------------------------------------[]
+ */
+Boolean_t sbc_init_common(t10_lu_common_t *lu);
+void sbc_fini_common(t10_lu_common_t *lu);
+void sbc_task_mgmt(t10_lu_common_t *lu, TaskOp_t op);
+void sbc_init_per(t10_lu_impl_t *itl);
+void sbc_fini_per(t10_lu_impl_t *itl);
+Boolean_t ssc_init_common(t10_lu_common_t *lu);
+void ssc_fini_common(t10_lu_common_t *lu);
+void ssc_task_mgmt(t10_lu_common_t *lu, TaskOp_t op);
+void ssc_init_per(t10_lu_impl_t *itl);
+void ssc_fini_per(t10_lu_impl_t *itl);
+Boolean_t raw_init_common(t10_lu_common_t *lu);
+void raw_fini_common(t10_lu_common_t *lu);
+void raw_init_per(t10_lu_impl_t *itl);
+void raw_fini_per(t10_lu_impl_t *itl);
+Boolean_t osd_init_common(t10_lu_common_t *lu);
+void osd_fini_common(t10_lu_common_t *lu);
+void osd_init_per(t10_lu_impl_t *itl);
+void osd_fini_per(t10_lu_impl_t *itl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _T10_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_osd.c b/usr/src/cmd/iscsi/iscsitgtd/t10_osd.c
new file mode 100644
index 0000000000..a200dd1030
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_osd.c
@@ -0,0 +1,591 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Implementation of OSD emulation |
+ * | |
+ * | NOTE: At this point in time this file is nothing more than a |
+ * | place holder for the implementation. As the project evolves we'll |
+ * | add more and more functionality. |
+ * []------------------------------------------------------------------[]
+ */
+#include <sys/types.h>
+#include <aio.h>
+#include <sys/asynch.h>
+#include <sys/mman.h>
+#include <stddef.h>
+#include <strings.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/generic/inquiry.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/generic/mode.h>
+#include <sys/scsi/generic/dad_mode.h>
+
+#include "t10.h"
+#include "t10_spc.h"
+#include "t10_osd.h"
+#include "utility.h"
+
+/*
+ * Forward declarations
+ */
+static void osd_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+static void osd_data(t10_cmd_t *cmd, emul_handle_t e, size_t offset,
+ char *data, size_t data_len);
+static scsi_cmd_table_t osd_table[];
+static void osd_list(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+
+/*
+ * []----
+ * | osd_init_common -- Initialize LU data which is common to all I_T_Ls
+ * []----
+ */
+/*ARGSUSED*/
+Boolean_t
+osd_init_common(t10_lu_common_t *lu)
+{
+ osd_params_t *o;
+ char *str;
+ xml_node_t *node = lu->l_root;
+
+ if ((o = (osd_params_t *)calloc(1, sizeof (*o))) == NULL)
+ return (False);
+
+ if (xml_find_value_str(node, XML_ELEMENT_SIZE, &str) == True) {
+ o->o_size = strtoll(str, NULL, 0);
+ free(str);
+ } else {
+ free(o);
+ return (False);
+ }
+
+ lu->l_dtype_params = (void *)o;
+ return (True);
+}
+
+/*ARGSUSED*/
+void
+osd_fini_common(t10_lu_common_t *lu)
+{
+ free(lu->l_dtype_params);
+}
+
+/*
+ * []----
+ * | osd_init_per -- Initialize per I_T_L information
+ * []----
+ */
+/*ARGSUSED*/
+void
+osd_init_per(t10_lu_impl_t *itl)
+{
+ itl->l_cmd = osd_cmd;
+ itl->l_data = osd_data;
+ itl->l_cmd_table = osd_table;
+
+ /*
+ * The first time an I_T nexus connects to a LU it is supposed
+ * to receive an unit attention upon the first command sent.
+ */
+ itl->l_status = KEY_UNIT_ATTENTION;
+ itl->l_asc = SPC_ASC_PWR_ON;
+ itl->l_ascq = SPC_ASCQ_PWR_ON;
+}
+
+/*ARGSUSED*/
+void
+osd_fini_per(t10_lu_impl_t *itl)
+{
+}
+
+/*
+ * []----
+ * | osd_cmd -- start a SCSI command
+ * |
+ * | This routine is called from within the SAM-3 Task router.
+ * []----
+ */
+static void
+osd_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ scsi_cmd_table_t *e;
+
+ e = &cmd->c_lu->l_cmd_table[cdb[0]];
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d Cmd %s\n",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ e->cmd_name == NULL ? "(no name)" : e->cmd_name);
+#endif
+ (*e->cmd_start)(cmd, cdb, cdb_len);
+}
+
+/*
+ * []----
+ * | osd_data -- Data phase for command.
+ * |
+ * | Normally this is only called for the WRITE command. Other commands
+ * | that have a data in phase will probably be short circuited when
+ * | we call trans_rqst_dataout() and the data is already available.
+ * | At least this is true for iSCSI. FC however will need a DataIn phase
+ * | for commands like MODE SELECT and PGROUT.
+ * []----
+ */
+static void
+osd_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+ size_t data_len)
+{
+ scsi_cmd_table_t *e;
+
+ e = &cmd->c_lu->l_cmd_table[cmd->c_cdb[0]];
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d Data %s\n",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ e->cmd_name);
+#endif
+ (*e->cmd_data)(cmd, id, offset, data, data_len);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SCSI Object-Based Storage Device Commands |
+ * | T10/1355-D |
+ * | The following functions implement the emulation of OSD type |
+ * | commands. |
+ * []------------------------------------------------------------------[]
+ */
+static void
+osd_service_action(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ osd_generic_cdb_t *o;
+ uint16_t service_action;
+
+ /*
+ * debug only -- no need to drop core if someone doesn't play right.
+ */
+ assert(cdb_len == sizeof (*o));
+ if (cdb_len != sizeof (*o)) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, SPC_ASCQ_INVALID_CDB);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ o = (osd_generic_cdb_t *)cdb;
+ service_action = o->ocdb_basic.b_service_action[0] << 8 |
+ o->ocdb_basic.b_service_action[1];
+
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "OSD%x LUN%d service=0x%x, options=0x%x, specific_opts=0x%x,"
+ " fmt=0x%x", cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num, service_action, o->ocdb_options,
+ o->ocdb_specific_opts, o->ocdb_fmt);
+
+ switch (service_action) {
+ case OSD_APPEND:
+ case OSD_CREATE:
+ case OSD_CREATE_AND_WRITE:
+ case OSD_CREATE_COLLECTION:
+ case OSD_CREATE_PARTITION:
+ case OSD_FLUSH:
+ case OSD_FLUSH_COLLECTION:
+ case OSD_FLUSH_OSD:
+ case OSD_FLUSH_PARTITION:
+ case OSD_FORMAT_OSD:
+ case OSD_GET_ATTR:
+ case OSD_LIST:
+ osd_list(cmd, cdb, cdb_len);
+ break;
+
+ case OSD_LIST_COLLECTION:
+ case OSD_PERFORM_SCSI:
+ case OSD_TASK_MGMT:
+ default:
+ spc_unsupported(cmd, cdb, cdb_len);
+ break;
+ }
+}
+
+/*
+ * []----
+ * | osd_list -- return a list of objects
+ * []----
+ */
+static void
+osd_list(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ osd_cmd_list_t *o = (osd_cmd_list_t *)cdb;
+ osd_obj_id_t part;
+ osd_list_param_t *data;
+ uint64_t len,
+ alloc_len;
+
+ part = (uint64_t)o->ocdb_partition_id[0] << 56 |
+ (uint64_t)o->ocdb_partition_id[1] << 48 |
+ (uint64_t)o->ocdb_partition_id[2] << 40 |
+ (uint64_t)o->ocdb_partition_id[3] << 32 |
+ (uint64_t)o->ocdb_partition_id[4] << 24 |
+ (uint64_t)o->ocdb_partition_id[5] << 16 |
+ (uint64_t)o->ocdb_partition_id[6] << 8 |
+ (uint64_t)o->ocdb_partition_id[7];
+ len = (uint64_t)o->ocdb_length[0] << 56 |
+ (uint64_t)o->ocdb_length[1] << 48 |
+ (uint64_t)o->ocdb_length[2] << 40 |
+ (uint64_t)o->ocdb_length[3] << 32 |
+ (uint64_t)o->ocdb_length[4] << 24 |
+ (uint64_t)o->ocdb_length[5] << 16 |
+ (uint64_t)o->ocdb_length[6] << 8 |
+ (uint64_t)o->ocdb_length[7];
+
+ if (len == 0) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ queue_prt(mgmtq, Q_STE_NONIO, "part=0x%llx, len=0x%llx", part, len);
+ alloc_len = MAX(sizeof (*data), len);
+ if ((data = calloc(1, alloc_len)) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+ data->op_length[7] = sizeof (*data) - 8;
+ if (part == OSD_PARTITION_ROOT)
+ data->op_root = 1;
+
+ trans_send_datain(cmd, (char *)data, sizeof (*data), 0, free, True,
+ (emul_handle_t)data);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Support related functions for OSD |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * []----
+ * | Command table for OSD emulation. This is at the end of the file because
+ * | it's big and ugly. ;-) To make for fast translation to the appropriate
+ * | emulation routine we just have a big command table with all 256 possible
+ * | entries. Most will report STATUS_CHECK, unsupport operation. By doing
+ * | this we can avoid error checking for command range or the use of a switch
+ * | statement.
+ * []----
+ */
+static scsi_cmd_table_t osd_table[] = {
+ /* 0x00 -- 0x0f */
+ { spc_tur, NULL, NULL, "TEST_UNIT_READY" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_request_sense, NULL, NULL, "REQUEST_SENSE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "READ" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "WRITE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x10 -- 0x1f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_inquiry, NULL, NULL, "INQUIRY" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_mselect, spc_mselect_data, NULL, "MODE_SELECT" },
+ { spc_unsupported, NULL, NULL, "RESERVE" },
+ { spc_unsupported, NULL, NULL, "RELEASE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "MODE_SENSE" },
+ { spc_unsupported, NULL, NULL, "START_STOP" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_send_diag, NULL, NULL, "SEND_DIAG" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x20 -- 0x2f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "READ_CAPACITY" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "READ_G1" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "WRITE_G1" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x30 -- 0x3f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "SYNC_CACHE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x40 -- 0x4f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "LOG_SENSE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x50 -- 0x5f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "PERSISTENT_IN" },
+ { spc_unsupported, NULL, NULL, "PERSISTENT_OUT" },
+
+ /* 0x60 -- 0x6f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x70 -- 0x7f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { osd_service_action, NULL, NULL, "SERVICE_ACTION" },
+
+ /* 0x80 -- 0x8f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "READ_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "WRITE_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x90 -- 0x9f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "SVC_ACTION_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xa0 - 0xaf */
+ { spc_report_luns, NULL, NULL, "REPORT_LUNS" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_report_tpgs, NULL, NULL, "REPORT_TPGS" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xb0 -- 0xbf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xc0 -- 0xcf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xd0 -- 0xdf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xe0 -- 0xef */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xf0 -- 0xff */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+};
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_osd.h b/usr/src/cmd/iscsi/iscsitgtd/t10_osd.h
new file mode 100644
index 0000000000..3a6d4d64ef
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_osd.h
@@ -0,0 +1,174 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _T10_OSD_H
+#define _T10_OSD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Defines and structures for Object-Base Storage Device emulation
+ */
+
+/*
+ * OSD revision 10, section 4.6.2
+ * Partition_ID and User_Object_ID value assignments
+ */
+#define OSD_PARTITION_ROOT 0x00LL
+#define OSD_PARTITION_BASE 0x10000LL
+#define OSD_USER_OBJ_ROOT 0x00LL
+#define OSD_USER_OBJ_BASE 0x10000LL
+
+/*
+ * OSD revision 10, section 6.1
+ * Commands for OSD type devices
+ */
+#define OSD_APPEND 0x8807
+#define OSD_CREATE 0x8802
+#define OSD_CREATE_AND_WRITE 0x8812
+#define OSD_CREATE_COLLECTION 0x8815
+#define OSD_CREATE_PARTITION 0x880b
+#define OSD_FLUSH 0x8808
+#define OSD_FLUSH_COLLECTION 0x881a
+#define OSD_FLUSH_OSD 0x881c
+#define OSD_FLUSH_PARTITION 0x881b
+#define OSD_FORMAT_OSD 0x8801
+#define OSD_GET_ATTR 0x880e
+#define OSD_LIST 0x8803
+#define OSD_LIST_COLLECTION 0x8817
+#define OSD_PERFORM_SCSI 0x8f7e
+#define OSD_TASK_MGMT 0x8f7f
+
+typedef uint64_t osd_obj_id_t;
+
+typedef struct osd_params {
+ uint64_t o_size;
+} osd_params_t;
+
+/*
+ * OSD revision 10, section 5.1
+ * OSD CDB Format -- basic OSD CDB
+ */
+typedef struct osd_cmd_basic {
+ uint8_t b_code,
+ b_control,
+ b_rsvd[5],
+ b_add_cdblen,
+ b_service_action[2];
+} osd_cmd_basic_t;
+
+/*
+ * OSD revision 10, section 5.2.1
+ * OSD service action specific fields
+ * The specification doesn't repeat the fields found in the basic OSD CDB,
+ * but it's included here so that one structure contains everything.
+ */
+typedef struct osd_generic_cdb {
+ osd_cmd_basic_t ocdb_basic;
+ uint8_t ocdb_options;
+#if defined(_BIT_FIELDS_LTOH)
+ uint8_t ocdb_specific_opts : 4,
+ ocdb_fmt : 2,
+ : 2;
+#elif defined(_BIT_FIELDS_HTOL)
+ uint8_t : 2,
+ ocdb_fmt : 2,
+ ocdb_specific_opts : 4;
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+ uint8_t ocdb_ts_control,
+ ocdb_rsvd1[3],
+ ocdb_partition_id[8],
+ ocdb_object_id[8],
+ ocdb_rsvd2[4],
+ ocdb_length[8],
+ ocdb_start_addr[8],
+ ocdb_attr_params[28],
+ ocdb_capability[80],
+ ocdb_security_params[40];
+} osd_generic_cdb_t;
+
+/*
+ * []------------------------------------------------------------------[]
+ * | OSD revision 10, section 6.13 -- LIST command |
+ * []------------------------------------------------------------------[]
+ */
+typedef struct osd_cmd_list {
+ osd_cmd_basic_t ocdb_basic;
+ uint8_t ocdb_rsvd1;
+#if defined(_BIT_FIELDS_LTOH)
+ uint8_t ocdb_sort_order : 4,
+ ocdb_fmt : 2,
+ : 2;
+#elif defined(_BIT_FIELDS_HTOL)
+ uint8_t : 2,
+ ocdb_fmt : 2,
+ ocdb_sort_order : 4;
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+ uint8_t ocdb_ts_control,
+ ocdb_rsvd2[3],
+ ocdb_partition_id[8],
+ ocdb_rsvd3[8],
+ ocdb_list_id[4],
+ ocdb_length[8],
+ ocdb_object_id[8],
+ ocdb_attr_params[28],
+ ocdb_capability[80],
+ ocdb_security_params[40];
+} osd_cmd_list_t;
+
+/* ---- Table 66, LIST command parameter data ---- */
+typedef struct osd_list_param {
+ uint8_t op_length[8],
+ op_cont_obj_id[8],
+ op_list_id[4],
+ op_rsvd1[3];
+#if defined(_BIT_FIELDS_LTOH)
+ uint8_t op_root : 1,
+ op_lstchg : 1,
+ : 6;
+#elif defined(_BIT_FIELDS_HTOL)
+ uint8_t : 6,
+ op_lstchg : 1,
+ op_root : 1;
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+ osd_obj_id_t op_list[1];
+} osd_list_param_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _T10_OSD_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c b/usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c
new file mode 100644
index 0000000000..05a6b933fc
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c
@@ -0,0 +1,1519 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Implementation of SBC-2 emulation |
+ * []------------------------------------------------------------------[]
+ */
+#include <sys/types.h>
+#include <sys/asynch.h>
+#include <sys/mman.h>
+#include <stddef.h>
+#include <strings.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/sysmacros.h>
+
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/generic/inquiry.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/generic/mode.h>
+#include <sys/scsi/generic/dad_mode.h>
+#include <sys/scsi/impl/uscsi.h>
+
+#include "t10.h"
+#include "t10_spc.h"
+#include "utility.h"
+#include "target.h"
+
+typedef struct raw_io {
+ t10_aio_t r_aio;
+ t10_cmd_t *r_cmd;
+
+ uint8_t *r_cdb;
+ char *r_data;
+ size_t r_cdb_len,
+ r_data_len;
+ uint64_t r_offset,
+ r_lba;
+ size_t r_lba_cnt;
+ uint32_t r_status;
+} raw_io_t;
+
+typedef struct raw_params {
+ uint64_t r_size;
+ int r_dtype;
+} raw_params_t;
+
+typedef enum { RawDataToDevice, RawDataFromDevice, NoData } raw_direction_t;
+
+/*
+ * Forward declarations
+ */
+static scsi_cmd_table_t raw_table[];
+static void raw_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+static void raw_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset,
+ char *data, size_t data_len);
+static void raw_free_io(emul_handle_t id);
+static void do_dataout(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len,
+ size_t opt_data_len);
+static raw_io_t *do_datain(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len,
+ size_t data_len);
+static int do_uscsi(t10_cmd_t *cmd, raw_io_t *io, raw_direction_t dir);
+static void raw_read_cmplt(emul_handle_t id);
+static void raw_write_cmplt(emul_handle_t e);
+
+/*
+ * []----
+ * | raw_init_common -- Initialize LU data which is common to all I_T_Ls
+ * []----
+ */
+Boolean_t
+raw_init_common(t10_lu_common_t *lu)
+{
+ xml_node_t *node = lu->l_root;
+ char *str;
+ raw_params_t *r;
+
+ if ((r = (raw_params_t *)calloc(1, sizeof (*r))) == NULL)
+ return (False);
+
+ if (xml_find_value_str(node, XML_ELEMENT_SIZE, &str) == True) {
+ r->r_size = strtoll(str, NULL, 0);
+ free(str);
+ }
+ lu->l_dtype_params = (void *)r;
+ return (True);
+}
+
+void
+raw_fini_common(t10_lu_common_t *lu)
+{
+ free(lu->l_dtype_params);
+}
+
+/*
+ * []----
+ * | raw_init_per -- Initialize per I_T_L information
+ * []----
+ */
+void
+raw_init_per(t10_lu_impl_t *itl)
+{
+ itl->l_cmd = raw_cmd;
+ itl->l_data = raw_data;
+ itl->l_cmd_table = raw_table;
+
+ /*
+ * The first time an I_T nexus connects to a LU it is supposed
+ * to receive an unit attention upon the first command sent.
+ */
+ itl->l_status = KEY_UNIT_ATTENTION;
+ itl->l_asc = 0x29;
+ itl->l_ascq = 0x01;
+}
+
+/*ARGSUSED*/
+void
+raw_fini_per(t10_lu_impl_t *itl)
+{
+}
+
+/*
+ * []----
+ * | raw_cmd -- start a SCSI command
+ * |
+ * | This routine is called from within the SAM-3 Task router.
+ * []----
+ */
+static void
+raw_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ scsi_cmd_table_t *e;
+#ifdef FULL_DEBUG
+ char debug[80];
+#endif
+
+ e = &cmd->c_lu->l_cmd_table[cdb[0]];
+#ifdef FULL_DEBUG
+ (void) snprintf(debug, sizeof (debug), "RAW%d Cmd %s\n",
+ cmd->c_lu->l_common->l_num,
+ e->cmd_name == NULL ? "(no name)" : e->cmd_name);
+ queue_str(mgmtq, Q_STE_IO, msg_log, debug);
+#endif
+ (*e->cmd_start)(cmd, cdb, cdb_len);
+}
+
+/*
+ * []----
+ * | raw_data -- Data phase for command.
+ * |
+ * | Normally this is only called for the WRITE command. Other commands
+ * | that have a data in phase will probably be short circuited when
+ * | we call trans_rqst_dataout() and the data is already available.
+ * | At least this is true for iSCSI. FC however will need a DataIn phase
+ * | for commands like MODE SELECT and PGROUT.
+ * []----
+ */
+static void
+raw_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+ size_t data_len)
+{
+ scsi_cmd_table_t *e;
+#ifdef FULL_DEBUG
+ char debug[80];
+#endif
+
+ e = &cmd->c_lu->l_cmd_table[cmd->c_cdb[0]];
+#ifdef FULL_DEBUG
+ (void) snprintf(debug, sizeof (debug), "RAW%d Data %s\n",
+ cmd->c_lu->l_common->l_num, e->cmd_name);
+ queue_str(mgmtq, Q_STE_IO, msg_log, debug);
+#endif
+ (*e->cmd_data)(cmd, id, offset, data, data_len);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | The following methods handle special case requirements for the |
+ * | raw devices. |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * []----
+ * | raw_read_tape -- handle SCSI reads from raw tape
+ * |
+ * | Need to handle reads from SCSI tape differently than LBA devices
+ * | for two reasons.
+ * | (1) The command block for tape reads is different than for
+ * | LBA devices. There's only a count field.
+ * | (2) Since tapes have records it's not possible to break up
+ * | the read operations in the same manner as LBA devices.
+ * | All of the data must first be read in from the device
+ * | and then broken up to fit the transport. This is a slower
+ * | approach, but nobody expects tapes to be quick. If speed
+ * | is needed a better approach would be to create a virtual
+ * | tape device and then stage out the data to the device later.
+ * []----
+ */
+/*ARGSUSED*/
+static void
+raw_read_tape(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ size_t req_len,
+ xfer;
+ off_t offset = 0;
+ raw_io_t *io;
+ Boolean_t last;
+
+ req_len = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4];
+ if (cdb[1] & 0x1)
+ req_len *= 512;
+
+ if (((io = do_datain(cmd, cdb, CDB_GROUP0, req_len)) == NULL) ||
+ (io->r_status != STATUS_GOOD)) {
+ if (io != NULL)
+ raw_free_io(io);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ while (offset < io->r_data_len) {
+ xfer = min(T10_MAX_OUT(cmd), io->r_data_len - offset);
+ last = ((offset + xfer) >= io->r_data_len) ? True : False;
+
+ if (trans_send_datain(cmd, io->r_data + offset,
+ xfer, offset, raw_free_io, last, io) == False) {
+ raw_free_io(io);
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ offset += xfer;
+ }
+}
+
+/*
+ * []----
+ * | raw_read -- emulation of SCSI READ command
+ * []----
+ */
+/*ARGSUSED*/
+static void
+raw_read(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ /*LINTED*/
+ union scsi_cdb *u = (union scsi_cdb *)cdb;
+ diskaddr_t addr;
+ off_t offset = 0;
+ uint32_t cnt,
+ min;
+ raw_io_t *io;
+ uint64_t err_blkno;
+ int sense_len;
+ char debug[80];
+ raw_params_t *r;
+ uchar_t addl_sense_len;
+
+ if ((r = (raw_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ if (r->r_dtype == DTYPE_SEQUENTIAL) {
+ raw_read_tape(cmd, cdb, cdb_len);
+ return;
+ }
+
+ switch (u->scc_cmd) {
+ case SCMD_READ:
+ /*
+ * SBC-2 Revision 16, section 5.5
+ * Reserve bit checks
+ */
+ if ((cdb[1] & 0xe0) || (cdb[5] & 0x38)) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, 0x24, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ addr = (diskaddr_t)(uint32_t)GETG0ADDR(u);
+ cnt = GETG0COUNT(u);
+
+ /*
+ * SBC-2 Revision 16
+ * Section: 5.5 READ(6) command
+ * A TRANSFER LENGTH field set to zero specifies
+ * that 256 logical blocks shall be read.
+ */
+ if (cnt == 0)
+ cnt = 256;
+ break;
+
+ case SCMD_READ_G1:
+ /*
+ * SBC-2 Revision 16, section 5.6
+ * Reserve bit checks.
+ */
+ if ((cdb[1] & 6) || cdb[6] || (cdb[9] & 0x38)) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, 0x24, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ addr = (diskaddr_t)(uint32_t)GETG1ADDR(u);
+ cnt = GETG1COUNT(u);
+ break;
+
+ case SCMD_READ_G4:
+ /*
+ * SBC-2 Revision 16, section 5.8
+ * Reserve bit checks
+ */
+ if ((cdb[1] & 0x6) || (cdb[10] & 6) || cdb[14] ||
+ (cdb[15] & 0x38)) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, 0x24, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ addr = GETG4LONGADDR(u);
+ cnt = GETG4COUNT(u);
+ break;
+
+ default:
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if ((addr + cnt) > r->r_size) {
+
+ /*
+ * request exceed the capacity of disk
+ * set error block number to capacity + 1
+ */
+ err_blkno = r->r_size + 1;
+
+ /*
+ * XXX: What's SBC-2 say about ASC/ASCQ here. Solaris
+ * doesn't care about these values when key is set
+ * to KEY_ILLEGAL_REQUEST.
+ */
+ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN)
+ addl_sense_len = INFORMATION_SENSE_DESCR;
+ else
+ addl_sense_len = 0;
+
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, addl_sense_len);
+ spc_sense_info(cmd, err_blkno);
+ spc_sense_ascq(cmd, 0x21, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+
+ (void) snprintf(debug, sizeof (debug),
+ "RAW%d READ Illegal sector (0x%llx + 0x%x) > 0x%llx",
+ cmd->c_lu->l_common->l_num, addr, cnt, r->r_size);
+ queue_str(mgmtq, Q_STE_ERRS, msg_log, debug);
+ return;
+ }
+
+ cmd->c_lu->l_cmds_read++;
+ cmd->c_lu->l_sects_read += cnt;
+
+ if (cnt == 0) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ do {
+ if ((io = (raw_io_t *)calloc(1, sizeof (*io))) == NULL) {
+
+ /*
+ * We're pretty much dead in the water. If we can't
+ * allocate memory. It's unlikey we'll be able to
+ * allocate a sense buffer or queue the command
+ * up to be sent back to the transport for delivery.
+ */
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ min = MIN((cnt * 512) - offset, T10_MAX_OUT(cmd));
+
+ io->r_cmd = cmd;
+ io->r_lba = addr;
+ io->r_lba_cnt = cnt;
+ io->r_offset = offset;
+ io->r_data_len = min;
+ io->r_aio.a_aio_cmplt = raw_read_cmplt;
+ io->r_aio.a_id = io;
+
+#ifdef FULL_DEBUG
+ (void) snprintf(debug, sizeof (debug),
+ "RAW%d blk 0x%llx, cnt %d, offset 0x%llx, size %d",
+ cmd->c_lu->l_common->l_num, addr, cnt, io->r_offset, min);
+ queue_str(mgmtq, Q_STE_IO, msg_log, debug);
+#endif
+ if ((io->r_data = (char *)malloc(min)) == NULL) {
+ err_blkno = addr + ((offset + 511) / 512);
+ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN)
+ sense_len = INFORMATION_SENSE_DESCR;
+ else
+ sense_len = 0;
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR,
+ sense_len);
+ spc_sense_info(cmd, err_blkno);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ trans_aioread(cmd, io->r_data, min, (addr * 512LL) +
+ (off_t)io->r_offset, (aio_result_t *)io);
+ offset += min;
+ } while (offset < (off_t)(cnt * 512));
+}
+
+/*
+ * []----
+ * | raw_read_cmplt -- Once we have the data, need to send it along.
+ * []----
+ */
+static void
+raw_read_cmplt(emul_handle_t id)
+{
+ raw_io_t *io = (raw_io_t *)id;
+ int sense_len;
+ uint64_t err_blkno;
+ t10_cmd_t *cmd = io->r_cmd;
+ Boolean_t last;
+
+ if (io->r_aio.a_aio.aio_return != io->r_data_len) {
+ err_blkno = io->r_lba + ((io->r_offset + 511) / 512);
+ cmd->c_resid = (io->r_lba_cnt * 512) - io->r_offset;
+ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN)
+ sense_len = INFORMATION_SENSE_DESCR;
+ else
+ sense_len = 0;
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, sense_len);
+ spc_sense_info(cmd, err_blkno);
+ trans_send_complete(cmd, STATUS_CHECK);
+ raw_free_io(io);
+ return;
+ }
+
+ last = ((io->r_offset + io->r_data_len) < (io->r_lba_cnt * 512LL)) ?
+ False : True;
+ if (trans_send_datain(cmd, io->r_data, io->r_data_len,
+ io->r_offset, raw_free_io, last, io) == False) {
+ raw_free_io(io);
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+}
+
+/*ARGSUSED*/
+static void
+raw_write_tape(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ size_t request_len,
+ xfer;
+ raw_io_t *io;
+
+ request_len = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4];
+ request_len *= (cdb[1] & 0x1) ? 512 : 1;
+
+ if ((io = calloc(1, sizeof (*io))) == NULL) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ if ((io->r_data = malloc(request_len)) == NULL) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+ io->r_data_len = request_len;
+ io->r_cmd = cmd;
+
+ xfer = min(T10_MAX_OUT(cmd), request_len);
+ (void) trans_rqst_dataout(cmd, io->r_data, xfer, io->r_offset, io);
+}
+
+/*ARGSUSED*/
+void
+raw_write_tape_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+ size_t data_len)
+{
+ raw_io_t *io = (raw_io_t *)id;
+ size_t xfer;
+
+ if ((io->r_offset + data_len) < io->r_data_len) {
+ io->r_offset += data_len;
+ xfer = min(T10_MAX_OUT(cmd), io->r_data_len - io->r_offset);
+ (void) trans_rqst_dataout(cmd, io->r_data + io->r_offset, xfer,
+ io->r_offset, io);
+ return;
+ } else {
+ trans_send_complete(cmd, do_uscsi(cmd, io, RawDataToDevice));
+ }
+}
+
+/*
+ * []----
+ * | raw_write -- implement a SCSI write command.
+ * []----
+ */
+/*ARGSUSED*/
+static void
+raw_write(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ /*LINTED*/
+ union scsi_cdb *cdbp = (union scsi_cdb *)cdb;
+ off_t addr;
+ uint64_t err_blkno;
+ uint32_t cnt;
+ uchar_t addl_sense_len;
+ char debug[80]; /* debug */
+ raw_params_t *r;
+ raw_io_t *io;
+ size_t max_out;
+
+ if ((r = (raw_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ if (r->r_dtype == DTYPE_SEQUENTIAL) {
+ raw_write_tape(cmd, cdb, cdb_len);
+ return;
+ }
+
+ switch (cdb[0]) {
+ case SCMD_WRITE:
+ /*
+ * SBC-2 revision 16, section 5.24
+ * Reserve bit checks.
+ */
+ if ((cdb[1] & 0xe0) || (cdb[5] & 0x38)) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, 0x24, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ addr = (off_t)cdbp->g0_addr2 << 16 |
+ (off_t)cdbp->g0_addr1 << 8 | (off_t)cdbp->g0_addr0;
+ cnt = cdbp->g0_count0;
+ /*
+ * SBC-2 Revision 16/Section 5.24 WRITE(6)
+ * A TRANSFER LENGHT of 0 indicates that 256 logical blocks
+ * shall be written.
+ */
+ if (cnt == 0)
+ cnt = 256;
+ break;
+
+ case SCMD_WRITE_G1:
+ /*
+ * SBC-2 revision 16, section 5.25
+ * Reserve bit checks.
+ */
+ if ((cdb[1] & 0x6) || cdb[6] || (cdb[9] & 0x38)) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, 0x24, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ addr = (off_t)cdbp->g1_addr3 << 24 |
+ (off_t)cdbp->g1_addr2 << 16 |
+ (off_t)cdbp->g1_addr1 << 8 |
+ (off_t)cdbp->g1_addr0;
+ cnt = cdbp->g1_count1 << 8 | cdbp->g1_count0;
+ break;
+
+ case SCMD_WRITE_G4:
+ /*
+ * SBC-2 revision 16, section 5.27
+ * Reserve bit checks.
+ */
+ if ((cdb[1] & 0x6) || cdb[14] || (cdb[15] & 0x38)) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, 0x24, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ addr = (off_t)(cdbp->g4_addr3 & 0xff) << 56 |
+ (off_t)(cdbp->g4_addr2 & 0xff) << 48 |
+ (off_t)(cdbp->g4_addr1 & 0xff) << 40 |
+ (off_t)(cdbp->g4_addr0 & 0xff) << 32 |
+ (off_t)(cdbp->g4_addtl_cdb_data3 & 0xff) << 24 |
+ (off_t)(cdbp->g4_addtl_cdb_data2 & 0xff) << 16 |
+ (off_t)(cdbp->g4_addtl_cdb_data1 & 0xff) << 8 |
+ (off_t)(cdbp->g4_addtl_cdb_data0 & 0xff);
+ cnt = cdbp->g4_count3 << 24 | cdbp->g4_count2 << 16 |
+ cdbp->g4_count1 << 8 | cdbp->g4_count0;
+ break;
+
+ default:
+ queue_str(mgmtq, Q_STE_ERRS, msg_log,
+ "Unprocessed WRITE type");
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, 0x24, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+
+ }
+
+ if ((addr < 0) || ((addr + cnt) > r->r_size)) {
+
+ /*
+ * request exceed the capacity of disk
+ * set error block number to capacity + 1
+ */
+ err_blkno = r->r_size + 1;
+
+ /*
+ * XXX: What's SBC-2 say about ASC/ASCQ here. Solaris
+ * doesn't care about these values when key is set
+ * to KEY_ILLEGAL_REQUEST.
+ */
+ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN)
+ addl_sense_len = INFORMATION_SENSE_DESCR;
+ else
+ addl_sense_len = 0;
+
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, addl_sense_len);
+ spc_sense_info(cmd, err_blkno);
+ spc_sense_ascq(cmd, 0x21, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+
+ (void) snprintf(debug, sizeof (debug),
+ "RAW%d WRITE Illegal sector (0x%llx + 0x%x) > 0x%llx",
+ cmd->c_lu->l_common->l_num, addr, cnt, r->r_size);
+ queue_str(mgmtq, Q_STE_ERRS, msg_log, debug);
+ return;
+ }
+
+ if (cnt == 0) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ io = (raw_io_t *)cmd->c_emul_id;
+ if (io == NULL) {
+ if ((io = (raw_io_t *)calloc(1, sizeof (*io))) == NULL) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ io->r_lba = addr;
+ io->r_lba_cnt = cnt;
+ io->r_cmd = cmd;
+ io->r_aio.a_aio_cmplt = raw_write_cmplt;
+ io->r_aio.a_id = io;
+
+ /*
+ * Only update the statistics the first time through
+ * for this particular command. If the requested transfer
+ * is larger than the transport can handle this routine
+ * will be called many times.
+ */
+ cmd->c_lu->l_cmds_write++;
+ cmd->c_lu->l_sects_write += cnt;
+ }
+
+ /*
+ * If a transport sets the maximum output value to zero we'll
+ * just request the entire amount. Otherwise, transfer no more
+ * than the maximum output or the reminder, whichever is less.
+ */
+ max_out = cmd->c_lu->l_targ->s_maxout;
+ io->r_data_len = max_out ? MIN(max_out,
+ (cnt * 512) - io->r_offset) : (cnt * 512);
+
+#ifdef FULL_DEBUG
+ (void) snprintf(debug, sizeof (debug),
+ "RAW%d blk 0x%llx, cnt %d, offset 0x%llx, size %d",
+ cmd->c_lu->l_common->l_num, addr, cnt, io->r_offset,
+ io->r_data_len);
+ queue_str(mgmtq, Q_STE_IO, msg_log, debug);
+#endif
+
+ if ((io->r_data = (char *)malloc(io->r_data_len)) == NULL) {
+
+ /*
+ * NOTE: May need a different ASC code
+ */
+ err_blkno = addr + ((io->r_offset + 511) / 512);
+ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN)
+ addl_sense_len = INFORMATION_SENSE_DESCR;
+ else
+ addl_sense_len = 0;
+
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, addl_sense_len);
+ spc_sense_info(cmd, err_blkno);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+
+ }
+ if (trans_rqst_dataout(cmd, io->r_data, io->r_data_len, io->r_offset,
+ io) == False) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+}
+
+/*
+ * []----
+ * | raw_write_data -- store a chunk of data from the transport
+ * []----
+ */
+/*ARGSUSED*/
+void
+raw_write_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+ size_t data_len)
+{
+ raw_io_t *io = (raw_io_t *)id;
+ raw_params_t *r = T10_PARAMS_AREA(cmd);
+
+ if (r == NULL)
+ return;
+
+ if (r->r_dtype == DTYPE_SEQUENTIAL) {
+ raw_write_tape_data(cmd, id, offset, data, data_len);
+ return;
+ }
+
+ trans_aiowrite(cmd, data, data_len, (io->r_lba * 512) +
+ (off_t)io->r_offset, (aio_result_t *)io);
+}
+
+/*
+ * []----
+ * | raw_write_cmplt -- deal with end game of write
+ * |
+ * | See if all of the data for this write operation has been dealt
+ * | with. If so, send a final acknowledgement back to the transport.
+ * | If not, update the offset, calculate the next transfer size, and
+ * | start the process again.
+ * []---
+ */
+static void
+raw_write_cmplt(emul_handle_t e)
+{
+ raw_io_t *io = (raw_io_t *)e;
+ t10_cmd_t *cmd = io->r_cmd;
+
+ if ((io->r_offset + io->r_data_len) < (io->r_lba_cnt * 512)) {
+ free(io->r_data);
+
+ io->r_offset += io->r_data_len;
+ io->r_data_len = MIN(cmd->c_lu->l_targ->s_maxout,
+ (io->r_lba_cnt * 512) - io->r_offset);
+ raw_write(cmd, cmd->c_cdb, cmd->c_cdb_len);
+ return;
+ }
+ raw_free_io(io);
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+static void
+raw_reserve(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+
+ if ((io = do_datain(cmd, cdb, CDB_GROUP0, 0)) == NULL) {
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ trans_send_complete(cmd, io->r_status);
+ raw_free_io(io);
+ }
+}
+
+static void
+raw_release(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+
+ if ((io = do_datain(cmd, cdb, CDB_GROUP0, 0)) == NULL) {
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ trans_send_complete(cmd, io->r_status);
+ raw_free_io(io);
+ }
+}
+
+static void
+raw_msense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+ int len;
+
+ switch (cdb[0]) {
+ case SCMD_MODE_SENSE:
+ len = cdb[4];
+ break;
+
+ case SCMD_MODE_SENSE_G1:
+ len = (cdb[7] << 8) | cdb[8];
+ break;
+ }
+
+ if (((io = do_datain(cmd, cdb, CDB_GROUP0, len)) == NULL) ||
+ (io->r_status != STATUS_GOOD)) {
+ if (io != NULL)
+ raw_free_io(io);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ if (trans_send_datain(cmd, io->r_data, io->r_data_len, 0,
+ raw_free_io, True, io) == False) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+}
+
+static void
+raw_tur(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+
+ if ((io = do_datain(cmd, cdb, CDB_GROUP0, 0)) == NULL) {
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ trans_send_complete(cmd, io->r_status);
+ raw_free_io(io);
+ }
+}
+
+static void
+raw_request_sense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+
+ if (((io = do_datain(cmd, cdb, CDB_GROUP0, cdb[4])) == NULL) ||
+ (io->r_status != STATUS_GOOD)) {
+ if (io != NULL)
+ raw_free_io(io);
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ if (trans_send_datain(cmd, io->r_data, io->r_data_len, 0,
+ raw_free_io, True, io) == False) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+ }
+}
+
+static void
+raw_inquiry(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+ uint32_t len;
+ struct scsi_inquiry inq;
+ raw_params_t *r;
+
+ if ((r = (raw_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ len = (cdb[3] << 8) | cdb[4];
+ if (((io = do_datain(cmd, cdb, CDB_GROUP0, len)) == NULL) ||
+ (io->r_status != STATUS_GOOD)) {
+ if (io != NULL)
+ raw_free_io(io);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if ((cdb[1] & 1) == 0) {
+ bcopy(io->r_data, &inq, sizeof (inq));
+ r->r_dtype = inq.inq_dtype;
+ }
+ if (trans_send_datain(cmd, io->r_data, io->r_data_len, 0,
+ raw_free_io, True, io) == False) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+}
+
+static void
+raw_mselect(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ int len;
+
+ switch (cdb[0]) {
+ case SCMD_MODE_SELECT:
+ len = cdb[4];
+ cdb_len = CDB_GROUP0;
+ break;
+
+ case SCMD_MODE_SELECT_G1:
+ len = (cdb[7] << 8) | cdb[8];
+ cdb_len = CDB_GROUP1;
+ break;
+ }
+ do_dataout(cmd, cdb, cdb_len, len);
+}
+
+/*ARGSUSED*/
+static void
+raw_mselect_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+ size_t data_len)
+{
+ raw_io_t *io = (raw_io_t *)id;
+ trans_send_complete(cmd, do_uscsi(cmd, io, RawDataToDevice));
+}
+
+static void
+raw_startstop(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+
+ if ((io = do_datain(cmd, cdb, CDB_GROUP0, 0)) == NULL) {
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ trans_send_complete(cmd, io->r_status);
+ raw_free_io(io);
+ }
+}
+
+static void
+raw_rewind(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+
+ if ((io = do_datain(cmd, cdb, CDB_GROUP0, 0)) == NULL) {
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ trans_send_complete(cmd, io->r_status);
+ raw_free_io(io);
+ }
+}
+
+static void
+raw_send_diag(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ int len;
+
+ len = (cdb[3] << 8) | cdb[4];
+ do_dataout(cmd, cdb, CDB_GROUP0, len);
+}
+
+/*ARGSUSED*/
+static void
+raw_send_diag_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset,
+ char *data, size_t data_len)
+{
+ raw_io_t *io = (raw_io_t *)id;
+ trans_send_complete(cmd, do_uscsi(cmd, io, RawDataToDevice));
+}
+
+static void
+raw_recap(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ struct scsi_capacity cap;
+ raw_io_t *io;
+ raw_params_t *r;
+
+ if ((r = (raw_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ if (((io = do_datain(cmd, cdb, CDB_GROUP1, sizeof (cap))) == NULL) ||
+ (io->r_status != STATUS_GOOD)) {
+ if (io != NULL)
+ raw_free_io(io);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ bcopy(io->r_data, &cap, sizeof (cap));
+ /*
+ * Currently there's a bug in ZFS which doesn't report a capacity
+ * for any of the volumes. This means that when using ZFS the
+ * administrator must supply the device size.
+ */
+ if (cap.capacity != 0)
+ r->r_size = cap.capacity;
+ if (trans_send_datain(cmd, io->r_data, io->r_data_len, 0,
+ raw_free_io, True, io) == False) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+}
+
+static void
+raw_service_actiong4(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+ uint32_t len;
+ struct scsi_capacity_16 cap16;
+ raw_params_t *r;
+
+ if ((r = (raw_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ len = (cdb[10] << 24) | (cdb[11] << 16) | (cdb[12] << 8) | cdb[13];
+ if (((io = do_datain(cmd, cdb, CDB_GROUP4, len)) == NULL) ||
+ (io->r_status != STATUS_GOOD)) {
+ if (io != NULL)
+ raw_free_io(io);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ bcopy(io->r_data, &cap16, sizeof (cap16));
+ /*
+ * Currently there's a bug in ZFS which doesn't report a capacity
+ * for any of the volumes. This means that when using ZFS the
+ * administrator must supply the device size.
+ */
+ if (cap16.sc_capacity != 0)
+ r->r_size = cap16.sc_capacity;
+ if (trans_send_datain(cmd, io->r_data, io->r_data_len, 0,
+ raw_free_io, True, io) == False) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+}
+
+static void
+raw_synccache(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+
+ if ((io = do_datain(cmd, cdb, CDB_GROUP1, 0)) == NULL) {
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ trans_send_complete(cmd, io->r_status);
+ raw_free_io(io);
+ }
+}
+
+static void
+raw_write_fm(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+
+ if ((io = do_datain(cmd, cdb, CDB_GROUP0, 0)) == NULL) {
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ trans_send_complete(cmd, io->r_status);
+ raw_free_io(io);
+ }
+}
+
+static void
+raw_report_tpgs(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+ uint32_t len;
+
+ len = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
+ if (((io = do_datain(cmd, cdb, CDB_GROUP5, len)) == NULL) ||
+ (io->r_status != STATUS_GOOD)) {
+ if (io != NULL)
+ raw_free_io(io);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ if (trans_send_datain(cmd, io->r_data, io->r_data_len, 0,
+ raw_free_io, True, io) == False) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+}
+
+static void
+raw_read_limits(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ raw_io_t *io;
+
+ /*
+ * spec defines this command to return 6 bytes of data
+ */
+ if (((io = do_datain(cmd, cdb, CDB_GROUP0, 6)) == NULL) ||
+ (io->r_status != STATUS_GOOD)) {
+ if (io != NULL)
+ raw_free_io(io);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ if (trans_send_datain(cmd, io->r_data, io->r_data_len, 0,
+ raw_free_io, True, io) == False) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Support related functions for raw devices |
+ * []------------------------------------------------------------------[]
+ */
+
+static void
+do_dataout(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len, size_t opt_data_len)
+{
+ char *opt_data = NULL;
+ raw_io_t *io;
+
+ if ((io = calloc(1, sizeof (*io))) == NULL) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ if ((opt_data_len != 0) &&
+ ((opt_data = malloc(opt_data_len)) == NULL)) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ io->r_cdb = cdb;
+ io->r_cdb_len = cdb_len;
+ io->r_data = opt_data;
+ io->r_data_len = opt_data_len;
+ if (trans_rqst_dataout(cmd, opt_data, opt_data_len, 0, io) == False) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ }
+}
+
+static raw_io_t *
+do_datain(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len, size_t data_len)
+{
+ raw_io_t *io;
+
+ if ((io = calloc(1, sizeof (*io))) == NULL) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ return (NULL);
+ }
+
+ io->r_cdb = cdb;
+ io->r_cdb_len = cdb_len;
+ io->r_data_len = data_len;
+ if ((data_len != 0) && ((io->r_data = malloc(data_len)) == NULL)) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ free(io);
+ return (NULL);
+ }
+ (void) do_uscsi(cmd, io, data_len == 0 ? NoData : RawDataFromDevice);
+ return (io);
+}
+
+static int
+do_uscsi(t10_cmd_t *cmd, raw_io_t *io, raw_direction_t dir)
+{
+ struct uscsi_cmd u;
+ uchar_t sense_buf[128];
+
+ bzero(&u, sizeof (u));
+ u.uscsi_cdb = (caddr_t)io->r_cdb;
+ u.uscsi_cdblen = io->r_cdb_len;
+ u.uscsi_bufaddr = io->r_data;
+ u.uscsi_buflen = io->r_data_len;
+ u.uscsi_flags = ((dir == RawDataToDevice) ? USCSI_WRITE :
+ (dir == RawDataFromDevice) ? USCSI_READ : 0) | USCSI_RQENABLE;
+ u.uscsi_rqbuf = (char *)sense_buf;
+ u.uscsi_rqlen = sizeof (sense_buf);
+
+ if ((ioctl(cmd->c_lu->l_common->l_fd, USCSICMD, &u) == 0) &&
+ (u.uscsi_status == 0)) {
+ io->r_status = 0;
+ return (0);
+ }
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "RAW%d LUN%d USCSICMD errno %d, cmd_status %d, rqstatus %d, "
+ "rqresid %d",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, errno,
+ u.uscsi_status, u.uscsi_rqstatus, u.uscsi_rqresid);
+
+ if ((u.uscsi_rqlen - u.uscsi_rqresid) <
+ sizeof (struct scsi_extended_sense)) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "RAW%x LUN%d -- No sense data, got=%d, needed=%d",
+ cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num,
+ u.uscsi_rqlen - u.uscsi_rqresid,
+ sizeof (struct scsi_extended_sense));
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ io->r_status = STATUS_CHECK;
+ return (STATUS_CHECK);
+ } else {
+ spc_sense_raw(cmd, sense_buf, u.uscsi_rqlen - u.uscsi_rqresid);
+ io->r_status = u.uscsi_status;
+ return (u.uscsi_status);
+ }
+}
+
+static void
+raw_free_io(emul_handle_t id)
+{
+ raw_io_t *io = (raw_io_t *)id;
+
+ if (io->r_data_len)
+ free(io->r_data);
+ free(io);
+}
+
+/*
+ * []----
+ * | Command table for LBA emulation. This is at the end of the file because
+ * | it's big and ugly. ;-) To make for fast translation to the appropriate
+ * | emulation routine we just have a big command table with all 256 possible
+ * | entries. Most will report STATUS_CHECK, unsupport operation. By doing
+ * | this we can avoid error checking for command range.
+ * []----
+ */
+static scsi_cmd_table_t raw_table[] = {
+ /* 0x00 -- 0x0f */
+ { raw_tur, NULL, NULL, "TEST_UNIT_READY" },
+ { raw_rewind, NULL, NULL, "REWIND" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_request_sense, NULL, NULL, "REQUEST_SENSE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_read_limits, NULL, NULL, "READ_LIMITS" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_read, NULL, NULL, "READ" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_write, raw_write_data, NULL, "WRITE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x10 -- 0x1f */
+ { raw_write_fm, NULL, NULL, "WRITE_FILEMARKS" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_inquiry, NULL, NULL, "INQUIRY" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_mselect, raw_mselect_data, NULL, "MODE_SELECT" },
+ { raw_reserve, NULL, NULL, "RESERVE" },
+ { raw_release, NULL, NULL, "RELEASE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_msense, NULL, NULL, "MODE_SENSE" },
+ { raw_startstop, NULL, NULL, "START_STOP" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_send_diag, raw_send_diag_data, NULL, "SEND_DIAG" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x20 -- 0x2f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_recap, NULL, NULL, "READ_CAPACITY" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_read, NULL, NULL, "READ_G1" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_write, raw_write_data, NULL, "WRITE_G1" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x30 -- 0x3f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_synccache, NULL, NULL, "SYNC_CACHE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x40 -- 0x4f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x50 -- 0x5f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_mselect, raw_mselect_data, NULL, "MODE_SELECT" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_msense, NULL, NULL, "MODE_SENSE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x60 -- 0x6f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x70 -- 0x7f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x80 -- 0x8f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_read, NULL, NULL, "READ_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_write, raw_write_data, NULL, "WRITE_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x90 -- 0x9f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_service_actiong4, NULL, NULL, "SVC_ACTION_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xa0 - 0xaf */
+ { spc_report_luns, NULL, NULL, "REPORT_LUNS" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { raw_report_tpgs, NULL, NULL, "REPORT_TPGS" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xb0 -- 0xbf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xc0 -- 0xcf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xd0 -- 0xdf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xe0 -- 0xef */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xf0 -- 0xff */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+};
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_sam.c b/usr/src/cmd/iscsi/iscsitgtd/t10_sam.c
new file mode 100644
index 0000000000..dae87c1f9e
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_sam.c
@@ -0,0 +1,2191 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <aio.h>
+#include <sys/aio.h>
+#include <sys/asynch.h>
+#include <stddef.h>
+#include <strings.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include <sys/avl.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/ucontext.h>
+
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/generic/inquiry.h>
+
+#include "target.h"
+#include "queue.h"
+#include "t10.h"
+#include "t10_spc.h"
+#include "utility.h"
+
+/*
+ * []------------------------------------------------------------------[]
+ * | This file contains methods which isolate a transport from device |
+ * | emulation. The first part of the file contains method which are |
+ * | called by the transport to start commands or deliver data. The |
+ * | transport does not know anything about what emulation is being |
+ * | done. The emulation layer receieves cdb's and nows nothing about |
+ * | the transport. This is how it should be. There are a few special |
+ * | cases to deal with transports which have a notion of immediate |
+ * | data, but we're isolating that from the emulation layer. |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * Forward declarations
+ */
+static t10_lu_impl_t *t10_find_lun(t10_targ_impl_t *t, int lun);
+static void *lu_runner(void *v);
+static Boolean_t t10_lu_initialize(t10_lu_common_t *lu, char *basedir);
+static void *t10_aio_done(void *v);
+static Boolean_t lu_remove_cmds(msg_t *m, void *v);
+static void cmd_common_free(t10_lu_impl_t *lu, t10_cmd_t *cmd);
+static Boolean_t load_params(t10_lu_common_t *lu, char *basedir);
+static Boolean_t fallocate(int fd, off64_t len);
+/* ---- These are AVL comparison routines ---- */
+static int find_lu_by_num(const void *v1, const void *v2);
+static int find_lu_by_guid(const void *v1, const void *v2);
+static int find_lu_by_targ(const void *v1, const void *v2);
+static int find_cmd_by_addr(const void *v1, const void *v2);
+
+/*
+ * Global variables
+ */
+static avl_tree_t lu_list;
+static pthread_mutex_t lu_list_mutex;
+static int lu_id;
+target_queue_t *mgmtq;
+static pthread_mutex_t t10_mutex;
+static int t10_num;
+static sema_t t10_sema;
+
+/*
+ * []----
+ * | t10_init -- called once at the beginning of time to initialize globals
+ * []----
+ */
+void
+t10_init(target_queue_t *q)
+{
+ pthread_t junk;
+
+ mgmtq = q;
+ (void) pthread_mutex_init(&lu_list_mutex, NULL);
+ (void) pthread_mutex_init(&t10_mutex, NULL);
+ (void) sema_init(&t10_sema, 0, USYNC_THREAD, NULL);
+ avl_create(&lu_list, find_lu_by_guid, sizeof (t10_lu_common_t),
+ offsetof(t10_lu_common_t, l_all_luns));
+ (void) pthread_create(&junk, NULL, t10_aio_done, NULL);
+}
+
+/*ARGSUSED*/
+static void *
+t10_aio_done(void *v)
+{
+ aio_result_t *result;
+ t10_aio_t *a;
+
+ do {
+ if (sema_wait(&t10_sema) != 0) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SAM- same_wait returned error");
+ continue;
+ }
+
+ if ((result = aiowait(NULL)) == (aio_result_t *)-1) {
+ if (errno == EINVAL) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SAM- aiowait returned EINVAL");
+ /*
+ * It's possible for aiowait to return
+ * prematurely. So, post another semaphore,
+ * sleep for a second, and try the wait
+ * again.
+ */
+ (void) sema_post(&t10_sema);
+ (void) sleep(1);
+ continue;
+ } else
+ break;
+ } else {
+ a = (t10_aio_t *)result;
+ }
+ if ((a != NULL) && (a->a_aio_cmplt != NULL)) {
+ (*a->a_aio_cmplt)(a->a_id);
+ } else {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SAM aiowait returned results, but is NULL");
+ }
+ /*CONSTANTCONDITION*/
+ } while (1);
+
+ return (NULL);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Methods called by transports to interface with SAM-3 |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * []----
+ * | t10_handle_create -- Create the I_T nexus
+ * |
+ * | NOTES:
+ * | max_out can be set to 0 if the transport wishes to wait for all of
+ * | the data before receiving a DATAOUT message. Fibre Channel will most
+ * | likely set this to 0, whereas iSCSI will set max_out to the value
+ * | of MaxRecvDataSegment.
+ * | (*datain_cb)() is called, on the LU thread, when the emulation
+ * | module needs data *and* t10_send_cmd was called with opt_data_len, but
+ * | no opt_data.
+ * []----
+ */
+t10_targ_handle_t
+t10_handle_create(char *targ, int trans_version, int tpg, int max_out,
+ target_queue_t *tq, void (*datain_cb)(t10_cmd_t *, char *, size_t *))
+{
+ t10_targ_impl_t *t = calloc(1, sizeof (t10_targ_impl_t));
+
+ if (t == NULL)
+ return (NULL);
+
+ (void) pthread_mutex_lock(&t10_mutex);
+ t->s_targ_num = t10_num++;
+ (void) pthread_mutex_unlock(&t10_mutex);
+ t->s_targ_base = strdup(targ);
+ t->s_trans_vers = trans_version;
+ t->s_maxout = max_out;
+ t->s_to_transport = tq;
+ t->s_dataout_cb = datain_cb;
+
+ /*
+ * Once we actually support two or more transports it would be
+ * possible for a collision between the underlying transports
+ * target port group values since one wouldn't necessarily know
+ * anything about the other. We'll use the upper bits of the
+ * target port group value to separate them.
+ * If we were to support many transports and with one then running
+ * out of bit space we'd need to change the allocation method. Since
+ * these values aren't stored anywhere and just used by initiators
+ * to determine relative path numbering there's no issue with changing
+ * this later if need be.
+ */
+ switch (trans_version) {
+ case T10_TRANS_ISCSI:
+ t->s_tp_grp = 0x0000 | tpg;
+ break;
+
+ case T10_TRANS_FC:
+ t->s_tp_grp = 0x8000 | tpg;
+ break;
+ }
+
+ avl_create(&t->s_open_lu, find_lu_by_num, sizeof (t10_lu_impl_t),
+ offsetof(t10_lu_impl_t, l_open_targ_node));
+
+ (void) pthread_mutex_init(&t->s_mutex, NULL);
+ return ((t10_targ_handle_t)t);
+}
+
+void
+t10_handle_disable(t10_targ_handle_t tp)
+{
+ t10_targ_impl_t *t = (t10_targ_impl_t *)tp;
+ t10_lu_impl_t *l;
+ t10_shutdown_t s;
+ int lu_per_targ = 0;
+
+ (void) pthread_mutex_lock(&t->s_mutex);
+ if (avl_numnodes(&t->s_open_lu) != 0) {
+
+ s.t_q = queue_alloc();
+ l = avl_first(&t->s_open_lu);
+ while (l != NULL) {
+
+ s.t_lu = l;
+ queue_message_set(l->l_common->l_from_transports, 0,
+ msg_shutdown, (void *)&s);
+ queue_message_free(queue_message_get(s.t_q));
+ lu_per_targ++;
+ l = AVL_NEXT(&t->s_open_lu, l);
+ }
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "SAM%x Sent %d shutdown requests for %s",
+ t->s_targ_num, lu_per_targ, t->s_targ_base);
+ queue_free(s.t_q, NULL);
+ }
+ (void) pthread_mutex_unlock(&t->s_mutex);
+}
+
+void
+t10_handle_destroy(t10_targ_handle_t tp)
+{
+ t10_targ_impl_t *t = (t10_targ_impl_t *)tp;
+ t10_lu_impl_t *l;
+ t10_cmd_t *c,
+ *c2free;
+ int fast_free = 0;
+
+ (void) pthread_mutex_lock(&t->s_mutex);
+ if (avl_numnodes(&t->s_open_lu) != 0) {
+ while ((l = avl_first(&t->s_open_lu)) != NULL) {
+ avl_remove(&t->s_open_lu, l);
+
+ (void) pthread_mutex_lock(&l->l_cmd_mutex);
+ if (avl_numnodes(&l->l_cmds) != 0) {
+ c = avl_first(&l->l_cmds);
+ while (c != NULL) {
+ if (c->c_state ==
+ T10_Cmd_DataOut_Sent) {
+ c2free = c;
+ c = AVL_NEXT(&l->l_cmds, c);
+ fast_free++;
+ cmd_common_free(l, c2free);
+ free(c2free);
+ } else if (c->c_state ==
+ T10_Cmd_Canceled) {
+ c2free = c;
+ c = AVL_NEXT(&l->l_cmds, c);
+ cmd_common_free(l, c2free);
+ fast_free++;
+ } else {
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "SAM%x cmd->c_state %d, "
+ "trans_id 0x%x",
+ t->s_targ_num,
+ c->c_state,
+ c->c_trans_id);
+ c = AVL_NEXT(&l->l_cmds, c);
+ }
+ }
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "SAM%x FastFree %d ... "
+ "Waiting for %d cmds to drain",
+ t->s_targ_num, fast_free,
+ avl_numnodes(&l->l_cmds));
+ if (avl_numnodes(&l->l_cmds) != 0) {
+ l->l_wait_for_drain = True;
+ while (l->l_wait_for_drain == True) {
+ (void) pthread_cond_wait(
+ &l->l_cmd_cond,
+ &l->l_cmd_mutex);
+ }
+ }
+ }
+ (void) pthread_mutex_unlock(&l->l_cmd_mutex);
+ avl_destroy(&l->l_cmds);
+ free(l);
+ }
+ }
+ avl_destroy(&t->s_open_lu);
+ (void) pthread_mutex_unlock(&t->s_mutex);
+
+ free(t->s_targ_base);
+ free(t);
+}
+
+/*
+ * []----
+ * | t10_cmd_create -- creates a command pointer
+ * |
+ * | If an error occurs, a sense condition buffer will be created that can
+ * | be sent back to the initiator. The only time this should occur is during
+ * | LU setup and we've run out of resources like not having enough file
+ * | descriptors to open the backing store. If the cmdp is NULL, then there's
+ * | not even enough memory to create a command buffer and the transport
+ * | should shutdown it's connection a cleanly as possible.
+ * []----
+ */
+Boolean_t
+t10_cmd_create(t10_targ_handle_t t, int lun_number, uint8_t *cdb,
+ size_t cdb_len, transport_t trans_id, t10_cmd_t **cmdp)
+{
+ t10_cmd_t *cmd = NULL;
+
+ *cmdp = NULL;
+ if (t == NULL)
+ goto error;
+
+ if ((cmd = (t10_cmd_t *)calloc(1, sizeof (t10_cmd_t))) == NULL)
+ goto error;
+
+ if ((cmd->c_cdb = (uint8_t *)malloc(cdb_len)) == NULL)
+ goto error;
+
+ cmd->c_trans_id = trans_id;
+ *cmdp = cmd;
+ if ((cmd->c_lu = t10_find_lun((t10_targ_impl_t *)t, lun_number)) ==
+ NULL) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ goto error;
+ }
+
+ (void) pthread_mutex_lock(&cmd->c_lu->l_cmd_mutex);
+ avl_add(&cmd->c_lu->l_cmds, (void *)cmd);
+ cmd->c_state = T10_Cmd_Alloc;
+ (void) pthread_mutex_unlock(&cmd->c_lu->l_cmd_mutex);
+ bcopy(cdb, cmd->c_cdb, cdb_len);
+ cmd->c_cdb_len = cdb_len;
+
+ return (True);
+
+error:
+ cmd->c_state = T10_Cmd_Errored;
+ if (cmd->c_cdb) {
+ free(cmd->c_cdb);
+ cmd->c_cdb = NULL;
+ }
+
+ /*
+ * If we haven't set up the argument pointer, then free the memory
+ * that had been allocated to the command.
+ */
+ if (*cmdp == NULL)
+ free(cmd);
+ return (False);
+}
+
+/*
+ * []----
+ * | t10_send_cmd -- send the give command to appropriate LUN emulation
+ * |
+ * | NOTE: emul_id is only provided for DATA_OUT commands (write ops)
+ * | which have multiple phases to complete the request. The emulation
+ * | module will provide this value when it requests more data to be
+ * | sent.
+ * []----
+ */
+/*ARGSUSED*/
+Boolean_t
+t10_cmd_send(t10_targ_handle_t t, t10_cmd_t *cmd, char *opt_data,
+ size_t opt_data_len)
+{
+ if (cmd == NULL)
+ return (False);
+
+ cmd->c_data = opt_data;
+ cmd->c_data_len = opt_data_len;
+
+ queue_message_set(cmd->c_lu->l_common->l_from_transports, 0,
+ msg_cmd_send, (void *)cmd);
+
+ return (True);
+}
+
+/*ARGSUSED*/
+Boolean_t
+t10_cmd_data(t10_targ_handle_t t, t10_cmd_t *cmd, size_t offset, char *data,
+ size_t data_len)
+{
+ cmd->c_data = data;
+ cmd->c_data_len = data_len;
+ cmd->c_offset = offset;
+
+ queue_message_set(cmd->c_lu->l_common->l_from_transports, 0,
+ msg_cmd_data_out, (void *)cmd);
+
+ return (True);
+}
+
+void
+t10_cmd_state(t10_cmd_t *c, t10_cmd_event_t e)
+{
+ t10_lu_impl_t *lu = c->c_lu;
+
+ if (c->c_state == T10_Cmd_Errored) {
+ free(c);
+ return;
+ }
+ (void) pthread_mutex_lock(&lu->l_cmd_mutex);
+ switch (e) {
+ case T10_Cmd_Event_DataOut_Sent:
+ switch (c->c_state) {
+ case T10_Cmd_DataOut:
+ c->c_state = T10_Cmd_DataOut_Sent;
+ break;
+
+ default:
+ assert(0);
+ }
+ break;
+
+ case T10_Cmd_Event_DataIn_Recv:
+ switch (c->c_state) {
+ case T10_Cmd_DataOut_Sent:
+ c->c_state = T10_Cmd_DataOut;
+ break;
+
+ default:
+ assert(0);
+ }
+ break;
+
+ case T10_Cmd_Event_Canceled:
+ switch (c->c_state) {
+ case T10_Cmd_Free:
+ assert(0);
+
+ default:
+ c->c_state = T10_Cmd_Canceled;
+ break;
+ }
+ break;
+
+ case T10_Cmd_Event_Release:
+ cmd_common_free(lu, c);
+ free(c);
+ if ((lu->l_wait_for_drain == True) &&
+ (avl_numnodes(&lu->l_cmds) == 0)) {
+ lu->l_wait_for_drain = False;
+ (void) pthread_cond_signal(&lu->l_cmd_cond);
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ (void) pthread_mutex_unlock(&lu->l_cmd_mutex);
+}
+
+/*
+ * []----
+ * | t10_task_mgmt -- handle SAM-3 task management needs
+ * []----
+ */
+/*ARGSUSED*/
+Boolean_t
+t10_task_mgmt(t10_targ_handle_t t1, TaskOp_t op, int opt_lun, void *tag)
+{
+ t10_targ_impl_t *t = (t10_targ_impl_t *)t1;
+ t10_lu_impl_t search,
+ *lu;
+
+ switch (op) {
+ case InventoryChange:
+ if ((lu = avl_first(&t->s_open_lu)) != NULL) {
+ do {
+ /*CSTYLED*/
+ queue_message_set(lu->l_common->l_from_transports,
+ 0, msg_targ_inventory_change, (void *)lu);
+ } while ((lu = AVL_NEXT(&t->s_open_lu, lu)) != NULL);
+ }
+ return (True);
+
+ case ResetTarget:
+ if ((lu = avl_first(&t->s_open_lu)) != NULL) {
+ do {
+ /*CSTYLED*/
+ queue_message_set(lu->l_common->l_from_transports,
+ Q_HIGH, msg_reset_lu, (void *)lu);
+ } while ((lu = AVL_NEXT(&t->s_open_lu, lu)) != NULL);
+ return (True);
+ } else
+ return (False);
+
+ case ResetLun:
+ search.l_targ_lun = opt_lun;
+ if ((lu = avl_find(&t->s_open_lu, (void *)&search, NULL)) !=
+ NULL) {
+ queue_message_set(lu->l_common->l_from_transports,
+ Q_HIGH, msg_reset_lu, (void *)lu);
+ return (True);
+ } else
+ return (False);
+ break;
+
+ case CapacityChange:
+ search.l_targ_lun = opt_lun;
+ if ((lu = avl_find(&t->s_open_lu, (void *)&search, NULL)) !=
+ NULL) {
+ queue_message_set(lu->l_common->l_from_transports,
+ Q_HIGH, msg_lu_capacity_change,
+ (void *)(uintptr_t)opt_lun);
+ return (True);
+ } else
+ return (False);
+ break;
+
+ default:
+ return (False);
+ }
+}
+
+
+/*
+ * []----
+ * | t10_targ_stat -- Return stats on each LU associated with target.
+ * []----
+ */
+void
+t10_targ_stat(t10_targ_handle_t t1, char **buf)
+{
+ t10_targ_impl_t *t = (t10_targ_impl_t *)t1;
+ t10_lu_impl_t *itl;
+ char lb[32],
+ *p;
+
+ /*
+ * It's possible for the management interfaces to request stats
+ * even though a connection is not up and running.
+ */
+ if (t == NULL)
+ return;
+
+ itl = avl_first(&t->s_open_lu);
+ while (itl) {
+ buf_add_tag(buf, XML_ELEMENT_LUN, Tag_Start);
+ (void) snprintf(lb, sizeof (lb), "%d", itl->l_common->l_num);
+ buf_add_tag(buf, lb, Tag_String);
+
+ (void) snprintf(lb, sizeof (lb), "%lld", itl->l_cmds_read);
+ xml_add_tag(buf, XML_ELEMENT_READCMDS, lb);
+ (void) snprintf(lb, sizeof (lb), "%lld", itl->l_cmds_write);
+ xml_add_tag(buf, XML_ELEMENT_WRITECMDS, lb);
+ (void) snprintf(lb, sizeof (lb), "%lld", itl->l_sects_read);
+ xml_add_tag(buf, XML_ELEMENT_READBLKS, lb);
+ (void) snprintf(lb, sizeof (lb), "%lld", itl->l_sects_write);
+ xml_add_tag(buf, XML_ELEMENT_WRITEBLKS, lb);
+
+ switch (itl->l_common->l_state) {
+ case lu_online:
+ p = TGT_STATUS_ONLINE;
+ break;
+ case lu_offline:
+ p = TGT_STATUS_OFFLINE;
+ break;
+ case lu_errored:
+ p = TGT_STATUS_ERRORED;
+ break;
+ }
+ xml_add_tag(buf, XML_ELEMENT_STATUS, p);
+
+ buf_add_tag(buf, XML_ELEMENT_LUN, Tag_End);
+ itl = AVL_NEXT(&t->s_open_lu, itl);
+ }
+}
+
+/*
+ * []----
+ * | t10_thick_provision -- fill the backing store with real blocks
+ * |
+ * | The backing store is initially created as a hole-y file. The only
+ * | thing wrong with leaving the files hole-y is that if a system
+ * | administrator over provisions the storage at some point a client
+ * | will attempt to write to a block and receive an error unless the
+ * | administrator adds more backing store before that event. Now, depending
+ * | on the client a write error isn't fatal. However, for file systems
+ * | like UFS and ZFS, they can not currently deal with getting a write
+ * | error when it's their metadata and panic. That's not good. The concept
+ * | of "Thin Provisioning" is relatively new so we'll normally preallocate
+ * | the space, but have the option of doing the "Thin Provisioning".
+ * []----
+ */
+Boolean_t
+t10_thick_provision(char *target, int lun, target_queue_t *q)
+{
+ t10_targ_handle_t t;
+ t10_cmd_t *cmd = NULL;
+ uint8_t cdb[16]; /* ---- fake buffer ---- */
+ diskaddr_t offset = 0;
+ size_t size,
+ sync_size;
+ msg_t *m = NULL;
+ target_queue_t *rq = NULL;
+ char path[MAXPATHLEN];
+ xml_node_t *n1;
+ Boolean_t rval = False;
+ struct statvfs fs;
+
+ /*
+ * To guarantee that everything has been setup correctly
+ * we'll just use the standard interfaces. Otherwise we'd need
+ * to duplicate the code and therefore offer the chance of
+ * having something fixed/change in one location that isn't
+ * in another. Obvious right?
+ */
+ if ((t = t10_handle_create(target, 0, 0, 0, q, NULL)) == NULL) {
+ queue_prt(mgmtq, Q_STE_ERRS, "STE%x Failed to create handle",
+ lun);
+ return (False);
+ }
+ if (t10_cmd_create(t, lun, cdb, sizeof (cdb), 0, &cmd) == False) {
+ queue_prt(mgmtq, Q_STE_ERRS, "STE%x Failed to create cmd",
+ lun);
+ goto error;
+ }
+
+ /*
+ * Attempt to see if there is enough space currently for the LU.
+ * The initialization might still fail with out of space because someone
+ * else is consuming space while the initialization is occuring.
+ * Nothing we can do about that.
+ */
+ if (fstatvfs(cmd->c_lu->l_common->l_fd, &fs) != 0) {
+ queue_prt(mgmtq, Q_STE_ERRS, "STE%x statvfs failed for LU",
+ lun);
+ goto error;
+ } else if ((fs.f_frsize * fs.f_bfree) < cmd->c_lu->l_common->l_size) {
+ queue_prt(mgmtq, Q_STE_ERRS, "STE%x Not enough space for LU",
+ lun);
+ goto error;
+ }
+
+ if (fallocate(cmd->c_lu->l_common->l_fd, cmd->c_lu->l_common->l_size) ==
+ False) {
+ /*
+ * The lu_runner will use this buffer to copy data.
+ */
+ sync_size = 1024 * 1024;
+ if ((cmd->c_data = malloc(sync_size)) == NULL)
+ goto error;
+
+ while ((offset < cmd->c_lu->l_common->l_size) && (rq == NULL)) {
+ size = min(cmd->c_lu->l_common->l_size - offset,
+ sync_size);
+ cmd->c_offset = offset;
+ cmd->c_data_len = size;
+ /*CSTYLED*/
+ queue_message_set(cmd->c_lu->l_common->l_from_transports, 0,
+ msg_thick_provo, (void *)cmd);
+ while ((m = queue_message_get(q)) != NULL) {
+ switch (m->msg_type) {
+ case msg_thick_provo:
+ if ((int)(intptr_t)m->msg_data != 0) {
+
+ /*
+ * An error occurred during
+ * initialization which mean we
+ * need to remove this target.
+ */
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "STE%x received data "
+ "error at 0x%llx", lun,
+ offset);
+ goto error;
+ }
+ break;
+
+ case msg_shutdown:
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "---- Thick provo got shutdown");
+ rq = (target_queue_t *)m->msg_data;
+ queue_message_free(m);
+ continue; /* don't use break */
+
+ default:
+ assert(0);
+ }
+ break;
+ }
+ queue_message_free(m);
+ offset += size;
+ }
+ } else {
+ queue_prt(mgmtq, Q_STE_NONIO, "STE%x fallocate worked", lun);
+ }
+
+ /*
+ * A forced shutdown is still considered a successful completion.
+ * Write errors and malloc failures constitute a failure.
+ */
+ rval = True;
+
+ /* ---- Completed successfully ---- */
+ if (rq == NULL) {
+
+ /*
+ * Now that the initialization is complete, update the params
+ * file to indicate the status is online. Once done, send a
+ * message to the LU thread indicating same.
+ */
+ (void) snprintf(path, sizeof (path), "%s/%s/%s%d",
+ target_basedir, cmd->c_lu->l_targ->s_targ_base, PARAMBASE,
+ lun);
+
+ if ((n1 = xml_find_child(cmd->c_lu->l_common->l_root,
+ XML_ELEMENT_STATUS)) == NULL) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "STE%x couldn't find <status>", lun);
+ goto error;
+ }
+
+ if (xml_update_value_str(n1, XML_ELEMENT_STATUS,
+ TGT_STATUS_ONLINE) == False) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "STE%x Could update <status> to online", lun);
+ goto error;
+ }
+
+ if (xml_dump2file(cmd->c_lu->l_common->l_root, path) ==
+ False) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "STE%x failed to dump out params", lun);
+ goto error;
+ }
+
+ queue_message_set(cmd->c_lu->l_common->l_from_transports, 0,
+ msg_lu_online, 0);
+ }
+
+error:
+ if (cmd != NULL) {
+ if (cmd->c_data != NULL)
+ free(cmd->c_data);
+ t10_cmd_state(cmd, T10_Cmd_Event_Release);
+ }
+ if (t != NULL) {
+ t10_handle_disable(t);
+ t10_handle_destroy(t);
+ }
+ if (rq != NULL) {
+ queue_message_set(rq, 0, msg_shutdown_rsp, 0);
+ }
+
+ return (rval);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Methods called by emulation modules to interface with SAM-3 |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * []----
+ * | trans_send_datain -- send data to transport
+ * |
+ * | NOTES:
+ * | (1) offset is only valid when a transport has set max_out to a non-zero
+ * | value.
+ * | (2) The emulation code must free the memory, if it was allocated, when
+ * | the transport is finished with it. The callback routine is used
+ * | to provide the emulation code the notification. The callback will
+ * | not be run on the same thread as the emulation code so appropriate
+ * | locking may be required by the emulation code.
+ * | (3) If the boolean 'last' is True it means that the transport can
+ * | assume the data out is finished with a CMD_SUCCESS and no futher
+ * | communication from the emulation layer will occur.
+ * []----
+ */
+Boolean_t
+trans_send_datain(t10_cmd_t *cmd, char *data, size_t data_len, size_t offset,
+ void (*callback)(emul_handle_t e), Boolean_t last, emul_handle_t id)
+{
+ t10_cmd_t *c;
+
+ if (last == False) {
+
+ if ((c = calloc(1, sizeof (*c))) == NULL)
+ return (False);
+ bcopy(cmd, c, sizeof (*c));
+ if ((c->c_cdb = (uint8_t *)malloc(c->c_cdb_len)) == NULL) {
+ free(c);
+ return (False);
+ }
+ bcopy(cmd->c_cdb, c->c_cdb, c->c_cdb_len);
+
+ (void) pthread_mutex_lock(&cmd->c_lu->l_cmd_mutex);
+ c->c_state = T10_Cmd_Alloc;
+ avl_add(&c->c_lu->l_cmds, (void *)c);
+ (void) pthread_mutex_unlock(&cmd->c_lu->l_cmd_mutex);
+
+ } else
+ c = cmd;
+
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO,
+ "SAM%x LUN%d DataIn %d, offset %d, Last %s",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ data_len, offset, last == True ? "true" : "false");
+#endif
+
+ (void) pthread_mutex_lock(&cmd->c_lu->l_cmd_mutex);
+ c->c_state = T10_Cmd_DataIn;
+ (void) pthread_mutex_unlock(&cmd->c_lu->l_cmd_mutex);
+ c->c_emul_complete = callback;
+ c->c_emul_id = id;
+ c->c_data = data;
+ c->c_data_len = data_len;
+ c->c_offset = offset;
+ c->c_last = last;
+
+ queue_message_set(c->c_lu->l_to_transport, 0, msg_cmd_data_in,
+ (void *)c);
+ return (True);
+}
+
+/*
+ * []----
+ * | trans_rqst_dataout -- Request data from transport for command
+ * |
+ * | If the transport has indicated that data is immediately available,
+ * | which is common for iSCSI, then we'll copy that data into the buffer
+ * | and call the emulation modules datain function directly.
+ * []----
+ */
+Boolean_t
+trans_rqst_dataout(t10_cmd_t *cmd, char *data, size_t data_len, size_t offset,
+ emul_cmd_t emul_id)
+{
+ size_t max_xfer;
+
+ cmd->c_emul_id = emul_id;
+ /*
+ * Transport supports immediate data on writes. Currently
+ * on the iSCSI protocol has this feature.
+ * XXX Should all of this be done in the transport?
+ */
+ if (cmd->c_data_len) {
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO,
+ "SAM%x LUN%d DataOut rqst w/ immed, data_len %d",
+ cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num, data_len);
+#endif
+ if (cmd->c_data == NULL) {
+
+ /*
+ * When there's data available, but no buffer it
+ * means the transport has decided to leave the
+ * data on the socket and will read it in
+ * when called.
+ */
+ max_xfer = data_len;
+ assert(cmd->c_lu->l_targ->s_dataout_cb != NULL);
+ (*cmd->c_lu->l_targ->s_dataout_cb)(cmd, data,
+ &max_xfer);
+
+ } else {
+
+ /*
+ * The data is already in the command buffer so
+ * we need to copy it out.
+ */
+ max_xfer = MIN(cmd->c_data_len - cmd->c_resid,
+ data_len);
+ bcopy(cmd->c_data + cmd->c_resid, data, max_xfer);
+ cmd->c_resid = cmd->c_data_len - max_xfer;
+
+ /*
+ * It's expected since the transport allocated
+ * the space, this routine will free the memory
+ * instead.
+ */
+ (*cmd->c_lu->l_targ->s_dataout_cb)(cmd, data,
+ &max_xfer);
+ cmd->c_data = NULL;
+
+ }
+ cmd->c_data_len = 0;
+ (void) pthread_mutex_lock(&cmd->c_lu->l_cmd_mutex);
+ cmd->c_state = T10_Cmd_DataOut;
+ (void) pthread_mutex_unlock(&cmd->c_lu->l_cmd_mutex);
+ (*cmd->c_lu->l_data)(cmd, emul_id, offset, data, max_xfer);
+ return (True);
+ }
+
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO,
+ "SAM%x LUN%d DataOut Rqst data_len %d",
+ cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num, data_len);
+#endif
+
+ assert(cmd->c_data == NULL);
+
+ (void) pthread_mutex_lock(&cmd->c_lu->l_cmd_mutex);
+ cmd->c_state = T10_Cmd_DataOut;
+ (void) pthread_mutex_unlock(&cmd->c_lu->l_cmd_mutex);
+ cmd->c_data = data;
+ cmd->c_data_len = data_len;
+ cmd->c_offset = offset;
+ cmd->c_resid = 0;
+
+ /*
+ * Short cut. There's no reason to call the transport if the
+ * emulation code hasn't requested any data. If that's the
+ * case just call the emulation codes data function.
+ */
+ if (data_len == 0)
+ (*cmd->c_lu->l_data)(cmd, emul_id, offset, data, max_xfer);
+ else
+ queue_message_set(cmd->c_lu->l_to_transport, 0,
+ msg_cmd_data_rqst, (void *)cmd);
+ return (True);
+}
+
+/*
+ * []----
+ * | trans_send_complete -- notify transport command has finished.
+ * |
+ * | This routine is called either for when the emulation has completed
+ * | a command which doesn't have a data in phase so we can't use the 'last'
+ * | flag or there's been an error.
+ * | The sense data is expected to be created by calling spc_create_sense(),
+ * | the memory for that sense data will be freed when the transport calls
+ * | t10_destroy_cmd().
+ * |
+ * | NOTE [1]: If the t10_status equals STATUS_BUSY the command queue for this
+ * | ITL will be examined. If there are commands in progress the status will
+ * | be changed to STATUS_QFULL
+ * |
+ * | NOTE [2]: Do not access 'cmd' after calling this function. The transport
+ * | may receive the command, act on it, and then call
+ * | t10_cmd_state(cmd, T10_Cmd_Event_Release) before this function returns
+ * | thereby allowing 'cmd' to be freed and the space reallocated.
+ * []----
+ */
+void
+trans_send_complete(t10_cmd_t *cmd, int t10_status)
+{
+#ifdef FULL_DEBUG
+ struct scsi_extended_sense e;
+#endif
+
+ (void) pthread_mutex_lock(&cmd->c_lu->l_cmd_mutex);
+ cmd->c_state = T10_Cmd_Complete;
+
+ /*
+ * XXX Get the exact chapter and verse from the T10 documents.
+ * translate a STATUS_BUSY to STATUS_QFULL if there are outstanding
+ * commands in the queue.
+ */
+ if ((t10_status == STATUS_BUSY) &&
+ (avl_numnodes(&cmd->c_lu->l_cmds) != 0)) {
+ t10_status = STATUS_QFULL;
+ }
+
+ (void) pthread_mutex_unlock(&cmd->c_lu->l_cmd_mutex);
+
+ cmd->c_cmd_status = t10_status;
+ cmd->c_last = True;
+ cmd->c_data_len = 0;
+ cmd->c_data = 0;
+
+#ifdef FULL_DEBUG
+ if (t10_status != STATUS_GOOD) {
+ if (cmd->c_cmd_sense != NULL) {
+ bcopy(&cmd->c_cmd_sense[2], &e, sizeof (e));
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SAM%x LUN%d key_sense=0x%x, "
+ "ASC=0x%x, ASCQ=0x%x",
+ cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num,
+ e.es_key, e.es_add_code, e.es_qual_code);
+ } else {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SAM%x LUN%d key_sense=0x%x",
+ cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num, t10_status);
+ }
+ }
+#endif
+
+ queue_message_set(cmd->c_lu->l_to_transport, 0, msg_cmd_cmplt,
+ (void *)cmd);
+}
+
+void
+trans_aiowrite(t10_cmd_t *cmd, char *data, size_t data_len, off_t offset,
+ aio_result_t *aiop)
+{
+ t10_aio_t *taio;
+
+ if (aiowrite(cmd->c_lu->l_common->l_fd, data, data_len, offset, 0,
+ aiop) == -1) {
+ taio = (t10_aio_t *)aiop;
+ taio->a_aio.aio_return = -1;
+ (*taio->a_aio_cmplt)(taio->a_id);
+ } else
+ (void) sema_post(&t10_sema);
+}
+
+void
+trans_aioread(t10_cmd_t *cmd, char *data, size_t data_len, off_t offset,
+ aio_result_t *aiop)
+{
+ t10_aio_t *taio;
+
+ if (aioread(cmd->c_lu->l_common->l_fd, data, data_len, offset, 0,
+ aiop) == -1) {
+ taio = (t10_aio_t *)aiop;
+ taio->a_aio.aio_return = -1;
+ (*taio->a_aio_cmplt)(taio->a_id);
+ } else
+ (void) sema_post(&t10_sema);
+}
+
+/*
+ * []----
+ * | trans_params_area -- return dtype params using a command pointer
+ * |
+ * | Lock down the ITL structure from change so that we can cleanly access
+ * | the params area. This is needed to deal with the transport closing
+ * | a connection while commands are in flight. When those commands finish
+ * | cleanup work needs to be done. Yet, the logical unit common area
+ * | can already be released since it doesn't know there's something to wait
+ * | for.
+ * []----
+ */
+void *
+trans_params_area(t10_cmd_t *cmd)
+{
+ void *p = NULL;
+
+ (void) pthread_mutex_lock(&cmd->c_lu->l_mutex);
+ if (cmd->c_lu->l_common != NULL)
+ p = cmd->c_lu->l_common->l_dtype_params;
+ (void) pthread_mutex_unlock(&cmd->c_lu->l_mutex);
+ return (p);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Support routines for Routing and Task Management |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * []----
+ * | t10_find_lun -- Locate a per target LUN structure
+ * |
+ * | Finds per I_T_L structure. If this is the first time that this structure
+ * | has been accessed we allocate the structure and add it to the global
+ * | LUN structure. If that structure has never been accessed before it is
+ * | created along with a thread to handle the queue.
+ * []----
+ */
+/*ARGSUSED*/
+static t10_lu_impl_t *
+t10_find_lun(t10_targ_impl_t *t, int lun)
+{
+ t10_lu_impl_t *l = NULL,
+ search;
+ avl_index_t wc = 0, /* where common */
+ wt = 0; /* where target */
+ char *guid = NULL;
+ t10_lu_common_t lc,
+ *common = NULL;
+ xml_node_t *n = NULL,
+ *n1;
+ xmlTextReaderPtr r = NULL;
+ char path[MAXPATHLEN];
+ int xml_fd = -1;
+
+ bzero(&lc, sizeof (lc));
+
+ /*
+ * Only l_num is used by the AVL search routines so that's
+ * the only thing we'll set.
+ */
+ search.l_targ_lun = lun;
+
+ if ((l = avl_find(&t->s_open_lu, (void *)&search, &wt)) != NULL) {
+
+ /*
+ * This should be the normal fast path. At some point it
+ * might be good to look at optimizing this even more.
+ * If we know for example that the LUN numbers are sequential
+ * and there's fewer than 64 an array of pointers would be
+ * even faster than an AVL tree and not take up to much space.
+ */
+ return (l);
+ }
+
+ /*
+ * First access for this I_T_L so we need to allocate space for it.
+ */
+ if ((l = calloc(1, sizeof (*l))) == NULL)
+ return (NULL);
+
+ /*
+ * Initialize the various local fields. Certain fields will not be
+ * initialized until we've got the common LUN pointer.
+ */
+ (void) pthread_mutex_init(&l->l_cmd_mutex, NULL);
+ (void) pthread_mutex_init(&l->l_mutex, NULL);
+ (void) pthread_cond_init(&l->l_cmd_cond, NULL);
+ avl_create(&l->l_cmds, find_cmd_by_addr, sizeof (t10_cmd_t),
+ offsetof(t10_cmd_t, c_cmd_avl));
+
+ l->l_wait_for_drain = False;
+ l->l_to_transport = t->s_to_transport;
+ l->l_targ = t;
+ l->l_targ_lun = lun;
+
+ /*
+ * To locate the common LUN structure we need to find the GUID
+ * for this LUN. That's the only parsing this section of code will
+ * do to the params file.
+ */
+ (void) snprintf(path, sizeof (path), "%s/%s/%s%d",
+ target_basedir, t->s_targ_base, PARAMBASE, lun);
+
+ (void) pthread_mutex_lock(&lu_list_mutex);
+ if ((xml_fd = open(path, O_RDONLY)) < 0) {
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ goto error;
+ }
+ if ((r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd, NULL, NULL,
+ 0)) != NULL) {
+ while (xmlTextReaderRead(r) == 1)
+ if (xml_process_node(r, &n) == False)
+ break;
+ } else {
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ goto error;
+ }
+
+ (void) close(xml_fd);
+ xmlTextReaderClose(r);
+ xmlFreeTextReader(r);
+ r = NULL;
+
+ if (xml_find_value_str(n, XML_ELEMENT_GUID, &guid) == False) {
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ goto error;
+ }
+
+ if (strcmp(guid, "0") == 0) {
+ free(guid);
+ if (util_create_guid(&guid) == False) {
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ goto error;
+ }
+ if ((n1 = xml_find_child(n, XML_ELEMENT_GUID)) == NULL) {
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ goto error;
+ }
+ if (xml_update_value_str(n1, XML_ELEMENT_GUID, guid) == False) {
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ goto error;
+ }
+ if (xml_dump2file(n, path) == False) {
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ goto error;
+ }
+ }
+
+ if (xml_decode_bytes(guid, &lc.l_guid, &lc.l_guid_len) == False) {
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ goto error;
+ }
+
+ /*
+ * See if the common LUN for this GUID already exists.
+ */
+ wc = 0;
+ if ((common = avl_find(&lu_list, (void *)&lc, &wc)) == NULL) {
+
+ /*
+ * The GUID wasn't found, so create a new LUN structure
+ * and thread.
+ */
+ if ((common = calloc(1, sizeof (*common))) == NULL) {
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ goto error;
+ }
+
+ common->l_from_transports = queue_alloc();
+ common->l_num = lun;
+ common->l_internal_num = lu_id++;
+ common->l_guid = lc.l_guid;
+ common->l_guid_len = lc.l_guid_len;
+ common->l_fd = -1; /* not open yet */
+ common->l_mmap = MAP_FAILED;
+
+ (void) pthread_mutex_init(&common->l_common_mutex, NULL);
+
+ (void) snprintf(path, sizeof (path), "%s/%s", target_basedir,
+ t->s_targ_base);
+ if (t10_lu_initialize(common, path) == False) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SAM%x FAILED to initialize LU %d",
+ t->s_targ_num, lun);
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ goto error;
+ }
+
+ avl_create(&common->l_all_open, find_lu_by_targ,
+ sizeof (t10_lu_impl_t),
+ offsetof(t10_lu_impl_t, l_open_lu_node));
+
+ avl_insert(&lu_list, (void *)common, wc);
+ (void) pthread_create(&common->l_thr_id, NULL, lu_runner,
+ (void *)common);
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "SAM%x LU[%d.%d] Created new LU thread 0x%x",
+ t->s_targ_num, common->l_internal_num, common->l_num,
+ common->l_thr_id);
+
+ } else {
+
+ /*
+ * If there's a common LU structure already we free
+ * the guid which was created for the search. If an error
+ * occurs the guid space will be freed in the error handling
+ * code. If a new LU is created though we don't free the guid
+ * since the LU needs the information.
+ */
+ free(lc.l_guid);
+ lc.l_guid = NULL;
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "SAM%x Found existing LU[%d.%d]", t->s_targ_num,
+ common->l_internal_num, common->l_num);
+ }
+ (void) pthread_mutex_lock(&common->l_common_mutex);
+ (void) avl_find(&common->l_all_open, (void *)l, &wc);
+ avl_insert(&common->l_all_open, (void *)l, wc);
+ (void) pthread_mutex_unlock(&common->l_common_mutex);
+
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+
+ /*
+ * Now add this I_T_L to the targets list of open LUNs so that
+ * in the future we can get access through the AVL tree.
+ * We wait to add the LU to the target list until now so that we don't
+ * have to delete the node in case an error occurs.
+ */
+ avl_insert(&t->s_open_lu, (void *)l, wt);
+
+ (void) pthread_mutex_lock(&l->l_mutex);
+ l->l_common = common;
+ (void) pthread_mutex_unlock(&l->l_mutex);
+
+ /*
+ * The common LU thread is responsible for filling in the command
+ * functions and table.
+ */
+ queue_message_set(common->l_from_transports, 0, msg_lu_add, (void *)l);
+
+ free(guid);
+ xml_tree_free(n);
+
+ return (l);
+
+error:
+ if (guid)
+ free(guid);
+ if (xml_fd != -1)
+ (void) close(xml_fd);
+ if (r) {
+ xmlTextReaderClose(r);
+ xmlFreeTextReader(r);
+ }
+ if (n)
+ xml_tree_free(n);
+ if (l)
+ free(l);
+ if (lc.l_guid)
+ free(lc.l_guid);
+ if (common)
+ free(common);
+ return (NULL);
+}
+
+static Boolean_t
+t10_lu_initialize(t10_lu_common_t *lu, char *basedir)
+{
+ char *str = NULL;
+
+ if (load_params(lu, basedir) == False)
+ return (False);
+
+ if (xml_find_value_str(lu->l_root, XML_ELEMENT_DTYPE, &str) == True) {
+ if (strcmp(str, TGT_TYPE_DISK) == 0) {
+ lu->l_dtype = DTYPE_DIRECT;
+ if (sbc_init_common(lu) == False)
+ goto error;
+ } else if (strcmp(str, TGT_TYPE_RAW) == 0) {
+ lu->l_dtype = DTYPE_UNKNOWN;
+ if (raw_init_common(lu) == False)
+ goto error;
+ } else if (strcmp(str, TGT_TYPE_TAPE) == 0) {
+ lu->l_dtype = DTYPE_SEQUENTIAL;
+ if (ssc_init_common(lu) == False)
+ goto error;
+ } else if (strcmp(str, TGT_TYPE_OSD) == 0) {
+ lu->l_dtype = DTYPE_OSD;
+ if (osd_init_common(lu) == False)
+ goto error;
+ }
+ if (lu->l_dtype_params == NULL)
+ goto error;
+ free(str);
+ } else
+ goto error;
+
+ return (True);
+error:
+ if (str != NULL)
+ free(str);
+ return (False);
+}
+
+/*
+ * []----
+ * | lu_runner -- The workhorse for each LU
+ * |
+ * | This routine is the guts of the Task Router and Task Set for SAM-3.
+ * []----
+ */
+static void *
+lu_runner(void *v)
+{
+ t10_lu_common_t *lu = (t10_lu_common_t *)v;
+ msg_t *m;
+ t10_lu_impl_t *itl;
+ t10_cmd_t *cmd;
+ char *data,
+ *path;
+ size_t data_len,
+ new_size,
+ offset;
+ ssize_t cc;
+ void *provo_err;
+ t10_shutdown_t *s;
+
+ util_title(mgmtq, Q_STE_NONIO, lu->l_internal_num, "Start LU");
+ while ((m = queue_message_get(lu->l_from_transports)) != NULL) {
+
+ switch (m->msg_type) {
+ case msg_cmd_send:
+ cmd = (t10_cmd_t *)m->msg_data;
+ if (cmd->c_lu->l_status) {
+ spc_sense_create(cmd, cmd->c_lu->l_status, 0);
+ spc_sense_ascq(cmd, cmd->c_lu->l_asc,
+ cmd->c_lu->l_ascq);
+ /*
+ * Clear out the per LU values before
+ * calling trans_send_complete(). It's
+ * possible for the transport to handle
+ * this command and free it before returning.
+ */
+ cmd->c_lu->l_status = 0;
+ cmd->c_lu->l_asc = 0;
+ cmd->c_lu->l_ascq = 0;
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ lu->l_curr = cmd;
+ (*cmd->c_lu->l_cmd)(cmd, cmd->c_cdb,
+ cmd->c_cdb_len);
+ lu->l_curr = NULL;
+ }
+ break;
+
+ case msg_cmd_data_out:
+ cmd = (t10_cmd_t *)m->msg_data;
+ data = cmd->c_data;
+ data_len = cmd->c_data_len;
+ offset = cmd->c_offset;
+
+ /*
+ * We clear the c_data_len here because if the
+ * emulation routine processes the data and still
+ * needs more it will call trans_rqst_datain()
+ * which will look at c_data_len to see if there
+ * was immediate data available from the transport.
+ * In this case we've already processed the data
+ * and need to request more from the transport.
+ * c_data is set to NULL because there's an assert
+ * in trans_rqst_datain() checking that c_data is
+ * indeed null.
+ */
+ cmd->c_data_len = 0;
+ cmd->c_data = NULL;
+
+ lu->l_curr = cmd;
+ (*cmd->c_lu->l_data)(cmd, cmd->c_emul_id,
+ offset, data, data_len);
+ lu->l_curr = NULL;
+ break;
+
+ case msg_lu_add:
+ itl = (t10_lu_impl_t *)m->msg_data;
+ switch (lu->l_dtype) {
+ case DTYPE_DIRECT:
+ sbc_init_per(itl);
+ break;
+
+ case DTYPE_SEQUENTIAL:
+ ssc_init_per(itl);
+ break;
+
+ case DTYPE_OSD:
+ osd_init_per(itl);
+ break;
+
+ case DTYPE_UNKNOWN:
+ raw_init_per(itl);
+ break;
+ }
+ break;
+
+ case msg_reset_lu:
+ queue_reset(lu->l_from_transports);
+ (void) pthread_mutex_lock(&lu->l_common_mutex);
+ itl = avl_first(&lu->l_all_open);
+ do {
+ /*
+ * The current implementation is that we
+ * have a shared queue for each LU. That means
+ * if we reset a LU all I_T nexus' must
+ * receive a CHECK_CONDITION on their next
+ * command.
+ */
+ switch (lu->l_dtype) {
+ case DTYPE_DIRECT:
+ sbc_fini_per(itl);
+ sbc_init_per(itl);
+ break;
+
+ case DTYPE_SEQUENTIAL:
+ ssc_fini_per(itl);
+ ssc_init_per(itl);
+ break;
+
+ case DTYPE_UNKNOWN:
+ raw_fini_per(itl);
+ raw_init_per(itl);
+ break;
+
+ case DTYPE_OSD:
+ osd_fini_per(itl);
+ osd_init_per(itl);
+ }
+
+ itl = AVL_NEXT(&lu->l_all_open, itl);
+ } while (itl != NULL);
+ (void) pthread_mutex_unlock(&lu->l_common_mutex);
+ break;
+
+ case msg_shutdown:
+
+ s = (t10_shutdown_t *)m->msg_data;
+ itl = s->t_lu;
+ (void) pthread_mutex_lock(&lu_list_mutex);
+ (void) pthread_mutex_lock(&lu->l_common_mutex);
+ assert(avl_find(&lu->l_all_open, (void *)itl, NULL) !=
+ NULL);
+ avl_remove(&lu->l_all_open, (void *)itl);
+
+ queue_walker_free(lu->l_from_transports,
+ lu_remove_cmds, (void *)itl);
+ switch (lu->l_dtype) {
+ case DTYPE_DIRECT:
+ sbc_fini_per(itl);
+ break;
+
+ case DTYPE_SEQUENTIAL:
+ ssc_fini_per(itl);
+ break;
+
+ case DTYPE_OSD:
+ osd_fini_per(itl);
+ break;
+
+ case DTYPE_UNKNOWN:
+ raw_fini_per(itl);
+ break;
+ }
+ /*
+ * Don't remove reference to l_common area until after
+ * the emulation routines are finished since they
+ * are likely to reference l_dtype_params.
+ */
+ (void) pthread_mutex_lock(&itl->l_mutex);
+ itl->l_common = NULL;
+ (void) pthread_mutex_unlock(&itl->l_mutex);
+
+ queue_message_set(s->t_q, 0, msg_shutdown_rsp,
+ (void *)(uintptr_t)itl->l_targ_lun);
+ if (avl_numnodes(&lu->l_all_open) == 0) {
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "LU_%x No remaining targets for LU(%d)",
+ lu->l_internal_num, lu->l_fd);
+ if (lu->l_mmap != MAP_FAILED)
+ (void) munmap(lu->l_mmap,
+ lu->l_size);
+ if (close(lu->l_fd) != 0)
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "LU_%x Failed to close fd, "
+ "errno=%d", lu->l_internal_num,
+ errno);
+ switch (lu->l_dtype) {
+ case DTYPE_DIRECT:
+ sbc_fini_common(lu);
+ break;
+
+ case DTYPE_SEQUENTIAL:
+ ssc_fini_common(lu);
+ break;
+
+ case DTYPE_UNKNOWN:
+ raw_fini_common(lu);
+ break;
+ }
+ avl_remove(&lu_list, (void *)lu);
+ util_title(mgmtq, Q_STE_NONIO,
+ lu->l_internal_num, "End LU");
+ queue_free(lu->l_from_transports, NULL);
+ (void) pthread_mutex_unlock(
+ &lu->l_common_mutex);
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ xml_tree_free(lu->l_root);
+ free(lu->l_pid);
+ free(lu->l_vid);
+ free(lu->l_guid);
+ free(lu);
+ queue_message_free(m);
+ pthread_exit(NULL);
+ }
+ (void) pthread_mutex_unlock(&lu->l_common_mutex);
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+ break;
+
+ case msg_targ_inventory_change:
+ itl = (t10_lu_impl_t *)m->msg_data;
+ itl->l_status = KEY_UNIT_ATTENTION;
+ /*
+ * SPC-3 revision 21c, section 4.5.6, Table 28
+ * When LU inventory changes need to report
+ * a REPORTED LUNS DATA HAS CHANGED event.
+ */
+ itl->l_asc = 0x3f;
+ itl->l_ascq = 0x0e;
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "LU_%x Received InventoryChange for %d",
+ lu->l_internal_num, itl->l_common->l_num);
+ break;
+
+ case msg_thick_provo:
+ cmd = (t10_cmd_t *)m->msg_data;
+ if (lu->l_mmap != MAP_FAILED) {
+
+ /*
+ * If the file at c_offset is currently
+ * unallocated we'll read in that buffer
+ * which will be zeros and then write it
+ * back out which will force the underlying
+ * filesystem to allocate the blocks.
+ * If someone has already issued a write
+ * to this area we'll then just cause a
+ * useless, but safe read/write to occur.
+ */
+ lu->l_curr = cmd;
+ lu->l_curr_provo = True;
+ bcopy((char *)lu->l_mmap + cmd->c_offset,
+ cmd->c_data, cmd->c_data_len);
+ cmd->c_lu->l_cmds_read++;
+ cmd->c_lu->l_sects_read +=
+ cmd->c_data_len / 512;
+ bcopy(cmd->c_data,
+ (char *)lu->l_mmap + cmd->c_offset,
+ cmd->c_data_len);
+ cmd->c_lu->l_cmds_write++;
+ cmd->c_lu->l_sects_write +=
+ cmd->c_data_len / 512;
+ lu->l_curr = NULL;
+ lu->l_curr_provo = False;
+ provo_err = 0;
+
+ } else {
+ if ((cc = pread(lu->l_fd, cmd->c_data,
+ cmd->c_data_len, cmd->c_offset)) < 0) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "LU_%x pread errno=%d", lu->l_num,
+ errno);
+ } else if (pwrite(lu->l_fd, cmd->c_data, cc,
+ cmd->c_offset) != cc) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "LU_%x pwrite errno=%d",
+ lu->l_num, errno);
+ }
+ provo_err = (cc == cmd->c_data_len) ?
+ (void *)0 : (void *)1;
+ }
+ /*
+ * acknowledge this op and wait for next
+ */
+ queue_message_set(cmd->c_lu->l_to_transport, 0,
+ msg_thick_provo, provo_err);
+ break;
+
+ case msg_lu_capacity_change:
+ new_size = lseek(lu->l_fd, 0, SEEK_END);
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "LU_%x Capacity Change from 0x%llx to 0x%llx",
+ lu->l_internal_num, lu->l_size, new_size);
+ if ((path = malloc(MAXPATHLEN)) == NULL)
+ break;
+
+ (void) snprintf(path, MAXPATHLEN, "%s/%s",
+ target_basedir, itl->l_targ->s_targ_base);
+ (void) load_params(lu, path);
+ free(path);
+ switch (lu->l_dtype) {
+ case DTYPE_DIRECT:
+ sbc_task_mgmt(lu, CapacityChange);
+ break;
+
+ case DTYPE_SEQUENTIAL:
+ ssc_task_mgmt(lu, CapacityChange);
+ break;
+ }
+ (void) pthread_mutex_lock(&lu->l_common_mutex);
+ itl = avl_first(&lu->l_all_open);
+ while (itl != NULL) {
+ itl->l_status = KEY_UNIT_ATTENTION;
+ itl->l_asc = SPC_ASC_CAP_CHANGE;
+ itl->l_ascq = SPC_ASCQ_CAP_CHANGE;
+ itl = AVL_NEXT(&lu->l_all_open, itl);
+ }
+ (void) pthread_mutex_unlock(&lu->l_common_mutex);
+ break;
+
+ case msg_lu_online:
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "LU_%x Received online event", lu->l_internal_num);
+ if ((path = malloc(MAXPATHLEN)) == NULL)
+ break;
+
+ (void) pthread_mutex_lock(&lu->l_common_mutex);
+ itl = avl_first(&lu->l_all_open);
+ (void) pthread_mutex_unlock(&lu->l_common_mutex);
+ (void) snprintf(path, MAXPATHLEN, "%s/%s",
+ target_basedir, itl->l_targ->s_targ_base);
+ (void) load_params(lu, path);
+ free(path);
+ switch (lu->l_dtype) {
+ case DTYPE_DIRECT:
+ sbc_task_mgmt(lu, DeviceOnline);
+ break;
+
+ case DTYPE_SEQUENTIAL:
+ ssc_task_mgmt(lu, DeviceOnline);
+ break;
+ }
+ (void) pthread_mutex_lock(&lu->l_common_mutex);
+ itl = avl_first(&lu->l_all_open);
+ while (itl != NULL) {
+ switch (lu->l_dtype) {
+ case DTYPE_DIRECT:
+ sbc_init_per(itl);
+ break;
+
+ case DTYPE_SEQUENTIAL:
+ ssc_init_per(itl);
+ break;
+ }
+ itl = AVL_NEXT(&lu->l_all_open, itl);
+ }
+ (void) pthread_mutex_unlock(&lu->l_common_mutex);
+ break;
+
+ }
+ queue_message_free(m);
+ }
+
+ return (NULL);
+}
+
+/*
+ * []----
+ * | lu_buserr_handler -- deal with SIGBUS on mmap'd files
+ * |
+ * | Normally SIGBUS's are a real bad thing. With this project, which uses
+ * | mmap'd files that start out as hole-y, can represent more space than
+ * | the underlying storage has available. This is good and considered a
+ * | feature for "Thin Provisioning". However, this means that if the
+ * | administrator isn't on the ball the storage can fill up. Because of the
+ * | asynchronous nature of writing to a mmap'd file the OS will send a SIGBUS
+ * | to the thread which caused the problem. The thread will then locate its
+ * | data structure and in turn signal the initiator that a problem occurred.
+ * | Since we can't restart we're we left off because the out of space
+ * | condition is still present another thread is started to handle other
+ * | commands for the logical unit. The current thread will then exit.
+ * |
+ * | NOTE:
+ * | If for any reason this routine doesn't find what's it's expecting to
+ * | assert() will be called to create a core. This routine will only recover
+ * | from the expected case of a SIGBUS, otherwise something real bad has
+ * | happened and we need to see the core.
+ * []----
+ */
+/*ARGSUSED*/
+void
+lu_buserr_handler(int sig, siginfo_t *sip, void *v)
+{
+ t10_lu_common_t *lu;
+ pthread_t id = pthread_self();
+ char *fa;
+
+ if (pthread_mutex_trylock(&lu_list_mutex) != 0) {
+ assert(0);
+ return;
+ }
+ lu = avl_first(&lu_list);
+ while (lu != NULL) {
+ if (lu->l_thr_id == id)
+ break;
+ lu = AVL_NEXT(&lu_list, lu);
+ }
+ (void) pthread_mutex_unlock(&lu_list_mutex);
+
+ if ((lu == NULL) || (lu->l_curr == NULL)) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SAM%x BUS ERROR and couldn't find logical unit",
+ lu->l_num);
+ assert(0);
+ return;
+ }
+
+ if (lu->l_mmap == MAP_FAILED) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SAM%x BUS ERROR and device not mmap'd", lu->l_num);
+ assert(0);
+ return;
+ }
+
+ fa = (char *)sip->__data.__fault.__addr;
+ if ((fa < (char *)lu->l_mmap) ||
+ (fa > ((char *)lu->l_mmap + lu->l_size))) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SAM%x BUS ERROR occurred outsize of mmap bounds",
+ lu->l_num);
+ assert(0);
+ return;
+ }
+
+ if (lu->l_curr_provo == True) {
+ lu->l_curr_provo = False;
+ queue_message_set(lu->l_curr->c_lu->l_to_transport, 0,
+ msg_thick_provo, (void *)1);
+ } else {
+ spc_sense_create(lu->l_curr, KEY_MEDIUM_ERROR, 0);
+ spc_sense_ascq(lu->l_curr, SPC_ASC_WRITE_ERROR,
+ SPC_ASCQ_WRITE_ERROR);
+ trans_send_complete(lu->l_curr, STATUS_CHECK);
+ }
+
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SAM%x Caught an out-of-space issue", lu->l_num);
+
+ /*
+ * Now restart another thread to pick up where we've left off with
+ * processing commands for this logical unit.
+ */
+ (void) pthread_create(&lu->l_thr_id, NULL, lu_runner, (void *)lu);
+ pthread_exit((void *)0);
+}
+
+/*
+ * []----
+ * | lu_remove_cmds -- look for and free commands for a given ITL
+ * []----
+ */
+static Boolean_t
+lu_remove_cmds(msg_t *m, void *v)
+{
+ t10_lu_impl_t *lu = (t10_lu_impl_t *)v;
+ t10_cmd_t *c;
+
+ switch (m->msg_type) {
+ case msg_cmd_send:
+ case msg_cmd_data_out:
+ c = (t10_cmd_t *)m->msg_data;
+ if (c->c_lu == lu) {
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "SAM%x LUN %d, removed command during lu_remove",
+ c->c_lu->l_targ->s_targ_num, lu->l_common->l_num);
+ t10_cmd_state(c, T10_Cmd_Event_Release);
+ return (True);
+ }
+ break;
+ }
+ return (False);
+}
+
+/*
+ * []----
+ * | load_params -- load parameters and open LU backing store
+ * |
+ * | This routine can be called multiple times and will free and release
+ * | previous resources.
+ * []----
+ */
+static Boolean_t
+load_params(t10_lu_common_t *lu, char *basedir)
+{
+ char file[MAXPATHLEN],
+ *str;
+ int oflags = O_RDWR|O_LARGEFILE|O_NDELAY;
+ Boolean_t mmap_lun = True;
+ xml_node_t *node = NULL;
+ int version_maj = XML_VERS_LUN_MAJ,
+ version_min = XML_VERS_LUN_MIN,
+ xml_fd;
+ xmlTextReaderPtr r;
+
+ /*
+ * Clean up from previous call to this function. This occurs if
+ * the LU has grown since it was last opened.
+ */
+ if (lu->l_mmap != MAP_FAILED)
+ munmap(lu->l_mmap, lu->l_size);
+ if (lu->l_root != NULL) {
+ xml_tree_free(lu->l_root);
+ lu->l_root = NULL;
+ }
+ if (lu->l_fd != -1)
+ close(lu->l_fd);
+
+ (void) snprintf(file, sizeof (file), "%s/%s%d", basedir, PARAMBASE,
+ lu->l_num);
+
+ if ((xml_fd = open(file, O_RDONLY)) < 0)
+ return (False);
+ if ((r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd, NULL, NULL,
+ 0)) != NULL) {
+ while (xmlTextReaderRead(r) == 1)
+ if (xml_process_node(r, &node) == False)
+ break;
+ lu->l_root = node;
+ } else
+ return (False);
+
+ (void) close(xml_fd);
+ xmlTextReaderClose(r);
+ xmlFreeTextReader(r);
+
+ if (validate_version(node, &version_maj, &version_min) == False)
+ goto error;
+
+ if (xml_find_value_str(node, XML_ELEMENT_PID, &lu->l_pid) == False)
+ goto error;
+
+ if (xml_find_value_str(node, XML_ELEMENT_VID, &lu->l_vid) == False)
+ goto error;
+
+ /*
+ * If there's no <status> tag it just means this is an older param
+ * file and there's no need to treat it as an error. Just mark
+ * the device as online.
+ */
+ if (xml_find_value_str(node, XML_ELEMENT_STATUS, &str) == True) {
+ if (strcmp(str, TGT_STATUS_ONLINE) == 0)
+ lu->l_state = lu_online;
+ else if (strcmp(str, TGT_STATUS_OFFLINE) == 0)
+ lu->l_state = lu_offline;
+ else if (strcmp(str, TGT_STATUS_ERRORED) == 0)
+ lu->l_state = lu_errored;
+ } else
+ lu->l_state = lu_online;
+ free(str);
+
+ /*
+ * If offline, we need to check to see if there's an initialization
+ * thread running for this lun. If not, start one.
+ */
+ if ((lu->l_state == lu_offline) &&
+ (thick_provo_chk_thr(strrchr(basedir, '/') + 1, lu->l_num) ==
+ False)) {
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "LU_%d No initialization thread running", lu->l_num);
+ if (thin_provisioning == False) {
+ thick_provo_t *tp;
+ pthread_t junk;
+
+ if ((tp = calloc(1, sizeof (*tp))) != NULL) {
+ tp->targ_name = strdup(strrchr(basedir, '/')) +
+ 1;
+ tp->lun = lu->l_num;
+ tp->q = queue_alloc();
+ (void) pthread_create(&junk, NULL,
+ thick_provo_start, tp);
+ /* ---- wait for start message ---- */
+ queue_message_free(queue_message_get(tp->q));
+ }
+ }
+ }
+
+ /*
+ * The default is to disable the fast write acknowledgement which
+ * can be overridden in a couple of ways. First, see if the global
+ * fast-write-ack is enabled, then check the per logical unit flags.
+ * The per LU bit is settable via a SCSI command.
+ */
+ lu->l_fast_write_ack = False;
+ (void) xml_find_value_boolean(main_config, XML_ELEMENT_FAST,
+ &lu->l_fast_write_ack);
+ (void) xml_find_value_boolean(node, XML_ELEMENT_FAST,
+ &lu->l_fast_write_ack);
+ if (lu->l_fast_write_ack == False)
+ oflags |= O_SYNC;
+
+ /*
+ * Object-based Storage Devices currently use directories to
+ * represent the partitions and files in those directories to
+ * represent user objects and collections. Therefore, there's
+ * not just a single file to be opened, but potentially thousands.
+ * Therefore, stop here if we've got an OSD dtype.
+ */
+ if (xml_find_value_str(node, XML_ELEMENT_DTYPE, &str) == False)
+ goto error;
+ if (strcmp(str, TGT_TYPE_OSD) == 0) {
+ free(str);
+ return (True);
+ } else
+ free(str);
+
+ (void) snprintf(file, sizeof (file), "%s/%s%d", basedir, LUNBASE,
+ lu->l_num);
+ if ((lu->l_fd = open(file, oflags)) == -1)
+ goto error;
+
+#ifndef _LP64
+ /*
+ * Since the address space is so limited on 32bit machines
+ * disable mmap'ing the file by default.
+ */
+ mmap_lun = False;
+#endif
+ (void) xml_find_value_boolean(node, XML_ELEMENT_MMAP_LUN, &mmap_lun);
+ if (xml_find_value_str(node, XML_ELEMENT_SIZE, &str) == True) {
+ lu->l_size = strtoll(str, NULL, 0) * 512LL;
+ free(str);
+ } else
+ goto error;
+
+ if (mmap_lun == True) {
+ /*
+ * st_size will be wrong if the device is a block device
+ * but that's okay since you can't mmap in a block device.
+ * A block device will fall back to using AIO operations.
+ */
+ lu->l_mmap = mmap(0, lu->l_size, PROT_READ|PROT_WRITE,
+ MAP_SHARED|MAP_ALIGN, lu->l_fd, 0);
+ } else {
+
+ /*
+ * Since the default case will be to mmap
+ * in all files someone has asked that this
+ * lun not be mmap.
+ */
+ lu->l_mmap = MAP_FAILED;
+ }
+ return (True);
+error:
+ if (lu->l_root)
+ xml_tree_free(lu->l_root);
+ if (lu->l_pid)
+ free(lu->l_pid);
+ if (lu->l_vid)
+ free(lu->l_vid);
+ if (lu->l_fd != -1)
+ (void) close(lu->l_fd);
+ return (False);
+}
+
+/*
+ * []----
+ * | cmd_common_free -- frees data stored in the cmd used in two different spots
+ * |
+ * | NOTE: The mutex which protects c_state must be held when this routine
+ * | is called.
+ * []----
+ */
+static void
+cmd_common_free(t10_lu_impl_t *lu, t10_cmd_t *cmd)
+{
+ if (cmd->c_state != T10_Cmd_Free) {
+ avl_remove(&lu->l_cmds, cmd);
+ cmd->c_state = T10_Cmd_Free;
+ }
+
+ cmd->c_data = 0;
+ cmd->c_data_len = 0;
+
+ if (cmd->c_emul_complete != NULL) {
+ (*cmd->c_emul_complete)(cmd->c_emul_id);
+ cmd->c_emul_complete = NULL;
+ }
+ if (cmd->c_cdb) {
+ free(cmd->c_cdb);
+ cmd->c_cdb = NULL;
+ }
+ if (cmd->c_cmd_sense) {
+ free(cmd->c_cmd_sense);
+ cmd->c_cmd_sense = NULL;
+ }
+}
+
+/*
+ * []----
+ * | fallocate -- allocate blocks for file via file system interface
+ * |
+ * | This is a faster approach to allocating the blocks for a file.
+ * | Instead of reading and then writing each block which will force the
+ * | file system to allocate the data we simply ask the file system to
+ * | allocate the space. Unfortunately not all file systems support this
+ * | feature.
+ * []----
+ */
+static Boolean_t
+fallocate(int fd, off64_t len)
+{
+#ifdef FALLOCATE_SUPPORTED
+#if defined(_LARGEFILE64_SOURCE) && !defined(_LP64)
+ struct flock64 lck;
+
+ lck.l_whence = 0;
+ lck.l_start = 0;
+ lck.l_len = len;
+ lck.l_type = F_WRLCK;
+
+ if (fcntl(fd, F_ALLOCSP64, &lck) == -1)
+ return (False);
+ else
+ return (True);
+#else
+ struct flock lck;
+
+ lck.l_whence = 0;
+ lck.l_start = 0;
+ lck.l_len = len;
+ lck.l_type = F_WRLCK;
+
+ if (fcntl(fd, F_ALLOCSP, &lck) == -1)
+ return (False);
+ else
+ return (True);
+#endif
+#else
+ return (False);
+#endif
+}
+
+/*
+ * []----
+ * | find_lu_by_num -- AVL comparison which looks at LUN
+ * []----
+ */
+static int
+find_lu_by_num(const void *v1, const void *v2)
+{
+ t10_lu_impl_t *l1 = (t10_lu_impl_t *)v1,
+ *l2 = (t10_lu_impl_t *)v2;
+
+ if (l1->l_targ_lun < l2->l_targ_lun)
+ return (-1);
+ if (l1->l_targ_lun > l2->l_targ_lun)
+ return (1);
+ return (0);
+}
+
+/*
+ * []----
+ * | find_lu_by_guid -- AVL comparison which looks at GUID
+ * []----
+ */
+static int
+find_lu_by_guid(const void *v1, const void *v2)
+{
+ t10_lu_common_t *l1 = (t10_lu_common_t *)v1,
+ *l2 = (t10_lu_common_t *)v2;
+ int i;
+
+ if (l1->l_guid_len != l2->l_guid_len) {
+ return ((l1->l_guid_len < l2->l_guid_len) ? -1 : 1);
+ }
+ for (i = 0; i < l1->l_guid_len; i++) {
+ if (l1->l_guid[i] != l2->l_guid[i]) {
+ return ((l1->l_guid[i] < l2->l_guid[i]) ? -1 : 1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * []----
+ * | find_lu_by_targ -- AVL comparison which looks at the target
+ * |
+ * | NOTE:
+ * | The target value is the memory address of the per target structure.
+ * | Therefore, it's not persistent in any manner, nor can any association
+ * | be made between the target value and the initiator. It will be unique
+ * | however which is all that we're looking for.
+ * []----
+ */
+static int
+find_lu_by_targ(const void *v1, const void *v2)
+{
+ t10_lu_impl_t *l1 = (t10_lu_impl_t *)v1,
+ *l2 = (t10_lu_impl_t *)v2;
+
+ if ((uint64_t)(uintptr_t)l1->l_targ < (uint64_t)(uintptr_t)l2->l_targ)
+ return (-1);
+ else if ((uint64_t)(uintptr_t)l1->l_targ >
+ (uint64_t)(uintptr_t)l2->l_targ)
+ return (1);
+ else
+ return (0);
+}
+
+/*
+ * []----
+ * | find_cmd_by_addr -- AVL comparison using the simplist of methods
+ * []----
+ */
+static int
+find_cmd_by_addr(const void *v1, const void *v2)
+{
+ uint64_t cmd1 = (uint64_t)(uintptr_t)v1,
+ cmd2 = (uint64_t)(uintptr_t)v2;
+
+ if (cmd1 < cmd2)
+ return (-1);
+ else if (cmd1 > cmd2)
+ return (1);
+ else
+ return (0);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c b/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c
new file mode 100644
index 0000000000..daae15a17e
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c
@@ -0,0 +1,1865 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Implementation of SBC-2 emulation |
+ * []------------------------------------------------------------------[]
+ */
+#include <sys/types.h>
+#include <aio.h>
+#include <sys/asynch.h>
+#include <sys/mman.h>
+#include <stddef.h>
+#include <strings.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/generic/inquiry.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/generic/mode.h>
+#include <sys/scsi/generic/dad_mode.h>
+
+#include "t10.h"
+#include "t10_spc.h"
+#include "t10_sbc.h"
+#include "utility.h"
+
+/*
+ * Forward declarations
+ */
+static int sbc_mmap_overlap(const void *v1, const void *v2);
+static void sbc_overlap_store(disk_io_t *io);
+static void sbc_overlap_free(disk_io_t *io);
+static void sbc_overlap_check(disk_io_t *io);
+static void sbc_overlap_flush(disk_params_t *d);
+static void sbc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+static void sbc_data(t10_cmd_t *cmd, emul_handle_t e, size_t offset,
+ char *data, size_t data_len);
+static disk_io_t *sbc_io_alloc(t10_cmd_t *c);
+static void sbc_io_free(emul_handle_t e);
+static void sbc_read_cmplt(emul_handle_t e);
+static void sbc_write_cmplt(emul_handle_t e);
+static void sbc_read_capacity16(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+static char *sense_page3(disk_params_t *d, char *buf);
+static char *sense_page4(disk_params_t *d, char *buf);
+static char *sense_cache(disk_params_t *d, char *buf);
+static char *sense_mode_control(t10_lu_impl_t *lu, char *buf);
+static char *sense_info_ctrl(char *buf);
+static scsi_cmd_table_t lba_table[];
+
+/*
+ * []----
+ * | sbc_init_common -- Initialize LU data which is common to all I_T_Ls
+ * []----
+ */
+Boolean_t
+sbc_init_common(t10_lu_common_t *lu)
+{
+ disk_params_t *d;
+ xml_node_t *node = lu->l_root;
+
+ if ((d = (disk_params_t *)calloc(1, sizeof (*d))) == NULL)
+ return (False);
+
+ (void) xml_find_value_int(node, XML_ELEMENT_BPS,
+ (int *)&d->d_bytes_sect);
+ (void) xml_find_value_int(node, XML_ELEMENT_HEADS,
+ (int *)&d->d_heads);
+ (void) xml_find_value_int(node, XML_ELEMENT_SPT,
+ (int *)&d->d_spt);
+ (void) xml_find_value_int(node, XML_ELEMENT_CYLINDERS,
+ (int *)&d->d_cyl);
+ (void) xml_find_value_int(node, XML_ELEMENT_RPM,
+ (int *)&d->d_rpm);
+ (void) xml_find_value_int(node, XML_ELEMENT_INTERLEAVE,
+ (int *)&d->d_interleave);
+ d->d_fast_write = lu->l_fast_write_ack;
+ d->d_size = lu->l_size / (uint64_t)d->d_bytes_sect;
+ d->d_state = lu->l_state;
+
+ avl_create(&d->d_mmap_overlaps, sbc_mmap_overlap,
+ sizeof (disk_io_t), offsetof(disk_io_t, da_mmap_overlap));
+ (void) pthread_mutex_init(&d->d_mutex, NULL);
+ (void) pthread_cond_init(&d->d_mmap_cond, NULL);
+ (void) pthread_cond_init(&d->d_io_cond, NULL);
+ if ((d->d_io_reserved = (disk_io_t *)calloc(1, sizeof (disk_io_t))) ==
+ NULL) {
+ free(d);
+ return (False);
+ }
+
+ lu->l_dtype_params = (void *)d;
+ return (True);
+}
+
+void
+sbc_fini_common(t10_lu_common_t *lu)
+{
+ disk_params_t *d = lu->l_dtype_params;
+
+ sbc_overlap_flush(d);
+ avl_destroy(&d->d_mmap_overlaps);
+ free(d->d_io_reserved);
+ free(lu->l_dtype_params);
+}
+
+void
+sbc_task_mgmt(t10_lu_common_t *lu, TaskOp_t op)
+{
+ disk_params_t *d = (disk_params_t *)lu->l_dtype_params;
+
+ switch (op) {
+ case CapacityChange:
+ d->d_size = lu->l_size / (uint64_t)d->d_bytes_sect;
+ break;
+
+ case DeviceOnline:
+ d->d_state = lu->l_state;
+ break;
+ }
+}
+
+/*
+ * []----
+ * | sbc_init_per -- Initialize per I_T_L information
+ * []----
+ */
+void
+sbc_init_per(t10_lu_impl_t *itl)
+{
+ disk_params_t *d = (disk_params_t *)itl->l_common->l_dtype_params;
+
+ if (d->d_state == lu_online)
+ itl->l_cmd = sbc_cmd;
+ else
+ itl->l_cmd = spc_cmd_offline;
+ itl->l_data = sbc_data;
+ itl->l_cmd_table = lba_table;
+
+ /*
+ * The first time an I_T nexus connects to a LU it is supposed
+ * to receive an unit attention upon the first command sent.
+ */
+ itl->l_status = KEY_UNIT_ATTENTION;
+ itl->l_asc = 0x29;
+ itl->l_ascq = 0x01;
+}
+
+void
+sbc_fini_per(t10_lu_impl_t *itl)
+{
+ disk_params_t *d = (disk_params_t *)itl->l_common->l_dtype_params;
+ t10_lu_impl_t *lu;
+
+ if (d->d_reserve_owner == itl) {
+
+ /*
+ * Since we currently own the reservation, drop it,
+ * and restore everyone elses command pointer.
+ */
+ lu = avl_first(&itl->l_common->l_all_open);
+ do {
+ lu->l_cmd = sbc_cmd;
+ lu = AVL_NEXT(&itl->l_common->l_all_open, lu);
+ } while (lu != NULL);
+ d->d_reserve_owner = NULL;
+ }
+}
+
+/*
+ * []----
+ * | sbc_cmd -- start a SCSI command
+ * |
+ * | This routine is called from within the SAM-3 Task router.
+ * []----
+ */
+static void
+sbc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ scsi_cmd_table_t *e;
+
+ e = &cmd->c_lu->l_cmd_table[cdb[0]];
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d Cmd %s\n",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ e->cmd_name == NULL ? "(no name)" : e->cmd_name);
+#endif
+ (*e->cmd_start)(cmd, cdb, cdb_len);
+}
+
+/*
+ * []----
+ * | sbc_cmd_reserve -- Run commands when another I_T_L has a reservation
+ * []----
+ */
+static void
+sbc_cmd_reserved(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ scsi_cmd_table_t *e;
+
+ switch (cdb[0]) {
+ case SCMD_TEST_UNIT_READY:
+ case SCMD_INQUIRY:
+ case SCMD_REPORT_LUNS:
+ case SCMD_LOG_SENSE_G1:
+ case SCMD_READ_MEDIA_SERIAL:
+ case SCMD_REPORT_TARGET_PORT_GROUPS:
+ case SCMD_REQUEST_SENSE:
+ /*
+ * SPC-2, revision 20, Section 5.5.1 table 10
+ * The specification allows these three commands
+ * to run even through there's a reservation in place.
+ */
+ e = &cmd->c_lu->l_cmd_table[cdb[0]];
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO, "RESERVED: SBC%x LUN%d Cmd %s\n",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ e->cmd_name == NULL ? "(no name)" : e->cmd_name);
+#endif
+ (*e->cmd_start)(cmd, cdb, cdb_len);
+ break;
+
+ default:
+ trans_send_complete(cmd, STATUS_RESERVATION_CONFLICT);
+ }
+}
+
+/*
+ * []----
+ * | sbc_data -- Data phase for command.
+ * |
+ * | Normally this is only called for the WRITE command. Other commands
+ * | that have a data in phase will probably be short circuited when
+ * | we call trans_rqst_dataout() and the data is already available.
+ * | At least this is true for iSCSI. FC however will need a DataIn phase
+ * | for commands like MODE SELECT and PGROUT.
+ * []----
+ */
+static void
+sbc_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+ size_t data_len)
+{
+ scsi_cmd_table_t *e;
+
+ e = &cmd->c_lu->l_cmd_table[cmd->c_cdb[0]];
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d Data %s\n",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ e->cmd_name);
+#endif
+ (*e->cmd_data)(cmd, id, offset, data, data_len);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SCSI Block Commands - 2 |
+ * | T10/1417-D |
+ * | The following functions implement the emulation of SBC-2 type |
+ * | commands. |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * []----
+ * | sbc_read -- emulation of SCSI READ command
+ * []----
+ */
+/*ARGSUSED*/
+static void
+sbc_read(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ /*LINTED*/
+ union scsi_cdb *u = (union scsi_cdb *)cdb;
+ diskaddr_t addr;
+ off_t offset = 0;
+ uint32_t cnt,
+ min;
+ disk_io_t *io;
+ void *mmap_data = T10_MMAP_AREA(cmd);
+ uint64_t err_blkno;
+ disk_params_t *d;
+ uchar_t addl_sense_len;
+
+ if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ switch (u->scc_cmd) {
+ case SCMD_READ:
+ /*
+ * SBC-2 Revision 16, section 5.5
+ * Reserve bit checks
+ */
+ if ((cdb[1] & 0xe0) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ addr = (diskaddr_t)(uint32_t)GETG0ADDR(u);
+ cnt = GETG0COUNT(u);
+
+ /*
+ * SBC-2 Revision 16
+ * Section: 5.5 READ(6) command
+ * A TRANSFER LENGTH field set to zero specifies
+ * that 256 logical blocks shall be read.
+ */
+ if (cnt == 0)
+ cnt = 256;
+ break;
+
+ case SCMD_READ_G1:
+ /*
+ * SBC-2 Revision 16, section 5.6
+ * Reserve bit checks.
+ */
+ if ((cdb[1] & 6) || cdb[6] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[9])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ addr = (diskaddr_t)(uint32_t)GETG1ADDR(u);
+ cnt = GETG1COUNT(u);
+ break;
+
+ case SCMD_READ_G4:
+ /*
+ * SBC-2 Revision 16, section 5.8
+ * Reserve bit checks
+ */
+ if ((cdb[1] & 0x6) || cdb[14] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[15])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ addr = GETG4LONGADDR(u);
+ cnt = GETG4COUNT(u);
+ break;
+
+ default:
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if ((addr + cnt) > d->d_size) {
+
+ if (addr > d->d_size)
+ err_blkno = addr;
+ else
+ err_blkno = d->d_size;
+
+ /*
+ * XXX: What's SBC-2 say about ASC/ASCQ here. Solaris
+ * doesn't care about these values when key is set
+ * to KEY_ILLEGAL_REQUEST.
+ */
+ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN)
+ addl_sense_len = INFORMATION_SENSE_DESCR;
+ else
+ addl_sense_len = 0;
+
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, addl_sense_len);
+ spc_sense_info(cmd, err_blkno);
+ spc_sense_ascq(cmd, 0x21, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SBC%x LUN%d READ Illegal sector "
+ "(0x%llx + 0x%x) > 0x%ullx", cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num, addr, cnt, d->d_size);
+ return;
+ }
+
+ cmd->c_lu->l_cmds_read++;
+ cmd->c_lu->l_sects_read += cnt;
+
+ if (cnt == 0) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ do {
+ io = sbc_io_alloc(cmd);
+ min = MIN((cnt * 512) - offset, T10_MAX_OUT(cmd));
+
+ io->da_lba = addr;
+ io->da_lba_cnt = cnt;
+ io->da_offset = offset;
+ io->da_data_len = min;
+
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO,
+ "SBC%x LUN%d blk 0x%llx, cnt %d, offset 0x%llx, size %d",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ addr, cnt, io->da_offset, min);
+#endif
+ if (mmap_data != MAP_FAILED) {
+
+ io->da_clear_overlap = True;
+ io->da_data_alloc = False;
+ io->da_aio.a_aio.aio_return = min;
+ io->da_data = (char *)mmap_data + (addr * 512LL) +
+ io->da_offset;
+ sbc_overlap_store(io);
+ sbc_read_cmplt((emul_handle_t)io);
+
+ } else {
+ if ((io->da_data = (char *)malloc(min)) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+ io->da_clear_overlap = False;
+ io->da_data_alloc = True;
+ io->da_aio.a_aio_cmplt = sbc_read_cmplt;
+ io->da_aio.a_id = io;
+ trans_aioread(cmd, io->da_data, min, (addr * 512LL) +
+ (off_t)io->da_offset, (aio_result_t *)io);
+ }
+ offset += min;
+ } while (offset < (off_t)(cnt * 512));
+}
+
+/*
+ * []----
+ * | sbc_read_cmplt -- Once we have the data, need to send it along.
+ * []----
+ */
+static void
+sbc_read_cmplt(emul_handle_t id)
+{
+ disk_io_t *io = (disk_io_t *)id;
+ int sense_len;
+ uint64_t err_blkno;
+ t10_cmd_t *cmd = io->da_cmd;
+ Boolean_t last;
+
+ if (io->da_aio.a_aio.aio_return != io->da_data_len) {
+ err_blkno = io->da_lba + ((io->da_offset + 511) / 512);
+ cmd->c_resid = (io->da_lba_cnt * 512) - io->da_offset;
+ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN)
+ sense_len = INFORMATION_SENSE_DESCR;
+ else
+ sense_len = 0;
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, sense_len);
+ spc_sense_info(cmd, err_blkno);
+ trans_send_complete(cmd, STATUS_CHECK);
+ sbc_io_free(io);
+ return;
+ }
+
+ last = (io->da_offset + io->da_data_len) < (io->da_lba_cnt * 512LL) ?
+ False : True;
+ if (trans_send_datain(cmd, io->da_data, io->da_data_len, io->da_offset,
+ sbc_io_free, last, io) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*
+ * []----
+ * | sbc_write -- implement a SCSI write command.
+ * []----
+ */
+/*ARGSUSED*/
+static void
+sbc_write(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ union scsi_cdb *u;
+ diskaddr_t addr;
+ uint64_t err_blkno;
+ uint32_t cnt;
+ uchar_t addl_sense_len;
+ disk_params_t *d;
+ disk_io_t *io;
+ size_t max_out;
+ void *mmap_area;
+
+ if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ /*LINTED*/
+ u = (union scsi_cdb *)cdb;
+
+ switch (u->scc_cmd) {
+ case SCMD_WRITE:
+ /*
+ * SBC-2 revision 16, section 5.24
+ * Reserve bit checks.
+ */
+ if ((cdb[1] & 0xe0) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ addr = (diskaddr_t)(uint32_t)GETG0ADDR(u);
+ cnt = GETG0COUNT(u);
+ /*
+ * SBC-2 Revision 16/Section 5.24 WRITE(6)
+ * A TRANSFER LENGHT of 0 indicates that 256 logical blocks
+ * shall be written.
+ */
+ if (cnt == 0)
+ cnt = 256;
+ break;
+
+ case SCMD_WRITE_G1:
+ /*
+ * SBC-2 revision 16, section 5.25
+ * Reserve bit checks.
+ */
+ if ((cdb[1] & 0x6) || cdb[6] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[9])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ addr = (diskaddr_t)(uint32_t)GETG1ADDR(u);
+ cnt = GETG1COUNT(u);
+ break;
+
+ case SCMD_WRITE_G4:
+ /*
+ * SBC-2 revision 16, section 5.27
+ * Reserve bit checks.
+ */
+ if ((cdb[1] & 0x6) || cdb[14] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[15])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ addr = (diskaddr_t)GETG4LONGADDR(u);
+ cnt = GETG4COUNT(u);
+ break;
+
+ default:
+ queue_prt(mgmtq, Q_STE_ERRS, "Unprocessed WRITE type");
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+
+ }
+
+ if ((addr + cnt) > d->d_size) {
+
+ if (addr > d->d_size)
+ err_blkno = addr;
+ else
+ err_blkno = d->d_size;
+
+ /*
+ * XXX: What's SBC-2 say about ASC/ASCQ here. Solaris
+ * doesn't care about these values when key is set
+ * to KEY_ILLEGAL_REQUEST.
+ */
+ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN)
+ addl_sense_len = INFORMATION_SENSE_DESCR;
+ else
+ addl_sense_len = 0;
+
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, addl_sense_len);
+ spc_sense_info(cmd, err_blkno);
+ spc_sense_ascq(cmd, 0x21, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SBC%x LUN%d WRITE Illegal sector "
+ "(0x%llx + 0x%x) > 0x%ullx", cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num, addr, cnt, d->d_size);
+ return;
+ }
+
+ if (cnt == 0) {
+ queue_prt(mgmtq, Q_STE_NONIO,
+ "SBC%x LUN%d WRITE zero block count for addr 0x%x",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ addr);
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ io = (disk_io_t *)cmd->c_emul_id;
+ if (io == NULL) {
+ io = sbc_io_alloc(cmd);
+ io->da_lba = addr;
+ io->da_lba_cnt = cnt;
+ io->da_clear_overlap = False;
+ io->da_aio.a_aio_cmplt = sbc_write_cmplt;
+ io->da_aio.a_id = io;
+
+ /*
+ * Only update the statistics the first time through
+ * for this particular command. If the requested transfer
+ * is larger than the transport can handle this routine
+ * will be called many times.
+ */
+ cmd->c_lu->l_cmds_write++;
+ cmd->c_lu->l_sects_write += cnt;
+ }
+
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO,
+ "SBC%x LUN%d blk 0x%llx, cnt %d, offset 0x%llx, size %d",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr,
+ cnt, io->da_offset, io->da_data_len);
+#endif
+
+ /*
+ * If a transport sets the maximum output value to zero we'll
+ * just request the entire amount. Otherwise, transfer no more
+ * than the maximum output or the reminder, whichever is less.
+ */
+ max_out = cmd->c_lu->l_targ->s_maxout;
+ io->da_data_len = max_out ? MIN(max_out,
+ (cnt * 512) - io->da_offset) : (cnt * 512);
+
+ mmap_area = T10_MMAP_AREA(cmd);
+ if (mmap_area != MAP_FAILED) {
+
+ io->da_data_alloc = False;
+ io->da_data = (char *)mmap_area + (addr * 512LL) +
+ io->da_offset;
+ sbc_overlap_check(io);
+
+ } else if ((io->da_data = (char *)malloc(io->da_data_len)) == NULL) {
+
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+
+ } else {
+
+ io->da_data_alloc = True;
+ }
+ if (trans_rqst_dataout(cmd, io->da_data, io->da_data_len,
+ io->da_offset, io) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*
+ * []----
+ * | sbc_write_data -- store a chunk of data from the transport
+ * []----
+ */
+/*ARGSUSED*/
+void
+sbc_write_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+ size_t data_len)
+{
+ disk_io_t *io = (disk_io_t *)id;
+ disk_params_t *d;
+
+ if (cmd->c_lu->l_common->l_mmap == MAP_FAILED) {
+ trans_aiowrite(cmd, data, data_len, (io->da_lba * 512) +
+ (off_t)io->da_offset, (aio_result_t *)io);
+ } else {
+ if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ if (d->d_fast_write == False) {
+
+ /*
+ * We only need to worry about sync'ing the blocks
+ * in the mmap case because if the fast cache isn't
+ * enabled for AIO the file will be opened with F_SYNC
+ * which performs the correct action.
+ */
+ if (fsync(cmd->c_lu->l_common->l_fd) == -1) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ }
+
+ /*
+ * Since the data has already been transfered from the
+ * transport to the mmap area we just need to call
+ * the complete routine.
+ */
+ sbc_write_cmplt(id);
+ }
+}
+
+/*
+ * []----
+ * | sbc_write_cmplt -- deal with end game of write
+ * |
+ * | See if all of the data for this write operation has been dealt
+ * | with. If so, send a final acknowledgement back to the transport.
+ * | If not, update the offset, calculate the next transfer size, and
+ * | start the process again.
+ * []---
+ */
+static void
+sbc_write_cmplt(emul_handle_t e)
+{
+ disk_io_t *io = (disk_io_t *)e;
+ t10_cmd_t *cmd = io->da_cmd;
+
+ if ((io->da_offset + io->da_data_len) < (io->da_lba_cnt * 512)) {
+ if (io->da_data_alloc == True) {
+ io->da_data_alloc = False;
+ free(io->da_data);
+ }
+
+ io->da_offset += io->da_data_len;
+ io->da_data_len = MIN(cmd->c_lu->l_targ->s_maxout,
+ (io->da_lba_cnt * 512) - io->da_offset);
+ sbc_write(cmd, cmd->c_cdb, cmd->c_cdb_len);
+ return;
+ }
+ sbc_io_free(io);
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+/*ARGSUSED*/
+void
+sbc_startstop(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ /*
+ * SBC-2 revision 16, section 5.17
+ * Reserve bit checks
+ */
+ if ((cdb[1] & 0xfe) || cdb[2] || cdb[3] ||
+ (cdb[4] & ~(SBC_PWR_MASK|SBC_PWR_LOEJ|SBC_PWR_START)) ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ /*
+ * More reserve bit checks
+ */
+ switch ((cdb[4] & SBC_PWR_MASK) >> SBC_PWR_SHFT) {
+ case SBC_PWR_START_VALID:
+ /*
+ * It's an error to ask that the media be ejected.
+ *
+ * NOTE: Look for method to pass the START bit
+ * along to underlying storage. If we're asked to
+ * stop the drive there's not much that we can do
+ * for the virtual storage, but maybe everything else
+ * has been requested to stop as well.
+ */
+ if (cdb[4] & SBC_PWR_LOEJ) {
+ goto send_error;
+ }
+ break;
+
+ case SBC_PWR_ACTIVE:
+ case SBC_PWR_IDLE:
+ case SBC_PWR_STANDBY:
+ case SBC_PWR_OBSOLETE:
+ break;
+
+ case SBC_PWR_LU_CONTROL:
+ case SBC_PWR_FORCE_IDLE_0:
+ case SBC_PWR_FORCE_STANDBY_0:
+ break;
+
+ default:
+send_error:
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if ((cdb[1] & 1) == 0) {
+ /*
+ * Immediate bit is not set, so go ahead a flush things.
+ */
+ if (fsync(cmd->c_lu->l_common->l_fd) != 0) {
+ spc_sense_create(cmd, KEY_MEDIUM_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ }
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+/*
+ * []----
+ * | sbc_recap -- read capacity of device being emulated.
+ * []----
+ */
+/*ARGSUSED*/
+void
+sbc_recap(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ uint64_t capacity;
+ int len;
+ uint32_t lba;
+ struct scsi_capacity *cap;
+ disk_params_t *d;
+ disk_io_t *io;
+
+ if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ capacity = d->d_size;
+
+ len = sizeof (struct scsi_capacity);
+
+ /*
+ * SBC-2 Revision 16, section 5.10.1
+ * Any of the following conditions will generate an error.
+ * (1) PMI bit is zero and LOGICAL block address is non-zero
+ * (2) Rserved bytes are not zero
+ * (3) Reseved bits are not zero
+ * (4) Reserved CONTROL bits are not zero
+ */
+ if ((((cdb[8] & SBC_CAPACITY_PMI) == 0) &&
+ (cdb[2] || cdb[3] || cdb[4] || cdb[5])) ||
+ cdb[1] || cdb[6] || cdb[7] || (cdb[8] & ~SBC_CAPACITY_PMI) ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[9])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ /*
+ * if the device capacity larger than 32 bits then set
+ * the capacity of the device to all 0xf's.
+ * a device that supports LBAs larger than 32 bits which
+ * should be used read_capacity(16) comand to get the capacity.
+ * NOTE: the adjustment to subject one from the capacity is
+ * done below.
+ */
+ if (capacity & 0xFFFFFFFF00000000ULL)
+ capacity = 0xFFFFFFFF;
+
+ io = sbc_io_alloc(cmd);
+
+ if ((cap = (struct scsi_capacity *)calloc(1, len)) == NULL) {
+ sbc_io_free(io);
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+ io->da_data = (char *)cap;
+ io->da_data_alloc = True;
+ io->da_clear_overlap = False;
+ io->da_data_len = len;
+
+ if (capacity != 0xFFFFFFFF) {
+ /*
+ * Look at the PMI information
+ */
+ if (cdb[8] & SBC_CAPACITY_PMI) {
+ lba = cdb[2] << 24 | cdb[3] << 16 |
+ cdb[4] << 8 | cdb[5];
+ if (lba >= capacity)
+ cap->capacity = htonl(0xffffffff);
+ else
+ cap->capacity = (capacity - 1);
+ } else {
+ cap->capacity = htonl(capacity - 1);
+ }
+ } else {
+ cap->capacity = htonl(capacity);
+ }
+ cap->lbasize = htonl(d->d_bytes_sect);
+
+ if (trans_send_datain(cmd, io->da_data, io->da_data_len, 0,
+ sbc_io_free, True, io) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*ARGSUSED*/
+void
+sbc_msense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ struct mode_header *mode_hdr;
+ char *np;
+ disk_params_t *d;
+ disk_io_t *io;
+
+ if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ /*
+ * SPC-3 Revision 21c section 6.8
+ * Reserve bit checks
+ */
+ if ((cdb[1] & ~SPC_MODE_SENSE_DBD) ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ /*
+ * Zero length causes a simple ack to occur.
+ */
+ if (cdb[4] == 0) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ io = sbc_io_alloc(cmd);
+
+ /*
+ * Make sure that we have enough room in the data buffer. We'll
+ * only send back the amount requested though
+ */
+ io->da_data_len = MAX(cdb[4], sizeof (struct mode_format) +
+ sizeof (struct mode_geometry) +
+ sizeof (struct mode_control_scsi3) +
+ sizeof (struct mode_cache_scsi3) +
+ sizeof (struct mode_info_ctrl) + (MODE_BLK_DESC_LENGTH * 5));
+ if ((io->da_data = (char *)calloc(1, io->da_data_len)) == NULL) {
+ sbc_io_free(io);
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+ io->da_clear_overlap = False;
+ io->da_data_alloc = True;
+ mode_hdr = (struct mode_header *)io->da_data;
+
+ switch (cdb[2]) {
+ case MODE_SENSE_PAGE3_CODE:
+ if ((d->d_heads == 0) && (d->d_cyl == 0) && (d->d_spt == 0)) {
+ sbc_io_free(io);
+ spc_unsupported(cmd, cdb, cdb_len);
+ return;
+ }
+ mode_hdr->length = sizeof (struct mode_format);
+ mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH;
+ (void) sense_page3(d,
+ io->da_data + sizeof (*mode_hdr) + mode_hdr->bdesc_length);
+
+ break;
+
+ case MODE_SENSE_PAGE4_CODE:
+ if ((d->d_heads == 0) && (d->d_cyl == 0) && (d->d_spt == 0)) {
+ sbc_io_free(io);
+ spc_unsupported(cmd, cdb, cdb_len);
+ return;
+ }
+ mode_hdr->length = sizeof (struct mode_geometry);
+ mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH;
+ (void) sense_page4(d,
+ io->da_data + sizeof (*mode_hdr) + mode_hdr->bdesc_length);
+ break;
+
+ case MODE_SENSE_CACHE:
+ mode_hdr->length = sizeof (struct mode_cache_scsi3);
+ mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH;
+ (void) sense_cache(d,
+ io->da_data + sizeof (*mode_hdr) + mode_hdr->bdesc_length);
+ break;
+
+ case MODE_SENSE_CONTROL:
+ mode_hdr->length = sizeof (struct mode_control_scsi3);
+ mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH;
+ (void) sense_mode_control(cmd->c_lu,
+ io->da_data + sizeof (*mode_hdr) + mode_hdr->bdesc_length);
+ break;
+
+ case MODE_SENSE_INFO_CTRL:
+ (void) sense_info_ctrl(io->da_data);
+ break;
+
+ case MODE_SENSE_SEND_ALL:
+ /*
+ * SPC-3 revision 21c
+ * Section 6.9.1 Table 97
+ * "Return all subpage 00h mode pages in page_0 format"
+ */
+ if (io->da_data_len < (sizeof (struct mode_format) +
+ sizeof (struct mode_geometry) +
+ sizeof (struct mode_control_scsi3) +
+ sizeof (struct mode_info_ctrl))) {
+
+ /*
+ * Believe it or not, there's an initiator out
+ * there which sends a mode sense request for all
+ * of the pages, without always sending a data-in
+ * size which is large enough.
+ * NOTE: Need to check the error key returned
+ * here and see if something else should be used.
+ */
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+
+ } else {
+
+ /*
+ * If we don't have geometry then don't attempt
+ * report that information.
+ */
+ if (d->d_heads && d->d_cyl && d->d_spt) {
+ np = sense_page3(d, io->da_data);
+ np = sense_page4(d, np);
+ }
+ np = sense_cache(d, np);
+ np = sense_mode_control(cmd->c_lu, np);
+ (void) sense_info_ctrl(np);
+
+ }
+ break;
+
+ case 0x00:
+ /*
+ * SPC-3 Revision 21c, section 6.9.1
+ * Table 97 -- Mode page code usage for all devices
+ * Page Code 00 == Vendor specific. We are going to return
+ * zeros.
+ */
+ break;
+
+ default:
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SBC%x LUN%d Unsupported mode_sense request 0x%x",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ cdb[2]);
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ break;
+ }
+
+ if (trans_send_datain(cmd, io->da_data, cdb[4], 0, sbc_io_free,
+ True, io) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*ARGSUSED*/
+void
+sbc_synccache(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ /*
+ * SBC-2 revision 16, section 5.18
+ * Reserve bit checks
+ */
+ if ((cdb[1] & ~SBC_SYNC_CACHE_IMMED) || cdb[6] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[9])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ /*
+ * SBC-3, revision 16, section 5.18
+ * An IMMED bit set to one specifies that the device server
+ * shall return status as soon as the CDB has been validated.
+ */
+ if (cdb[1] & SBC_SYNC_CACHE_IMMED) {
+
+ /*
+ * Immediately return a status of GOOD. If an error
+ * occurs with the fsync the next command will pick
+ * up an error.
+ */
+ trans_send_complete(cmd, STATUS_GOOD);
+ if (fsync(cmd->c_lu->l_common->l_fd) == -1) {
+ cmd->c_lu->l_status = KEY_HARDWARE_ERROR;
+ cmd->c_lu->l_asc = 0x00;
+ cmd->c_lu->l_ascq = 0x00;
+ }
+ } else {
+ if (fsync(cmd->c_lu->l_common->l_fd) == -1) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else
+ trans_send_complete(cmd, STATUS_GOOD);
+ }
+ }
+}
+
+/*ARGSUSED*/
+void
+sbc_service_actiong4(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ switch (cdb[1] & SPC_GROUP4_SERVICE_ACTION_MASK) {
+ case SSVC_ACTION_READ_CAPACITY_G4:
+ sbc_read_capacity16(cmd, cdb, cdb_len);
+ break;
+ default:
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, 0x20, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ break;
+ }
+}
+
+/*ARGSUSED*/
+static void
+sbc_read_capacity16(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ uint64_t capacity,
+ lba;
+ int rep_size; /* response data size */
+ struct scsi_capacity_16 *cap16;
+ disk_params_t *d;
+ disk_io_t *io;
+
+ if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL)
+ return;
+
+ capacity = d->d_size;
+ /*
+ * READ_CAPACITY(16) command
+ */
+ rep_size = cdb[10] << 24 | cdb[11] << 16 | cdb[12] << 8 | cdb[13];
+ if (rep_size == 0) {
+
+ /*
+ * A zero length field means we're done.
+ */
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+ rep_size = MIN(rep_size, sizeof (*cap16));
+
+ /*
+ * Reserve bit checks.
+ */
+ if ((cdb[1] & ~SPC_GROUP4_SERVICE_ACTION_MASK) ||
+ (cdb[14] & ~SBC_CAPACITY_PMI) ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[15])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ lba = (uint64_t)cdb[2] << 56 | (uint64_t)cdb[3] << 48 |
+ (uint64_t)cdb[4] << 40 | (uint64_t)cdb[5] << 32 |
+ (uint64_t)cdb[6] << 24 | (uint64_t)cdb[7] << 16 |
+ (uint64_t)cdb[8] << 8 | (uint64_t)cdb[9];
+
+ io = sbc_io_alloc(cmd);
+
+ /*
+ * We'll malloc enough space for the structure so that we can
+ * set the values as we place. However, we'll set the transfer
+ * length to the minimum of the requested size and our structure.
+ * This is per SBC-2 revision 16, section 5.11.1 regarding
+ * ALLOCATION LENGTH.
+ */
+ if ((cap16 = (struct scsi_capacity_16 *)calloc(1, sizeof (*cap16))) ==
+ NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+ io->da_data = (char *)cap16;
+ io->da_data_len = sizeof (*cap16);
+ io->da_data_alloc = True;
+ io->da_clear_overlap = False;
+
+ if (cdb[14] & SBC_CAPACITY_PMI) {
+ if (lba >= capacity)
+ cap16->sc_capacity = htonll(0xffffffffffffffffULL);
+ else
+ cap16->sc_capacity = htonll(capacity - 1);
+ } else {
+ cap16->sc_capacity = htonll(capacity - 1);
+ }
+ cap16->sc_lbasize = htonl(d->d_bytes_sect);
+
+ if (trans_send_datain(cmd, io->da_data, io->da_data_len, 0,
+ sbc_io_free, True, io) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*ARGSUSED*/
+static void
+sbc_reserve(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+ t10_lu_impl_t *lu;
+
+ if (p == NULL)
+ return;
+
+ if (cdb[1] & 0xe0 || SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if ((p->d_reserve_owner != NULL) &&
+ (p->d_reserve_owner != cmd->c_lu)) {
+
+ trans_send_complete(cmd, STATUS_RESERVATION_CONFLICT);
+ return;
+
+ } else if (p->d_reserve_owner == cmd->c_lu) {
+
+ /*
+ * According SPC-2 revision 20, section 7.21.2
+ * It shall be permissible for an initiator to
+ * reserve a logic unit that is currently reserved
+ * by that initiator
+ */
+ trans_send_complete(cmd, STATUS_GOOD);
+ } else {
+
+ lu = avl_first(&cmd->c_lu->l_common->l_all_open);
+ do {
+ if (lu != cmd->c_lu)
+ lu->l_cmd = sbc_cmd_reserved;
+ lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
+ } while (lu != NULL);
+ p->d_reserve_owner = cmd->c_lu;
+ trans_send_complete(cmd, STATUS_GOOD);
+ }
+}
+
+/*ARGSUSED*/
+static void
+sbc_release(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+ t10_lu_impl_t *lu;
+
+ if (p == NULL)
+ return;
+
+ if (cdb[1] & 0xe0 || cdb[3] || cdb[4] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if (p->d_reserve_owner == NULL) {
+
+ /*
+ * If nobody is the owner this command is successful.
+ */
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ /*
+ * At this point the only way to get in here is to be the owner
+ * of the reservation.
+ */
+ lu = avl_first(&cmd->c_lu->l_common->l_all_open);
+ do {
+ lu->l_cmd = sbc_cmd;
+ lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
+ } while (lu != NULL);
+ p->d_reserve_owner = NULL;
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Support related functions for SBC-2 |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * []----
+ * | sense_page3 -- Create page3 sense code for Disk.
+ * |
+ * | This is a separate routine because this is called in two different
+ * | locations.
+ * []----
+ */
+static char *
+sense_page3(disk_params_t *d, char *buf)
+{
+ struct mode_format mode_fmt;
+
+ bzero(&mode_fmt, sizeof (mode_fmt));
+ mode_fmt.mode_page.code = MODE_SENSE_PAGE3_CODE;
+ mode_fmt.mode_page.length = sizeof (struct mode_format) -
+ sizeof (struct mode_page);
+ mode_fmt.data_bytes_sect = htons(d->d_bytes_sect);
+ mode_fmt.sect_track = htons(d->d_spt);
+ mode_fmt.interleave = htons(d->d_interleave);
+ bcopy(&mode_fmt, buf, sizeof (mode_fmt));
+
+ return (buf + sizeof (mode_fmt));
+}
+
+/*
+ * []----
+ * | sense_page4 -- Create page4 sense code for Disk.
+ * |
+ * | This is a separate routine because this is called in two different
+ * | locations.
+ * []----
+ */
+static char *
+sense_page4(disk_params_t *d, char *buf)
+{
+ struct mode_geometry mode_geom;
+
+ bzero(&mode_geom, sizeof (mode_geom));
+ mode_geom.mode_page.code = MODE_SENSE_PAGE4_CODE;
+ mode_geom.mode_page.length =
+ sizeof (struct mode_geometry) - sizeof (struct mode_page);
+ mode_geom.heads = d->d_heads;
+ mode_geom.cyl_ub = d->d_cyl >> 16;
+ mode_geom.cyl_mb = d->d_cyl >> 8;
+ mode_geom.cyl_lb = d->d_cyl;
+ mode_geom.rpm = htons(d->d_rpm);
+ bcopy(&mode_geom, buf, sizeof (mode_geom));
+
+ return (buf + sizeof (mode_geom));
+}
+
+static char *
+sense_cache(disk_params_t *d, char *buf)
+{
+ struct mode_cache_scsi3 mode_cache;
+
+ bzero(&mode_cache, sizeof (mode_cache));
+
+ mode_cache.wce = d->d_fast_write == True ? 1 : 0;
+ bcopy(&mode_cache, buf, sizeof (mode_cache));
+
+ return (buf + sizeof (mode_cache));
+}
+
+/*
+ * []----
+ * | sense_mode_control -- Create mode control page for disk
+ * []----
+ */
+static char *
+sense_mode_control(t10_lu_impl_t *lu, char *buf)
+{
+ struct mode_control_scsi3 m;
+
+ bzero(&m, sizeof (m));
+ m.mode_page.code = MODE_SENSE_CONTROL;
+ m.mode_page.length = sizeof (struct mode_control_scsi3) -
+ sizeof (struct mode_page);
+ m.d_sense = (lu->l_dsense_enabled == True) ? 1 : 0;
+ bcopy(&m, buf, sizeof (m));
+
+ return (buf + sizeof (m));
+}
+
+/*
+ * []----
+ * | sense_info_ctrl -- Create mode information control page
+ * []----
+ */
+static char *
+sense_info_ctrl(char *buf)
+{
+ struct mode_info_ctrl info;
+
+ bzero(&info, sizeof (info));
+ info.mode_page.code = 0x1c;
+ info.mode_page.length = sizeof (struct mode_info_ctrl) -
+ sizeof (struct mode_page);
+ bcopy(&info, buf, sizeof (info));
+
+ return (buf + sizeof (info));
+}
+
+/*
+ * []----
+ * | sbc_io_alloc -- return a disk_io_t structure
+ * |
+ * | If the call to calloc fails we use the structure that was allocate
+ * | during the initial common initialization call. This will allow the
+ * | daemon to at least make progress.
+ * []----
+ */
+static disk_io_t *
+sbc_io_alloc(t10_cmd_t *c)
+{
+ disk_io_t *io;
+ disk_params_t *d = T10_PARAMS_AREA(c);
+
+ if ((io = (disk_io_t *)calloc(1, sizeof (*io))) == NULL) {
+ (void) pthread_mutex_lock(&d->d_mutex);
+ if (d->d_io_used == True) {
+ d->d_io_need = True;
+ while (d->d_io_used == True)
+ pthread_cond_wait(&d->d_io_cond, &d->d_mutex);
+ d->d_io_need = False;
+ }
+ d->d_io_used = True;
+ io = d->d_io_reserved;
+ (void) pthread_mutex_unlock(&d->d_mutex);
+ }
+
+ io->da_cmd = c;
+ io->da_params = d;
+
+ return (io);
+}
+
+/*
+ * []----
+ * | sbc_io_free -- free local i/o buffers when transport is finished
+ * |
+ * | If the io structure being free is the preallocated buffer see if
+ * | anyone is waiting for the buffer. If so, wake them up.
+ * []----
+ */
+static void
+sbc_io_free(emul_handle_t e)
+{
+ disk_io_t *io = (disk_io_t *)e;
+
+ if (io->da_clear_overlap == True)
+ sbc_overlap_free(io);
+
+ if (io->da_data_alloc == True)
+ free(io->da_data);
+
+ if (io == io->da_params->d_io_reserved) {
+ (void) pthread_mutex_lock(&io->da_params->d_mutex);
+ io->da_params->d_io_used = False;
+ if (io->da_params->d_io_need == True)
+ pthread_cond_signal(&io->da_params->d_io_cond);
+ (void) pthread_mutex_unlock(&io->da_params->d_mutex);
+ } else {
+ free(io);
+ }
+}
+
+static int
+sbc_mmap_overlap(const void *v1, const void *v2)
+{
+ disk_io_t *d1 = (disk_io_t *)v1,
+ *d2 = (disk_io_t *)v2;
+
+ if ((d1->da_data + d1->da_data_len) < d2->da_data)
+ return (-1);
+ if (d1->da_data > (d2->da_data + d2->da_data_len))
+ return (1);
+ return (0);
+}
+
+static void
+sbc_overlap_store(disk_io_t *io)
+{
+ disk_params_t *d = io->da_params;
+ avl_index_t where = 0;
+
+ assert(d != NULL);
+
+ (void) pthread_mutex_lock(&d->d_mutex);
+ (void) avl_find(&d->d_mmap_overlaps, (void *)io, &where);
+ avl_insert(&d->d_mmap_overlaps, (void *)io, where);
+ (void) pthread_mutex_unlock(&d->d_mutex);
+}
+
+static void
+sbc_overlap_free(disk_io_t *io)
+{
+ disk_params_t *d = io->da_params;
+
+ assert(d != NULL);
+
+ (void) pthread_mutex_lock(&d->d_mutex);
+ avl_remove(&d->d_mmap_overlaps, (void *)io);
+ if (d->d_mmap_paused == True) {
+ d->d_mmap_paused = False;
+ (void) pthread_cond_signal(&d->d_mmap_cond);
+ }
+ (void) pthread_mutex_unlock(&d->d_mutex);
+}
+
+static void
+sbc_overlap_check(disk_io_t *io)
+{
+ disk_params_t *d = io->da_params;
+
+ assert(d != NULL);
+recheck:
+ (void) pthread_mutex_lock(&d->d_mutex);
+ if (avl_find(&d->d_mmap_overlaps, (void *)io, NULL) != NULL) {
+ d->d_mmap_paused = True;
+ while (d->d_mmap_paused == True)
+ (void) pthread_cond_wait(&d->d_mmap_cond,
+ &d->d_mutex);
+
+ /*
+ * After waiting on the condition variable the link
+ * list has changed because someone removed a command.
+ * So, drop the lock and reexamine the list.
+ */
+ (void) pthread_mutex_unlock(&d->d_mutex);
+ goto recheck;
+ }
+ (void) pthread_mutex_unlock(&d->d_mutex);
+}
+
+/*
+ * []----
+ * | sbc_overlap_flush -- wait until everyone has reported in
+ * []----
+ */
+static void
+sbc_overlap_flush(disk_params_t *d)
+{
+ assert(d != NULL);
+recheck:
+ (void) pthread_mutex_lock(&d->d_mutex);
+ if (avl_numnodes(&d->d_mmap_overlaps) != 0) {
+ d->d_mmap_paused = True;
+ while (d->d_mmap_paused == True)
+ (void) pthread_cond_wait(&d->d_mmap_cond,
+ &d->d_mutex);
+
+ /*
+ * After waiting on the condition variable the link
+ * list has changed because someone removed a command.
+ * So, drop the lock and reexamine the list.
+ */
+ (void) pthread_mutex_unlock(&d->d_mutex);
+ goto recheck;
+ }
+ (void) pthread_mutex_unlock(&d->d_mutex);
+}
+
+/*
+ * []----
+ * | Command table for LBA emulation. This is at the end of the file because
+ * | it's big and ugly. ;-) To make for fast translation to the appropriate
+ * | emulation routine we just have a big command table with all 256 possible
+ * | entries. Most will report STATUS_CHECK, unsupport operation. By doing
+ * | this we can avoid error checking for command range.
+ * []----
+ */
+static scsi_cmd_table_t lba_table[] = {
+ /* 0x00 -- 0x0f */
+ { spc_tur, NULL, NULL, "TEST_UNIT_READY" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_request_sense, NULL, NULL, "REQUEST_SENSE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { sbc_read, NULL, sbc_read_cmplt, "READ" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { sbc_write, sbc_write_data, sbc_write_cmplt, "WRITE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x10 -- 0x1f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_inquiry, NULL, NULL, "INQUIRY" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_mselect, spc_mselect_data, NULL, "MODE_SELECT" },
+ { sbc_reserve, NULL, NULL, "RESERVE" },
+ { sbc_release, NULL, NULL, "RELEASE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { sbc_msense, NULL, NULL, "MODE_SENSE" },
+ { sbc_startstop, NULL, NULL, "START_STOP" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_send_diag, NULL, NULL, "SEND_DIAG" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x20 -- 0x2f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { sbc_recap, NULL, NULL, "READ_CAPACITY" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { sbc_read, NULL, sbc_read_cmplt, "READ_G1" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { sbc_write, sbc_write_data, sbc_write_cmplt, "WRITE_G1" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x30 -- 0x3f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { sbc_synccache, NULL, NULL, "SYNC_CACHE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x40 -- 0x4f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "LOG_SENSE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x50 -- 0x5f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "PERSISTENT_IN" },
+ { spc_unsupported, NULL, NULL, "PERSISTENT_OUT" },
+
+ /* 0x60 -- 0x6f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x70 -- 0x7f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x80 -- 0x8f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { sbc_read, NULL, sbc_read_cmplt, "READ_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { sbc_write, sbc_write_data, sbc_write_cmplt, "WRITE_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x90 -- 0x9f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { sbc_service_actiong4, NULL, NULL, "SVC_ACTION_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xa0 - 0xaf */
+ { spc_report_luns, NULL, NULL, "REPORT_LUNS" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_report_tpgs, NULL, NULL, "REPORT_TPGS" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xb0 -- 0xbf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xc0 -- 0xcf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xd0 -- 0xdf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xe0 -- 0xef */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xf0 -- 0xff */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+};
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h b/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h
new file mode 100644
index 0000000000..af1404d3c4
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h
@@ -0,0 +1,188 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _T10_SBC_H
+#define _T10_SBC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * SBC-2 specific structures and defines
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SBC_CAPACITY_PMI 0x01
+
+#define SBC_SYNC_CACHE_IMMED 0x02
+#define SBC_SYNC_CACHE_NV 0x04
+
+/*
+ * SBC-2 revision 16, section 5.17 START_STOP
+ * Table 49 -- POWER CONDITION field
+ */
+#define SBC_PWR_MASK 0xf0
+#define SBC_PWR_SHFT 4
+#define SBC_PWR_START_VALID 0x00
+#define SBC_PWR_ACTIVE 0x01
+#define SBC_PWR_IDLE 0x02
+#define SBC_PWR_STANDBY 0x03
+#define SBC_PWR_OBSOLETE 0x05 /* JIST checks this one */
+#define SBC_PWR_LU_CONTROL 0x07
+#define SBC_PWR_FORCE_IDLE_0 0x0a
+#define SBC_PWR_FORCE_STANDBY_0 0x0b
+#define SBC_PWR_LOEJ 0x02
+#define SBC_PWR_START 0x01
+
+typedef struct disk_params {
+ /*
+ * Size of LUN in blocks
+ */
+ diskaddr_t d_size;
+
+ /*
+ * Number of bytes per section. This will probably
+ * Become vary important for the T10 Data Integrity
+ * Stuff.
+ */
+ uint32_t d_bytes_sect;
+ uint32_t d_heads,
+ d_spt,
+ d_cyl;
+ /*
+ * Another bogus values.
+ */
+ uint32_t d_rpm,
+ d_interleave;
+
+ /*
+ * This lock protects access to both the d_mmap_overlaps AVL tree
+ * and use of the reserved disk_io buffer when memory is exhausted.
+ */
+ pthread_mutex_t d_mutex;
+
+ /*
+ * When using mmap backing store it's possible for an application
+ * to issue a read and a write command of the same block without
+ * first waiting for the read to complete. Since we pass the address
+ * to the mmap area back to the transport and continue it's possible
+ * to start processing the write command which will change the data
+ * before the read has completed. To prevent this condition read
+ * commands store their data address and length into the avl tree.
+ * The write command will see if it's potential address is the same
+ * as one of the read commands. If so, the write command will pause
+ * until the read completes.
+ */
+ avl_tree_t d_mmap_overlaps;
+ pthread_cond_t d_mmap_cond;
+ Boolean_t d_mmap_paused;
+
+ pthread_cond_t d_io_cond;
+ Boolean_t d_io_need;
+ Boolean_t d_io_used;
+ struct disk_io *d_io_reserved;
+
+ Boolean_t d_fast_write;
+ t10_lu_state_t d_state;
+
+ t10_lu_impl_t *d_reserve_owner;
+} disk_params_t;
+
+typedef struct disk_io {
+ /*
+ * This structure needs to be the first member. The first member
+ * of this structure is an aio_result_t. If we need to issue
+ * an aio request a generic handler is called for all aio requests.
+ * To allow this generic handler a means to callback to the appropriate
+ * emulation routines a generic header is used. The generic handler
+ * can cast the pointer returned from aiowait to a t10_aio_t structure.
+ * From there it can determine the call back routine and pass it
+ * a specific pointer.
+ */
+ t10_aio_t da_aio;
+
+ /*
+ * Communication with the SAM-3 layer requires us to send back this
+ * pointer which was passed in at the command start.
+ */
+ t10_cmd_t *da_cmd;
+
+ /*
+ * (1) During AIO operations we need to allocate space to hold the
+ * data. This pointer represents that data which will be freed
+ * from our callback (argument to trans_send_datain) after the
+ * transport has finished with it.
+ * (2) During mmap ops the memory address of the requested data block
+ * will be stored here along with the transfer size. This will
+ * be used by the overlap protection to see if we must hold
+ * off a write op.
+ */
+ char *da_data;
+ size_t da_data_len;
+
+ /*
+ * True if da_data has been malloc'd verses mmap and therefore
+ * we need to free it when the free routine is called.
+ */
+ Boolean_t da_data_alloc;
+
+ /*
+ * True if an overlap value was stored which needs to be cleared.
+ */
+ Boolean_t da_clear_overlap;
+
+ /*
+ * If we're breaking up the transfer to comply with max_out
+ * then da_offset indicates where in the transfer we're currently
+ * at.
+ */
+ uint64_t da_offset;
+
+ /*
+ * This is the LBA of a SCSI READ or WRITE command. Once decoded
+ * from the cdb we don't want to recompute it each time it's needed
+ * in the different phases.
+ */
+ diskaddr_t da_lba;
+ size_t da_lba_cnt;
+
+ disk_params_t *da_params;
+
+ /*
+ * Normal command overlap protection is done by the SAM-3 layer.
+ * This overlap is to prevent a write op from changing data before
+ * an existing read op has transmitted the data.
+ */
+ avl_node_t da_mmap_overlap;
+} disk_io_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _T10_SBC_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_spc.c b/usr/src/cmd/iscsi/iscsitgtd/t10_spc.c
new file mode 100644
index 0000000000..c8206c3273
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc.c
@@ -0,0 +1,1121 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SPC-3 Support Functions |
+ * | These routines are not directly called by the SAM-3 layer. Those |
+ * | who write device emulation modules are free to call these routine |
+ * | to carry out housekeeping chores. |
+ * []------------------------------------------------------------------[]
+ */
+
+#include <sys/types.h>
+#include <sys/asynch.h>
+#include <sys/param.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/generic/inquiry.h>
+#include <sys/scsi/generic/mode.h>
+#include <sys/scsi/generic/commands.h>
+
+#include "t10.h"
+#include "t10_spc.h"
+#include "target.h"
+
+static void spc_free(emul_handle_t id);
+
+/*
+ * []----
+ * | spc_unsupported -- generic routine to indicate we don't support this cmd
+ * []----
+ */
+/*ARGSUSED*/
+void
+spc_unsupported(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ char debug[80];
+
+ (void) snprintf(debug, sizeof (debug),
+ "SAM%d LUN%d Command 0x%x (%s) unsupported\n",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ cdb[0], cmd->c_lu->l_cmd_table[cdb[0]].cmd_name != NULL ?
+ cmd->c_lu->l_cmd_table[cdb[0]].cmd_name : "No description");
+ queue_str(mgmtq, Q_STE_ERRS, msg_log, debug);
+
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, 0x20, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+}
+
+/*
+ * []----
+ * | spc_tur -- test unit ready
+ * []----
+ */
+/*ARGSUSED*/
+void
+spc_tur(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ /*
+ * SPC-3 Revision 21c, section 6.31
+ * Reserve bit checks
+ */
+ if (cdb[1] || cdb[2] || cdb[3] || cdb[4] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+/*
+ * []----
+ * | spc_request_sense --
+ * []----
+ */
+/*ARGSUSED*/
+void
+spc_request_sense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ /* ---- Check for reserved bit conditions ---- */
+ if ((cdb[1] & 0xfe) || cdb[2] || cdb[3] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ } else {
+ /*
+ * Since we always run with autosense enabled there's
+ * no sense data to return. That may change in the future
+ * if we decide to add such support, but for now return
+ * success always.
+ */
+ spc_sense_create(cmd, 0, 0);
+ trans_send_complete(cmd, STATUS_GOOD);
+ }
+}
+
+/*
+ * []----
+ * | spc_inquiry -- Standard INQUIRY command
+ * []----
+ */
+/*ARGSUSED*/
+void
+spc_inquiry(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ uint8_t *rsp_buf,
+ *rbp; /* temporary var */
+ struct scsi_inquiry *inq;
+ uint32_t len,
+ page83_len,
+ rqst_len,
+ rtn_len;
+ struct vpd_hdr *vhp;
+ struct vpd_desc vd;
+ size_t scsi_len;
+ t10_lu_common_t *lu = cmd->c_lu->l_common;
+ void *v;
+ uint16_t *vdv;
+
+ /*
+ * Information obtained from:
+ * SPC-3 Revision 21c
+ * Section 6.4.1 INQUIRY command
+ * Need to generate a CHECK CONDITION with ILLEGAL REQUEST
+ * and INVALID FIELD IN CDB (0x24/0x00) if any of the following is
+ * true.
+ * (1) If the EVPD bit is not set, then the page code must be zero.
+ * (2) If any bit other than EVPD is set.
+ * (3) If any of the reserved bits in the CONTROL byte are set.
+ */
+ if (((cdb[1] == 0) && (cdb[2] != 0)) || (cdb[1] & ~1) ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ rqst_len = cdb[3] << 8 | cdb[4];
+ /*
+ * Zero length is not an error and we should just acknowledge
+ * the operation.
+ */
+ if (rqst_len == 0) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ /*
+ * We send back a total of six Vital Product Data descriptors
+ * plus associated data. Three are EUI values, two are for AULA
+ * support being simple 4 byte values, and one is the IQN string.
+ * NOTE: The IQN string is just an artifact of when the target
+ * was created. Once FC support is added, the t_name would be
+ * either a "naa." or "eui." string.
+ */
+ scsi_len = ((strlen(cmd->c_lu->l_targ->s_targ_base) + 1) + 3) & ~3;
+ page83_len = (sizeof (struct vpd_desc) * 6) + scsi_len +
+ (lu->l_guid_len * 3) + (sizeof (uint32_t) * 2);
+
+ /*
+ * We always allocate enough space so that the code can create
+ * either the full inquiry data or page 0x83 data.
+ */
+ len = sizeof (struct vpd_hdr) + page83_len;
+ len = max(rqst_len, max(sizeof (*inq), len));
+
+ /*
+ * Allocate space with an alignment that will work for any casting.
+ */
+ if ((v = memalign(sizeof (void *), len)) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+ bzero(v, len);
+ rsp_buf = (uint8_t *)v;
+
+ /*
+ * EVPD not set returns the standard inquiry data.
+ */
+ if (cdb[1] == 0) {
+ /*
+ * Return whatever is the smallest amount between the
+ * INQUIRY data or the amount requested.
+ */
+ rtn_len = min(rqst_len, sizeof (*inq));
+
+ inq = (struct scsi_inquiry *)rsp_buf;
+ /*
+ * SPC-3 Revision 21c, Section 6.4.2 .. Table 82 -- Version
+ * This target will comply with T10/1416-D
+ */
+ inq->inq_ansi = SPC_INQ_VERS_SPC_3;
+
+ inq->inq_len = sizeof (*inq) - 4;
+ inq->inq_dtype = lu->l_dtype;
+ inq->inq_normaca = 0;
+ inq->inq_rdf = SPC_INQ_RDF;
+ inq->inq_cmdque = 1;
+ inq->inq_linked = 0;
+
+ /*
+ * JIST Requires that we show support for hierarchical
+ * support (HiSup).
+ */
+ inq->inq_hisup = 1;
+
+ /*
+ * SPC-4, revision 1a, section 6.4.2
+ * Stand INQUIRY Data
+ * To support MPxIO we enable ALUA support and identify
+ * all paths to this device as being active/optimized
+ * we defaults to symmertrical devices.
+ */
+ inq->inq_tpgs = 1;
+
+ (void) snprintf(inq->inq_vid,
+ sizeof (inq->inq_vid) + sizeof (inq->inq_pid) +
+ sizeof (inq->inq_revision), "%-8s%-16s%-4s",
+ lu->l_vid, lu->l_pid,
+ DEFAULT_REVISION);
+
+ /*
+ * SPC-3 Revision 21c, section 6.4.2
+ * Table 85 -- Version Descriptor values
+ * Starting at byte 58 there are up to 8 version
+ * descriptors which are 16bits in size.
+ *
+ * NOTE: The ordering of these values is according
+ * to the standard. First comes the architectural
+ * version and then followed by: physical transport,
+ * SCSI transport, primary command set version, and
+ * finally the device type command set.
+ */
+ vdv = &((uint16_t *)v)[29];
+
+ /* SAM-3 T10/1561-D rev 14 */
+ *vdv++ = htons(SPC_INQ_VD_SAM3);
+
+ /* physical transport code unknown */
+ *vdv++ = htons(0x0000);
+
+ *vdv++ = htons(cmd->c_lu->l_targ->s_trans_vers);
+
+ /* SPC ANSI X3.301:1997 */
+ *vdv++ = htons(SPC_INQ_VD_SPC3);
+
+ if (lu->l_dtype == DTYPE_DIRECT) {
+ /* SBC-2 T10/1417-D rev 5a */
+ *vdv++ = htons(SPC_INQ_VD_SBC2);
+ } else if (lu->l_dtype == DTYPE_SEQUENTIAL) {
+ /* SSC-2 (no version) */
+ *vdv++ = htons(SPC_INQ_VD_SSC3);
+ } else if (lu->l_dtype == DTYPE_OSD) {
+ /* OSD T10/1355-D revision 10 */
+ *vdv++ = htons(SPC_INQ_VD_OSD);
+ }
+
+ } else {
+
+ /*
+ * Return the smallest amount of data between the requested
+ * amount and the size of the Page83 data.
+ */
+ rtn_len = min(rqst_len, sizeof (struct vpd_hdr) + page83_len);
+
+ /* ---- Common information returned with all page types ---- */
+ rsp_buf[0] = lu->l_dtype;
+ rsp_buf[1] = cdb[2];
+
+ switch (cdb[2]) {
+ default:
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+
+ case SPC_INQ_PAGE0:
+ /*
+ * SPC-3 Revision 21c Section 7.6.10
+ * EVPD page 0 returns information about which pages
+ * are supported. We support page 0x00 and 0x83.
+ * NOTE: The value found in byte[3] is the returned
+ * page length as defined by (n - 3) where 'n' is
+ * the last valid byte. In this case 5.
+ */
+ rsp_buf[3] = 2;
+ rsp_buf[4] = SPC_INQ_PAGE0;
+ rsp_buf[5] = SPC_INQ_PAGE83;
+ break;
+
+ case SPC_INQ_PAGE83:
+ /*
+ * Information obtained from:
+ * SPC-3 Revision 21c
+ * Section 7.6.4.1
+ */
+
+ /* ---- VPD header ---- */
+ vhp = (struct vpd_hdr *)v;
+ vhp->device_type = lu->l_dtype;
+ vhp->periph_qual = SPC_INQUIRY_PERIPH_CONN;
+ vhp->page_code = cdb[2];
+ vhp->page_len[0] = hibyte(loword(page83_len));
+ vhp->page_len[1] = lobyte(loword(page83_len));
+ rbp = (uint8_t *)v + sizeof (*vhp);
+
+ /* ---- VPD descriptor ---- */
+ /*
+ * SPC-4 revision 1a, section 7.6.3.8
+ * Target port group designator format
+ */
+ vd.code_set = SPC_INQUIRY_CODE_SET_BINARY;
+ vd.id_type = SPC_INQUIRY_ID_TYPE_TARG_PORT;
+ vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
+ vd.association = SPC_INQUIRY_ASSOC_TARGPORT;
+ vd.piv = 1;
+ vd.len = 4;
+ bcopy(&vd, rbp, sizeof (vd));
+ rbp += sizeof (vd);
+
+ len = SPC_DEFAULT_TPG;
+ rbp[2] = hibyte(loword(len));
+ rbp[3] = lobyte(loword(len));
+ rbp += vd.len;
+
+ /* ---- VPD descriptor ---- */
+ /*
+ * SPC-4, revision 1a, section 7.6.3.7
+ * Relative target port designator format
+ */
+ vd.code_set = SPC_INQUIRY_CODE_SET_BINARY;
+ vd.id_type = SPC_INQUIRY_ID_TYPE_RELATIVE;
+ vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
+ vd.association = SPC_INQUIRY_ASSOC_TARGPORT;
+ vd.piv = 1;
+ vd.len = 4;
+ bcopy(&vd, rbp, sizeof (vd));
+ rbp += sizeof (vd);
+
+ rbp[2] = hibyte(loword(cmd->c_lu->l_targ->s_tp_grp));
+ rbp[3] = lobyte(loword(cmd->c_lu->l_targ->s_tp_grp));
+ rbp += vd.len;
+
+ /* ---- VPD descriptor ---- */
+ vd.code_set = SPC_INQUIRY_CODE_SET_BINARY;
+ vd.id_type = SPC_INQUIRY_ID_TYPE_EUI;
+ vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
+ vd.association = SPC_INQUIRY_ASSOC_LUN;
+ vd.piv = 1;
+ vd.len = lu->l_guid_len;
+ bcopy(&vd, rbp, sizeof (vd));
+ rbp += sizeof (vd);
+
+ bcopy(lu->l_guid, &rbp[0], lu->l_guid_len);
+ rbp += lu->l_guid_len;
+
+ /* ---- VPD descriptor ---- */
+ vd.code_set = SPC_INQUIRY_CODE_SET_UTF8;
+ vd.id_type = SPC_INQUIRY_ID_TYPE_SCSI;
+ vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
+ vd.association = SPC_INQUIRY_ASSOC_LUN;
+ vd.piv = 1;
+ vd.len = scsi_len;
+ bcopy(&vd, rbp, sizeof (vd));
+ rbp += sizeof (vd);
+
+ /*
+ * SPC-3 revision 23, section 7.6.3.11
+ * Use the string length and not the scsi_len because
+ * we've rounded up the length to a multiple of four
+ * as required by the specification.
+ */
+ bcopy(cmd->c_lu->l_targ->s_targ_base, &rbp[0],
+ strlen(cmd->c_lu->l_targ->s_targ_base));
+ rbp += vd.len;
+
+ /* ---- VPD descriptor ---- */
+ vd.code_set = SPC_INQUIRY_CODE_SET_BINARY;
+ vd.id_type = SPC_INQUIRY_ID_TYPE_EUI;
+ vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
+ vd.association = SPC_INQUIRY_ASSOC_TARG;
+ vd.piv = 1;
+ vd.len = lu->l_guid_len;
+ bcopy(&vd, rbp, sizeof (vd));
+ rbp += sizeof (vd);
+
+ /*
+ * XXX Is this right XXX
+ * Should we be using some other name.
+ */
+ bcopy(lu->l_guid, &rbp[0], lu->l_guid_len);
+ rbp += lu->l_guid_len;
+
+ /* ---- VPD descriptor ---- */
+ vd.code_set = SPC_INQUIRY_CODE_SET_BINARY;
+ vd.id_type = SPC_INQUIRY_ID_TYPE_EUI;
+ vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
+ vd.association = SPC_INQUIRY_ASSOC_TARGPORT;
+ vd.piv = 1;
+ vd.len = lu->l_guid_len;
+ bcopy(&vd, rbp, sizeof (vd));
+ rbp += sizeof (vd);
+
+ /*
+ * XXX Is this right XXX
+ * Should we be using some other name.
+ */
+ bcopy(lu->l_guid, &rbp[0], lu->l_guid_len);
+
+ /*
+ * rbp is updated here even though nobody will
+ * currently use it. Currently is the optertive word
+ * here. If for some reason we add another VDP
+ * then the pointer will be correct.
+ */
+ rbp += lu->l_guid_len;
+
+ break;
+ }
+ }
+
+ if (trans_send_datain(cmd, (char *)rsp_buf, rtn_len, 0, spc_free,
+ True, rsp_buf) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*
+ * []----
+ * | spc_mselect -- Generic MODE SELECT command
+ * []----
+ */
+/*ARGSUSED*/
+void
+spc_mselect(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ char *buf;
+
+ /*
+ * SPC-3 revision 21c, section 6.7
+ * Reserve bit checks
+ */
+ if ((cdb[1] & 0xee) || cdb[2] || cdb[3] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if (cdb[4] == 0) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ if (((buf = (char *)calloc(1, cdb[4])) == NULL) ||
+ (trans_rqst_dataout(cmd, buf, cdb[4], 0, 0) == False)) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*
+ * []----
+ * | spc_mselect_data -- DataIn phase of MODE SELECT command
+ * []----
+ */
+/*ARGSUSED*/
+void
+spc_mselect_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+ size_t data_len)
+{
+ struct mode_control_scsi3 mode_ctl_page;
+ struct mode_header hdr;
+
+ bcopy(data, &hdr, sizeof (hdr));
+ bcopy(data + sizeof (hdr) + hdr.bdesc_length, &mode_ctl_page,
+ sizeof (mode_ctl_page));
+
+ switch (mode_ctl_page.mode_page.code) {
+ case MODE_SENSE_CONTROL:
+ /*
+ * SPC-3 revision 21c, section 7.4.6
+ * Table 239 describes the fields. We're only interested
+ * in the descriptor sense bit.
+ */
+ if (mode_ctl_page.d_sense == 1) {
+ cmd->c_lu->l_dsense_enabled = True;
+ } else {
+ cmd->c_lu->l_dsense_enabled = False;
+ }
+ break;
+
+ default:
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ break;
+ }
+ free(data);
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+/*
+ * []----
+ * | spc_report_luns --
+ * []----
+ */
+/*ARGSUSED*/
+void
+spc_report_luns(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ int expected_data;
+ uint8_t *buf = NULL;
+ int entries = 0,
+ len,
+ len_network,
+ select,
+ lun_idx,
+ lun_val;
+ char *str;
+ xml_node_t *targ,
+ *lun_list,
+ *lun;
+
+ /*
+ * SPC-3 Revision 21c section 6.21
+ * Error checking.
+ */
+ if (cdb[1] || cdb[3] || cdb[4] ||
+ (cdb[2] & ~SPC_RPT_LUNS_SELECT_MASK) || cdb[5] || cdb[10] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[11])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ expected_data = cdb[6] << 24 | cdb[7] << 16 | cdb[8] << 8 | cdb[9];
+ if (expected_data < 16) {
+ /*
+ * The allocation length should be at least 16 according
+ * to SPC-3.
+ */
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ select = cdb[2];
+
+ targ = NULL;
+ while ((targ = xml_node_next(targets_config, XML_ELEMENT_TARG, targ)) !=
+ NULL) {
+ if (xml_find_value_str(targ, XML_ELEMENT_INAME, &str) ==
+ False) {
+ goto error;
+ }
+ if (strcmp(str, cmd->c_lu->l_targ->s_targ_base) == 0) {
+ free(str);
+ break;
+ } else
+ free(str);
+ }
+ if (!targ)
+ goto error;
+ if ((lun_list = xml_node_next(targ, XML_ELEMENT_LUNLIST, NULL)) == NULL)
+ goto error;
+
+ lun = NULL;
+ while ((lun = xml_node_next(lun_list, XML_ELEMENT_LUN, lun)) != NULL)
+ entries++;
+
+
+ len = entries * SCSI_REPORTLUNS_ADDRESS_SIZE;
+ if ((buf = (uint8_t *)calloc(1, MAX(expected_data, len))) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+
+ len_network = htonl(len);
+ bcopy(&len_network, buf, sizeof (len_network));
+
+ if (expected_data >= (len + SCSI_REPORTLUNS_ADDRESS_SIZE)) {
+
+ lun_idx = SCSI_REPORTLUNS_ADDRESS_SIZE;
+ lun = NULL;
+ while ((lun = xml_node_next(lun_list, XML_ELEMENT_LUN, lun)) !=
+ NULL) {
+ if (xml_find_value_int(lun, XML_ELEMENT_LUN,
+ &lun_val) == False)
+ goto error;
+ if (spc_encode_lu_addr(&buf[lun_idx], select,
+ lun_val) == False)
+ continue;
+ lun_idx += SCSI_REPORTLUNS_ADDRESS_SIZE;
+ }
+ if (trans_send_datain(cmd, (char *)buf, expected_data, 0,
+ spc_free, True, buf) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+ } else {
+ /*
+ * This will return the size needed to complete this request
+ * and a implicit LUN zero.
+ */
+ if (trans_send_datain(cmd, (char *)buf, 16, 0, spc_free, True,
+ buf) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+ }
+ return;
+
+error:
+ if (buf)
+ free(buf);
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+}
+
+/*ARGSUSED*/
+void
+spc_report_tpgs(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ rtpg_hdr_t *r;
+ rtpg_desc_t *dp;
+ rtpg_targ_desc_t *tp;
+ int rqst_len,
+ alloc_len,
+ i;
+ t10_lu_common_t *lu = cmd->c_lu->l_common;
+ t10_lu_impl_t *lu_per;
+
+ if (disable_tpgs == True) {
+ spc_unsupported(cmd, cdb, cdb_len);
+ return;
+ }
+
+ /*
+ * Reserve bit checks
+ */
+ if ((cdb[1] & 0xe0) || (cdb[1] & ~SPC_MI_SVC_MASK) ||
+ ((cdb[1] & SPC_MI_SVC_MASK) != SPC_MI_SVC_RTPG) ||
+ cdb[2] || cdb[3] || cdb[4] || cdb[5] ||
+ cdb[10] || SAM_CONTROL_BYTE_RESERVED(cdb[11])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ /*
+ * We only have one target port group which it's size is
+ * accounted for in rtpg_hdr_t. Take the number of tgts
+ * and subtract one since the first is accounted for in
+ * rtpg_targ_desc_t
+ */
+ alloc_len = ((avl_numnodes(&lu->l_all_open) - 1) *
+ sizeof (rtpg_targ_desc_t)) + sizeof (rtpg_hdr_t);
+
+ /*
+ * Make sure that we have enough room to store everything
+ * that we want to, but only returned the requested about
+ * of data which is why d->d_len is set to the request amount.
+ * A client could issue a REPORT_TPGS with a length of 4 bytes
+ * which would be just enough to see how much space is actually
+ * used.
+ */
+ rqst_len = cdb[6] << 24 | cdb[7] << 16 |
+ cdb[8] << 8 | cdb[9];
+
+ if (rqst_len == 0) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ if ((r = (rtpg_hdr_t *)calloc(1, alloc_len)) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+
+ i = alloc_len - sizeof (r->len);
+ r->len[0] = hibyte(hiword(i));
+ r->len[1] = lobyte(hiword(i));
+ r->len[2] = hibyte(loword(i));
+ r->len[3] = lobyte(loword(i));
+ dp = &r->desc_list[0];
+ dp->tpg_cnt = avl_numnodes(&lu->l_all_open);
+ dp->status_code = 0;
+ dp->access_state = 0; /* Active/optimized */
+ dp->pref = 1;
+ dp->t_sup = 1;
+ dp->u_sup = 1;
+ dp->s_sup = 1;
+ dp->an_sup = 0;
+ dp->ao_sup = 1;
+ i = SPC_DEFAULT_TPG;
+ dp->tpg[0] = hibyte(loword(i));
+ dp->tpg[1] = lobyte(loword(i));
+
+ tp = &dp->targ_list[0];
+ lu_per = avl_first(&lu->l_all_open);
+ do {
+ tp->rel_tpi[0] = hibyte(loword(lu_per->l_targ->s_tp_grp));
+ tp->rel_tpi[1] = lobyte(loword(lu_per->l_targ->s_tp_grp));
+ lu_per = AVL_NEXT(&lu->l_all_open, lu_per);
+ } while (lu_per != NULL);
+
+ if (trans_send_datain(cmd, (char *)r, MIN(rqst_len, alloc_len), 0,
+ spc_free, True, (char *)r) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*ARGSUSED*/
+void
+spc_send_diag(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ /*
+ * SPC-3 Revision 21c, section 6.27
+ * Reserve bit checks
+ */
+ if ((cdb[1] & ~SPC_SEND_DIAG_SELFTEST) || cdb[2] || cdb[3] || cdb[4] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ /*
+ * There's no diagnostics to be run at this time. So, always
+ * return success. If, at some point in the future, it's determined
+ * that something can be done which is meaningful then place the
+ * code here.
+ */
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+static void
+spc_free(emul_handle_t id)
+{
+ free(id);
+}
+
+/*
+ * []----
+ * | spc_cmd_offline -- return IN_PROGRESS for media related commands
+ * |
+ * | During LU initialization the device is in an offline state. When
+ * | offlined only non-media related commands are allowed to proceed.
+ * | TEST_UNIT_READY is considered a media command since it must return
+ * | a CHECK_CONDITION if a media command would do so.
+ * []----
+ */
+void
+spc_cmd_offline(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ scsi_cmd_table_t *e;
+ int old_dtype;
+
+ e = &cmd->c_lu->l_cmd_table[cdb[0]];
+
+ switch (cdb[0]) {
+ case SCMD_TEST_UNIT_READY:
+ case SCMD_START_STOP:
+ case SCMD_READ:
+ case SCMD_READ_G1:
+ case SCMD_READ_G4:
+ case SCMD_WRITE:
+ case SCMD_WRITE_G1:
+ case SCMD_WRITE_G4:
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO, "SPC%x LUN%d Cmd %s\n",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ e->cmd_name == NULL ? "(no name)" : e->cmd_name);
+#endif
+ spc_sense_create(cmd, KEY_NOT_READY, 0);
+ spc_sense_ascq(cmd, SPC_ASC_IN_PROG, SPC_ASCQ_IN_PROG);
+ trans_send_complete(cmd, STATUS_CHECK);
+ break;
+
+ case SCMD_INQUIRY:
+ /*
+ * While the device is being initialized any inquiry commands
+ * will return an unknown device type. This will cause the
+ * transport to hold off further plumbing until the
+ * initialization is complete and we send the inventory change
+ * notice.
+ */
+ old_dtype = cmd->c_lu->l_common->l_dtype;
+ cmd->c_lu->l_common->l_dtype = 0x1f;
+ (*e->cmd_start)(cmd, cdb, cdb_len);
+ cmd->c_lu->l_common->l_dtype = old_dtype;
+ break;
+
+ default:
+ (*e->cmd_start)(cmd, cdb, cdb_len);
+ break;
+ }
+}
+
+/*
+ * []----
+ * | spc_sense_create -- allocate sense structure
+ * |
+ * | If additional header sense length is requested and the I_T_Q has
+ * | enabled descriptor sense data allocate the space.
+ * []----
+ */
+void
+spc_sense_create(t10_cmd_t *cmd, int sense_key, int addl_sense_len)
+{
+ char *buf;
+ int size;
+
+ /*
+ * It's possible under certain conditions -- namely a malloc error
+ * when setting up a command -- that the pointer to the ITL structure
+ * isn't valid. If that's the case don't attempt to dereference it
+ * to look at the dsense_enabled flag.
+ */
+ if ((cmd->c_lu != NULL) && (cmd->c_lu->l_dsense_enabled == True)) {
+ struct scsi_descr_sense_hdr d;
+
+ if ((buf = (char *)calloc(1,
+ sizeof (d) + 2 + addl_sense_len)) == NULL)
+ return;
+
+ size = sizeof (d) + addl_sense_len;
+ bzero(&d, sizeof (d));
+ d.ds_class = CLASS_EXTENDED_SENSE;
+ d.ds_code = CODE_FMT_DESCR_CURRENT;
+ d.ds_key = sense_key;
+ d.ds_addl_sense_length = addl_sense_len;
+ bcopy(&d, &buf[2], sizeof (d));
+
+ } else {
+ struct scsi_extended_sense e;
+
+ if ((buf = (char *)calloc(1, sizeof (e) + 2)) == NULL)
+ return;
+ size = sizeof (e);
+ bzero(&e, sizeof (e));
+ e.es_class = CLASS_EXTENDED_SENSE;
+ e.es_code = CODE_FMT_FIXED_CURRENT;
+ e.es_key = sense_key;
+ bcopy(&e, &buf[2], size);
+
+ }
+
+ /* ---- First two bytes of the sense store the length ---- */
+ buf[0] = hibyte(loword(size));
+ buf[1] = lobyte(loword(size));
+
+ cmd->c_cmd_sense = buf;
+ cmd->c_cmd_sense_len = size + 2;
+}
+
+/*
+ * []----
+ * | spc_sense_raw -- copy an existing sense buffer for return.
+ * |
+ * | If an emulation module already has a sense buffer there's no need
+ * | to decode the sense data and call the various spc_sense_ routines
+ * | to reencode the information.
+ * []----
+ */
+void
+spc_sense_raw(t10_cmd_t *cmd, uchar_t *sense_buf, size_t sense_len)
+{
+ ushort_t s = (ushort_t)sense_len;
+ if ((cmd->c_cmd_sense = malloc(sense_len + 2)) == NULL)
+ return;
+ bcopy(sense_buf, &cmd->c_cmd_sense[2], sense_len);
+ cmd->c_cmd_sense[0] = hibyte(s);
+ cmd->c_cmd_sense[1] = lobyte(s);
+ cmd->c_cmd_sense_len = s + 2;
+}
+
+void
+spc_sense_ascq(t10_cmd_t *cmd, int asc, int ascq)
+{
+ struct scsi_extended_sense s;
+ struct scsi_descr_sense_hdr d;
+
+ bcopy(&cmd->c_cmd_sense[2], &s, sizeof (s));
+
+ switch (s.es_code) {
+ case CODE_FMT_DESCR_CURRENT:
+ case CODE_FMT_DESCR_DEFERRED:
+ bcopy(&cmd->c_cmd_sense[2], &d, sizeof (d));
+ d.ds_add_code = asc;
+ d.ds_qual_code = ascq;
+ bcopy(&d, &cmd->c_cmd_sense[2], sizeof (d));
+ break;
+
+ default:
+ s.es_add_code = asc;
+ s.es_qual_code = ascq;
+ bcopy(&s, &cmd->c_cmd_sense[2], sizeof (s));
+ break;
+ }
+}
+
+void
+spc_sense_info(t10_cmd_t *cmd, uint64_t info)
+{
+ struct scsi_information_sense_descr isd;
+ struct scsi_extended_sense s;
+ char *p;
+ uint32_t fixed_info = (uint32_t)info;
+
+ switch (cmd->c_cmd_sense[2] & 0x0f) {
+ case CODE_FMT_DESCR_CURRENT:
+ case CODE_FMT_DESCR_DEFERRED:
+ isd.isd_descr_type = DESCR_INFORMATION;
+ isd.isd_addl_length = 0x0a;
+ isd.isd_valid = 1;
+ isd.isd_information[0] = (info >> 56) & 0xff;
+ isd.isd_information[1] = (info >> 48) & 0xff;
+ isd.isd_information[2] = (info >> 40) & 0xff;
+ isd.isd_information[3] = (info >> 32) & 0xff;
+ isd.isd_information[4] = (info >> 24) & 0xff;
+ isd.isd_information[5] = (info >> 16) & 0xff;
+ isd.isd_information[6] = (info >> 8) & 0xff;
+ isd.isd_information[7] = info & 0xff;
+ p = &cmd->c_cmd_sense[2] + sizeof (struct scsi_descr_sense_hdr);
+ bcopy(&isd, p, sizeof (isd));
+ break;
+
+ case CODE_FMT_VENDOR_SPECIFIC:
+ case CODE_FMT_FIXED_CURRENT:
+ case CODE_FMT_FIXED_DEFERRED:
+ default:
+ bcopy(&cmd->c_cmd_sense[2], &s, sizeof (s));
+
+ s.es_valid = 1;
+ if (info > FIXED_SENSE_ADDL_INFO_LEN) {
+ s.es_info_1 = 0xff;
+ s.es_info_2 = 0xff;
+ s.es_info_3 = 0xff;
+ s.es_info_4 = 0xff;
+ } else {
+ s.es_info_1 = hibyte(hiword(fixed_info));
+ s.es_info_2 = lobyte(hiword(fixed_info));
+ s.es_info_3 = hibyte(loword(fixed_info));
+ s.es_info_4 = lobyte(loword(fixed_info));
+ }
+ bcopy(&s, &cmd->c_cmd_sense[2], sizeof (s));
+ break;
+ }
+}
+
+void
+spc_sense_flags(t10_cmd_t *cmd, int flags)
+{
+ struct scsi_extended_sense s;
+
+ bcopy(&cmd->c_cmd_sense[2], &s, sizeof (s));
+ if (flags & SPC_SENSE_EOM)
+ s.es_eom = 1;
+ if (flags & SPC_SENSE_FM)
+ s.es_filmk = 1;
+ if (flags & SPC_SENSE_ILI)
+ s.es_ili = 1;
+ bcopy(&s, &cmd->c_cmd_sense[2], sizeof (s));
+}
+
+/*
+ * []----
+ * | spc_decode_lu_addr -- Decodes LU addressing as specified in SAM-3
+ * []----
+ */
+Boolean_t
+spc_decode_lu_addr(uint8_t *buf, int len, uint32_t *val)
+{
+ uint32_t lun;
+
+ if (len < 2)
+ return (False);
+
+ switch (buf[0] & SCSI_REPORTLUNS_ADDRESS_MASK) {
+ case SCSI_REPORTLUNS_ADDRESS_PERIPHERAL:
+ case SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE:
+ lun = ((buf[0] & 0x3f) << 8) | (buf[1] & 0xff);
+ break;
+
+ /*
+ * Since we never encode a LUN using this method, we
+ * shouldn't receive it back.
+ */
+ case SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT:
+ return (False);
+
+ case SCSI_REPORTLUNS_ADDRESS_EXTENDED_UNIT:
+ switch (buf[0] & SCSI_REPORTLUNS_ADDRESS_EXTENDED_MASK) {
+ case SCSI_REPORTLUNS_ADDRESS_EXTENDED_2B:
+ lun = buf[1] & 0xff;
+ break;
+
+ case SCSI_REPORTLUNS_ADDRESS_EXTENDED_4B:
+ if (len < 4)
+ return (False);
+ lun = buf[1] << 16 | buf[2] << 8 | (buf[3] & 0xff);
+ break;
+
+ case SCSI_REPORTLUNS_ADDRESS_EXTENDED_6B:
+ if (len < 6)
+ return (False);
+ /*
+ * This should be able to handle a 40-bit LUN,
+ * but since our LUNs are only 32-bit we don't
+ * bother to decode buf[1]. This is okay since
+ * we generate the LUN in the first place.
+ */
+ lun = buf[2] << 24 | buf[3] << 16 |
+ buf[4] << 8 | (buf[5] & 0xff);
+ break;
+
+ case SCSI_REPORTLUNS_ADDRESS_EXTENDED_8B:
+ /*
+ * Since we current don't support larger than
+ * 32-bit LUNs we'll never create an extended
+ * address using this format. So, if we are to
+ * get this format in, just return an error.
+ */
+ return (False);
+
+ break;
+ }
+ }
+ *val = lun;
+ return (True);
+}
+
+/*
+ * []----
+ * | spc_encode_lu_addr -- encode, based on SAM-3/SPC-3 specs, a LUN
+ * |
+ * | NOTE: This routine only deals with 32-bit logical unit numbers.
+ * | If this program ever switches to using larger values we need to
+ * | simply deal with those formats.
+ * []----
+ */
+Boolean_t
+spc_encode_lu_addr(uint8_t *buf, int select_field, uint32_t lun)
+{
+ if (lun < 256) {
+
+ /*
+ * SAM-3 revision 14, Section 4.9.6.
+ * No bus identifier for our luns.
+ */
+ buf[0] = SCSI_REPORTLUNS_ADDRESS_PERIPHERAL;
+ buf[1] = lun;
+
+ } else if (lun < 0x3fff) {
+
+ /*
+ * SAM-3 revision 14, Section 4.9.7.
+ * 14-bit flat address space.
+ */
+ buf[0] = SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE |
+ (lun >> 8 & 0x3f);
+ buf[1] = lun & 0xff;
+
+ } else if (select_field == SCSI_REPORTLUNS_SELECT_ALL) {
+
+ buf[0] = SCSI_REPORTLUNS_ADDRESS_EXTENDED_UNIT |
+ SCSI_REPORTLUNS_ADDRESS_EXTENDED_6B;
+ /*
+ * 32-bit limitation. This format should be able to
+ * handle a 40-bit LUN.
+ */
+ buf[1] = 0;
+ buf[2] = lun >> 24 & 0xff;
+ buf[3] = lun >> 16 & 0xff;
+ buf[4] = lun >> 8 & 0xff;
+ buf[5] = lun & 0xff;
+ } else
+ /*
+ * Either the user hasn't requested extended unit addressing
+ * or the LU is greater than 32bits. Since internally we
+ * only have 32bit numbers it's more likely that the initiator
+ * hasn't selected a correct report format.
+ */
+ return (False);
+ return (True);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_spc.h b/usr/src/cmd/iscsi/iscsitgtd/t10_spc.h
new file mode 100644
index 0000000000..90ec0ac9f1
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc.h
@@ -0,0 +1,362 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SPC_H
+#define _SPC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SPC-3 |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * FIXED_SENSE_ADDL_INFO_LEN is the length of INFORMATION field
+ * in fixed format sense data
+ */
+#define FIXED_SENSE_ADDL_INFO_LEN 0xFFFFFFFF
+#define INFORMATION_SENSE_DESCR sizeof (struct scsi_information_sense_descr)
+
+#include <sys/scsi/generic/mode.h>
+
+/*
+ * SPC Command Functions
+ */
+void spc_tur(struct t10_cmd *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_request_sense(struct t10_cmd *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_unsupported(struct t10_cmd *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_inquiry(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_mselect(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_mselect_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset,
+ char *data, size_t data_len);
+void spc_report_luns(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_report_tpgs(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_msense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_startstop(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_send_diag(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+
+/*
+ * SPC Support Functions
+ */
+void spc_cmd_offline(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_sense_create(struct t10_cmd *cmd, int sense_key, int addl_sense_len);
+void spc_sense_ascq(struct t10_cmd *cmd, int asc, int ascq);
+void spc_sense_info(t10_cmd_t *cmd, uint64_t info);
+void spc_sense_flags(t10_cmd_t *cmd, int flags);
+void spc_sense_raw(t10_cmd_t *cmd, uchar_t *sense_buf, size_t sense_len);
+Boolean_t spc_decode_lu_addr(uint8_t *buf, int len, uint32_t *val);
+Boolean_t spc_encode_lu_addr(uint8_t *buf, int select_field, uint32_t lun);
+
+/*
+ * SPC flags to use when setting various sense code flags
+ */
+#define SPC_SENSE_EOM 0x01
+#define SPC_SENSE_FM 0x02
+#define SPC_SENSE_ILI 0x04
+
+#ifdef _BIG_ENDIAN
+#define htonll(x) (x)
+#define ntohll(x) (x)
+#else
+#define htonll(x) ((((unsigned long long)htonl(x & 0xffffffff)) << 32) + \
+ htonl(x >> 32))
+#define ntohll(x) ((((unsigned long long)ntohl(x)) << 32) + ntohl(x >> 32))
+#endif
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SPC-3, revision 21c -- ASC/ASCQ values |
+ * | The full tables can be found in Appendix D (numerical order) or |
+ * | section 4.5.6 (alphabetical order). There are close to fifteen |
+ * | pages of values which will not be included here. Only those used |
+ * | by the code. |
+ * []------------------------------------------------------------------[]
+ */
+#define SPC_ASC_INVALID_CDB 0x24
+#define SPC_ASCQ_INVALID_CDB 0x00
+#define SPC_ASC_PWR_RESET 0x29
+#define SPC_ASCQ_PWR_RESET 0x00
+#define SPC_ASC_PWR_ON 0x29
+#define SPC_ASCQ_PWR_ON 0x01
+#define SPC_ASC_BUS_RESET 0x29
+#define SPC_ASCQ_BUS_RESET 0x02
+#define SPC_ASC_FM_DETECTED 0x00 /* file-mark detected */
+#define SPC_ASCQ_FM_DETECTED 0x01
+#define SPC_ASC_EOP 0x00 /* end-of-partition/medium detected */
+#define SPC_ASCQ_EOP 0x02
+#define SPC_ASC_WRITE_ERROR 0x0c
+#define SPC_ASCQ_WRITE_ERROR 0x00
+#define SPC_ASC_CAP_CHANGE 0x2a
+#define SPC_ASCQ_CAP_CHANGE 0x09
+#define SPC_ASC_IN_PROG 0x04
+#define SPC_ASCQ_IN_PROG 0x07
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SAM-3, revision 14, section 5.2 - Command descriptor block (CDB) |
+ * | |
+ * | "All CDBs shall contain a CONTROL byte (see table 21). The |
+ * | location of the CONTROL byte within a CDB depends on the CDB |
+ * | format (see SPC-3)." |
+ * | |
+ * | bits meaning |
+ * | 6-7 vendor specific (we don't use so must be zero) |
+ * | 3-5 reserved must be zero |
+ * | 2 NACA (currently we don't support so must be zero) |
+ * | 1 Obsolete |
+ * | 0 Link (currently we don't support so must be zero) |
+ * | |
+ * | So, this means the control byte must be zero and therefore if |
+ * | this macro returns a non-zero value the emulation code should |
+ * | return a CHECK CONDITION with status set to ILLEGAL REQUEST |
+ * | and the additional sense code set to INVALID FIELD IN CDB. |
+ * | |
+ * | In the future this will likely change with support routines |
+ * | added for dealing with NACA and Linked commands. |
+ * []------------------------------------------------------------------[]
+ */
+#define SAM_CONTROL_BYTE_RESERVED(byte) (byte)
+
+/* ---- Disable Block Descriptors ---- */
+#define SPC_MODE_SENSE_DBD 0x8
+
+#define SPC_GROUP4_SERVICE_ACTION_MASK 0x1f
+
+#define SPC_SEND_DIAG_SELFTEST 0x04
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SPC-3 revision 21c, section 6.4 -- INQUIRY |
+ * | Various defines. The structure for the inquiry command can be |
+ * | found in /usr/include/sys/scsi/generic/inquiry.h |
+ * []------------------------------------------------------------------[]
+ */
+#define SPC_INQUIRY_CODE_SET_BINARY 1
+#define SPC_INQUIRY_CODE_SET_ASCII 2
+#define SPC_INQUIRY_CODE_SET_UTF8 3
+
+/* ---- Table 82: Inquiry Version ---- */
+#define SPC_INQ_VERS_NONE 0x00
+#define SPC_INQ_VERS_OBSOLETE 0x02
+#define SPC_INQ_VERS_SPC_1 0x03
+#define SPC_INQ_VERS_SPC_2 0x04
+#define SPC_INQ_VERS_SPC_3 0x05
+
+/* ---- INQUIRY Response Data Format field ---- */
+#define SPC_INQ_RDF 0x02 /* all other values are OBSOLETE */
+
+/*
+ * Table 85 -- Version descriptor values
+ * There are many, many different values available, so we'll only include
+ * those that we actually use.
+ */
+#define SPC_INQ_VD_SAM3 0x0076
+#define SPC_INQ_VD_SPC3 0x0307
+#define SPC_INQ_VD_SBC2 0x0322
+#define SPC_INQ_VD_SSC3 0x0400
+#define SPC_INQ_VD_OSD 0x0355
+
+#define SPC_INQ_PAGE0 0x00
+#define SPC_INQ_PAGE83 0x83
+
+/* ---- REPORT LUNS select report has valid values of 0, 1, or 2 ---- */
+#define SPC_RPT_LUNS_SELECT_MASK 0x03
+
+/* ---- Table 293: IDENTIFIER TYPE field ---- */
+#define SPC_INQUIRY_ID_TYPE_T10ID 1 /* ref 7.6.4.3 */
+#define SPC_INQUIRY_ID_TYPE_EUI 2 /* ref 7.6.4.4 */
+#define SPC_INQUIRY_ID_TYPE_NAA 3 /* ref 7.6.4.5 */
+#define SPC_INQUIRY_ID_TYPE_RELATIVE 4 /* ref 7.6.4.6 */
+#define SPC_INQUIRY_ID_TYPE_TARG_PORT 5 /* ref 7.6.4.7 */
+#define SPC_INQUIRY_ID_TYPE_LUN 6 /* ref 7.6.4.8 */
+#define SPC_INQUIRY_ID_TYPE_MD5 7 /* ref 7.6.4.9 */
+#define SPC_INQUIRY_ID_TYPE_SCSI 8 /* ref 7.6.4.10 */
+
+/* ---- Table 292: ASSOCIATION field ---- */
+#define SPC_INQUIRY_ASSOC_LUN 0
+#define SPC_INQUIRY_ASSOC_TARGPORT 1
+#define SPC_INQUIRY_ASSOC_TARG 2
+
+/* ---- Table 80: Peripheral qualifier ---- */
+#define SPC_INQUIRY_PERIPH_CONN 0
+#define SPC_INQUIRY_PERIPH_DISCONN 1
+#define SPC_INQUIRY_PERIPH_INVALID 3
+
+/* ---- Table 256: PROTOCOL IDENTIFIER values ---- */
+#define SPC_INQUIRY_PROTOCOL_FC 0
+#define SPC_INQUIRY_PROTOCOL_PSCSI 1
+#define SPC_INQUIRY_PROTOCOL_SSA 2
+#define SPC_INQUIRY_PROTOCOL_IEEE1394 3
+#define SPC_INQUIRY_PROTOCOL_SCSIRDMA 4
+#define SPC_INQUIRY_PROTOCOL_ISCSI 5
+#define SPC_INQUIRY_PROTOCOL_SAS 6
+#define SPC_INQUIRY_PROTOCOL_ADT 7
+#define SPC_INQUIRY_PROTOCOL_ATA 8
+
+#define SPC_DEFAULT_TPG 1
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SPC-4 revision 1a, section 6.25 -- REPORT TARGET PORT GROUPS |
+ * | Structures and defines |
+ * []------------------------------------------------------------------[]
+ */
+/*
+ * The service action must be set to 0x0A. This command is really a
+ * MAINTENANCE_IN command with a specific service action.
+ */
+#define SPC_MI_SVC_MASK 0x1f
+#define SPC_MI_SVC_RTPG 0x0a
+
+/* ---- Table 167: Target port descriptor format ---- */
+typedef struct rtpg_targ_desc {
+ uchar_t obsolete[2],
+ rel_tpi[2];
+} rtpg_targ_desc_t;
+
+/* ---- Table 164: Target port group descript format ---- */
+typedef struct rtpg_desc {
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t access_state : 4,
+ : 3,
+ pref : 1;
+ uchar_t ao_sup : 1,
+ an_sup : 1,
+ s_sup : 1,
+ u_sup : 1,
+ : 3,
+ t_sup : 1;
+#elif defined(_BIT_FIELDS_HTOL)
+ uchar_t pref : 1,
+ : 3,
+ access_state : 4;
+ uchar_t t_sup : 1,
+ : 3,
+ u_sup : 1,
+ s_sup : 1,
+ an_sup : 1,
+ ao_sup : 1;
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+ uchar_t tpg[2],
+ reserve_1,
+ status_code,
+ vendor_spec,
+ tpg_cnt;
+ rtpg_targ_desc_t targ_list[1];
+} rtpg_desc_t;
+
+/* ---- Table 163: parameter data format. ---- */
+typedef struct rtpg_data {
+ uchar_t len[4];
+ rtpg_desc_t desc_list[1];
+} rtpg_hdr_t;
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SPC-3, revision 21c, section 6.6 -- LOG_SENSE |
+ * | Structure and defines |
+ * []------------------------------------------------------------------[]
+ */
+#define SSC_LOG_SP 0x01 /* save parameters */
+#define SSC_LOG_PPC 0x02 /* parameter pointer control */
+#define SPC_LOG_PAGE_MASK 0x3f
+
+/* ---- section 7.2.1, Table 192: Log Parameter ---- */
+typedef struct spc_log_select_param {
+ char param_code[2];
+#if defined(_BIT_FIELDS_LTOH)
+ char lp : 1, /* list parameter */
+ lbin : 1,
+ tmc : 2, /* threshold met criteria */
+ etc : 1, /* enable threshold comparison */
+ tsd : 1, /* target save disable */
+ ds : 1, /* disable save */
+ du : 1; /* disable update */
+#elif defined(_BIT_FIELDS_HTOL)
+ char du : 1, /* disable update */
+ ds : 1, /* disable save */
+ tsd : 1, /* target save disable */
+ etc : 1, /* enable threshold comparison */
+ tmc : 2, /* threshold met criteria */
+ lbin : 1,
+ lp : 1; /* list parameter */
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+ char len; /* length of bytes to follow */
+} spc_log_select_param_t;
+
+/* ---- section 7.2.12, table 218: Supported log pages ---- */
+typedef struct spc_log_supported_pages {
+ char page_code,
+ resvd,
+ length[2],
+ list[1];
+} spc_log_supported_pages_t;
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SPC-3, revision 21c, section 6.9 -- MODE_SENSE |
+ * | Structures and defines |
+ * []------------------------------------------------------------------[]
+ */
+/* ---- Section 7.4.11, Table 250: Information Controller Page ---- */
+struct mode_info_ctrl {
+ struct mode_page mode_page;
+ /*
+ * Currently we don't sent any of this information and it's set
+ * to zero's. We only care about the size.
+ */
+ char info_data[10];
+};
+
+#define MODE_SENSE_PAGE3_CODE 0x03
+#define MODE_SENSE_PAGE4_CODE 0x04
+#define MODE_SENSE_CACHE 0x08
+#define MODE_SENSE_CONTROL 0x0a
+#define MODE_SENSE_COMPRESSION 0x0f
+#define MODE_SENSE_DEV_CONFIG 0x10
+#define MODE_SENSE_INFO_CTRL 0x1c
+#define MODE_SENSE_SEND_ALL 0x3f
+
+#define SCSI_REPORTLUNS_ADDRESS_SIZE 8
+#define SCSI_REPORTLUNS_ADDRESS_MASK 0xC0
+#define SCSI_REPORTLUNS_ADDRESS_PERIPHERAL 0x00
+#define SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE 0x40
+#define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT 0x80
+#define SCSI_REPORTLUNS_ADDRESS_EXTENDED_UNIT 0xC0
+#define SCSI_REPORTLUNS_ADDRESS_EXTENDED_2B 0x00
+#define SCSI_REPORTLUNS_ADDRESS_EXTENDED_4B 0x10
+#define SCSI_REPORTLUNS_ADDRESS_EXTENDED_6B 0x20
+#define SCSI_REPORTLUNS_ADDRESS_EXTENDED_8B 0x30
+#define SCSI_REPORTLUNS_ADDRESS_EXTENDED_MASK 0x30
+#define SCSI_REPORTLUNS_SELECT_ALL 0x02
+
+#endif /* _SPC_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_ssc.c b/usr/src/cmd/iscsi/iscsitgtd/t10_ssc.c
new file mode 100644
index 0000000000..1de425e042
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_ssc.c
@@ -0,0 +1,1627 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Implementation of SSC-2 emulation
+ */
+
+#include <strings.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <aio.h>
+#include <sys/asynch.h>
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/targets/stdef.h>
+#include <netinet/in.h>
+
+#include "target.h"
+#include "utility.h"
+#include "t10.h"
+#include "t10_spc.h"
+#include "t10_ssc.h"
+
+/*
+ * []----
+ * | Forward declarations
+ * []----
+ */
+static scsi_cmd_table_t ssc_table[];
+static void ssc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+static void ssc_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset,
+ char *data, size_t data_len);
+static void ssc_free(emul_handle_t e);
+static void ssc_write_cmplt(emul_handle_t e);
+static void ssc_read_cmplt(emul_handle_t id);
+static void ssc_setup_tape(ssc_params_t *s, t10_lu_common_t *lu);
+static uint32_t find_last_obj_id(char *file_mark, off_t eod);
+static char *sense_dev_config(ssc_params_t *s, char *data);
+static char *sense_compression(ssc_params_t *s, char *data);
+
+/*
+ * []----
+ * | ssc_init_common -- initialize common information that all ITLs will use
+ * []----
+ */
+Boolean_t
+ssc_init_common(t10_lu_common_t *lu)
+{
+ ssc_params_t *s;
+ ssc_obj_mark_t mark;
+
+ if (lu->l_mmap == MAP_FAILED)
+ return (False);
+
+ if ((s = (ssc_params_t *)calloc(1, sizeof (*s))) == NULL)
+ return (False);
+
+ s->s_size = lu->l_size;
+ s->s_fast_write_ack = lu->l_fast_write_ack;
+
+ bcopy(lu->l_mmap, &mark, sizeof (mark));
+ if (mark.som_sig != SSC_OBJ_SIG) {
+ ssc_setup_tape(s, lu);
+ }
+ s->s_cur_fm = 0;
+ s->s_cur_rec = sizeof (ssc_obj_mark_t);
+ s->s_prev_rec = s->s_cur_rec;
+ s->s_state = lu->l_state;
+
+ lu->l_dtype_params = (void *)s;
+ return (True);
+}
+
+/*
+ * []----
+ * | ssc_fini_common -- free any resources
+ * []----
+ */
+void
+ssc_fini_common(t10_lu_common_t *lu)
+{
+ free(lu->l_dtype_params);
+}
+
+void
+ssc_task_mgmt(t10_lu_common_t *lu, TaskOp_t op)
+{
+ ssc_params_t *s = (ssc_params_t *)lu->l_dtype_params;
+
+ switch (op) {
+ case CapacityChange:
+ s->s_size = lu->l_size;
+ break;
+
+ case DeviceOnline:
+ s->s_state = lu->l_state;
+ }
+}
+
+/*
+ * []----
+ * | ssc_init_per -- initialize per ITL information
+ * []----
+ */
+void
+ssc_init_per(t10_lu_impl_t *itl)
+{
+ ssc_params_t *s = (ssc_params_t *)itl->l_common->l_dtype_params;
+
+ if (s->s_state == lu_online)
+ itl->l_cmd = ssc_cmd;
+ else
+ itl->l_cmd = spc_cmd_offline;
+ itl->l_data = ssc_data;
+ itl->l_cmd_table = ssc_table;
+
+ itl->l_status = KEY_UNIT_ATTENTION;
+ itl->l_asc = SPC_ASC_PWR_ON;
+ itl->l_ascq = SPC_ASCQ_PWR_ON;
+}
+
+/*
+ * []----
+ * | ssc_fini_per -- release or free any ITL resources
+ * []----
+ */
+/*ARGSUSED*/
+void
+ssc_fini_per(t10_lu_impl_t *itl)
+{
+}
+
+/*
+ * []----
+ * | ssc_cmd -- start a SCSI command
+ * |
+ * | This routine is called from within the SAM-3 Task router.
+ * []----
+ */
+static void
+ssc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ scsi_cmd_table_t *e;
+
+ e = &cmd->c_lu->l_cmd_table[cdb[0]];
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO, "SSC%x LUN%d Cmd %s\n",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ e->cmd_name == NULL ? "(no name)" : e->cmd_name);
+#endif
+ (*e->cmd_start)(cmd, cdb, cdb_len);
+}
+
+/*
+ * []----
+ * | ssc_data -- Data phase for command.
+ * |
+ * | Normally this is only called for the WRITE command. Other commands
+ * | that have a data in phase will probably be short circuited when
+ * | we call trans_rqst_dataout() and the data is already available.
+ * | At least this is true for iSCSI. FC however will need a DataIn phase
+ * | for commands like MODE SELECT and PGROUT.
+ * []----
+ */
+static void
+ssc_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+ size_t data_len)
+{
+ scsi_cmd_table_t *e;
+
+ e = &cmd->c_lu->l_cmd_table[cmd->c_cdb[0]];
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO, "SSC%x LUN%d Data %s\n",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ e->cmd_name);
+#endif
+ (*e->cmd_data)(cmd, id, offset, data, data_len);
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | SCSI Streaming Commands - 3 |
+ * | T10/1611-D Revision 01c |
+ * | The following functions implement the emulation of SSC-3 type |
+ * | commands. |
+ * []------------------------------------------------------------------[]
+ */
+
+/*ARGSUSED*/
+static void
+ssc_read(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ ssc_io_t *io;
+ ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd);
+ ssc_obj_mark_t fm,
+ rm;
+ int fixed,
+ sili;
+ off_t offset = 0;
+ size_t xfer,
+ req_len;
+ void *mmap = cmd->c_lu->l_common->l_mmap;
+
+ fixed = cdb[1] & 0x01;
+ sili = cdb[1] & 0x02;
+
+ if (s == NULL)
+ return;
+
+ /*
+ * Standard error checking.
+ */
+ if ((sili && fixed) || (cdb[1] & 0xfc) ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ req_len = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4];
+ req_len *= fixed ? 512 : 1;
+
+ if (req_len == 0) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO,
+ "SSC%x LUN%d read 0x%x bytes",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, req_len);
+#endif
+
+ bcopy((char *)mmap + s->s_cur_fm, &fm, sizeof (fm));
+ bcopy((char *)mmap + s->s_cur_fm + s->s_cur_rec, &rm, sizeof (rm));
+
+ if (rm.som_sig != SSC_OBJ_SIG) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SSC%x LUN%d bad RECORD-MARK",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num);
+ spc_sense_create(cmd, KEY_MEDIUM_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ } else if (rm.som_type != SSC_OBJ_TYPE_RM) {
+ s->s_cur_fm += fm.o_fm.size;
+ s->s_cur_rec = sizeof (ssc_obj_mark_t);
+ s->s_prev_rec = s->s_cur_rec;
+
+ spc_sense_create(cmd, KEY_NO_SENSE, 0);
+ spc_sense_ascq(cmd, SPC_ASC_FM_DETECTED, SPC_ASCQ_FM_DETECTED);
+ spc_sense_info(cmd, req_len);
+ spc_sense_flags(cmd, SPC_SENSE_FM);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ } else if ((sili == 0) &&
+ ((rm.o_rm.size - sizeof (ssc_obj_mark_t)) != req_len)) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SSC%x LUN%d Wrong size read",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num);
+
+ s->s_prev_rec = s->s_cur_rec;
+ s->s_cur_rec += rm.o_rm.size;
+
+ spc_sense_create(cmd, KEY_NO_SENSE, 0);
+ spc_sense_flags(cmd, SPC_SENSE_ILI);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ do {
+ if ((io = (ssc_io_t *)calloc(1, sizeof (*io))) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+
+ xfer = MIN((req_len - offset), T10_MAX_OUT(cmd));
+ io->sio_cmd = cmd;
+ io->sio_offset = offset;
+ io->sio_total = req_len;
+ io->sio_data_len = xfer;
+ io->sio_data = (char *)mmap + s->s_cur_fm +
+ s->s_cur_rec + sizeof (ssc_obj_mark_t) + offset;
+ io->sio_aio.a_aio.aio_return = xfer;
+
+ ssc_read_cmplt((emul_handle_t)io);
+ offset += xfer;
+ } while (offset < req_len);
+
+ s->s_prev_rec = s->s_cur_rec;
+ s->s_cur_rec += req_len + sizeof (ssc_obj_mark_t);
+}
+
+static void
+ssc_read_cmplt(emul_handle_t id)
+{
+ ssc_io_t *io = (ssc_io_t *)id;
+ t10_cmd_t *cmd = io->sio_cmd;
+
+ if (io->sio_aio.a_aio.aio_return != io->sio_data_len) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if ((io->sio_offset + io->sio_data_len) < io->sio_total) {
+ if (trans_send_datain(cmd, io->sio_data, io->sio_data_len,
+ io->sio_offset, ssc_free, False, io) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+ } else {
+ if (trans_send_datain(cmd, io->sio_data, io->sio_data_len,
+ io->sio_offset, ssc_free, True, io) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+ }
+}
+
+/*ARGSUSED*/
+static void
+ssc_write(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ ssc_obj_mark_t mark;
+ size_t request_len,
+ max_xfer;
+ int fixed,
+ prev_id;
+ ssc_io_t *io;
+ ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd);
+
+ if (s == NULL)
+ return;
+
+ if ((cdb[1] & 0xfe) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ fixed = cdb[1];
+ request_len = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4];
+ request_len *= fixed ? 512 : 1;
+
+#ifdef FULL_DEBUG
+ queue_prt(mgmtq, Q_STE_IO,
+ "SSC%x LUN%d write %d, fixed %d",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ request_len, fixed);
+#endif
+ io = cmd->c_emul_id;
+ if (io == NULL) {
+ if ((io = calloc(1, sizeof (*io))) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+ io->sio_total = request_len;
+ io->sio_cmd = cmd;
+ io->sio_offset = 0;
+
+ /*
+ * Writing looses all information after the current
+ * file-mark. So, check to see if the current file-mark
+ * size doesn't reflect the end-of-media. If not, update
+ * it.
+ */
+ bcopy((char *)cmd->c_lu->l_common->l_mmap + s->s_cur_fm,
+ &mark, sizeof (mark));
+ if (mark.o_fm.size !=
+ (s->s_size - sizeof (ssc_obj_mark_t) - s->s_cur_fm)) {
+ mark.o_fm.size = s->s_size - sizeof (ssc_obj_mark_t) -
+ s->s_cur_fm;
+ bcopy(&mark, (char *)cmd->c_lu->l_common->l_mmap +
+ s->s_cur_fm, sizeof (mark));
+ }
+
+ /*
+ * End-of-Partition detection
+ */
+ if ((s->s_cur_rec + request_len) > (mark.o_fm.size)) {
+ spc_sense_create(cmd, KEY_VOLUME_OVERFLOW, 0);
+ spc_sense_ascq(cmd, SPC_ASC_EOP, SPC_ASCQ_EOP);
+ spc_sense_flags(cmd, SPC_SENSE_EOM);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if ((s->s_cur_fm == 0) &&
+ (s->s_cur_rec == sizeof (ssc_obj_mark_t))) {
+
+ /*
+ * The current position is a BOM. By setting
+ * the prev_id value to -1 the code below will
+ * create the first ID with a value of zero
+ * Which is what the specification requires.
+ */
+ prev_id = -1;
+
+ } else if (s->s_cur_rec == sizeof (ssc_obj_mark_t)) {
+
+ /*
+ * If the current position is at the beginning of
+ * this file-mark use the object ID found in
+ * the file-mark header. It will have been updated
+ * from the last object ID in the previous file-mark.
+ *
+ * NOTE: We're counting on 'mark' still referring
+ * to the current file mark here.
+ */
+ prev_id = mark.o_fm.last_obj_id;
+ } else {
+ bcopy((char *)cmd->c_lu->l_common->l_mmap +
+ s->s_cur_fm + s->s_prev_rec, &mark, sizeof (mark));
+ prev_id = mark.o_rm.obj_id;
+ }
+
+ bzero(&mark, sizeof (mark));
+ mark.som_sig = SSC_OBJ_SIG;
+ mark.som_type = SSC_OBJ_TYPE_RM;
+ mark.o_rm.size = request_len +
+ sizeof (ssc_obj_mark_t);
+ mark.o_rm.obj_id = prev_id + 1;
+ bcopy(&mark, (char *)cmd->c_lu->l_common->l_mmap +
+ s->s_cur_fm + s->s_cur_rec, sizeof (mark));
+ }
+
+ max_xfer = min(io->sio_total - io->sio_offset,
+ cmd->c_lu->l_targ->s_maxout);
+ io->sio_aio.a_aio.aio_return = max_xfer;
+ io->sio_data_len = max_xfer;
+ io->sio_data = (char *)cmd->c_lu->l_common->l_mmap +
+ s->s_cur_fm + s->s_cur_rec + sizeof (mark) + io->sio_offset;
+
+ if (trans_rqst_dataout(cmd, io->sio_data, io->sio_data_len,
+ io->sio_offset, io) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*ARGSUSED*/
+static void
+ssc_write_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+ size_t data_len)
+{
+ ssc_io_t *io = (ssc_io_t *)id;
+ ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd);
+
+ if (s == NULL)
+ return;
+
+ if (s->s_fast_write_ack == False) {
+ /*
+ * We only need to worry about sync'ing the blocks
+ * in the mmap case because if the fast cache isn't
+ * enabled for AIO the file will be opened with F_SYNC
+ * which performs the correct action.
+ */
+ if (fsync(cmd->c_lu->l_common->l_fd) == -1) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ }
+ ssc_write_cmplt((emul_handle_t)io);
+}
+
+static void
+ssc_write_cmplt(emul_handle_t e)
+{
+ ssc_io_t *io = (ssc_io_t *)e;
+ t10_cmd_t *cmd = io->sio_cmd;
+ ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd);
+
+ if (s == NULL)
+ return;
+
+ if ((io->sio_offset + io->sio_data_len) < io->sio_total) {
+ io->sio_offset += io->sio_data_len;
+ ssc_write(cmd, cmd->c_cdb, cmd->c_cdb_len);
+ return;
+ }
+
+ s->s_prev_rec = s->s_cur_rec;
+ s->s_cur_rec += io->sio_total + sizeof (ssc_obj_mark_t);
+ free(io);
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+/*ARGSUSED*/
+static void
+ssc_rewind(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd);
+
+ if (s == NULL)
+ return;
+
+ if ((cdb[1] & ~SSC_REWIND_IMMED) || cdb[2] || cdb[3] || cdb[4] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ s->s_cur_fm = 0;
+ s->s_cur_rec = sizeof (ssc_obj_mark_t);
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+/*ARGSUSED*/
+static void
+ssc_read_limits(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ struct read_blklim *rb;
+ int min_size = 512;
+
+ if (cdb[1] || cdb[2] || cdb[3] || cdb[4] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if ((rb = (struct read_blklim *)calloc(1, sizeof (*rb))) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+
+ /*
+ * maximum block size is set to zero to indicate no maximum block
+ * limit is specified.
+ */
+ rb->granularity = 9; /* 512 block sizes */
+ rb->min_hi = hibyte(min_size);
+ rb->min_lo = lobyte(min_size);
+
+ if (trans_send_datain(cmd, (char *)rb, sizeof (*rb), 0, ssc_free,
+ True, (emul_handle_t)rb) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*ARGSUSED*/
+static void
+ssc_space(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ int code,
+ count;
+ ssc_params_t *s = T10_PARAMS_AREA(cmd);
+ ssc_obj_mark_t mark;
+ t10_lu_common_t *lu = cmd->c_lu->l_common;
+
+ if (s == NULL)
+ return;
+
+ if ((cdb[1] & 0xf0) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ code = cdb[1] & 0x0f;
+ count = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4];
+
+ if ((count == 0) && (code != SSC_SPACE_CODE_END_OF_DATA)) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ }
+
+ switch (code) {
+ case SSC_SPACE_CODE_BLOCKS:
+ if (count < 0) {
+ bcopy((char *)lu->l_mmap + s->s_cur_fm + s->s_cur_rec,
+ &mark, sizeof (mark));
+ if ((mark.som_sig == SSC_OBJ_SIG) &&
+ (mark.som_type == SSC_OBJ_TYPE_RM)) {
+ count = mark.o_rm.obj_id + count;
+
+ /*
+ * If the count is still negative it means
+ * the request is still attempting to go
+ * beyond the beginning of the file mark.
+ */
+ if (count < 0) {
+ count *= -1;
+ spc_sense_create(cmd, KEY_NO_SENSE, 0);
+ spc_sense_ascq(cmd,
+ SPC_ASC_FM_DETECTED,
+ SPC_ASCQ_FM_DETECTED);
+ spc_sense_info(cmd, count);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ s->s_cur_rec = s->s_cur_fm + sizeof (mark);
+ } else {
+ /*
+ * Something is not right. We'll let the
+ * processing below determine exactly what
+ * is wrong. So don't update the record
+ * mark and sent the count to 1.
+ */
+ count = 1;
+ }
+ }
+
+ while (count) {
+ bcopy((char *)lu->l_mmap + s->s_cur_fm + s->s_cur_rec,
+ &mark, sizeof (mark));
+
+ /*
+ * Something internally bad has happened with
+ * the marks in the file.
+ */
+ if (mark.som_sig != SSC_OBJ_SIG) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SSC%x LUN%d, bad sig mark: "
+ "expected=0x%x, got=0x%x",
+ cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num,
+ SSC_OBJ_SIG, mark.som_sig);
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ /*
+ * Hit a filemark. Update the current record if
+ * we're not at the End-Of-Medium.
+ */
+ if (mark.som_type == SSC_OBJ_TYPE_FM) {
+ if (mark.o_fm.eom == True) {
+ spc_sense_create(cmd, KEY_MEDIUM_ERROR,
+ 0);
+ spc_sense_ascq(cmd, SPC_ASC_EOP,
+ SPC_ASCQ_EOP);
+ spc_sense_info(cmd, count);
+ spc_sense_flags(cmd, SPC_SENSE_EOM);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ s->s_cur_fm += s->s_cur_rec;
+ s->s_cur_rec += sizeof (mark);
+ spc_sense_create(cmd, KEY_NO_SENSE, 0);
+ spc_sense_ascq(cmd, SPC_ASC_FM_DETECTED,
+ SPC_ASCQ_FM_DETECTED);
+ spc_sense_info(cmd, count);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ s->s_cur_rec += mark.o_rm.size;
+ count--;
+ }
+ trans_send_complete(cmd, STATUS_CHECK);
+ break;
+
+ case SSC_SPACE_CODE_FILEMARKS:
+ if (count < 0) {
+ bcopy((char *)lu->l_mmap + s->s_cur_fm, &mark,
+ sizeof (mark));
+ if ((mark.som_sig == SSC_OBJ_SIG) &&
+ (mark.som_type == SSC_OBJ_TYPE_FM)) {
+ count = mark.o_fm.num + count;
+
+ /*
+ * If the count is still negative it means
+ * the request is still attempting to go
+ * beyond the beginning of the file mark.
+ */
+ if (count < 0) {
+ count *= -1;
+ spc_sense_create(cmd, KEY_NO_SENSE, 0);
+ spc_sense_ascq(cmd,
+ SPC_ASC_FM_DETECTED,
+ SPC_ASCQ_FM_DETECTED);
+ spc_sense_info(cmd, count);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ s->s_cur_fm = 0;
+ s->s_cur_rec = sizeof (ssc_obj_mark_t);
+ } else {
+ /*
+ * Something is not right. We'll let the
+ * processing below determine exactly what
+ * is wrong. So don't update the record
+ * mark and sent the count to 1.
+ */
+ count = 1;
+ }
+ }
+
+ while (count--) {
+ bcopy((char *)lu->l_mmap + s->s_cur_fm, &mark,
+ sizeof (mark));
+ if (mark.som_sig != SSC_OBJ_SIG) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SSC%x LUN%d, bad sig mark: "
+ "expected=0x%x, got=0x%x",
+ cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num,
+ SSC_OBJ_SIG, mark.som_sig);
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ if (mark.som_type != SSC_OBJ_TYPE_FM) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SSC%x LUN%d, bad mark type: "
+ "expected=0x%x, got=0x%x",
+ cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num,
+ SSC_OBJ_TYPE_FM, mark.som_type);
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ if (mark.o_fm.eom == True) {
+ spc_sense_create(cmd, KEY_MEDIUM_ERROR, 0);
+ spc_sense_ascq(cmd, SPC_ASC_EOP, SPC_ASCQ_EOP);
+ spc_sense_info(cmd, count);
+ spc_sense_flags(cmd, SPC_SENSE_EOM);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ s->s_cur_fm += mark.o_fm.size;
+ }
+ trans_send_complete(cmd, STATUS_GOOD);
+ break;
+
+ default:
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+}
+
+/*
+ * []----
+ * | ssc_msense -- MODE SENSE command
+ * |
+ * | This command is part of the SPC set, but is device specific enough
+ * | that it must be emulated in each device type.
+ * []----
+ */
+/*ARGSUSED*/
+static void
+ssc_msense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ ssc_params_t *s = T10_PARAMS_AREA(cmd);
+ struct mode_header *mode_hdr;
+ int request_len,
+ alloc_len;
+ char *data,
+ *np;
+
+ /*
+ * SPC-3 Revision 21c section 6.8
+ * Reserve bit checks
+ */
+ if ((cdb[1] & ~8) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ /*
+ * Zero length causes a simple ack to occur.
+ */
+ if (cdb[4] == 0) {
+ trans_send_complete(cmd, STATUS_GOOD);
+ return;
+ } else {
+ request_len = cdb[4];
+ alloc_len = max(request_len,
+ sizeof (*mode_hdr) + MODE_BLK_DESC_LENGTH +
+ sizeof (ssc_data_compression_t) + sizeof (*mode_hdr) +
+ MODE_BLK_DESC_LENGTH + sizeof (ssc_device_config_t));
+ }
+
+ queue_prt(mgmtq, Q_STE_NONIO, "SSC%x LUN%d: MODE_SENSE(0x%x)",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, cdb[2]);
+
+ if ((data = memalign(sizeof (void *), alloc_len)) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+
+ mode_hdr = (struct mode_header *)data;
+
+ switch (cdb[2]) {
+ case MODE_SENSE_COMPRESSION:
+ mode_hdr->length = sizeof (ssc_data_compression_t);
+ mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH;
+ (void) sense_compression(s, data + sizeof (*mode_hdr) +
+ mode_hdr->bdesc_length);
+ break;
+
+ case MODE_SENSE_DEV_CONFIG:
+ mode_hdr->length = sizeof (ssc_device_config_t);
+ mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH;
+ (void) sense_dev_config(s, data + sizeof (*mode_hdr) +
+ mode_hdr->bdesc_length);
+ break;
+
+ case MODE_SENSE_SEND_ALL:
+ np = sense_compression(s, data);
+ (void) sense_dev_config(s, np);
+ break;
+
+ case 0x00:
+ bzero(data, alloc_len);
+ break;
+
+ default:
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SSC%x LUN%d: MODE SENSE(0x%x) not handled",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+ cdb[2]);
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if (trans_send_datain(cmd, (char *)data, request_len, 0, ssc_free,
+ True, data) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*ARGSUSED*/
+static void
+ssc_read_pos(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ int service_action,
+ request_len,
+ alloc_len;
+ pos_short_form_t *sf;
+ void *data;
+ ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd);
+ ssc_obj_mark_t mark;
+
+ if (s == NULL)
+ return;
+
+ /*
+ * Standard reserve bit check
+ */
+ if ((cdb[1] & 0xc0) || cdb[2] || cdb[3] || cdb[4] || cdb[5] ||
+ cdb[6] || SAM_CONTROL_BYTE_RESERVED(cdb[9])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ service_action = cdb[1] & 0x1f;
+ request_len = (cdb[7] << 8) | cdb[8];
+ switch (service_action) {
+ case SSC_READ_POS_SHORT_FORM:
+ alloc_len = max(request_len, sizeof (*sf));
+ if ((data = memalign(sizeof (void *), alloc_len)) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+ sf = (pos_short_form_t *)data;
+ bcopy((char *)cmd->c_lu->l_common->l_mmap + s->s_cur_fm,
+ &mark, sizeof (mark));
+ if ((mark.o_fm.bom == True) &&
+ (s->s_cur_rec == sizeof (ssc_obj_mark_t)))
+ sf->bop = 1;
+ bcopy((char *)cmd->c_lu->l_common->l_mmap + s->s_cur_fm +
+ s->s_cur_rec, &mark, sizeof (mark));
+ sf->first_obj[0] = hibyte(hiword(mark.o_rm.obj_id));
+ sf->first_obj[1] = lobyte(hiword(mark.o_rm.obj_id));
+ sf->first_obj[2] = hibyte(loword(mark.o_rm.obj_id));
+ sf->first_obj[3] = lobyte(loword(mark.o_rm.obj_id));
+
+ /*
+ * We mark the last object to be the same as the first
+ * object which indicates that nothing is currently
+ * buffered.
+ */
+ sf->last_obj[0] = sf->first_obj[0];
+ sf->last_obj[1] = sf->first_obj[1];
+ sf->last_obj[2] = sf->first_obj[2];
+ sf->last_obj[3] = sf->first_obj[3];
+
+ break;
+
+ case SSC_READ_POS_LONG_FORM:
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+
+ default:
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ if (trans_send_datain(cmd, (char *)data, request_len, 0, ssc_free,
+ True, data) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*ARGSUSED*/
+static void
+ssc_rpt_density(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ ssc_density_t *d;
+ ssc_density_media_t *dm;
+ ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd);
+ size_t cap = s->s_size / (1024 * 1024);
+ int medium_type,
+ request_len,
+ alloc_len;
+ void *data;
+
+ if (s == NULL)
+ return;
+
+ if ((cdb[1] & 0xfc) || cdb[2] || cdb[3] || cdb[4] || cdb[5] ||
+ cdb[6] || SAM_CONTROL_BYTE_RESERVED(cdb[9])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ medium_type = cdb[1] & 0x2;
+ request_len = (cdb[7] << 8) | cdb[8];
+ alloc_len = max(request_len, max(sizeof (*d), sizeof (*dm)));
+ if ((data = memalign(sizeof (void *), alloc_len)) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+ if (medium_type == 0) {
+ d = (ssc_density_t *)data;
+ d->d_hdr.len = htons(sizeof (*d) -
+ sizeof (ssc_density_header_t));
+ d->d_prim_code = 1;
+ d->d_wrtok = 1;
+ d->d_deflt = 1;
+ d->d_tracks[1] = 1;
+ d->d_capacity[0] = hibyte(hiword(cap));
+ d->d_capacity[1] = lobyte(hiword(cap));
+ d->d_capacity[2] = hibyte(loword(cap));
+ d->d_capacity[3] = lobyte(loword(cap));
+ bcopy(cmd->c_lu->l_common->l_vid, d->d_organization,
+ min(sizeof (d->d_organization),
+ strlen(cmd->c_lu->l_common->l_vid)));
+ bcopy(cmd->c_lu->l_common->l_pid, d->d_description,
+ min(sizeof (d->d_description),
+ strlen(cmd->c_lu->l_common->l_pid)));
+ } else {
+ dm = (ssc_density_media_t *)data;
+ dm->d_hdr.len = htons(sizeof (*d) -
+ sizeof (ssc_density_header_t));
+ bcopy(cmd->c_lu->l_common->l_vid, d->d_organization,
+ min(sizeof (d->d_organization),
+ strlen(cmd->c_lu->l_common->l_vid)));
+ bcopy(cmd->c_lu->l_common->l_pid, dm->d_description,
+ min(sizeof (dm->d_description),
+ strlen(cmd->c_lu->l_common->l_pid)));
+ }
+
+ if (trans_send_datain(cmd, (char *)data, request_len, 0, ssc_free,
+ True, (emul_handle_t)data) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+}
+
+/*ARGSUSED*/
+static void
+ssc_write_fm(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ int marks_requested;
+ off_t next_size;
+ ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd);
+ ssc_obj_mark_t mark_fm;
+
+ if (s == NULL)
+ return;
+
+ if ((cdb[1] & 0xfc) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ marks_requested = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4];
+ while (marks_requested--) {
+ /*
+ * Get the last file-mark and update it's size.
+ */
+ bcopy((char *)cmd->c_lu->l_common->l_mmap + s->s_cur_fm,
+ &mark_fm, sizeof (mark_fm));
+ if (mark_fm.som_sig != SSC_OBJ_SIG) {
+ spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ next_size = mark_fm.o_fm.size - s->s_cur_rec;
+ mark_fm.o_fm.size = s->s_cur_rec;
+ bcopy(&mark_fm, (char *)cmd->c_lu->l_common->l_mmap +
+ s->s_cur_fm, sizeof (mark_fm));
+
+ /*
+ * Write new mark and update internal location of mark.
+ */
+ mark_fm.o_fm.last_obj_id =
+ find_last_obj_id((char *)cmd->c_lu->l_common->l_mmap +
+ s->s_cur_fm, s->s_cur_rec);
+ mark_fm.o_fm.bom = False;
+ mark_fm.o_fm.size = next_size;
+ s->s_cur_fm += s->s_cur_rec;
+ s->s_cur_rec = sizeof (ssc_obj_mark_t);
+ s->s_prev_rec = s->s_cur_rec;
+ bcopy(&mark_fm, (char *)cmd->c_lu->l_common->l_mmap +
+ s->s_cur_fm, sizeof (mark_fm));
+ mark_fm.o_fm.size = 0;
+ bcopy(&mark_fm, (char *)cmd->c_lu->l_common->l_mmap +
+ s->s_cur_fm + s->s_cur_rec, sizeof (mark_fm));
+ }
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+/*ARGSUSED*/
+static void
+ssc_locate(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+}
+
+/*ARGSUSED*/
+static void
+ssc_erase(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ ssc_obj_mark_t mark;
+ ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd);
+
+ if (s == NULL)
+ return;
+
+ bzero(&mark, sizeof (mark));
+ mark.som_sig = SSC_OBJ_SIG;
+ mark.som_type = SSC_OBJ_TYPE_FM;
+ mark.o_fm.bom = True;
+ mark.o_fm.eom = False;
+ mark.o_fm.size = s->s_size - sizeof (mark);
+
+ bcopy(&mark, (char *)cmd->c_lu->l_common->l_mmap, sizeof (mark));
+}
+
+/*ARGSUSED*/
+static void
+ssc_load(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ ssc_params_t *s = T10_PARAMS_AREA(cmd);
+ t10_lu_common_t *lu = cmd->c_lu->l_common;
+ ssc_obj_mark_t mark;
+
+ /*
+ * SSC-3, revision 02, section 7.2 LOAD/UNLOAD command
+ * Check for various reserve bits.
+ */
+ if ((cdb[1] & ~SSC_LOAD_CMD_IMMED) || cdb[2] || cdb[3] ||
+ (cdb[4] & ~(SSC_LOAD_CMD_LOAD | SSC_LOAD_CMD_RETEN |
+ SSC_LOAD_CMD_EOT | SSC_LOAD_CMD_HOLD)) ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ queue_prt(mgmtq, Q_STE_NONIO, "SSC%x LUN%d load bits 0x%x",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, cdb[4]);
+
+ /*
+ * There are four possible actions based on the LOAD and HOLD
+ * bits.
+ */
+ switch (cdb[4] & (SSC_LOAD_CMD_LOAD|SSC_LOAD_CMD_HOLD)) {
+ case SSC_LOAD_CMD_LOAD|SSC_LOAD_CMD_HOLD:
+ /*
+ * Load the media into the system if not already done
+ * so, but do not position tape. The EOT and RETEN should
+ * be zero. Since this emulation currently is always available
+ * we're good to go.
+ */
+ break;
+
+ case SSC_LOAD_CMD_LOAD:
+ /*
+ * Without the HOLD bit the tape should be positioned at
+ * the beginning of partition 0.
+ */
+ s->s_cur_fm = 0;
+ s->s_cur_rec = sizeof (ssc_obj_mark_t);
+ break;
+
+ case SSC_LOAD_CMD_HOLD:
+ /*
+ * Without the LOAD bit we leave the tape online, but look
+ * at the RETEN and EOT bits. The RETEN doesn't mean anything
+ * for this virtual tape, but we can reposition to EOT.
+ */
+ if (cdb[4] & SSC_LOAD_CMD_EOT) {
+ /*CONSTANTCONDITION*/
+ while (1) {
+ bcopy((char *)lu->l_mmap + s->s_cur_fm, &mark,
+ sizeof (mark));
+ if (mark.som_sig != SSC_OBJ_SIG) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SSC%x LUN%d, bad sig mark: "
+ "expected=0x%x, got=0x%x",
+ cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num,
+ SSC_OBJ_SIG, mark.som_sig);
+ spc_sense_create(cmd,
+ KEY_MEDIUM_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ if (mark.som_type != SSC_OBJ_TYPE_FM) {
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "SSC%x LUN%d, bad mark type: "
+ "expected=0x%x, got=0x%x",
+ cmd->c_lu->l_targ->s_targ_num,
+ cmd->c_lu->l_common->l_num,
+ SSC_OBJ_TYPE_FM, mark.som_type);
+ spc_sense_create(cmd,
+ KEY_MEDIUM_ERROR, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+ if (mark.o_fm.eom == True)
+ break;
+ s->s_cur_fm += mark.o_fm.size;
+ }
+ }
+ break;
+
+ case 0:
+ /*
+ * Unload the current tape.
+ */
+
+ break;
+ }
+ trans_send_complete(cmd, STATUS_GOOD);
+}
+
+/*ARGSUSED*/
+static void
+ssc_logsense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+ spc_log_supported_pages_t p;
+ void *v;
+
+ /*
+ * Reserve bit checks
+ */
+ if ((cdb[1] & ~(SSC_LOG_SP|SSC_LOG_PPC)) || cdb[3] || cdb[4] ||
+ SAM_CONTROL_BYTE_RESERVED(cdb[9])) {
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ return;
+ }
+
+ queue_prt(mgmtq, Q_STE_ERRS, "SSC%x LUN%d page code 0x%x",
+ cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, cdb[2]);
+
+ switch (cdb[2] & SPC_LOG_PAGE_MASK) {
+ case 0:
+ if ((v = malloc(sizeof (p))) == NULL) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ return;
+ }
+ bzero(&p, sizeof (p));
+ p.length[0] = 1;
+ bcopy(&p, v, sizeof (p));
+ if (trans_send_datain(cmd, (char *)v, sizeof (p), 0, free,
+ True, (emul_handle_t)v) == False) {
+ trans_send_complete(cmd, STATUS_BUSY);
+ }
+ break;
+
+ default:
+ spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+ spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0);
+ trans_send_complete(cmd, STATUS_CHECK);
+ break;
+ }
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Support functions for the SSC command set |
+ * []------------------------------------------------------------------[]
+ */
+static uint32_t
+find_last_obj_id(char *file_mark, off_t eod)
+{
+ ssc_obj_mark_t rm;
+ off_t offset = sizeof (ssc_obj_mark_t);
+ uint32_t obj_id = 0xffffffff;
+
+ bcopy(file_mark + offset, &rm, sizeof (rm));
+ while (rm.som_type == SSC_OBJ_TYPE_RM) {
+ obj_id = rm.o_rm.obj_id;
+ offset += rm.o_rm.size;
+ if (offset >= eod)
+ break;
+ bcopy(file_mark + offset, &rm, sizeof (rm));
+ }
+ return (obj_id);
+}
+
+static void
+ssc_setup_tape(ssc_params_t *s, t10_lu_common_t *lu)
+{
+ ssc_obj_mark_t mark;
+
+ /*
+ * Add Begin-of-Partition marker
+ */
+ bzero(&mark, sizeof (mark));
+ mark.som_sig = SSC_OBJ_SIG;
+ mark.som_type = SSC_OBJ_TYPE_FM;
+ mark.o_fm.bom = True;
+ mark.o_fm.eom = False;
+ mark.o_fm.size = s->s_size - sizeof (mark);
+ bcopy(&mark, lu->l_mmap, sizeof (mark));
+
+ /*
+ * Add first file-record with a zero size.
+ */
+ bzero(&mark, sizeof (mark));
+ mark.som_sig = SSC_OBJ_SIG;
+ mark.som_type = SSC_OBJ_TYPE_RM;
+ mark.o_rm.size = 0;
+ mark.o_rm.obj_id = 0xffffffff;
+ bcopy(&mark, (char *)lu->l_mmap + sizeof (ssc_obj_mark_t),
+ sizeof (mark));
+
+ /*
+ * Add End-of-Partiton marker
+ */
+ bzero(&mark, sizeof (mark));
+ mark.som_sig = SSC_OBJ_SIG;
+ mark.som_type = SSC_OBJ_TYPE_FM;
+ mark.o_fm.bom = False;
+ mark.o_fm.eom = True;
+ mark.o_fm.size = 0;
+ bcopy(&mark, (char *)lu->l_mmap + s->s_size - sizeof (mark),
+ sizeof (mark));
+}
+
+static char *
+sense_compression(ssc_params_t *s, char *data)
+{
+ ssc_data_compression_t d;
+
+ bzero(&d, sizeof (d));
+ d.mode_page.code = MODE_SENSE_COMPRESSION;
+ d.mode_page.length = sizeof (d) - sizeof (struct mode_page);
+ bcopy(&d, data, sizeof (d));
+
+ return (data + sizeof (d));
+}
+
+static char *
+sense_dev_config(ssc_params_t *s, char *data)
+{
+ ssc_device_config_t d;
+
+ bzero(&d, sizeof (d));
+ d.mode_page.code = MODE_SENSE_DEV_CONFIG;
+ d.mode_page.length = sizeof (d) - sizeof (struct mode_page);
+ d.lois = 1;
+ d.socf = 1;
+ d.rewind_on_reset = 1;
+ bcopy(&d, data, sizeof (d));
+
+ return (data + sizeof (d));
+}
+
+static void
+ssc_free(emul_handle_t e)
+{
+ free(e);
+}
+
+/*
+ * []----
+ * | Command table for SSC emulation. This is at the end of the file because
+ * | it's big and ugly. ;-) To make for fast translation to the appropriate
+ * | emulation routine we just have a big command table with all 256 possible
+ * | entries. Most will report STATUS_CHECK, unsupport operation. By doing
+ * | this we can avoid error checking for command range.
+ * []----
+ */
+static scsi_cmd_table_t ssc_table[] = {
+ /* 0x00 -- 0x0f */
+ { spc_tur, NULL, NULL, "TEST_UNIT_READY" },
+ { ssc_rewind, NULL, NULL, "REWIND"},
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_request_sense, NULL, NULL, "REQUEST_SENSE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_read_limits, NULL, NULL, "READ BLOCK LIMITS"},
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_read, NULL, ssc_read_cmplt, "READ(6)" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_write, ssc_write_data, ssc_write_cmplt, "WRITE(6)" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x10 -- 0x1f */
+ { ssc_write_fm, NULL, NULL, "WRITE_FILEMARKS(6)"},
+ { ssc_space, NULL, NULL, "SPACE(6)"},
+ { spc_inquiry, NULL, NULL, "INQUIRY" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_mselect, spc_mselect_data, NULL, "MODE_SELECT(6)" },
+ { spc_request_sense, NULL, NULL, "RESERVE" },
+ { spc_request_sense, NULL, NULL, "RELEASE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_erase, NULL, NULL, "ERASE(6)"},
+ { ssc_msense, NULL, NULL, "MODE_SENSE(6)" },
+ { ssc_load, NULL, NULL, "LOAD_UNLOAD" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_send_diag, NULL, NULL, "SEND_DIAG" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x20 -- 0x2f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "READ_CAPACITY" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_read, NULL, ssc_read_cmplt, "READ_G1" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_write, ssc_write_data, ssc_write_cmplt, "WRITE_G1" },
+ { ssc_locate, NULL, NULL, "LOCATE(10)"},
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x30 -- 0x3f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_read_pos, NULL, NULL, "READ POSITION"},
+ { spc_unsupported, NULL, NULL, "SYNC_CACHE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x40 -- 0x4f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_rpt_density, NULL, NULL, "REPORT DENSITY SUPPORT"},
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_logsense, NULL, NULL, "LOG SENSE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x50 -- 0x5f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x60 -- 0x6f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x70 -- 0x7f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x80 -- 0x8f */
+ { ssc_write_fm, NULL, NULL, "WRITE_FILEMARKS(16)"},
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_read, NULL, ssc_read_cmplt, "READ_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_write, ssc_write_data, ssc_write_cmplt, "WRITE_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0x90 -- 0x9f */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { ssc_locate, NULL, NULL, "LOCATE(16)"},
+ { ssc_erase, NULL, NULL, "ERASE" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, "SVC_ACTION_G4" },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xa0 - 0xaf */
+ { spc_report_luns, NULL, NULL, "REPORT_LUNS" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_report_tpgs, NULL, NULL, "REPORT_TPGS" },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xb0 -- 0xbf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xc0 -- 0xcf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xd0 -- 0xdf */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xe0 -- 0xef */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+
+ /* 0xf0 -- 0xff */
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+ { spc_unsupported, NULL, NULL, NULL },
+};
diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_ssc.h b/usr/src/cmd/iscsi/iscsitgtd/t10_ssc.h
new file mode 100644
index 0000000000..6096138899
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_ssc.h
@@ -0,0 +1,317 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _T10_SSC_H
+#define _T10_SSC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Implementation specific headers for SCSI Streaming Commands (Tapes)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define SSC_SPACE_CODE_BLOCKS 0x00 /* Mandatory support */
+#define SSC_SPACE_CODE_FILEMARKS 0x01 /* Mandatory support */
+#define SSC_SPACE_CODE_SEQ_FILEMARKS 0x02 /* Optional support */
+#define SSC_SPACE_CODE_END_OF_DATA 0x03 /* Optional support */
+
+#define SSC_READ_POS_SHORT_FORM 0x00
+#define SSC_READ_POS_LONG_FORM 0x06
+
+#define SSC_OBJ_SIG 0x22005454
+#define SSC_OBJ_TYPE_FM 1
+#define SSC_OBJ_TYPE_RM 2
+
+#define SSC_REWIND_IMMED 0x01
+
+/*
+ * SSC-3, revision 01c, section 7.2
+ * LOAD/UNLOAD bits
+ */
+#define SSC_LOAD_CMD_LOAD 0x01
+#define SSC_LOAD_CMD_RETEN 0x02
+#define SSC_LOAD_CMD_EOT 0x04
+#define SSC_LOAD_CMD_HOLD 0x08
+/* ---- bit 0 of byte 1 ---- */
+#define SSC_LOAD_CMD_IMMED 0x01
+
+/*
+ * On disk file and record mark object structure.
+ */
+typedef struct ssc_obj_mark {
+ uint32_t som_sig;
+ uint32_t som_type;
+ union {
+ struct {
+ Boolean_t bom,
+ eom;
+ off_t size;
+ uint32_t num,
+ last_obj_id;
+ } _fm_;
+ struct {
+ off_t size;
+ /*
+ * SSC-3 Revision 1c, Section 3.1.40
+ * Object Identifier relative to beginning of
+ * partition.
+ */
+ uint32_t obj_id;
+ } _rm_;
+ char _filler_[512 - (sizeof (uint32_t) * 2)];
+ } _u_;
+} ssc_obj_mark_t;
+
+#define o_fm _u_._fm_
+#define o_rm _u_._rm_
+
+/*
+ * In core structure which indicates current file-mark and record-mark
+ */
+typedef struct ssc_params {
+ Boolean_t s_fast_write_ack;
+ off_t s_size;
+ off_t s_cur_rec, /* starts at sizeof (ssc_obj_mark) */
+ s_prev_rec,
+ s_cur_fm; /* starts at 0 */
+ t10_lu_state_t s_state;
+} ssc_params_t;
+
+/*
+ * During asynchronous I/O there are a few things needed to complete
+ * the operation.
+ */
+typedef struct ssc_io {
+ /*
+ * Look at SBC for the reason that this member must be first
+ */
+ t10_aio_t sio_aio;
+
+ t10_cmd_t *sio_cmd;
+
+ char *sio_data;
+ size_t sio_data_len;
+ off_t sio_offset; /* offset from s_cur_rec */
+ size_t sio_total;
+} ssc_io_t;
+
+/*
+ * READ POSITION data format, short form
+ */
+typedef struct pos_short_form {
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t rsvd2 : 1,
+ perr : 1,
+ lolu : 1,
+ rsvd1 : 1,
+ bycu : 1,
+ locu : 1,
+ eop : 1,
+ bop : 1;
+#elif defined(_BIT_FIELDS_HTOL)
+ uchar_t bop : 1,
+ eop : 1,
+ locu : 1,
+ bycu : 1,
+ rsvd1 : 1,
+ lolu : 1,
+ perr : 1,
+ rsvd2 : 1;
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+ uchar_t partition,
+ rsvd3[2],
+ first_obj[4],
+ last_obj[4],
+ rsvd4,
+ objs_in_buf[3],
+ bytes_in_buf[4];
+} pos_short_form_t;
+
+/*
+ * REPORT DENSITY SUPPORT header
+ */
+typedef struct ssc_density_header {
+ uint16_t len,
+ rsvd;
+} ssc_density_header_t;
+
+typedef struct ssc_density {
+ ssc_density_header_t d_hdr;
+ uchar_t d_prim_code,
+ d_secondary_code;
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t d_dlv : 1,
+ d_rsvd : 4,
+ d_deflt : 1,
+ d_dup : 1,
+ d_wrtok : 1;
+#elif defined(_BIT_FIELDS_HTOL)
+ uchar_t d_wrtok : 1,
+ d_dup : 1,
+ d_deflt : 1,
+ d_rsvd : 4,
+ d_dlv : 1;
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+ uchar_t d_len[2],
+ d_bpm[3],
+ d_width[2],
+ d_tracks[2],
+ d_capacity[4],
+ d_organization[8],
+ d_name[8],
+ d_description[20];
+} ssc_density_t;
+
+typedef struct ssc_density_media {
+ ssc_density_header_t d_hdr;
+ uchar_t d_type,
+ d_resvd1,
+ d_len[2],
+ d_num_codes,
+ d_codes[9],
+ d_width[2],
+ d_medium_len[2],
+ d_rsvd2[2],
+ d_organization[8],
+ d_medium_name[8],
+ d_description[20];
+} ssc_density_media_t;
+
+/*
+ * MODE_SENSE/MODE_SELECT, Page Code 0xf
+ * Data Compression
+ */
+typedef struct ssc_data_compression {
+ struct mode_page mode_page;
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t c_rsvd1 : 6,
+ c_dcc : 1,
+ c_dce : 1;
+ uchar_t c_rsdv2 : 5,
+ c_red : 2,
+ c_dde : 1;
+#elif defined(_BIT_FIELDS_HTOL)
+ uchar_t c_dce : 1,
+ c_dcc : 1,
+ c_rsvd1 : 6;
+ uchar_t c_dde : 1,
+ c_red : 2,
+ c_rsvd2 : 5;
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+ uchar_t c_compression_algorithm[4],
+ c_decompression_algorithm[4],
+ c_rsvd3[4];
+} ssc_data_compression_t;
+
+/*
+ * MODE_SENSE/MODE_SELECT, Page Code 0x10
+ * Device Configuration
+ */
+typedef struct ssc_device_config {
+ struct mode_page mode_page;
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t active_format : 5,
+ caf : 1,
+ obsolete1 : 1,
+ rsvd : 1;
+ uchar_t active_partion,
+ wo_buf_ratio,
+ ro_buf_ratio,
+ wr_delay_time[2];
+ uchar_t rew : 1,
+ robo : 1,
+ socf : 2,
+ avc : 1,
+ obsolete2 : 1,
+ lois : 1,
+ obr : 1;
+ uchar_t obsolete3;
+ uchar_t bam : 1,
+ baml : 1,
+ swp : 1,
+ sew : 1,
+ eeg : 1,
+ eod_defined : 3;
+ uchar_t obj_size_ew[3],
+ data_comp_algorithm;
+ uchar_t prmwp : 1,
+ perswp : 1,
+ asocwp : 1,
+ rewind_on_reset : 2,
+ oir : 1,
+ wtre : 2;
+#elif defined(_BIT_FIELDS_HTOL)
+ uchar_t rsvd : 1,
+ obsolete1 : 1,
+ caf : 1,
+ active_format : 5;
+ uchar_t active_partion,
+ wo_buf_ratio,
+ ro_buf_ratio,
+ wr_delay_time[2];
+ uchar_t obr : 1,
+ lois : 1,
+ obsolete2 : 1,
+ avc : 1,
+ socf : 2,
+ robo : 1,
+ rew : 1;
+ uchar_t obsolete3;
+ uchar_t eod_defined : 3,
+ eeg : 1,
+ sew : 1,
+ swp : 1,
+ baml : 1,
+ bam : 1;
+ uchar_t obj_size_ew[3],
+ data_comp_algorithm;
+ uchar_t wtre : 2,
+ oir : 1,
+ rewind_on_reset : 2,
+ asocwp : 1,
+ perswp : 1,
+ prmwp : 1;
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+} ssc_device_config_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _T10_SSC_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/target.h b/usr/src/cmd/iscsi/iscsitgtd/target.h
new file mode 100644
index 0000000000..9209c1d74f
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/target.h
@@ -0,0 +1,206 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TARGET_H
+#define _TARGET_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Block comment which describes the contents of this file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEFAULT_CONFIG_LOCATION "/etc/iscsi/target_config.xml"
+#define DEFAULT_TARGET_BASEDIR "/iscsi_targets"
+#define DEFAULT_TARGET_LOG "/tmp/target_log"
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Common strings which are used throughout the target. |
+ * []------------------------------------------------------------------[]
+ */
+/*
+ * Target type strings are used to distinguish between the emulated types.
+ * These strings are stored in the params file so they must not be changed
+ * without thinking about upgrade issues.
+ */
+#define TGT_TYPE_DISK "disk"
+#define TGT_TYPE_TAPE "tape"
+#define TGT_TYPE_OSD "osd"
+#define TGT_TYPE_RAW "raw"
+
+/*
+ * During the creation phase of a LU it starts out offline during block
+ * initialization, once initialization is complete it will transition to
+ * online. If during the initialization an error occurs it will be so marked.
+ */
+#define TGT_STATUS_OFFLINE "offline"
+#define TGT_STATUS_ONLINE "online"
+#define TGT_STATUS_ERRORED "errored"
+
+/*
+ * Base file names for the logical units (LU). The format used is params.%d and
+ * lun.%d These are used both to build the LU name and when searching the
+ * target directory for valid luns. Don't change these names unless the upgrade
+ * path has been thought about.
+ */
+#define PARAMBASE "params."
+#define LUNBASE "lun."
+#define OSDBASE "osd_root."
+
+#define ISCSI_TARGET_ALIAS "TargetAlias"
+
+/*
+ * The IQN names that are created use libuuid + the local target name
+ * as the idr_str portion of: iqn.1986-03.com.sun:<version>:<id_str>
+ * In case this changes we also include a version number. Currently
+ * version 1 is used by the Solaris iSCSI Initiator which has the MAC address,
+ * timestamp, and hostname.
+ */
+#define TARGET_NAME_VERS 2
+#define TARGET_NOFILE 10000
+
+/*
+ * Minimum and maximum values for Target Portal Group Tag as specified
+ * by RFC3720
+ */
+#define TPGT_MIN 1
+#define TPGT_MAX 65535
+
+/*
+ * Minimum and maximum values for MaxRecvDataSegmentLength
+ */
+#define MAXRCVDATA_MIN 512
+#define MAXRCVDATA_MAX ((1 << 24) - 1)
+
+/*
+ * Major/minor versioning for the configuration files.
+ * If we find a configuration file that has a higher
+ * major number than we support we exit. Major number
+ * changes are for radical structure differences. Shouldn't
+ * happen, but we've got a means of detecting such a situation
+ * a bailing out before doing any damage. Minor number changes
+ * mean additions to the current format have been added. For
+ * right now, we use -1, which means ignore the minor number. In
+ * the future it would be possible for the software to determine
+ * that a file had certain additions, but maybe not all changes.
+ */
+#define XML_VERS_MAIN_MAJ 1
+#define XML_VERS_MAIN_MIN -1
+#define XML_VERS_TARG_MAJ 1
+#define XML_VERS_TARG_MIN -1
+#define XML_VERS_LUN_MAJ 1
+#define XML_VERS_LUN_MIN -1
+#define XML_VERS_RESULT_MAJ 1
+#define XML_VERS_RESULT_MIN -1
+
+/*
+ * Default values of the LUN parameters
+ */
+#define DEFAULT_LUN_SIZE ((1024 * 1024 * 1024) / 512)
+#define DEFAULT_RPM 7200
+#define DEFAULT_HEADS 16
+#define DEFAULT_CYLINDERS 100
+#define DEFAULT_SPT 128
+#define DEFAULT_BYTES_PER 512
+#define DEFAULT_INTERLEAVE 1
+#define DEFAULT_PID "SOLARIS"
+#define DEFAULT_VID "SUN"
+#define DEFAULT_REVISION "1"
+
+/*
+ * SPC-3 revision 21c, section 7.6.4.4.4
+ * EUI-64 based 16-byte IDENTIFIER field format
+ */
+typedef struct eui_16 {
+ uchar_t e_vers,
+ e_resrv1,
+ e_mac[6],
+ e_company_id[3],
+ e_resv2,
+ e_timestamp[4];
+} eui_16_t;
+
+#define SUN_EUI_16_VERS 1
+
+#define SUN_EN 0x2a;
+#define MIN_VAL 4
+
+#ifndef min
+#define min(a, b) ((a) > (b) ? (b) : (a))
+#endif
+#ifndef max
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+typedef struct {
+ char *name;
+ char *(*func)(char *, char *);
+} admin_table_t;
+
+#include <sys/socket.h>
+#include "xml.h"
+#include "queue.h"
+
+void create_func(xml_node_t *, target_queue_t *, target_queue_t *);
+void modify_func(xml_node_t *, target_queue_t *, target_queue_t *);
+void remove_func(xml_node_t *, target_queue_t *, target_queue_t *);
+void list_func(xml_node_t *, target_queue_t *, target_queue_t *);
+void logout_targ(char *targ);
+char *update_basedir(char *, char *);
+char *valid_radius_srv(char *name, char *prop);
+Boolean_t if_find_mac(target_queue_t *mgmt);
+void if_target_address(char **text, int *text_length, struct sockaddr *sp);
+Boolean_t process_target_config();
+
+
+extern admin_table_t admin_prop_list[];
+extern char *target_basedir;
+extern char *target_log;
+extern char *config_file;
+extern xml_node_t *targets_config;
+extern xml_node_t *main_config;
+extern uchar_t *mac_addr;
+extern size_t mac_len;
+extern int main_vers_maj,
+ main_vers_min,
+ targets_vers_maj,
+ targets_vers_min;
+extern Boolean_t enforce_strict_guid,
+ thin_provisioning,
+ disable_tpgs,
+ dbg_timestamps;
+extern pthread_mutex_t targ_config_mutex;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TARGET_H */
diff --git a/usr/src/cmd/iscsi/iscsitgtd/util.c b/usr/src/cmd/iscsi/iscsitgtd/util.c
new file mode 100644
index 0000000000..ddddc4c678
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/util.c
@@ -0,0 +1,1691 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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 <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <utility.h>
+#include <fcntl.h>
+#include <syslog.h>
+
+#include "local_types.h"
+#include "target.h"
+#include "utility.h"
+#include "errcode.h"
+#include "xml.h"
+#include <sys/scsi/generic/commands.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);
+
+void
+util_init()
+{
+ (void) pthread_mutex_init(&thick_mutex, NULL);
+}
+
+/*
+ * []----
+ * | read_retry -- read data into buffer
+ * |
+ * | There have been problems on Solaris when reads from a socket
+ * | fail to return the expected amount of data. It seems to occur
+ * | around 2KB, but nothing reproduciable. By issuing multiple reads
+ * | and adjusting the buffer pointer we can get around this problem.
+ * | The suspection at this point is an interaction with threads, sockets,
+ * | and large iSCSI transfers.
+ * []----
+ */
+int
+read_retry(int fd, char *buf, int count)
+{
+#define SMALLER_READS
+#ifdef SMALLER_READS
+ int cc,
+ min,
+ total = 0;
+
+ while (count) {
+ min = MIN(count, 512);
+ cc = read(fd, buf, min);
+ if (cc == -1) {
+ if ((errno == EAGAIN) || (errno == 0))
+ continue;
+ else
+ return (-1);
+ }
+ if (cc == 0)
+ break;
+ buf += cc;
+ count -= cc;
+ total += cc;
+ }
+ return (total);
+#else
+ return (read(fd, buf, count));
+#endif
+}
+
+/*
+ * []----
+ * | 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(xml_node_t *targ, char *initiator_name, Boolean_t req_chap)
+{
+ xml_node_t *acl,
+ *inode = NULL,
+ *tgt_initiator = NULL;
+ char *dummy;
+ Boolean_t valid = False,
+ found_chap = False;
+
+ /*
+ * If there's no ACL for this target everyone has access.
+ */
+ if ((acl = xml_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.
+ */
+ while ((inode = xml_node_next(main_config, XML_ELEMENT_INIT,
+ inode)) != NULL) {
+ if (xml_find_value_str(inode, XML_ELEMENT_INAME, &dummy) ==
+ True) {
+ if (strcmp(dummy, initiator_name) == 0) {
+ free(dummy);
+ if (xml_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 = xml_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)
+{
+ xml_node_t *tpgt = NULL,
+ *x;
+ char buf[80];
+ char ipaddr[4];
+
+ while ((tpgt = xml_node_next(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->x_child == NULL) {
+ return (False);
+ }
+ for (x = tpgt->x_child; x; x = x->x_sibling) {
+ 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,
+ xml_node_t *targ)
+{
+ xml_node_t *tpgt_list,
+ *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],
+ net_buf[INET6_ADDRSTRLEN];
+
+ if ((tpgt_list = xml_node_next(targ, XML_ELEMENT_TPGTLIST,
+ NULL)) == NULL) {
+ if_target_address(text, text_length,
+ (struct sockaddr *)&c->c_target_sockaddr);
+ return;
+ }
+
+ while ((tpgt = xml_node_next(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) {
+ /*CSTYLED*/
+ 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 {
+ /*CSTYLED*/
+ 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);
+ }
+ }
+}
+
+#ifdef notused
+/*
+ * []----
+ * | create_tpgt_list -- create XML list of tpgt's for target
+ * |
+ * | Caller must free the data returned. The incoming tname is the
+ * | iSCSI node name (we normally use the IQN form).
+ * []----
+ */
+char *
+create_tpgt_list(char *tname)
+{
+ char *buf = NULL,
+ *p;
+ xml_node_t *tnode = NULL,
+ *tpgtlist = NULL,
+ *tpgt = NULL;
+
+ while ((tnode = xml_node_next(targets_config, XML_ELEMENT_TARG,
+ tnode)) != NULL) {
+ if (xml_find_value_str(tnode, XML_ELEMENT_INAME, &p) ==
+ False) {
+ continue;
+ }
+ if (strcmp(tname, p) != 0) {
+ free(p);
+ continue;
+ } else
+ free(p);
+ if ((tpgtlist = xml_node_next(tnode, XML_ELEMENT_TPGTLIST,
+ NULL)) == NULL) {
+
+ /*
+ * No TPGT list available so just return a NULL.
+ * This is not an error.
+ */
+ return (NULL);
+ }
+
+ buf_add_tag(&buf, XML_ELEMENT_TPGTLIST, Tag_Start);
+ while ((tpgt = xml_node_next(tpgtlist, XML_ELEMENT_TPGT,
+ tpgt)) != NULL) {
+ xml_add_tag(&buf, XML_ELEMENT_TPGT, tpgt->x_value);
+ }
+ buf_add_tag(&buf, XML_ELEMENT_TPGTLIST, Tag_End);
+ return (buf);
+ }
+ return (NULL);
+}
+#endif
+
+/*
+ * []----
+ * | 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)
+{
+ xml_node_t *targ = NULL;
+ Boolean_t rval = True;
+ char *targ_name = NULL;
+
+ while ((rval == True) && ((targ = xml_node_next(targets_config,
+ XML_ELEMENT_TARG, targ)) != NULL)) {
+
+ if (check_access(targ, c->c_sess->s_i_name, False) == True) {
+
+ if (xml_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", 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);
+}
+
+#ifdef notused
+/*
+ * []----
+ * | add_target_alias -- Add TargetAlias property if available.
+ * []----
+ */
+Boolean_t
+add_target_alias(iscsi_conn_t *c, char **text, int *text_length)
+{
+ xml_node_t *targ = NULL;
+ char *targ_name = NULL,
+ *alias_name = NULL;
+ Boolean_t rval = True;
+
+ /*
+ * Most discovery sessions don't have a target name which is
+ * what they're looking for in the first place.
+ */
+ if (c->c_sess->s_type == SessionDiscovery)
+ return (True);
+
+ while ((targ = xml_node_next(targets_config, XML_ELEMENT_TARG,
+ targ)) != NULL) {
+
+ /*
+ * This is a hard error. Since we use node-name quite often
+ * up to this point in time if this fails either we've run
+ * out of memory or there's a memory corruption problem.
+ */
+ if (xml_find_value_str(targ, XML_ELEMENT_INAME, &targ_name) ==
+ False)
+ return (False);
+
+ if (strcmp(targ_name, c->c_sess->s_t_name) == 0) {
+ if (xml_find_value_str(targ, XML_ELEMENT_ALIAS,
+ &alias_name) == True) {
+
+ /*
+ * Target name matches and we've got an alias.
+ */
+ rval = add_text(text, text_length,
+ ISCSI_TARGET_ALIAS, alias_name);
+ queue_prt(c->c_mgmtq, Q_CONN_LOGIN,
+ "CON%x %-24s = %s", c->c_num,
+ ISCSI_TARGET_ALIAS, alias_name);
+ break;
+ } else if (xml_find_value_str(targ, XML_ELEMENT_TARG,
+ &alias_name) == True) {
+
+ /*
+ * If we don't have a user settable alias
+ * then use the local name the administrator
+ * setup.
+ */
+ rval = add_text(text, text_length,
+ ISCSI_TARGET_ALIAS, alias_name);
+ queue_prt(c->c_mgmtq, Q_CONN_LOGIN,
+ "CON%x %-24s = %s", c->c_num,
+ ISCSI_TARGET_ALIAS, alias_name);
+ break;
+ }
+ }
+
+ /*
+ * We don't have to worry about freeing alias_name because
+ * it will only be set if we found a match and had an alias
+ * in which case we'd never get here.
+ */
+ free(targ_name);
+ targ_name = NULL;
+ }
+
+ if (targ_name)
+ free(targ_name);
+ if (alias_name)
+ free(alias_name);
+
+ return (rval);
+}
+#endif
+
+/*
+ * []----
+ * | 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,
+ 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,
+ *n,
+ *cur_pair,
+ param_rsp[32];
+ int plen; /* pair length */
+ Boolean_t rval = True;
+ 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",
+ 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",
+ c->c_num, cur_pair, n);
+
+ /*
+ * At this point, 'cur_pair' points at the name and 'n'
+ * points at the value.
+ */
+ if (strcmp("HeaderDigest", cur_pair) == 0) {
+
+ rval = parse_digest_vals(&c->c_header_digest,
+ cur_pair, n, text, text_length);
+
+ } else if (strcmp("DataDigest", cur_pair) == 0) {
+
+ rval = parse_digest_vals(&c->c_data_digest, cur_pair,
+ n, text, text_length);
+
+ } else if (strcmp("InitiatorName", cur_pair) == 0) {
+
+ send_named_msg(c, msg_initiator_name, n);
+
+ } else if (strcmp("InitiatorAlias", cur_pair) == 0) {
+
+ send_named_msg(c, msg_initiator_alias, n);
+
+ } else if (strcmp("TargetName", cur_pair) == 0) {
+
+ send_named_msg(c, msg_target_name, n);
+
+ /*
+ * Had to wait until now before loading any parameters
+ * because they are based on the TargetName which
+ * hasn't been known until now. This might fail
+ * because the target doesn't exist or the initiator
+ * doesn't have permission to access this target.
+ */
+ if ((rval = connection_parameters_get(c, n)) == False) {
+ *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);
+ }
+
+ } else if (strcmp("SessionType", cur_pair) == 0) {
+
+ c->c_sess->s_type = strcmp(n, "Discovery") == 0 ?
+ SessionDiscovery : SessionNormal;
+
+ } 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);
+ }
+
+ } 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);
+
+ } else if (strcmp("DefaultTime2Wait", cur_pair) == 0) {
+
+ c->c_default_time_2_wait = strtol(n, NULL, 0);
+
+ } else if (strcmp("DefaultTime2Retain", cur_pair) == 0) {
+
+ c->c_default_time_2_retain = strtol(n, NULL, 0);
+
+ } 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);
+
+ } else if (strcmp("IFMarker", cur_pair) == 0) {
+
+ c->c_ifmarker = False;
+ rval = add_text(text, text_length, cur_pair, "No");
+
+ } else if (strcmp("OFMarker", cur_pair) == 0) {
+
+ c->c_ofmarker = False;
+ rval = add_text(text, text_length, cur_pair, "No");
+
+ } else if (strcmp("InitialR2T", cur_pair) == 0) {
+
+ c->c_initialR2T = True;
+ rval = add_text(text, text_length, cur_pair, "Yes");
+
+ } 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 there 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);
+
+ } else if (strcmp("MaxBurstLength", cur_pair) == 0) {
+
+ c->c_max_burst_len = strtol(n, NULL, 0);
+
+ } 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);
+
+ } 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);
+
+ } 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);
+
+ } 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);
+
+ } 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
+ * packetss on different connections.
+ */
+ c->c_data_sequence_in_order = True;
+ rval = add_text(text, text_length, cur_pair, "Yes");
+
+ } 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",
+ c->c_num, cur_pair, n);
+ }
+
+ 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);
+}
+
+void
+connection_parameters_default(iscsi_conn_t *c)
+{
+ c->c_tpgt = 1;
+}
+
+/*
+ * []----
+ * | 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.
+ * []----
+ */
+int
+find_main_tpgt(struct sockaddr_storage *pst)
+{
+ char ip_addr[16];
+ xml_node_t *tpgt = NULL,
+ *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 = xml_node_next(main_config, XML_ELEMENT_TPGT,
+ tpgt)) != NULL) {
+
+ ip_node = NULL;
+ while ((ip_node = xml_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 (1);
+}
+
+static int
+convert_to_tpgt(iscsi_conn_t *c, xml_node_t *targ)
+{
+ xml_node_t *list,
+ *tpgt = NULL;
+ int addr_tpgt,
+ pos_tpgt;
+
+ /*
+ * 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));
+
+ /*
+ * If this target doesn't have a list of target portal group tags
+ * just return the default which is 1.
+ */
+ list = xml_node_next(targ, XML_ELEMENT_TPGTLIST, NULL);
+ if (list == NULL)
+ return (addr_tpgt);
+
+ while ((tpgt = xml_node_next(list, XML_ELEMENT_TPGT, tpgt)) != NULL) {
+ (void) xml_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
+ * []----
+ */
+xml_node_t *
+find_target_node(char *targ_name)
+{
+ xml_node_t *tnode = NULL;
+ char *iname;
+
+ while ((tnode = xml_node_next(targets_config, XML_ELEMENT_TARG,
+ tnode)) != NULL) {
+ if (xml_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)
+{
+ xml_node_t *targ,
+ *alias;
+ Boolean_t rval = False;
+
+ if ((targ = find_target_node(targ_name)) != NULL) {
+
+ if (check_access(targ, c->c_sess->s_i_name, False) == False)
+ 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)
+ return (False);
+ if ((alias = xml_node_next(targ, XML_ELEMENT_ALIAS, NULL)) ==
+ NULL) {
+ (void) xml_find_value_str(targ, XML_ELEMENT_TARG,
+ &c->c_targ_alias);
+ } else {
+ (void) xml_find_value_str(alias, XML_ELEMENT_ALIAS,
+ &c->c_targ_alias);
+ }
+
+ (void) xml_find_value_int(targ, XML_ELEMENT_MAXCMDS,
+ &c->c_maxcmdsn);
+ rval = True;
+ }
+
+ return (rval);
+}
+
+Boolean_t
+validate_version(xml_node_t *node, int *maj_p, int *min_p)
+{
+ char *vers_str = NULL,
+ *minor_part;
+ int maj,
+ min;
+
+ if ((xml_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) && ((n2 - n1) < 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) && ((n2 - n1) < SNA32_CHECK))));
+}
+
+Boolean_t
+update_config_main(char **msg)
+{
+ if (xml_dump2file(main_config, config_file) == False) {
+ xml_rtn_msg(msg, ERR_UPDATE_MAINCFG_FAILED);
+ return (False);
+ } else
+ return (True);
+}
+
+Boolean_t
+update_config_targets(char **msg)
+{
+ char path[MAXPATHLEN];
+
+ (void) snprintf(path, sizeof (path), "%s/config.xml", target_basedir);
+ if (xml_dump2file(targets_config, path) == False) {
+ if (msg != NULL)
+ xml_rtn_msg(msg, ERR_UPDATE_TARGCFG_FAILED);
+ return (False);
+ } else
+ return (True);
+}
+
+Boolean_t
+util_create_guid(char **guid)
+{
+ 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;
+
+ 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] = 0;
+ eui.e_company_id[1] = 0;
+ eui.e_company_id[2] = SUN_EN;
+
+ } else {
+ bzero(&eui, sizeof (eui));
+
+ eui.e_vers = SUN_EUI_16_VERS;
+ /* ---- [0] & [1] are zero for Sun's IEEE identifier ---- */
+ eui.e_company_id[2] = SUN_EN;
+ 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.
+ */
+ sleep(1);
+ }
+
+ if (xml_encode_bytes((uint8_t *)&eui, sizeof (eui), 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 sectors, find
+ * | the best fit for making C * H * S == sectors
+ * []----
+ */
+void
+create_geom(diskaddr_t size, int *cylinder, int *heads, int *spt)
+{
+ diskaddr_t sects = size >> 9,
+ c, h, s, t;
+ int pass;
+
+ /*
+ * For certain odd size LU we can't generate correct geometry.
+ * If this occurs the values will be set to zero and the MODE
+ * pages will return unsupported.
+ */
+ *cylinder = 0;
+ *heads = 0;
+ *spt = 0;
+
+ for (pass = 0; pass < 2; pass++)
+ for (c = 0x8000; c > 0; c >>= 1) {
+ t = sects / c;
+ if ((t == 0) || ((sects % c) != 0))
+ continue;
+ for (h = 1; h < 0xff; h++)
+ if ((t % h) == 0) {
+ s = t / h;
+ if (s > 0xffff)
+ continue;
+ if ((pass == 0) &&
+ ((c < MIN_VAL) || (h < MIN_VAL) ||
+ (s < MIN_VAL))) {
+ continue;
+ }
+
+ *cylinder = (int)c;
+ *heads = (int)h;
+ *spt = (int)s;
+ return;
+ }
+ }
+}
+
+/*
+ * []----
+ * | 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 ----", 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];
+
+ buf_add_tag_and_attr(buf, XML_ELEMENT_ERROR, "version='1.0'");
+ (void) snprintf(lbuf, sizeof (lbuf), "%d", code);
+ xml_add_tag(buf, XML_ELEMENT_CODE, lbuf);
+ xml_add_tag(buf, XML_ELEMENT_MESSAGE, errcode_to_str(code));
+ 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",
+ 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");
+ syslog(LOG_ERR, "Failed to remove target/lun after "
+ "initialization failure");
+ }
+ }
+
+ free(tp->targ_name);
+ queue_free(tp->q, NULL);
+ free(tp);
+
+ 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)
+{
+ xml_node_t *targ = NULL,
+ *list,
+ *lun,
+ *node,
+ *c;
+ char path[MAXPATHLEN],
+ *tname = NULL,
+ *iname = NULL,
+ *bs_path = NULL;
+ int chk,
+ xml_fd;
+ Boolean_t bs_delete = False;
+ xmlTextReaderPtr r;
+
+ (void) pthread_mutex_lock(&targ_config_mutex);
+ while ((targ = xml_node_next(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 ((xml_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) {
+ (void) pthread_mutex_unlock(&targ_config_mutex);
+ 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 (xml_find_value_str(targ, XML_ELEMENT_INAME, &iname) ==
+ False) {
+ xml_rtn_msg(msg, ERR_INTERNAL_ERROR);
+ (void) pthread_mutex_unlock(&targ_config_mutex);
+ return;
+ }
+ }
+
+ if ((list = xml_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 = xml_node_next(list, XML_ELEMENT_LUN, lun)) !=
+ NULL) {
+ if (xml_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 = xml_node_next(list, XML_ELEMENT_LUN, lun)) !=
+ NULL) {
+ if (xml_find_value_int(lun, XML_ELEMENT_LUN, &chk) ==
+ False)
+ goto error;
+
+ if (chk == lun_num) {
+ lun = xml_alloc_node(XML_ELEMENT_LUN, Int,
+ &lun_num);
+ (void) xml_remove_child(list, lun, MatchBoth);
+ xml_free_node(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);
+
+ /*
+ * See if there's a backing store for this lun, which means LUNBASE
+ * was just a symbolic link, and delete the backing store if we
+ * created it in the first place.
+ */
+ xml_fd = open(path, O_RDONLY);
+ if ((r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd, NULL, NULL, 0)) !=
+ NULL) {
+ node = NULL;
+ while (xmlTextReaderRead(r) == 1)
+ if (xml_process_node(r, &node) == False)
+ break;
+ close(xml_fd);
+ xmlTextReaderClose(r);
+ xmlFreeTextReader(r);
+ xmlCleanupParser();
+
+ (void) xml_find_value_str(node, XML_ELEMENT_BACK, &bs_path);
+ if ((xml_find_value_boolean(node, XML_ELEMENT_DELETE_BACK,
+ &bs_delete) == True) && (bs_delete == True))
+ unlink(bs_path);
+ if (bs_path != NULL)
+ free(bs_path);
+ xml_tree_free(node);
+ }
+
+ (void) unlink(path);
+
+ /*
+ * 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) {
+ c = xml_alloc_node(XML_ELEMENT_TARG, String, tname);
+ (void) xml_remove_child(targets_config, c, MatchBoth);
+ xml_free_node(c);
+ (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);
+ }
+
+ /*
+ * Not much we can do here if we fail to updated the config.
+ */
+ if (update_config_targets(msg) == False)
+ syslog(LOG_ERR, "Failed to update target configuration!");
+
+error:
+ (void) pthread_mutex_unlock(&targ_config_mutex);
+ if (iname != NULL)
+ free(iname);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/util_err.c b/usr/src/cmd/iscsi/iscsitgtd/util_err.c
new file mode 100644
index 0000000000..83c8828a0c
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/util_err.c
@@ -0,0 +1,195 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libintl.h>
+#include "errcode.h"
+
+char *
+errcode_to_str(err_code_t err_code)
+{
+ switch (err_code) {
+ case ERR_SUCCESS:
+ return ((char *)gettext("Operation completed successfully"));
+ case ERR_NULL_XML_MESSAGE:
+ return ((char *)gettext("Null XML message"));
+ case ERR_SYNTAX_EMPTY:
+ return ((char *)gettext("Syntax error: "
+ "Empty XML message or syntax error"));
+ case ERR_SYNTAX_MISSING_ALL:
+ return ((char *)gettext("Syntax error: Missing --all"));
+ case ERR_SYNTAX_MISSING_BACKING_STORE:
+ return ((char *)gettext("Syntax error: Missing backing-store"));
+ case ERR_SYNTAX_MISSING_INAME:
+ return ((char *)gettext("Syntax error: Missing iscsi name"));
+ case ERR_SYNTAX_MISSING_IPADDR:
+ return ((char *)gettext("Syntax error: Missing IP address"));
+ case ERR_SYNTAX_MISSING_NAME:
+ return ((char *)gettext("Syntax error: Missing name"));
+ case ERR_SYNTAX_MISSING_OBJECT:
+ return ((char *)gettext("Syntax error: Missing object"));
+ case ERR_SYNTAX_MISSING_OPERAND:
+ return ((char *)gettext("Syntax error: Missing operand"));
+ case ERR_SYNTAX_MISSING_SIZE:
+ return ((char *)gettext("Syntax error: Missing size"));
+ case ERR_SYNTAX_MISSING_TYPE:
+ return ((char *)gettext("Syntax error: Missing type"));
+ case ERR_SYNTAX_EMPTY_ACL:
+ return ((char *)gettext("Syntax error: empty acl"));
+ case ERR_SYNTAX_EMPTY_ALIAS:
+ return ((char *)gettext("Syntax error: empty alias"));
+ case ERR_SYNTAX_EMPTY_CHAPNAME:
+ return ((char *)gettext("Empty chap-name"));
+ case ERR_SYNTAX_EMPTY_CHAPSECRET:
+ return ((char *)gettext("Empty 'chap-secret' element"));
+ case ERR_SYNTAX_EMPTY_IPADDR:
+ return ((char *)gettext("Syntax error: empty ip address"));
+ case ERR_SYNTAX_EMPTY_MAXRECV:
+ return ((char *)gettext("Syntax error: empty maxrecv"));
+ case ERR_SYNTAX_EMPTY_TPGT:
+ return ((char *)gettext("Syntax error: empty TPGT"));
+ case ERR_SYNTAX_INVALID_NAME:
+ return ((char *)gettext("Syntax error: name must only use "
+ "a..z, A..Z, 0-9, dot(.), dash(-), colon(:) characters"));
+ case ERR_INVALID_COMMAND:
+ return ((char *)gettext("Invalid command"));
+ case ERR_INVALID_OBJECT:
+ return ((char *)gettext("Invalid object"));
+ case ERR_INVALID_BASEDIR:
+ return ((char *)gettext("Invalid base directory"));
+ case ERR_INVALID_IP:
+ return ((char *)gettext("Invalid IP address"));
+ case ERR_INVALID_TPGT:
+ return ((char *)gettext("Invalid TPGT"));
+ case ERR_INVALID_MAXRECV:
+ return ((char *)gettext("Invalid MaxRecvDataSegmentLength"));
+ case ERR_INVALID_RADSRV:
+ return ((char *)gettext("Invalid RADIUS server name"));
+ case ERR_INVALID_SIZE:
+ return ((char *)gettext("Invalid size parameter"));
+ case ERR_INIT_EXISTS:
+ return ((char *)gettext("Initiator already exists"));
+ case ERR_LUN_EXISTS:
+ return ((char *)gettext("LUN already exists"));
+ case ERR_LUN_INVALID_RANGE:
+ return ((char *)gettext("LUN must be between 0 and 16383"));
+ case ERR_TPGT_EXISTS:
+ return ((char *)gettext("TPGT already exists"));
+ case ERR_ACL_NOT_FOUND:
+ return ((char *)gettext("Acl list not found"));
+ case ERR_INIT_NOT_FOUND:
+ return ((char *)gettext("Initiator not found"));
+ case ERR_TARG_NOT_FOUND:
+ return ((char *)gettext("Target not found"));
+ case ERR_LUN_NOT_FOUND:
+ return ((char *)gettext("LUN not found"));
+ case ERR_TPGT_NOT_FOUND:
+ return ((char *)gettext("TPGT not found"));
+ case ERR_ACCESS_RAW_DEVICE_FAILED:
+ return ((char *)gettext("Failed to "
+ "access direct access device"));
+ case ERR_CREATE_METADATA_FAILED:
+ return ((char *)gettext("Failed to "
+ "create meta data for tape device"));
+ case ERR_CREATE_SYMLINK_FAILED:
+ return ((char *)gettext("Failed to "
+ "create symbol link to backing store"));
+ case ERR_CREATE_NAME_TO_LONG:
+ return ((char *)gettext("Name must be less than 166 "
+ "characters"));
+ case ERR_NAME_TO_LONG:
+ return ((char *)gettext("Name to long, must be less that 223 "
+ "characters"));
+ case ERR_DISK_BACKING_SIZE_OR_FILE:
+ return ((char *)gettext("Size must be zero if backing store "
+ "exists"));
+ case ERR_DISK_BACKING_MUST_BE_REGULAR_FILE:
+ return ((char *)gettext("For type "
+ "'disk' backing must be a regular file"));
+ case ERR_DISK_BACKING_NOT_VALID_RAW:
+ return ((char *)gettext("Backing store is not valid raw "
+ "device"));
+ case ERR_STAT_BACKING_FAILED:
+ return ((char *)gettext("Failed to "
+ "stat(2) backing for 'disk'"));
+ case ERR_RAW_PART_NOT_CAP:
+ return ((char *)gettext("Partition size doesn't match capacity"
+ " of device, use p0 or ctd name"));
+ case ERR_CREATE_TARGET_DIR_FAILED:
+ return ((char *)gettext("Failed to "
+ "create target directory"));
+ case ERR_ENCODE_GUID_FAILED:
+ return ((char *)gettext("Failed to encode GUID value"));
+ case ERR_INIT_XML_READER_FAILED:
+ return ((char *)gettext("Failed to initialize XML reader"));
+ case ERR_OPEN_PARAM_FILE_FAILED:
+ return ((char *)gettext("Failed to open parameter file"));
+ case ERR_UPDATE_MAINCFG_FAILED:
+ return ((char *)gettext("Failed to "
+ "update main configuration file"));
+ case ERR_UPDATE_TARGCFG_FAILED:
+ return ((char *)gettext("Failed to "
+ "update target configuration file"));
+ case ERR_VALID_TARG_EXIST:
+ return ((char *)gettext("Valid targets "
+ "exist under current base directory"));
+ case ERR_TARGCFG_MISSING_INAME:
+ return ((char *)gettext("Missing "
+ "iscsi name in target configuration"));
+ case ERR_NO_MATCH:
+ return ((char *)gettext("No match"));
+ case ERR_NO_MEM:
+ return ((char *)gettext("Internal error: no memory"));
+ case ERR_LUN_ZERO_NOT_LAST:
+ return ((char *)gettext("LUN 0 must be the last one deleted"));
+ case ERR_LUN_ZERO_NOT_FIRST:
+ return ((char *)gettext("LUN 0 must exist before creating "
+ "other LUNs"));
+ case ERR_SIZE_MOD_BLOCK:
+ return ((char *)gettext("Size must be multiple of 512"));
+ case ERR_CANT_SHRINK_LU:
+ return ((char *)gettext("Shinking of LU is not supported"));
+ case ERR_RESIZE_WRONG_TYPE:
+ return ((char *)gettext("Backing store must be regular file"));
+ case ERR_RESIZE_WRONG_DTYPE:
+ return ((char *)gettext("Can't resize 'raw' targets"));
+ case ERR_LUN_NOT_GROWN:
+ return ((char *)gettext("Failed to grown LU"));
+ case ERR_FILE_TO_BIG:
+ return ((char *)gettext("Requested size is to large for "
+ "system"));
+ case ERR_FAILED_TO_CREATE_LU:
+ return ((char *)gettext("Failed to create backing store"));
+ case ERR_INTERNAL_ERROR:
+ return ((char *)gettext("Internal error"));
+ case ERR_TAPE_NOT_SUPPORTED_IN_32BIT:
+ return ((char *)gettext("Tape emulation not supported in "
+ "32-bit mode"));
+ default:
+ return ((char *)gettext("Internal error: unknown message"));
+ }
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/util_ifname.c b/usr/src/cmd/iscsi/iscsitgtd/util_ifname.c
new file mode 100644
index 0000000000..c29f2c0364
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/util_ifname.c
@@ -0,0 +1,508 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/dlpi.h>
+#include <libdlpi.h>
+#include <ctype.h>
+#include <sys/sysmacros.h>
+#include <net/if_types.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <net/if_dl.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <strings.h>
+#include <stropts.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "local_types.h"
+#include "target.h"
+#include "queue.h"
+#include "utility.h"
+
+#define LOCAL_LOOPBACK "lo0"
+
+/*
+ * This entire file is all about getting these two variables. To create a
+ * unique iSCSI IQN string we need information that is unique. What Sun has
+ * decided is to use a MAC address along with a timestamp. No other machine
+ * at this given time will have the same MAC address and as time moves along
+ * well, time will change.
+ */
+uchar_t *mac_addr;
+size_t mac_len;
+
+#define PATH_PART "/dev/"
+
+static struct lifreq *if_setup(int *n);
+static void dump_addr_to_ascii(struct sockaddr *addr, char *buf,
+ size_t len);
+static int strputmsg(int fd, uint8_t *ctl_buf, size_t ctl_len, int flags);
+static int strgetmsg(int fd, char *ctl_buf, size_t *ctl_lenp, char *data_buf,
+ size_t *data_lenp);
+static Boolean_t grab_address(char *ifname, uchar_t **addr, size_t *lp);
+
+/*
+ * []----
+ * | if_find_mac -- Finds a valid MAC address to use for GUID & IQN creation
+ * |
+ * | To create both the GUID and the IQN string we need to make them unique
+ * | and do so without requiring the user to have to register each target
+ * | creation with Sun. Each machine that's using iSCSI will have a network
+ * | interface from which we can obtain the MAC address. That guarantees
+ * | uniqueness within the network, but doesn't guarantee uniqueness with
+ * | the machine. So when creating the GUID/IQN we also use a timestamp.
+ * []----
+ */
+Boolean_t
+if_find_mac(target_queue_t *mgmt)
+{
+ struct lifreq *lifrp, *first;
+ int n;
+ char *str = NULL;
+
+ first = if_setup(&n);
+ for (lifrp = first; n > 0; n--, lifrp++) {
+ if (grab_address(lifrp->lifr_name, &mac_addr,
+ &mac_len) == True) {
+ str = _link_ntoa(mac_addr, str, mac_len, IFT_OTHER);
+ if ((str != NULL) && (mgmt != NULL)) {
+ queue_prt(mgmt, Q_GEN_DETAILS,
+ "MAIN %s: %s \n", lifrp->lifr_name, str);
+ free(str);
+ }
+ /* ---- grab the first valid MAC address ---- */
+ break;
+ }
+ }
+ if (first)
+ free(first);
+ return (mac_len == 0 ? False : True);
+}
+
+/*
+ * []----
+ * | if_target_address -- setup IP address for SendTargets
+ * |
+ * | This routine is called when the iSCSI target is returning SendTargets
+ * | data during a discovery phase. The target name is returned along
+ * | with all of the IP address that can access that target. There's one
+ * | catch, the first address in the list will be the address used by
+ * | the initiator if it doesn't support multiple connections per session.
+ * | Therefore, whatever connection the initiator used is the first one
+ * | that should be in our list. The ramificiations of not doing this are
+ * | possible performance issues. Take for example a setup where both the
+ * | initiator and target have 10GbE and 1GbE interfaces. The initiator wants
+ * | to use the 10GbE interface because of it's speed. If the target returns
+ * | a list of addresses with the 1GbE listed first, that's the one which
+ * | the initiator would use. Not good.
+ * []----
+ */
+void
+if_target_address(char **text, int *text_length, struct sockaddr *sp)
+{
+ struct lifreq *lp,
+ *first;
+ int n,
+ i,
+ s;
+ struct sockaddr_in *sin4_cur,
+ *sin4_pos;
+ struct sockaddr_in6 *sin6_cur,
+ *sin6_pos;
+ char ta[80],
+ ip_buf[INET6_ADDRSTRLEN];
+ int fromlen;
+
+ if (sp->sa_family == AF_INET) {
+ fromlen = sizeof (struct sockaddr_in);
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return;
+
+ /*LINTED*/
+ sin4_cur = (struct sockaddr_in *)sp;
+
+ /*
+ * Ugh. Would you believe that even though this array
+ * is defined as zero, we get back non-zero data from
+ * getsockname().
+ */
+ bzero(&sin4_cur->sin_zero[0], sizeof (sin4_cur->sin_zero));
+
+ } else if (sp->sa_family == AF_INET6) {
+ fromlen = sizeof (struct sockaddr_in6);
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
+ return;
+
+ /*LINTED*/
+ sin6_cur = (struct sockaddr_in6 *)sp;
+ } else
+ return;
+
+ first = if_setup(&n);
+ for (lp = first, i = 0; i < n; i++, lp++) {
+
+ if (ioctl(s, SIOCGLIFADDR, lp) < 0)
+ continue;
+
+ if (sp->sa_family != lp->lifr_addr.ss_family)
+ continue;
+
+ /*
+ * Change the possible incoming addresses port number,
+ * which would be zero, to that of the current incoming
+ * port number. Otherwise the comparison will not match.
+ */
+ if (sp->sa_family == AF_INET) {
+ sin4_pos = (struct sockaddr_in *)&lp->lifr_addr;
+ sin4_pos->sin_port = sin4_cur->sin_port;
+ } else if (sp->sa_family == AF_INET6) {
+ sin6_pos = (struct sockaddr_in6 *)&lp->lifr_addr;
+ sin6_pos->sin6_port = sin6_cur->sin6_port;
+ } else
+ goto clean_up;
+ }
+
+ for (lp = first, i = 0; i < n; i++, lp++) {
+
+ if (bcmp(sp, &lp->lifr_addr, fromlen) == 0) {
+ dump_addr_to_ascii((struct sockaddr *)&lp->lifr_addr,
+ ip_buf, sizeof (ip_buf));
+
+ if (sp->sa_family == AF_INET) {
+ (void) snprintf(ta, sizeof (ta), "%s,1",
+ ip_buf);
+ } else if (sp->sa_family == AF_INET6) {
+ (void) snprintf(ta, sizeof (ta), "[%s],1",
+ ip_buf);
+ } else
+ goto clean_up;
+
+ (void) add_text(text, text_length, "TargetAddress", ta);
+
+ /*
+ * There is possiblity that both IPv4 & IPv6 enabled on
+ * certain interface, then we will see that interface
+ * twice identically in the list.
+ * Of course we need only one of them, not both.
+ */
+ break;
+ }
+ }
+
+ for (lp = first, i = 0; i < n; i++, lp++) {
+ /*
+ * We allow for the loopback address to match the discovery
+ * address above since it's entirely possible to create
+ * a target on the same machine that you're running the
+ * initiator. Now, when we provide the list of other
+ * possible interfaces to use we don't want to include
+ * the loopback because that's obviously not a valid I/F
+ * for a remote node.
+ */
+ if (strcmp(lp->lifr_name, LOCAL_LOOPBACK) == 0)
+ continue;
+
+ if (bcmp(sp, &lp->lifr_addr, fromlen) != 0) {
+ dump_addr_to_ascii((struct sockaddr *)&lp->lifr_addr,
+ ip_buf, sizeof (ip_buf));
+
+ if (sp->sa_family == AF_INET) {
+ (void) snprintf(ta, sizeof (ta), "%s,1",
+ ip_buf);
+ } else if (sp->sa_family == AF_INET6) {
+ (void) snprintf(ta, sizeof (ta), "[%s],1",
+ ip_buf);
+ } else
+ goto clean_up;
+ (void) add_text(text, text_length, "TargetAddress", ta);
+ }
+ }
+
+clean_up:
+ (void) close(s);
+ if (first)
+ free(first);
+}
+
+/*
+ * []----
+ * | dump_addr_to_ascii -- Use appropriate translation routine
+ * []----
+ */
+static void
+dump_addr_to_ascii(struct sockaddr *addr, char *buf, size_t len)
+{
+ struct sockaddr_in *sin4;
+ struct sockaddr_in6 *sin6;
+
+ if (addr->sa_family == AF_INET) {
+ /*LINTED*/
+ sin4 = (struct sockaddr_in *)addr;
+ (void) inet_ntop(AF_INET, &sin4->sin_addr, buf, len);
+ } else if (addr->sa_family == AF_INET6) {
+ /*LINTED*/
+ sin6 = (struct sockaddr_in6 *)addr;
+ (void) inet_ntop(AF_INET6, &sin6->sin6_addr, buf, len);
+ }
+}
+
+/*
+ * []----
+ * | if_setup -- Load up the interface names
+ * |
+ * | If this routine returns NULL, argument 'n' is also guaranteed to
+ * | be set to 0.
+ * []----
+ */
+static struct lifreq *
+if_setup(int *n)
+{
+ struct lifnum lifn;
+ struct lifconf lifc;
+ int numifs;
+ unsigned bufsize;
+ char *buf;
+ int s;
+
+ *n = 0;
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
+ /*
+ * If we failed to open an IPv6 socket
+ * try IPv4 socket instead
+ */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return (NULL);
+ }
+
+ lifn.lifn_family = AF_UNSPEC;
+ lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
+ if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0)
+ return (NULL);
+
+ numifs = lifn.lifn_count;
+
+ bufsize = numifs * sizeof (struct lifreq);
+ if ((buf = malloc(bufsize)) == NULL) {
+ /*
+ * This call is made so early on that if we're out of memory
+ * here, just say goodbye.
+ */
+ return (NULL);
+ }
+
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
+ lifc.lifc_len = bufsize;
+ lifc.lifc_buf = buf;
+
+ if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
+ free(buf);
+ return (NULL);
+ }
+
+ (void) close(s);
+ *n = lifc.lifc_len / sizeof (struct lifreq);
+ return (lifc.lifc_req);
+}
+
+static int
+strputmsg(int fd, uint8_t *ctl_buf, size_t ctl_len, int flags)
+{
+ struct strbuf ctl;
+
+ bzero(&ctl, sizeof (ctl));
+ ctl.buf = (char *)ctl_buf;
+ ctl.len = ctl_len;
+
+ return (putmsg(fd, &ctl, NULL, flags));
+}
+
+static int
+strgetmsg(int fd, char *ctl_buf, size_t *ctl_lenp, char *data_buf,
+ size_t *data_lenp)
+{
+ struct strbuf ctl;
+ struct strbuf data;
+ int flags = 0,
+ res;
+
+ bzero(&ctl, sizeof (ctl));
+ ctl.buf = ctl_buf;
+ ctl.len = 0;
+ ctl.maxlen = (ctl_lenp != NULL) ? *ctl_lenp : 0;
+
+ bzero(&data, sizeof (data));
+ data.buf = data_buf;
+ data.len = 0;
+ data.maxlen = (data_lenp != NULL) ? *data_lenp : 0;
+
+ res = getmsg(fd, &ctl, &data, &flags);
+ if (ctl_lenp != NULL)
+ *ctl_lenp = ctl.len;
+ if (data_lenp != NULL)
+ *data_lenp = data.len;
+
+ return (res);
+}
+
+static Boolean_t
+ppa_attach(int fd, int ppa)
+{
+ union DL_primitives *buf = NULL;
+ dl_attach_req_t dlar;
+ size_t size;
+ Boolean_t rval = False;
+
+ size = 0;
+ size = MAX(sizeof (dl_ok_ack_t), size);
+ size = MAX(sizeof (dl_error_ack_t), size);
+
+ if ((buf = malloc(size)) == NULL)
+ return (False);
+
+ dlar.dl_primitive = DL_ATTACH_REQ;
+ dlar.dl_ppa = ppa;
+
+ if (strputmsg(fd, (uint8_t *)&dlar, DL_ATTACH_REQ_SIZE, 0) == -1)
+ goto error;
+
+ if (strgetmsg(fd, (char *)buf, &size, NULL, NULL) == -1)
+ goto error;
+
+ if (size < sizeof (t_uscalar_t))
+ goto error;
+
+ switch (buf->dl_primitive) {
+ case DL_OK_ACK:
+ if (size == DL_OK_ACK_SIZE)
+ rval = True;
+ break;
+ }
+
+error:
+ if (buf != NULL)
+ free(buf);
+
+ return (rval);
+}
+
+static Boolean_t
+grab_address(char *ifname, uchar_t **addr, size_t *lp)
+{
+ char *dev_name = NULL,
+ *p;
+ size_t len;
+ int ppa,
+ fd = -1;
+ union DL_primitives *buf = NULL;
+ dl_phys_addr_req_t dlpar;
+ dl_phys_addr_ack_t *dlpaap;
+ Boolean_t rval = False;
+
+ if (strcmp(ifname, LOCAL_LOOPBACK) == 0)
+ return (False);
+
+ bzero(&dlpar, sizeof (dlpar));
+ len = strlen(PATH_PART) + strlen(ifname) + 1;
+ if ((dev_name = (char *)malloc(len)) == NULL) {
+ goto error;
+ }
+ (void) snprintf(dev_name, len, "%s%s", PATH_PART, ifname);
+
+ if ((fd = open(dev_name, O_RDWR)) < 0) {
+ for (p = dev_name; *p; p++) {
+ if (isdigit(*p)) {
+ ppa = atoi(p);
+ *p = '\0';
+ if (((fd = open(dev_name, O_RDWR)) < 0) ||
+ (ppa_attach(fd, ppa) == False)) {
+ goto error;
+ }
+ break;
+ }
+ }
+ if (fd == -1)
+ goto error;
+ }
+
+ len = 0;
+ len = MAX(sizeof (dl_phys_addr_ack_t) + MAXADDRLEN, len);
+ len = MAX(sizeof (dl_error_ack_t), len);
+
+ if ((buf = calloc(len, 1)) == NULL)
+ goto error;
+ dlpar.dl_primitive = DL_PHYS_ADDR_REQ;
+ dlpar.dl_addr_type = DL_CURR_PHYS_ADDR;
+
+ if (strputmsg(fd, (uint8_t *)&dlpar, DL_PHYS_ADDR_REQ_SIZE, 0) == -1) {
+ goto error;
+ }
+
+ if (strgetmsg(fd, (char *)buf, &len, NULL, NULL) == -1) {
+ goto error;
+ }
+
+ switch (buf->dl_primitive) {
+ case DL_PHYS_ADDR_ACK:
+ if (len < DL_PHYS_ADDR_ACK_SIZE) {
+ goto error;
+ }
+
+ dlpaap = (dl_phys_addr_ack_t *)buf;
+ if (dlpaap->dl_addr_offset != 0) {
+ if (dlpaap->dl_addr_length == 0) {
+ goto error;
+ }
+ *addr = malloc(dlpaap->dl_addr_length);
+ if (*addr == NULL)
+ goto error;
+ bcopy((char *)buf + dlpaap->dl_addr_offset, *addr,
+ dlpaap->dl_addr_length);
+ *lp = dlpaap->dl_addr_length;
+ rval = True;
+ }
+ break;
+ }
+
+error:
+ if (fd != -1)
+ (void) close(fd);
+ if (dev_name != NULL)
+ free(dev_name);
+ if (buf != NULL)
+ free(buf);
+ return (rval);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/util_port.c b/usr/src/cmd/iscsi/iscsitgtd/util_port.c
new file mode 100644
index 0000000000..0c62e1b456
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/util_port.c
@@ -0,0 +1,260 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <assert.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "queue.h"
+#include "port.h"
+#include "iscsi_conn.h"
+#include "utility.h"
+
+pthread_mutex_t port_mutex;
+int port_conn_num;
+iscsi_conn_t *conn_head,
+ *conn_tail;
+
+void
+port_init()
+{
+ (void) pthread_mutex_init(&port_mutex, NULL);
+ port_conn_num = 0;
+}
+
+void *
+port_watcher(void *v)
+{
+ int s,
+ fd,
+ on = 1;
+ char debug[80];
+ struct sockaddr_in sin_ip;
+ struct sockaddr_in6 sin6_ip;
+ struct sockaddr_storage st;
+ socklen_t socklen;
+ iscsi_conn_t *conn;
+ port_args_t *p = (port_args_t *)v;
+ target_queue_t *q = p->port_mgmtq;
+ int l,
+ accept_err_sleep = 1;
+ pthread_t junk;
+ struct in_addr addr;
+ struct in6_addr addr6;
+
+ /*
+ * Try creating an IPv6 socket first
+ * If failed, try creating an IPv4 socket
+ */
+ if ((s = socket(PF_INET6, SOCK_STREAM, 0)) == -1) {
+
+ queue_str(q, Q_GEN_ERRS, msg_log, "Opening IPv4 socket");
+ if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
+ queue_str(q, Q_GEN_ERRS, msg_log,
+ "Can't open socket");
+ return (NULL);
+ } else {
+ bzero(&sin_ip, sizeof (sin_ip));
+ sin_ip.sin_family = AF_INET;
+ sin_ip.sin_port = htons(p->port_num);
+ sin_ip.sin_addr.s_addr = INADDR_ANY;
+
+ (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof (on));
+
+ if ((bind(s, (struct sockaddr *)&sin_ip,
+ sizeof (sin_ip))) < 0) {
+ (void) snprintf(debug, sizeof (debug),
+ "bind on port %d failed, errno %d",
+ p->port_num, errno);
+ queue_str(q, Q_GEN_ERRS, msg_status, debug);
+ return (NULL);
+ }
+ }
+
+ } else {
+
+ queue_str(q, Q_GEN_DETAILS, msg_log, "Got IPv6 socket");
+ bzero(&sin6_ip, sizeof (sin6_ip));
+ sin6_ip.sin6_family = AF_INET6;
+ sin6_ip.sin6_port = htons(p->port_num);
+ sin6_ip.sin6_addr = in6addr_any;
+
+ (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
+ sizeof (on));
+
+ if ((bind(s, (struct sockaddr *)&sin6_ip, sizeof (sin6_ip)))
+ < 0) {
+ (void) snprintf(debug, sizeof (debug),
+ "bind on port %d failed, errno %d",
+ p->port_num, errno);
+ queue_str(q, Q_GEN_ERRS, msg_status, debug);
+ return (NULL);
+ }
+ }
+
+ if (listen(s, 5) < 0) {
+ queue_str(q, Q_GEN_ERRS, msg_status, "listen failed");
+ return (NULL);
+ }
+
+ /*CONSTANTCONDITION*/
+ while (1) {
+
+ socklen = sizeof (st);
+ if ((fd = accept(s, (struct sockaddr *)&st,
+ &socklen)) < 0) {
+ accept_err_sleep *= 2;
+ (void) sleep(accept_err_sleep);
+ if (accept_err_sleep > 60) {
+ accept_err_sleep = 1;
+ queue_prt(q, Q_GEN_ERRS,
+ "accept failed, errno %d", errno);
+ }
+ continue;
+ }
+
+ l = 128 * 1024;
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&l,
+ sizeof (l)) < 0)
+ queue_str(q, Q_GEN_ERRS, msg_status,
+ "setsockopt failed");
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&l,
+ sizeof (l)) < 0)
+ queue_str(q, Q_GEN_ERRS, msg_status,
+ "setsockopt failed");
+ l = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&l,
+ sizeof (l)) < 0)
+ queue_str(q, Q_GEN_ERRS, msg_status,
+ "setsockopt keepalive failed");
+
+ if ((conn = (iscsi_conn_t *)calloc(sizeof (iscsi_conn_t),
+ 1)) == NULL) {
+ /*
+ * If we fail to get memory this is all rather
+ * pointless, since it's unlikely that queue_str
+ * could malloc memory to send a message.
+ */
+ queue_str(q, Q_GEN_ERRS, msg_status,
+ "connection malloc failed");
+ return (NULL);
+ }
+
+ /*
+ * Save initiator sockaddr for future use
+ */
+ conn->c_initiator_sockaddr = st;
+
+ socklen = sizeof (st);
+ if (getsockname(fd, (struct sockaddr *)&st,
+ &socklen) == 0) {
+ /*
+ * Save target sockaddr for future use
+ */
+
+ addr6 = ((struct sockaddr_in6 *)&st)->sin6_addr;
+ if (st.ss_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&addr6)) {
+ /*
+ * If target address is IPv4 mapped IPv6 address
+ * convert it to IPv4 address
+ */
+ IN6_V4MAPPED_TO_INADDR(&addr6, &addr);
+ ((struct sockaddr_in *)&st)->sin_addr = addr;
+ st.ss_family = AF_INET;
+ }
+
+ conn->c_target_sockaddr = st;
+ }
+
+ conn->c_fd = fd;
+ conn->c_mgmtq = q;
+ conn->c_up_at = time(NULL);
+ conn->c_state = S1_FREE;
+ (void) pthread_mutex_init(&conn->c_mutex, NULL);
+ (void) pthread_mutex_init(&conn->c_state_mutex, NULL);
+ (void) pthread_mutex_lock(&port_mutex);
+ conn->c_num = port_conn_num++;
+ if (conn_head == NULL) {
+ conn_head = conn;
+ assert(conn_tail == NULL);
+ conn_tail = conn;
+ } else {
+ conn_tail->c_next = conn;
+ conn->c_prev = conn_tail;
+ conn_tail = conn;
+ }
+ (void) pthread_mutex_unlock(&port_mutex);
+
+ (void) pthread_create(&junk, NULL, conn_process, conn);
+ }
+ return (NULL);
+}
+
+void
+port_conn_remove(iscsi_conn_t *c)
+{
+ iscsi_conn_t *n;
+
+ (void) pthread_mutex_lock(&port_mutex);
+ if (conn_head == c) {
+ conn_head = c->c_next;
+ if (conn_head == NULL)
+ conn_tail = NULL;
+ else
+ conn_head->c_prev = NULL;
+ } else {
+ n = c->c_prev;
+ n->c_next = c->c_next;
+ if (c->c_next != NULL)
+ c->c_next->c_prev = n;
+ else {
+ assert(conn_tail == c);
+ conn_tail = n;
+ }
+ }
+
+ /*
+ * The connection queue is freed here so that it's protected by
+ * locks. The main thread of the deamon when processing incoming
+ * management requests will send them on the connection queues.
+ * The main thread will grab the port_mutex so that we know the
+ * queue is still valid.
+ */
+ queue_free(c->c_dataq, conn_queue_data_remove);
+ (void) pthread_mutex_unlock(&port_mutex);
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/util_queue.c b/usr/src/cmd/iscsi/iscsitgtd/util_queue.c
new file mode 100644
index 0000000000..48d78779fb
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/util_queue.c
@@ -0,0 +1,483 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <assert.h>
+#include <syslog.h>
+#include <synch.h>
+
+#include "queue.h"
+#include "iscsi_conn.h"
+#include "utility.h"
+#include "target.h"
+#include "t10.h"
+
+FILE *qlog = NULL;
+int qlog_lvl = 0;
+
+pthread_mutex_t q_mutex;
+int queue_num;
+
+void
+queue_init()
+{
+ (void) pthread_mutex_init(&q_mutex, NULL);
+ queue_log(True);
+}
+
+target_queue_t *
+queue_alloc()
+{
+ target_queue_t *q =
+ (target_queue_t *)calloc(1, sizeof (target_queue_t));
+
+ if (q == NULL)
+ return (NULL);
+
+ (void) pthread_mutex_lock(&q_mutex);
+ q->q_num = queue_num++;
+ (void) pthread_mutex_unlock(&q_mutex);
+
+ (void) sema_init(&q->q_sema, 0, USYNC_THREAD, NULL);
+ (void) pthread_mutex_init(&q->q_mutex, NULL);
+
+ return (q);
+}
+
+void
+queue_log(Boolean_t on)
+{
+ (void) pthread_mutex_lock(&q_mutex);
+ if ((on == True) && (qlog == NULL) && (qlog_lvl != 0)) {
+ qlog = fopen(target_log, "ab");
+ } else if ((on == False) && (qlog != NULL)) {
+ (void) fclose(qlog);
+ qlog = NULL;
+ }
+ (void) pthread_mutex_unlock(&q_mutex);
+}
+
+/*
+ * []----
+ * | queue_message_set -- add a given message to the queue.
+ * []----
+ */
+void
+queue_message_set(target_queue_t *q, uint32_t lvl, msg_type_t type,
+ void *data)
+{
+ msg_t *msg;
+
+ if ((msg = (msg_t *)calloc(sizeof (msg_t), 1)) == NULL)
+ return;
+
+ msg->msg_pri_level = lvl;
+ msg->msg_type = type;
+ msg->msg_data = data;
+ msg->msg_next = NULL;
+
+ (void) pthread_mutex_lock(&q->q_mutex);
+
+ if (q->q_head == NULL) {
+ q->q_head = msg;
+ assert(q->q_tail == NULL);
+ q->q_tail = msg;
+ } else if (lvl & Q_HIGH) {
+ msg->msg_next = q->q_head;
+ q->q_head->msg_prev = msg;
+ q->q_head = msg;
+ } else {
+ q->q_tail->msg_next = msg;
+ msg->msg_prev = q->q_tail;
+ q->q_tail = msg;
+ }
+
+ (void) pthread_mutex_unlock(&q->q_mutex);
+
+ (void) sema_post(&q->q_sema);
+}
+
+/*
+ * []----
+ * | queue_message_get -- retrieve the first message in the queue
+ * []----
+ */
+msg_t *
+queue_message_get(target_queue_t *q)
+{
+ msg_t *m;
+
+ while (sema_wait(&q->q_sema) == -1)
+ (void) sleep(1);
+ (void) pthread_mutex_lock(&q->q_mutex);
+ m = q->q_head;
+ if (m == NULL) {
+ assert(q->q_tail == NULL);
+ (void) pthread_mutex_unlock(&q->q_mutex);
+ return (NULL);
+ }
+ q->q_head = m->msg_next;
+ if (q->q_head == NULL)
+ q->q_tail = NULL;
+ (void) pthread_mutex_unlock(&q->q_mutex);
+
+ return (m);
+}
+
+/*
+ * []----
+ * | queue_message_try_get -- see if there's a message available
+ * []----
+ */
+msg_t *
+queue_message_try_get(target_queue_t *q)
+{
+ msg_t *m;
+
+ if (sema_trywait(&q->q_sema) != 0)
+ return (NULL);
+ (void) pthread_mutex_lock(&q->q_mutex);
+ m = q->q_head;
+ q->q_head = m->msg_next;
+ if (q->q_head == NULL)
+ q->q_tail = NULL;
+ (void) pthread_mutex_unlock(&q->q_mutex);
+
+ return (m);
+}
+
+/*
+ * []----
+ * | queue_walker_free -- Run through a queue and free certain messages.
+ * |
+ * | Users of the queues should not walk the queue structure themselves
+ * | unless they also need to grab the lock. To prevent that level of
+ * | knowledge of the queue structures this method is provided to enable
+ * | other subsystems to walk the queue looking for messages which need
+ * | to be deleted.
+ * []----
+ */
+void
+queue_walker_free(target_queue_t *q, Boolean_t (*func)(msg_t *m, void *v),
+ void *v1)
+{
+ msg_t *m, /* current working message */
+ *n; /* next message */
+
+ (void) pthread_mutex_lock(&q->q_mutex);
+ m = q->q_head;
+ while (m) {
+ if ((*func)(m, v1) == True) {
+ if (m == q->q_head) {
+ q->q_head = m->msg_next;
+ if (m->msg_next == NULL)
+ q->q_tail = NULL;
+ else
+ m->msg_next->msg_prev = NULL;
+ } else {
+ m->msg_prev->msg_next = m->msg_next;
+ if (m->msg_next == NULL)
+ q->q_tail = m->msg_prev;
+ else
+ m->msg_next->msg_prev = m->msg_prev;
+ }
+ n = m->msg_next;
+ queue_message_free(m);
+ m = n;
+ } else {
+ m = m->msg_next;
+ }
+ }
+ (void) pthread_mutex_unlock(&q->q_mutex);
+}
+
+/*
+ * []----
+ * | queue_reset -- Flush a queue of all command messages messages.
+ * []----
+ */
+void
+queue_reset(target_queue_t *q)
+{
+ msg_t *m,
+ *n;
+
+ (void) pthread_mutex_lock(&q->q_mutex);
+ m = q->q_head;
+ while (m != NULL) {
+
+ switch (m->msg_type) {
+ case msg_cmd_data_out:
+ case msg_cmd_send:
+ if (m == q->q_head) {
+ q->q_head = m->msg_next;
+ if (m->msg_next == NULL)
+ q->q_tail = NULL;
+ else
+ m->msg_next->msg_prev = NULL;
+ } else {
+ assert(m->msg_prev != NULL);
+ m->msg_prev->msg_next = m->msg_next;
+ if (m->msg_next == NULL)
+ q->q_tail = m->msg_prev;
+ else
+ m->msg_next->msg_prev = m->msg_prev;
+ }
+ n = m->msg_next;
+ free(m);
+ m = n;
+ sema_wait(&q->q_sema);
+ break;
+
+ case msg_reset_lu:
+ case msg_shutdown:
+ case msg_lu_add:
+ case msg_lu_remove:
+ case msg_lu_online:
+ case msg_thick_provo:
+ /*
+ * Don't flush the control messages
+ */
+ m = m->msg_next;
+ break;
+
+ default:
+ queue_prt(mgmtq, Q_STE_ERRS,
+ "---- Unexpected msg type %d ----", m->msg_type);
+ m = m->msg_next;
+ break;
+ }
+ }
+
+ (void) pthread_mutex_unlock(&q->q_mutex);
+}
+
+void
+queue_message_free(msg_t *m)
+{
+ free(m);
+}
+
+/*
+ * []----
+ * | queue_free -- free resources used by queue structure
+ * []----
+ */
+void
+queue_free(target_queue_t *q, void (*free_func)(msg_t *))
+{
+ msg_t *m,
+ *n;
+
+ (void) pthread_mutex_lock(&q->q_mutex);
+ m = q->q_head;
+ while (m != NULL) {
+ if (free_func != NULL)
+ (*free_func)(m);
+ n = m->msg_next;
+ free(m);
+ m = n;
+ }
+ (void) pthread_mutex_unlock(&q->q_mutex);
+
+ (void) pthread_mutex_destroy(&q->q_mutex);
+ (void) sema_destroy(&q->q_sema);
+ free(q);
+}
+
+void
+queue_prt(target_queue_t *q, int type, char *fmt, ...)
+{
+ va_list ap;
+ char buf[80];
+
+ va_start(ap, fmt);
+ /* LINTED variable format specifier */
+ (void) vsnprintf(buf, sizeof (buf), fmt, ap);
+ queue_str(q, type, msg_log, buf);
+ va_end(ap);
+}
+
+/*
+ * []----
+ * | queue_str -- helper function which sends a string to the queue
+ * []----
+ */
+void
+queue_str(target_queue_t *q, uint32_t lvl, msg_type_t type, char *fmt)
+{
+ int len;
+ char *m;
+ hrtime_t h = gethrtime(),
+ delta;
+ static hrtime_t last_h = 0;
+
+ (void) pthread_mutex_lock(&q_mutex);
+ if ((qlog) && (qlog_lvl & lvl)) {
+ (void) fprintf(qlog, "%s\n", fmt);
+ (void) fflush(qlog);
+ }
+ (void) pthread_mutex_unlock(&q_mutex);
+
+ if ((dbg_timestamps == True) && (lvl != 0) && ((lvl & Q_HIGH) == 0)) {
+ len = strlen(fmt) + 12;
+ m = malloc(len);
+ delta = h - last_h;
+ last_h = h;
+ (void) snprintf(m, len, "%9.3f %s",
+ (double)delta / (double)1000000.0, fmt);
+ queue_message_set(q, lvl, type, (void *)m);
+ } else {
+ len = strlen(fmt) + 1;
+ m = malloc(len);
+ (void) strncpy(m, fmt, len);
+ queue_message_set(q, lvl, type, (void *)m);
+ }
+}
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Specialized free routines for queue data. |
+ * | It is possible for a shutdown to start because the STE thread |
+ * | receives an error while reading from the socket. If at the same |
+ * | time the connection poll thread is processing a PDU it could place |
+ * | a msg_ste_datain package on the STE queue. When the STE hits the |
+ * | shutdown message first it will exit and we need to clean up |
+ * | anything on that queue which means freeing memory in the |
+ * | appropriate manner. This is just one example and there are several |
+ * | others. Another method to deal with this would be to have a closed |
+ * | flag such that any futher calls to queue_message_set would return |
+ * | an error. This would require any calls to queue_message_set() deal |
+ * | with this condition. The approach used here seems cleaner. |
+ * | The drawback to this approach is that if any new messages are |
+ * | added then the developer had better add it to these routines as |
+ * | appropriate. |
+ * []------------------------------------------------------------------[]
+ */
+
+/*
+ * []----
+ * | sess_queue_data_remove -- free any message data left on the sess queue
+ * |
+ * | XXX This should be recoded so that we're doing the cleanup within
+ * | the session code. Peal off any messages and deal with them there.
+ * []----
+ */
+void
+sess_queue_data_remove(msg_t *m)
+{
+ mgmt_request_t *mq;
+ char **buf;
+
+ syslog(LOG_ERR, "sess_queue_data: type %d", m->msg_type);
+ switch (m->msg_type) {
+ default:
+ syslog(LOG_ERR, "Unknown session type data being free'd, %d",
+ m->msg_type);
+ free(m->msg_data);
+ break;
+
+ case msg_shutdown:
+ case msg_shutdown_rsp:
+ case msg_ste_media_error:
+ syslog(LOG_ERR, "Impossible message left in session queue"
+ " of type %d", m->msg_type);
+ break;
+
+ case msg_cmd_data_out:
+ break;
+
+ case msg_initiator_name:
+ case msg_initiator_alias:
+ case msg_target_name:
+ free(((name_request_t *)m->msg_data)->nr_name);
+ break;
+
+ case msg_mgmt_rqst:
+ mq = (mgmt_request_t *)m->msg_data;
+ (void) pthread_mutex_lock(&mq->m_resp_mutex);
+ xml_add_tag(mq->m_u.m_resp, "queue_freed", NULL);
+ (void) pthread_mutex_unlock(&mq->m_resp_mutex);
+ queue_message_set(mq->m_q, 0, msg_mgmt_rply, 0);
+ break;
+
+ case msg_mgmt_rply:
+ mq = (mgmt_request_t *)m->msg_data;
+ buf = mq->m_u.m_resp;
+ buf_add_tag(buf, XML_ELEMENT_STATS, Tag_End);
+ buf_add_tag(buf, XML_ELEMENT_CONN, Tag_End);
+
+ (void) pthread_mutex_unlock(&mq->m_resp_mutex);
+ queue_message_set(mq->m_q, 0, msg_mgmt_rply, 0);
+ break;
+
+ case msg_reset_targ:
+ case msg_reset_lu:
+ /* ---- these are safe to ignore, no data to free ---- */
+ break;
+ }
+}
+
+/*
+ * []----
+ * | conn_queue_data_remove -- free any message data left on the conn queue
+ * []----
+ */
+void
+conn_queue_data_remove(msg_t *m)
+{
+ mgmt_request_t *mq;
+
+ syslog(LOG_ERR, "conn_queue_data: type %d", m->msg_type);
+ switch (m->msg_type) {
+ case msg_cmd_data_rqst:
+ case msg_cmd_data_out:
+ case msg_cmd_cmplt:
+ syslog(LOG_ERR, "Free'ing data which should already be gone");
+ free(m->msg_data);
+ break;
+
+ case msg_mgmt_rqst:
+ mq = (mgmt_request_t *)m->msg_data;
+ (void) pthread_mutex_lock(&mq->m_resp_mutex);
+ if (mq->m_u.m_resp != NULL)
+ xml_add_tag(mq->m_u.m_resp, "queue_freed", NULL);
+ (void) pthread_mutex_unlock(&mq->m_resp_mutex);
+ queue_message_set(mq->m_q, 0, msg_mgmt_rply, 0);
+ break;
+
+ default:
+ syslog(LOG_ERR, "Unknown connection message being free'd: %d",
+ m->msg_type);
+ free(m->msg_data);
+ break;
+ }
+}
diff --git a/usr/src/cmd/iscsi/iscsitgtd/utility.h b/usr/src/cmd/iscsi/iscsitgtd/utility.h
new file mode 100644
index 0000000000..5d2145515e
--- /dev/null
+++ b/usr/src/cmd/iscsi/iscsitgtd/utility.h
@@ -0,0 +1,96 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TARGET_UTILITY_H
+#define _TARGET_UTILITY_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Block comment which describes the contents of this file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "iscsi_conn.h"
+#include <sys/iscsi_protocol.h>
+#include "errcode.h"
+
+#define SNA32_CHECK 2147483648UL
+
+/*
+ * Generates a lot more information on each transfer. Disabling this reduces
+ * one possible performance impact in the data path. Event with logging turned
+ * off the messages are still queued which requires malloc's and possible lock
+ * contention on the management queue. This is a gut feeling, rather than
+ * actual tests to confirm.
+ */
+#undef FULL_DEBUG
+
+typedef struct thick_provo {
+ struct thick_provo *next,
+ *prev;
+ char *targ_name;
+ int lun;
+ target_queue_t *q;
+} thick_provo_t;
+
+void util_init();
+int read_retry(int fd, char *buf, int count);
+Boolean_t parse_text(iscsi_conn_t *c, int dlen, char **text,
+ int *text_length, int *errcode);
+Boolean_t add_text(char **text, int *current_length, char *name, char *val);
+char *task_to_str(int func);
+void create_geom(diskaddr_t size, int *cylinder, int *heads, int *spt);
+void connection_parameters_default(iscsi_conn_t *c);
+int sna_lt(uint32_t n1, uint32_t n2);
+int sna_lte(uint32_t n1, uint32_t n2);
+void xml_rtn_msg(char **buf, err_code_t code);
+Boolean_t update_config_targets(char **msg);
+Boolean_t update_config_main(char **msg);
+Boolean_t add_target_alias(iscsi_conn_t *c, char **text, int *test_length);
+Boolean_t validate_version(xml_node_t *node, int *maj, int *min);
+char *create_tpgt_list(char *tname);
+int find_main_tpgt(struct sockaddr_storage *pst);
+Boolean_t check_access(xml_node_t *targ, char *initiator_name,
+ Boolean_t req_chap);
+xml_node_t *find_target_node(char *targ_name);
+void util_title(target_queue_t *q, int type, int num, char *title);
+Boolean_t util_create_guid(char **guid);
+Boolean_t strtoll_multiplier(char *str, uint64_t *sp);
+void thick_provo_stop(char *targ, int lun);
+void *thick_provo_start(void *v);
+Boolean_t thick_provo_chk_thr(char *targ, int lun);
+void remove_target_common(char *name, int lun, char **msg);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TARGET_UTILITY_H */
diff --git a/usr/src/lib/libsecdb/exec_attr.txt b/usr/src/lib/libsecdb/exec_attr.txt
index 6ded54c77d..6cbee3ea1f 100644
--- a/usr/src/lib/libsecdb/exec_attr.txt
+++ b/usr/src/lib/libsecdb/exec_attr.txt
@@ -85,6 +85,7 @@ File System Management:suser:cmd:::/usr/sbin/fsck:euid=0
File System Management:suser:cmd:::/usr/sbin/fsdb:euid=0
File System Management:suser:cmd:::/usr/sbin/fstyp:euid=0
File System Management:suser:cmd:::/usr/sbin/fuser:euid=0
+File System Management:suser:cmd:::/usr/sbin/iscsitgtadm:euid=0,privs=basic
File System Management:suser:cmd:::/usr/sbin/mkfile:euid=0
File System Management:suser:cmd:::/usr/sbin/mkfs:euid=0
File System Management:suser:cmd:::/usr/sbin/mount:uid=0
diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile
index f68c4145c1..f26c774f97 100644
--- a/usr/src/pkgdefs/Makefile
+++ b/usr/src/pkgdefs/Makefile
@@ -207,6 +207,8 @@ COMMON_SUBDIRS= \
SUNWippcore \
SUNWipplr \
SUNWipplu \
+ SUNWiscsitgtr \
+ SUNWiscsitgtu \
SUNWixgb \
SUNWkrbr \
SUNWkrbu \
diff --git a/usr/src/pkgdefs/SUNWiscsitgtr/Makefile b/usr/src/pkgdefs/SUNWiscsitgtr/Makefile
new file mode 100644
index 0000000000..1ca5a4fecf
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtr/Makefile
@@ -0,0 +1,38 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+DATAFILES += depend i.manifest r.manifest
+
+.KEEP_STATE:
+
+all: $(FILES)
+
+install: all pkg
+
+include ../Makefile.targ
+
diff --git a/usr/src/pkgdefs/SUNWiscsitgtr/pkginfo.tmpl b/usr/src/pkgdefs/SUNWiscsitgtr/pkginfo.tmpl
new file mode 100644
index 0000000000..3ffbf99ac5
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtr/pkginfo.tmpl
@@ -0,0 +1,50 @@
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWiscsitgtr"
+NAME="Sun iSCSI Target (Root)"
+ARCH="ISA"
+CATEGORY="system"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="root"
+CLASSES="manifest none rbac"
+DESC="Sun iSCSI Target"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+VERSION="ONVERS,REV=0.0.0"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+MAXINST="1000"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
diff --git a/usr/src/pkgdefs/SUNWiscsitgtr/prototype_com b/usr/src/pkgdefs/SUNWiscsitgtr/prototype_com
new file mode 100644
index 0000000000..cadfeb7b3a
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtr/prototype_com
@@ -0,0 +1,52 @@
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+#
+i copyright
+i pkginfo
+i depend
+i i.manifest
+i r.manifest
+#
+# SUNWiscsitgtr files
+#
+d none var 755 root sys
+d none var/svc 755 root sys
+d none var/svc/manifest 755 root sys
+d none var/svc/manifest/system 755 root sys
+f manifest var/svc/manifest/system/iscsi_target.xml 0444 root sys
+d none etc 755 root sys
+d none etc/iscsi 0755 root sys
diff --git a/usr/src/pkgdefs/SUNWiscsitgtr/prototype_i386 b/usr/src/pkgdefs/SUNWiscsitgtr/prototype_i386
new file mode 100644
index 0000000000..8377903804
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtr/prototype_i386
@@ -0,0 +1,48 @@
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+# List files which are Intel specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWiscsitgtr
+#
diff --git a/usr/src/pkgdefs/SUNWiscsitgtr/prototype_sparc b/usr/src/pkgdefs/SUNWiscsitgtr/prototype_sparc
new file mode 100644
index 0000000000..5fdae1683d
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtr/prototype_sparc
@@ -0,0 +1,48 @@
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWiscsitgtr
+#
+
diff --git a/usr/src/pkgdefs/SUNWiscsitgtu/Makefile b/usr/src/pkgdefs/SUNWiscsitgtu/Makefile
new file mode 100644
index 0000000000..5e7da20fa8
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtu/Makefile
@@ -0,0 +1,37 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(FILES) depend
+
+install: all pkg
+
+include ../Makefile.targ
+
diff --git a/usr/src/pkgdefs/SUNWiscsitgtu/depend b/usr/src/pkgdefs/SUNWiscsitgtu/depend
new file mode 100644
index 0000000000..cbb04340d6
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtu/depend
@@ -0,0 +1,51 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+# This package information file defines software dependencies associated
+# with the pkg. You can define three types of pkg dependencies with this file:
+# P indicates a prerequisite for installation
+# I indicates an incompatible package
+# R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# (<arch>)<version>
+# (<arch>)<version>
+# ...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar Core Architecture, (Root)
+P SUNWcakr Core Solaris Kernel Architecture (Root)
+P SUNWkvm Core Architecture, (Kvm)
+P SUNWcsr Core Solaris, (Root)
+P SUNWckr Core Solaris Kernel (Root)
+P SUNWcnetr Core Solaris Network Infrastructure (Root)
+P SUNWcsu Core Solaris, (Usr)
+P SUNWcsd Core Solaris Devices
+P SUNWcsl Core Solaris Libraries
+P SUNWiscsitgtr Sun iSCSI Target
diff --git a/usr/src/pkgdefs/SUNWiscsitgtu/pkginfo.tmpl b/usr/src/pkgdefs/SUNWiscsitgtu/pkginfo.tmpl
new file mode 100644
index 0000000000..f7dd2b3e49
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtu/pkginfo.tmpl
@@ -0,0 +1,49 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWiscsitgtu"
+NAME="Sun iSCSI Target (Usr)"
+ARCH="ISA"
+CATEGORY="system"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="usr"
+CLASSES="none"
+DESC="Sun iSCSI Target"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+VERSION="ONVERS,REV=0.0.0"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+MAXINST="1000"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
diff --git a/usr/src/pkgdefs/SUNWiscsitgtu/preremove b/usr/src/pkgdefs/SUNWiscsitgtu/preremove
new file mode 100644
index 0000000000..9ed9f3d8e4
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtu/preremove
@@ -0,0 +1,58 @@
+#! /usr/bin/sh
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+PATH="/usr/bin:/usr/sbin:${PATH}"
+export PATH
+
+SERVICE="svc:/system/iscsitgt:default"
+
+#
+# Exit if not removing from the running system
+#
+[ "${PKG_INSTALL_ROOT:-/}" = "/" ] || exit 0
+
+#
+# Confirm service is installed, otherwise exit.
+#
+/usr/bin/svcprop -q ${SERVICE} || exit 0
+
+#
+# Check to see if the service is enabled and if so disable it.
+#
+
+SVCPROP=`svcprop -p general/enabled ${SERVICE}`
+
+if [ "${SVCPROP}" = "true" ]; then
+ svcadm disable ${SERVICE}
+ if [ $? -ne 0 ]; then
+ echo "\n$0 Disabling of ${SERVICE} failed!\n" >&2
+ exit 1
+ fi
+fi
+
+exit 0
diff --git a/usr/src/pkgdefs/SUNWiscsitgtu/prototype_com b/usr/src/pkgdefs/SUNWiscsitgtu/prototype_com
new file mode 100644
index 0000000000..b974ad3fd4
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtu/prototype_com
@@ -0,0 +1,48 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+#
+i copyright
+i pkginfo
+i depend
+i preremove
+#
+# SUNWiscsitgtu
+#
+d none usr 0755 root sys
+d none usr/sbin 0755 root bin
+f none usr/sbin/iscsitadm 0555 root bin
+l none usr/sbin/iscsitgtd=../../usr/lib/isaexec
diff --git a/usr/src/pkgdefs/SUNWiscsitgtu/prototype_i386 b/usr/src/pkgdefs/SUNWiscsitgtu/prototype_i386
new file mode 100644
index 0000000000..1005a2c399
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtu/prototype_i386
@@ -0,0 +1,52 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+# List files which are Intel specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWiscsitgtu
+#
+d none usr/sbin/amd64 755 root bin
+f none usr/sbin/amd64/iscsitgtd 555 root bin
+d none usr/sbin/i86 755 root bin
+f none usr/sbin/i86/iscsitgtd 555 root bin
diff --git a/usr/src/pkgdefs/SUNWiscsitgtu/prototype_sparc b/usr/src/pkgdefs/SUNWiscsitgtu/prototype_sparc
new file mode 100644
index 0000000000..6483a31b8a
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWiscsitgtu/prototype_sparc
@@ -0,0 +1,49 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWiscsitgtu
+#
+d none usr/sbin/sparcv9 755 root bin
+f none usr/sbin/sparcv9/iscsitgtd 555 root bin
diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386
index 7f76889c94..c208110ff3 100644
--- a/usr/src/pkgdefs/etc/exception_list_i386
+++ b/usr/src/pkgdefs/etc/exception_list_i386
@@ -802,3 +802,11 @@ usr/include/nss.h i386
#
# bmc (IPMI) interfaces shared within ON.
usr/include/sys/bmc_intf.h i386
+#
+# These files are used by the iSCSI Target which is in this consolidation
+# and the iSCSI Initiator which is in the DMG consolidation. There's no
+# reason to ship these files.
+#
+usr/include/sys/iscsi_protocol.h i386
+usr/include/sys/iscsi_authclient.h i386
+usr/include/sys/iscsi_authclientglue.h i386
diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc
index 493427a850..a4d454f2d1 100644
--- a/usr/src/pkgdefs/etc/exception_list_sparc
+++ b/usr/src/pkgdefs/etc/exception_list_sparc
@@ -871,3 +871,11 @@ usr/include/nss.h sparc
# This file is used in ON to build DSCP clients. It is not for customers.
#
usr/include/libdscp.h sparc
+#
+# These files are used by the iSCSI Target which is in this consolidation
+# and the iSCSI Initiator which is in the DMG consolidation. There's no
+# reason to ship these files.
+#
+usr/include/sys/iscsi_protocol.h sparc
+usr/include/sys/iscsi_authclient.h sparc
+usr/include/sys/iscsi_authclientglue.h sparc
diff --git a/usr/src/uts/common/io/emul64_bsd.c b/usr/src/uts/common/io/emul64_bsd.c
index 9e033b2bfe..64823f81e7 100644
--- a/usr/src/uts/common/io/emul64_bsd.c
+++ b/usr/src/uts/common/io/emul64_bsd.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -199,19 +198,6 @@ static emul64_rng_overlap_t bsd_tgt_overlap(emul64_tgt_t *, diskaddr_t, int);
char *emul64_name = "emul64";
-/* XXX replace with FORMG0COUNT */
-#define GETG0COUNT(cdb) ((cdb)->g0_count0)
-
-#define GETG1COUNT(cdb) ((cdb)->g1_count1 << 8) + \
- ((cdb)->g1_count0)
-
-#define GETG4COUNT(cdb) \
- ((uint64_t)(cdb)->g4_count3 << 24) + \
- ((uint64_t)(cdb)->g4_count2 << 16) + \
- ((uint64_t)(cdb)->g4_count1 << 8) + \
- ((uint64_t)(cdb)->g4_count0)
-
-
/*
* Initialize globals in this file.
*/
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index ce2cdf39a4..60f5eef05a 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -259,6 +259,9 @@ CHKHDRS= \
ipc.h \
ipc_impl.h \
isa_defs.h \
+ iscsi_authclient.h \
+ iscsi_authclientglue.h \
+ iscsi_protocol.h \
jioctl.h \
kbd.h \
kbdreg.h \
diff --git a/usr/src/uts/common/sys/iscsi_authclient.h b/usr/src/uts/common/sys/iscsi_authclient.h
new file mode 100644
index 0000000000..2d0756c07a
--- /dev/null
+++ b/usr/src/uts/common/sys/iscsi_authclient.h
@@ -0,0 +1,403 @@
+/*
+ * 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 2000 by Cisco Systems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ISCSI_AUTHCLIENT_H
+#define _ISCSI_AUTHCLIENT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file is the include file for for iscsiAuthClient.c
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum { iscsiAuthStringMaxLength = 256 };
+enum { iscsiAuthStringBlockMaxLength = 1024 };
+enum { iscsiAuthLargeBinaryMaxLength = 1024 };
+
+enum { iscsiAuthRecvEndMaxCount = 10 };
+
+enum { iscsiAuthClientSignature = 0x5984B2E3 };
+
+enum { iscsiAuthChapResponseLength = 16 };
+
+/*
+ * Note: The ordering of these values are chosen to match
+ * the ordering of the keys as shown in the iSCSI spec.
+ * The table IscsiAuthClientKeyInfo in iscsiAuthClient.c
+ * must also match this order.
+ */
+enum iscsiAuthKeyType_t {
+ iscsiAuthKeyTypeNone = -1,
+ iscsiAuthKeyTypeFirst = 0,
+ iscsiAuthKeyTypeAuthMethod = iscsiAuthKeyTypeFirst,
+ iscsiAuthKeyTypeChapAlgorithm,
+ iscsiAuthKeyTypeChapUsername,
+ iscsiAuthKeyTypeChapResponse,
+ iscsiAuthKeyTypeChapIdentifier,
+ iscsiAuthKeyTypeChapChallenge,
+ iscsiAuthKeyTypeMaxCount,
+ iscsiAuthKeyTypeLast = iscsiAuthKeyTypeMaxCount - 1
+};
+typedef enum iscsiAuthKeyType_t IscsiAuthKeyType;
+
+enum {
+ /*
+ * Common options for all keys.
+ */
+ iscsiAuthOptionReject = -2,
+ iscsiAuthOptionNotPresent = -1,
+ iscsiAuthOptionNone = 1,
+
+ iscsiAuthMethodChap = 2,
+ iscsiAuthMethodMaxCount = 2,
+
+ iscsiAuthChapAlgorithmMd5 = 5,
+ iscsiAuthChapAlgorithmMaxCount = 2
+};
+
+enum iscsiAuthNegRole_t {
+ iscsiAuthNegRoleOriginator = 1,
+ iscsiAuthNegRoleResponder = 2
+};
+typedef enum iscsiAuthNegRole_t IscsiAuthNegRole;
+
+/*
+ * Note: These values are chosen to map to the values sent
+ * in the iSCSI header.
+ */
+enum iscsiAuthVersion_t {
+ iscsiAuthVersionDraft8 = 2,
+ iscsiAuthVersionRfc = 0
+};
+typedef enum iscsiAuthVersion_t IscsiAuthVersion;
+
+enum iscsiAuthStatus_t {
+ iscsiAuthStatusNoError = 0,
+ iscsiAuthStatusError,
+ iscsiAuthStatusPass,
+ iscsiAuthStatusFail,
+ iscsiAuthStatusContinue,
+ iscsiAuthStatusInProgress
+};
+typedef enum iscsiAuthStatus_t IscsiAuthStatus;
+
+enum iscsiAuthDebugStatus_t {
+ iscsiAuthDebugStatusNotSet = 0,
+
+ iscsiAuthDebugStatusAuthPass,
+ iscsiAuthDebugStatusAuthRemoteFalse,
+
+ iscsiAuthDebugStatusAuthFail,
+
+ iscsiAuthDebugStatusAuthMethodBad,
+ iscsiAuthDebugStatusChapAlgorithmBad,
+ iscsiAuthDebugStatusPasswordDecryptFailed,
+ iscsiAuthDebugStatusPasswordTooShortWithNoIpSec,
+ iscsiAuthDebugStatusAuthServerError,
+ iscsiAuthDebugStatusAuthStatusBad,
+ iscsiAuthDebugStatusAuthPassNotValid,
+ iscsiAuthDebugStatusSendDuplicateSetKeyValue,
+ iscsiAuthDebugStatusSendStringTooLong,
+ iscsiAuthDebugStatusSendTooMuchData,
+
+ iscsiAuthDebugStatusAuthMethodExpected,
+ iscsiAuthDebugStatusChapAlgorithmExpected,
+ iscsiAuthDebugStatusChapIdentifierExpected,
+ iscsiAuthDebugStatusChapChallengeExpected,
+ iscsiAuthDebugStatusChapResponseExpected,
+ iscsiAuthDebugStatusChapUsernameExpected,
+
+ iscsiAuthDebugStatusAuthMethodNotPresent,
+ iscsiAuthDebugStatusAuthMethodReject,
+ iscsiAuthDebugStatusAuthMethodNone,
+ iscsiAuthDebugStatusChapAlgorithmReject,
+ iscsiAuthDebugStatusChapChallengeReflected,
+ iscsiAuthDebugStatusPasswordIdentical,
+
+ iscsiAuthDebugStatusLocalPasswordNotSet,
+
+ iscsiAuthDebugStatusChapIdentifierBad,
+ iscsiAuthDebugStatusChapChallengeBad,
+ iscsiAuthDebugStatusChapResponseBad,
+ iscsiAuthDebugStatusUnexpectedKeyPresent,
+ iscsiAuthDebugStatusTbitSetIllegal,
+ iscsiAuthDebugStatusTbitSetPremature,
+
+ iscsiAuthDebugStatusRecvMessageCountLimit,
+ iscsiAuthDebugStatusRecvDuplicateSetKeyValue,
+ iscsiAuthDebugStatusRecvStringTooLong,
+ iscsiAuthDebugStatusRecvTooMuchData
+};
+typedef enum iscsiAuthDebugStatus_t IscsiAuthDebugStatus;
+
+enum iscsiAuthNodeType_t {
+ iscsiAuthNodeTypeInitiator = 1,
+ iscsiAuthNodeTypeTarget = 2
+};
+typedef enum iscsiAuthNodeType_t IscsiAuthNodeType;
+
+enum iscsiAuthPhase_t {
+ iscsiAuthPhaseConfigure = 1,
+ iscsiAuthPhaseNegotiate, /* Negotiating */
+ iscsiAuthPhaseAuthenticate, /* Authenticating */
+ iscsiAuthPhaseDone, /* Authentication done */
+ iscsiAuthPhaseError
+};
+typedef enum iscsiAuthPhase_t IscsiAuthPhase;
+
+enum iscsiAuthLocalState_t {
+ iscsiAuthLocalStateSendAlgorithm = 1,
+ iscsiAuthLocalStateRecvAlgorithm,
+ iscsiAuthLocalStateRecvChallenge,
+ iscsiAuthLocalStateDone,
+ iscsiAuthLocalStateError
+};
+typedef enum iscsiAuthLocalState_t IscsiAuthLocalState;
+
+enum iscsiAuthRemoteState_t {
+ iscsiAuthRemoteStateSendAlgorithm = 1,
+ iscsiAuthRemoteStateSendChallenge,
+ iscsiAuthRemoteStateRecvResponse,
+ iscsiAuthRemoteStateAuthRequest,
+ iscsiAuthRemoteStateDone,
+ iscsiAuthRemoteStateError
+};
+typedef enum iscsiAuthRemoteState_t IscsiAuthRemoteState;
+
+
+typedef void IscsiAuthClientCallback(void *, void *, int);
+
+
+struct iscsiAuthClientGlobalStats_t {
+ unsigned long requestSent;
+ unsigned long responseReceived;
+};
+typedef struct iscsiAuthClientGlobalStats_t IscsiAuthClientGlobalStats;
+
+struct iscsiAuthBufferDesc_t {
+ unsigned int length;
+ void *address;
+};
+typedef struct iscsiAuthBufferDesc_t IscsiAuthBufferDesc;
+
+struct iscsiAuthKey_t {
+ unsigned int present:1;
+ unsigned int processed:1;
+ unsigned int valueSet:1; /* 1 if the value is set to be valid */
+ char *string;
+};
+typedef struct iscsiAuthKey_t IscsiAuthKey;
+
+struct iscsiAuthLargeBinaryKey_t {
+ unsigned int length;
+ unsigned char *largeBinary;
+ };
+typedef struct iscsiAuthLargeBinaryKey_t IscsiAuthLargeBinaryKey;
+
+struct iscsiAuthKeyBlock_t {
+ unsigned int transitBit:1; /* To transit: TRUE or FALSE */
+ unsigned int duplicateSet:1; /* Set the value more than once */
+ unsigned int stringTooLong:1; /* Key value too long */
+ unsigned int tooMuchData:1; /* The keypair data blk overflows */
+ unsigned int blockLength:16; /* The length of the keypair data blk */
+ char *stringBlock;
+ IscsiAuthKey key[iscsiAuthKeyTypeMaxCount];
+};
+typedef struct iscsiAuthKeyBlock_t IscsiAuthKeyBlock;
+
+struct iscsiAuthStringBlock_t {
+ char stringBlock[iscsiAuthStringBlockMaxLength];
+};
+typedef struct iscsiAuthStringBlock_t IscsiAuthStringBlock;
+
+struct iscsiAuthLargeBinary_t {
+ unsigned char largeBinary[iscsiAuthLargeBinaryMaxLength];
+};
+typedef struct iscsiAuthLargeBinary_t IscsiAuthLargeBinary;
+
+struct iscsiAuthClient_t {
+ unsigned long signature;
+
+ void *glueHandle;
+ struct iscsiAuthClient_t *next;
+ unsigned int authRequestId;
+
+ IscsiAuthNodeType nodeType;
+ unsigned int authMethodCount;
+ int authMethodList[iscsiAuthMethodMaxCount];
+ IscsiAuthNegRole authMethodNegRole;
+ unsigned int chapAlgorithmCount;
+ int chapAlgorithmList[iscsiAuthChapAlgorithmMaxCount];
+
+ /*
+ * To indicate if remote authentication is enabled (0 = no 1 = yes)
+ * For the case of initiator, remote authentication enabled means
+ * enabling target authentication.
+ */
+ int authRemote;
+
+ char username[iscsiAuthStringMaxLength];
+ int passwordPresent;
+ unsigned int passwordLength;
+ unsigned char passwordData[iscsiAuthStringMaxLength];
+ char methodListName[iscsiAuthStringMaxLength];
+ IscsiAuthVersion version;
+ unsigned int chapChallengeLength;
+ int ipSec;
+ int base64;
+
+ unsigned int authMethodValidCount;
+ int authMethodValidList[iscsiAuthMethodMaxCount];
+ int authMethodValidNegRole;
+ const char *rejectOptionName;
+ const char *noneOptionName;
+
+ int recvInProgressFlag;
+ int recvEndCount;
+ IscsiAuthClientCallback *callback;
+ void *userHandle;
+ void *messageHandle;
+
+ IscsiAuthPhase phase;
+ IscsiAuthLocalState localState;
+ IscsiAuthRemoteState remoteState;
+ IscsiAuthStatus remoteAuthStatus;
+ IscsiAuthDebugStatus debugStatus;
+ int negotiatedAuthMethod;
+ int negotiatedChapAlgorithm;
+ int authResponseFlag;
+ int authServerErrorFlag;
+ int transitBitSentFlag;
+
+ unsigned int sendChapIdentifier;
+ IscsiAuthLargeBinaryKey sendChapChallenge;
+ char chapUsername[iscsiAuthStringMaxLength];
+
+ int recvChapChallengeStatus;
+ IscsiAuthLargeBinaryKey recvChapChallenge;
+
+ char scratchKeyValue[iscsiAuthStringMaxLength];
+
+ IscsiAuthKeyBlock recvKeyBlock; /* Received keypair data */
+ IscsiAuthKeyBlock sendKeyBlock; /* Keypair data to be sent */
+};
+typedef struct iscsiAuthClient_t IscsiAuthClient;
+
+
+#ifdef __cplusplus
+}
+#endif
+#include <sys/iscsi_authclientglue.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern IscsiAuthClientGlobalStats iscsiAuthClientGlobalStats;
+
+
+extern int iscsiAuthClientInit(int, int, IscsiAuthBufferDesc *);
+extern int iscsiAuthClientFinish(IscsiAuthClient *);
+
+extern int iscsiAuthClientRecvBegin(IscsiAuthClient *);
+extern int iscsiAuthClientRecvEnd(IscsiAuthClient *,
+ IscsiAuthClientCallback *, void *, void *);
+
+extern const char *iscsiAuthClientGetKeyName(int);
+extern int iscsiAuthClientGetNextKeyType(int *);
+extern int iscsiAuthClientKeyNameToKeyType(const char *);
+extern int iscsiAuthClientRecvKeyValue(IscsiAuthClient *, int, const char *);
+extern int iscsiAuthClientSendKeyValue(IscsiAuthClient *, int, int *, char *,
+ unsigned int);
+extern int iscsiAuthClientRecvTransitBit(IscsiAuthClient *, int);
+extern int iscsiAuthClientSendTransitBit(IscsiAuthClient *, int *);
+
+extern int iscsiAuthClientSetAuthMethodList(IscsiAuthClient *, unsigned int,
+ const int *);
+extern int iscsiAuthClientSetAuthMethodNegRole(IscsiAuthClient *, int);
+extern int iscsiAuthClientSetChapAlgorithmList(IscsiAuthClient *, unsigned int,
+ const int *);
+extern int iscsiAuthClientSetUsername(IscsiAuthClient *, const char *);
+extern int iscsiAuthClientSetPassword(IscsiAuthClient *, const unsigned char *,
+ unsigned int);
+extern int iscsiAuthClientSetAuthRemote(IscsiAuthClient *, int);
+extern int iscsiAuthClientSetGlueHandle(IscsiAuthClient *, void *);
+extern int iscsiAuthClientSetMethodListName(IscsiAuthClient *, const char *);
+extern int iscsiAuthClientSetIpSec(IscsiAuthClient *, int);
+extern int iscsiAuthClientSetBase64(IscsiAuthClient *, int);
+extern int iscsiAuthClientSetChapChallengeLength(IscsiAuthClient *,
+ unsigned int);
+extern int iscsiAuthClientSetVersion(IscsiAuthClient *, int);
+extern int iscsiAuthClientCheckPasswordNeeded(IscsiAuthClient *, int *);
+
+extern int iscsiAuthClientGetAuthPhase(IscsiAuthClient *, int *);
+extern int iscsiAuthClientGetAuthStatus(IscsiAuthClient *, int *);
+extern int iscsiAuthClientAuthStatusPass(int);
+extern int iscsiAuthClientGetAuthMethod(IscsiAuthClient *, int *);
+extern int iscsiAuthClientGetChapAlgorithm(IscsiAuthClient *, int *);
+extern int iscsiAuthClientGetChapUsername(IscsiAuthClient *, char *,
+ unsigned int);
+
+extern int iscsiAuthClientSendStatusCode(IscsiAuthClient *, int *);
+extern int iscsiAuthClientGetDebugStatus(IscsiAuthClient *, int *);
+extern const char *iscsiAuthClientDebugStatusToText(int);
+
+/*
+ * The following is called by platform dependent code.
+ */
+extern void iscsiAuthClientAuthResponse(IscsiAuthClient *, int);
+
+/*
+ * The following routines are considered platform dependent,
+ * and need to be implemented for use by iscsiAuthClient.c.
+ */
+
+extern int iscsiAuthClientChapAuthRequest(IscsiAuthClient *, char *,
+ unsigned int,
+ unsigned char *, unsigned int, unsigned char *, unsigned int);
+extern void iscsiAuthClientChapAuthCancel(IscsiAuthClient *);
+
+extern int iscsiAuthClientTextToNumber(const char *, unsigned long *);
+extern void iscsiAuthClientNumberToText(unsigned long, char *, unsigned int);
+
+extern void iscsiAuthRandomSetData(unsigned char *, unsigned int);
+extern void iscsiAuthMd5Init(IscsiAuthMd5Context *);
+extern void iscsiAuthMd5Update(IscsiAuthMd5Context *, unsigned char *,
+ unsigned int);
+extern void iscsiAuthMd5Final(unsigned char *, IscsiAuthMd5Context *);
+
+extern int iscsiAuthClientData(unsigned char *, unsigned int *, unsigned char *,
+ unsigned int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ISCSI_AUTHCLIENT_H */
diff --git a/usr/src/uts/common/sys/iscsi_authclientglue.h b/usr/src/uts/common/sys/iscsi_authclientglue.h
new file mode 100644
index 0000000000..6001110f8b
--- /dev/null
+++ b/usr/src/uts/common/sys/iscsi_authclientglue.h
@@ -0,0 +1,49 @@
+/*
+ * 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 2000 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * iSCSI Software Initiator
+ */
+
+#ifndef _ISCSI_AUTHCLIENTGLUE_H
+#define _ISCSI_AUTHCLIENTGLUE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <md5.h>
+
+typedef MD5_CTX IscsiAuthMd5Context;
+
+extern int iscsiAuthIscsiServerHandle;
+extern int iscsiAuthIscsiClientHandle;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ISCSI_AUTHCLIENTGLUE_H */
diff --git a/usr/src/uts/common/sys/iscsi_protocol.h b/usr/src/uts/common/sys/iscsi_protocol.h
new file mode 100644
index 0000000000..1f529c4d84
--- /dev/null
+++ b/usr/src/uts/common/sys/iscsi_protocol.h
@@ -0,0 +1,704 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ISCSI_PROTOCOL_H
+#define _ISCSI_PROTOCOL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * iSCSI connection daemon
+ * Copyright (C) 2001 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * This file sets up definitions of messages and constants used by the
+ * iSCSI protocol.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/isa_defs.h>
+
+#define ISCSI_MAX_NAME_LEN 224
+
+/* prototypes for iscsi_crc.c */
+uint32_t iscsi_crc32c(void *address, unsigned long length);
+uint32_t iscsi_crc32c_continued(void *address, unsigned long length,
+ uint32_t crc);
+
+/* iSCSI listen port for incoming connections */
+#define ISCSI_LISTEN_PORT 3260
+
+/* assumes a pointer to a 3-byte array */
+#define ntoh24(p) (((p)[0] << 16) | ((p)[1] << 8) | ((p)[2]))
+
+/* assumes a pointer to a 3 byte array, and an integer value */
+#define hton24(p, v) {\
+ p[0] = (((v) >> 16) & 0xFF); \
+ p[1] = (((v) >> 8) & 0xFF); \
+ p[2] = ((v) & 0xFF); \
+}
+
+
+/* for Login min, max, active version fields */
+#define ISCSI_MIN_VERSION 0x00
+#define ISCSI_DRAFT8_VERSION 0x02
+#define ISCSI_DRAFT20_VERSION 0x00
+#define ISCSI_MAX_VERSION 0x02
+
+/* Min. and Max. length of a PDU we can support */
+#define ISCSI_MIN_PDU_LENGTH (8 << 9) /* 4KB */
+#define ISCSI_MAX_PDU_LENGTH (0xffffffff) /* Huge */
+
+/* Padding word length */
+#define ISCSI_PAD_WORD_LEN 4
+
+/* Max. number of Key=Value pairs in a text message */
+#define ISCSI_MAX_KEY_VALUE_PAIRS 8192
+
+/* text separtor between key value pairs exhanged in login */
+#define ISCSI_TEXT_SEPARATOR '='
+
+/* Sun's initiator session ID */
+#define ISCSI_SUN_ISID_0 0x40 /* ISID - EN format */
+#define ISCSI_SUN_ISID_1 0x00 /* Sec B */
+#define ISCSI_SUN_ISID_2 0x00 /* Sec B */
+#define ISCSI_SUN_ISID_3 0x2A /* Sec C - 42 = Sun's EN */
+
+/* Reserved value for initiator/target task tag */
+#define ISCSI_RSVD_TASK_TAG 0xffffffff
+
+/* maximum length for text keys/values */
+#define KEY_MAXLEN 64
+#define VALUE_MAXLEN 255
+#define TARGET_NAME_MAXLEN VALUE_MAXLEN
+
+#define ISCSI_DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH 8192
+
+/* most PDU types have a final bit */
+#define ISCSI_FLAG_FINAL 0x80
+
+/*
+ * Strings used during SendTargets requests
+ */
+#define ISCSI_TEXT_SEPARATOR '='
+#define TARGETNAME "TargetName="
+#define TARGETADDRESS "TargetAddress="
+
+/* iSCSI Template Message Header */
+typedef struct _iscsi_hdr {
+ uint8_t opcode;
+ uint8_t flags; /* Final bit */
+ uint8_t rsvd2[2];
+ uint8_t hlength; /* AHSs total length */
+ uint8_t dlength[3]; /* Data length */
+ uint8_t lun[8];
+ uint32_t itt; /* Initiator Task Tag */
+ uint8_t rsvd3[8];
+ uint32_t expstatsn;
+ uint8_t other[16];
+} iscsi_hdr_t;
+
+typedef struct _iscsi_rsp_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t rsvd1[3];
+ uint8_t dlength[3];
+ uint8_t rsvd2[8];
+ uint32_t itt;
+ uint8_t rsvd3[4];
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint8_t rsvd4[12];
+} iscsi_rsp_hdr_t;
+
+/* Opcode encoding bits */
+#define ISCSI_OP_RETRY 0x80
+#define ISCSI_OP_IMMEDIATE 0x40
+#define ISCSI_OPCODE_MASK 0x3F
+
+/* Client to Server Message Opcode values */
+#define ISCSI_OP_NOOP_OUT 0x00
+#define ISCSI_OP_SCSI_CMD 0x01
+#define ISCSI_OP_SCSI_TASK_MGT_MSG 0x02
+#define ISCSI_OP_LOGIN_CMD 0x03
+#define ISCSI_OP_TEXT_CMD 0x04
+#define ISCSI_OP_SCSI_DATA 0x05
+#define ISCSI_OP_LOGOUT_CMD 0x06
+#define ISCSI_OP_SNACK_CMD 0x10
+
+/* Server to Client Message Opcode values */
+#define ISCSI_OP_NOOP_IN 0x20
+#define ISCSI_OP_SCSI_RSP 0x21
+#define ISCSI_OP_SCSI_TASK_MGT_RSP 0x22
+#define ISCSI_OP_LOGIN_RSP 0x23
+#define ISCSI_OP_TEXT_RSP 0x24
+#define ISCSI_OP_SCSI_DATA_RSP 0x25
+#define ISCSI_OP_LOGOUT_RSP 0x26
+#define ISCSI_OP_RTT_RSP 0x31
+#define ISCSI_OP_ASYNC_EVENT 0x32
+#define ISCSI_OP_REJECT_MSG 0x3f
+
+
+/* SCSI Command Header */
+typedef struct _iscsi_scsi_cmd_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t rsvd[2];
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t lun[8];
+ uint32_t itt; /* Initiator Task Tag */
+ uint32_t data_length;
+ uint32_t cmdsn;
+ uint32_t expstatsn;
+ uint8_t scb[16]; /* SCSI Command Block */
+ /*
+ * Additional Data (Command Dependent)
+ */
+} iscsi_scsi_cmd_hdr_t;
+
+/* Command PDU flags */
+#define ISCSI_FLAG_CMD_READ 0x40
+#define ISCSI_FLAG_CMD_WRITE 0x20
+#define ISCSI_FLAG_CMD_ATTR_MASK 0x07 /* 3 bits */
+
+/* SCSI Command Attribute values */
+#define ISCSI_ATTR_UNTAGGED 0
+#define ISCSI_ATTR_SIMPLE 1
+#define ISCSI_ATTR_ORDERED 2
+#define ISCSI_ATTR_HEAD_OF_QUEUE 3
+#define ISCSI_ATTR_ACA 4
+
+
+/* SCSI Response Header */
+typedef struct _iscsi_scsi_rsp_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t response;
+ uint8_t cmd_status;
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t rsvd[8];
+ uint32_t itt; /* Initiator Task Tag */
+ uint32_t rsvd1;
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint32_t expdatasn;
+ uint32_t bi_residual_count;
+ uint32_t residual_count;
+ /*
+ * Response or Sense Data (optional)
+ */
+} iscsi_scsi_rsp_hdr_t;
+
+/* 10.2.2.3 - Extended CDB Additional Header Segment */
+
+typedef struct _iscsi_addl_hdr {
+ iscsi_scsi_cmd_hdr_t ahs_isch;
+ uint8_t ahs_hlen_hi;
+ uint8_t ahs_hlen_lo;
+ uint8_t ahs_key;
+ uint8_t ahs_resv;
+ uint8_t ahs_extscb[4];
+} iscsi_addl_hdr_t;
+
+/* Command Response PDU flags */
+#define ISCSI_FLAG_CMD_BIDI_OVERFLOW 0x10
+#define ISCSI_FLAG_CMD_BIDI_UNDERFLOW 0x08
+#define ISCSI_FLAG_CMD_OVERFLOW 0x04
+#define ISCSI_FLAG_CMD_UNDERFLOW 0x02
+
+/* iSCSI Status values. Valid if Rsp Selector bit is not set */
+#define ISCSI_STATUS_CMD_COMPLETED 0
+#define ISCSI_STATUS_TARGET_FAILURE 1
+#define ISCSI_STATUS_SUBSYS_FAILURE 2
+
+
+/* Asynchronous Event Header */
+typedef struct _iscsi_async_evt_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t rsvd2[2];
+ uint8_t rsvd3;
+ uint8_t dlength[3];
+ uint8_t lun[8];
+ uint8_t rsvd4[8];
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint8_t async_event;
+ uint8_t async_vcode;
+ uint16_t param1;
+ uint16_t param2;
+ uint16_t param3;
+ uint8_t rsvd5[4];
+} iscsi_async_evt_hdr_t;
+
+/* iSCSI Event Indicator values */
+#define ISCSI_ASYNC_EVENT_SCSI_EVENT 0
+#define ISCSI_ASYNC_EVENT_REQUEST_LOGOUT 1
+#define ISCSI_ASYNC_EVENT_DROPPING_CONNECTION 2
+#define ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS 3
+#define ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION 4
+#define ISCSI_ASYNC_EVENT_VENDOR_SPECIFIC 255
+
+
+/* NOP-Out Message */
+typedef struct _iscsi_nop_out_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t rsvd2;
+ uint8_t rsvd3;
+ uint8_t dlength[3];
+ uint8_t lun[8];
+ uint32_t itt; /* Initiator Task Tag */
+ uint32_t ttt; /* Target Transfer Tag */
+ uint32_t cmdsn;
+ uint32_t expstatsn;
+ uint8_t rsvd4[16];
+} iscsi_nop_out_hdr_t;
+
+
+/* NOP-In Message */
+typedef struct _iscsi_nop_in_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t rsvd2;
+ uint8_t rsvd3;
+ uint8_t dlength[3];
+ uint8_t lun[8];
+ uint32_t itt; /* Initiator Task Tag */
+ uint32_t ttt; /* Target Transfer Tag */
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint8_t rsvd4[12];
+} iscsi_nop_in_hdr_t;
+
+/* SCSI Task Management Message Header */
+typedef struct _iscsi_scsi_task_mgt_hdr {
+ uint8_t opcode;
+ uint8_t function;
+ uint8_t rsvd1[2];
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t lun[8];
+ uint32_t itt; /* Initiator Task Tag */
+ uint32_t rtt; /* Reference Task Tag */
+ uint32_t cmdsn;
+ uint32_t expstatsn;
+ uint32_t refcmdsn;
+ uint32_t expdatasn;
+ uint8_t rsvd2[8];
+} iscsi_scsi_task_mgt_hdr_t;
+
+#define ISCSI_FLAG_TASK_MGMT_FUNCTION_MASK 0x7F
+
+/* Function values */
+#define ISCSI_TM_FUNC_ABORT_TASK 1
+#define ISCSI_TM_FUNC_ABORT_TASK_SET 2
+#define ISCSI_TM_FUNC_CLEAR_ACA 3
+#define ISCSI_TM_FUNC_CLEAR_TASK_SET 4
+#define ISCSI_TM_FUNC_LOGICAL_UNIT_RESET 5
+#define ISCSI_TM_FUNC_TARGET_WARM_RESET 6
+#define ISCSI_TM_FUNC_TARGET_COLD_RESET 7
+#define ISCSI_TM_FUNC_TASK_REASSIGN 8
+
+
+/* SCSI Task Management Response Header */
+typedef struct _iscsi_scsi_task_mgt_rsp_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t response; /* see Response values below */
+ uint8_t qualifier;
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t rsvd2[8];
+ uint32_t itt; /* Initiator Task Tag */
+ uint32_t rtt; /* Reference Task Tag */
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint8_t rsvd3[12];
+} iscsi_scsi_task_mgt_rsp_hdr_t;
+
+
+/* Response values */
+#define SCSI_TCP_TM_RESP_COMPLETE 0x00
+#define SCSI_TCP_TM_RESP_NO_TASK 0x01
+#define SCSI_TCP_TM_RESP_NO_LUN 0x02
+#define SCSI_TCP_TM_RESP_TASK_ALLEGIANT 0x03
+#define SCSI_TCP_TM_RESP_NO_FAILOVER 0x04
+#define SCSI_TCP_TM_RESP_IN_PRGRESS 0x05
+#define SCSI_TCP_TM_RESP_REJECTED 0xff
+
+/* Ready To Transfer Header */
+typedef struct _iscsi_rtt_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t rsvd2[2];
+ uint8_t rsvd3[12];
+ uint32_t itt; /* Initiator Task Tag */
+ uint32_t ttt; /* Target Transfer Tag */
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint32_t rttsn;
+ uint32_t data_offset;
+ uint32_t data_length;
+} iscsi_rtt_hdr_t;
+
+
+/* SCSI Data Hdr */
+typedef struct _iscsi_data_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t rsvd2[2];
+ uint8_t rsvd3;
+ uint8_t dlength[3];
+ uint8_t lun[8];
+ uint32_t itt;
+ uint32_t ttt;
+ uint32_t rsvd4;
+ uint32_t expstatsn;
+ uint32_t rsvd5;
+ uint32_t datasn;
+ uint32_t offset;
+ uint32_t rsvd6;
+ /*
+ * Payload
+ */
+} iscsi_data_hdr_t;
+
+/* SCSI Data Response Hdr */
+typedef struct _iscsi_data_rsp_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t rsvd2;
+ uint8_t cmd_status;
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t lun[8];
+ uint32_t itt;
+ uint32_t ttt;
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint32_t datasn;
+ uint32_t offset;
+ uint32_t residual_count;
+} iscsi_data_rsp_hdr_t;
+
+/* Data Response PDU flags */
+#define ISCSI_FLAG_DATA_ACK 0x40
+#define ISCSI_FLAG_DATA_OVERFLOW 0x04
+#define ISCSI_FLAG_DATA_UNDERFLOW 0x02
+#define ISCSI_FLAG_DATA_STATUS 0x01
+
+
+/* Text Header */
+typedef struct _iscsi_text_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t rsvd2[2];
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t rsvd4[8];
+ uint32_t itt;
+ uint32_t ttt;
+ uint32_t cmdsn;
+ uint32_t expstatsn;
+ uint8_t rsvd5[16];
+ /*
+ * Text - key=value pairs
+ */
+} iscsi_text_hdr_t;
+
+#define ISCSI_FLAG_TEXT_CONTINUE 0x40
+
+/* Text Response Header */
+typedef struct _iscsi_text_rsp_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t rsvd2[2];
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t rsvd4[8];
+ uint32_t itt;
+ uint32_t ttt;
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint8_t rsvd5[12];
+ /*
+ * Text Response - key:value pairs
+ */
+} iscsi_text_rsp_hdr_t;
+
+/* Login Header */
+typedef struct _iscsi_login_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t max_version; /* Max. version supported */
+ uint8_t min_version; /* Min. version supported */
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t isid[6]; /* Initiator Session ID */
+ uint16_t tsid; /* Target Session ID */
+ uint32_t itt; /* Initiator Task Tag */
+ uint16_t cid;
+ uint16_t rsvd3;
+ uint32_t cmdsn;
+ uint32_t expstatsn;
+ uint8_t rsvd5[16];
+} iscsi_login_hdr_t;
+
+/* Login PDU flags */
+#define ISCSI_FLAG_LOGIN_TRANSIT 0x80
+#define ISCSI_FLAG_LOGIN_CONTINUE 0x40
+#define ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK 0x0C /* 2 bits */
+#define ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK 0x03 /* 2 bits */
+
+#define ISCSI_LOGIN_CURRENT_STAGE(flags) \
+ ((flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2)
+#define ISCSI_LOGIN_NEXT_STAGE(flags) \
+ (flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK)
+
+
+/* Login Response Header */
+typedef struct _iscsi_login_rsp_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t max_version; /* Max. version supported */
+ uint8_t active_version; /* Active version */
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t isid[6]; /* Initiator Session ID */
+ uint16_t tsid; /* Target Session ID */
+ uint32_t itt; /* Initiator Task Tag */
+ uint32_t rsvd3;
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint8_t status_class; /* see Login RSP ststus classes below */
+ uint8_t status_detail; /* see Login RSP Status details below */
+ uint8_t rsvd4[10];
+} iscsi_login_rsp_hdr_t;
+
+/* Login stage (phase) codes for CSG, NSG */
+#define ISCSI_SECURITY_NEGOTIATION_STAGE 0
+#define ISCSI_OP_PARMS_NEGOTIATION_STAGE 1
+#define ISCSI_FULL_FEATURE_PHASE 3
+
+/* Login Status response classes */
+#define ISCSI_STATUS_CLASS_SUCCESS 0x00
+#define ISCSI_STATUS_CLASS_REDIRECT 0x01
+#define ISCSI_STATUS_CLASS_INITIATOR_ERR 0x02
+#define ISCSI_STATUS_CLASS_TARGET_ERR 0x03
+
+/* Login Status response detail codes */
+/* Class-0 (Success) */
+#define ISCSI_LOGIN_STATUS_ACCEPT 0x00
+
+/* Class-1 (Redirection) */
+#define ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP 0x01
+#define ISCSI_LOGIN_STATUS_TGT_MOVED_PERM 0x02
+
+/* Class-2 (Initiator Error) */
+#define ISCSI_LOGIN_STATUS_INIT_ERR 0x00
+#define ISCSI_LOGIN_STATUS_AUTH_FAILED 0x01
+#define ISCSI_LOGIN_STATUS_TGT_FORBIDDEN 0x02
+#define ISCSI_LOGIN_STATUS_TGT_NOT_FOUND 0x03
+#define ISCSI_LOGIN_STATUS_TGT_REMOVED 0x04
+#define ISCSI_LOGIN_STATUS_NO_VERSION 0x05
+#define ISCSI_LOGIN_STATUS_ISID_ERROR 0x06
+#define ISCSI_LOGIN_STATUS_MISSING_FIELDS 0x07
+#define ISCSI_LOGIN_STATUS_CONN_ADD_FAILED 0x08
+#define ISCSI_LOGIN_STATUS_NO_SESSION_TYPE 0x09
+#define ISCSI_LOGIN_STATUS_NO_SESSION 0x0a
+#define ISCSI_LOGIN_STATUS_INVALID_REQUEST 0x0b
+
+/* Class-3 (Target Error) */
+#define ISCSI_LOGIN_STATUS_TARGET_ERROR 0x00
+#define ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE 0x01
+#define ISCSI_LOGIN_STATUS_NO_RESOURCES 0x02
+
+/* Logout Header */
+typedef struct _iscsi_logout_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t rsvd1[2];
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t rsvd2[8];
+ uint32_t itt; /* Initiator Task Tag */
+ uint16_t cid;
+ uint8_t rsvd3[2];
+ uint32_t cmdsn;
+ uint32_t expstatsn;
+ uint8_t rsvd4[16];
+} iscsi_logout_hdr_t;
+
+/* Logout PDU flags */
+#define ISCSI_FLAG_LOGOUT_REASON_MASK 0x7F
+
+/* logout reason_code values */
+
+#define ISCSI_LOGOUT_REASON_CLOSE_SESSION 0
+#define ISCSI_LOGOUT_REASON_CLOSE_CONNECTION 1
+#define ISCSI_LOGOUT_REASON_RECOVERY 2
+#define ISCSI_LOGOUT_REASON_AEN_REQUEST 3
+
+/* Logout Response Header */
+typedef struct _iscsi_logout_rsp_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t response; /* see Logout response values below */
+ uint8_t rsvd2;
+ uint8_t hlength;
+ uint8_t dlength[3];
+ uint8_t rsvd3[8];
+ uint32_t itt; /* Initiator Task Tag */
+ uint32_t rsvd4;
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint32_t rsvd5;
+ uint16_t t2wait;
+ uint16_t t2retain;
+ uint32_t rsvd6;
+} iscsi_logout_rsp_hdr_t;
+
+/* logout response status values */
+
+#define ISCSI_LOGOUT_SUCCESS 0
+#define ISCSI_LOGOUT_CID_NOT_FOUND 1
+#define ISCSI_LOGOUT_RECOVERY_UNSUPPORTED 2
+#define ISCSI_LOGOUT_CLEANUP_FAILED 3
+
+
+/* SNACK Header */
+typedef struct _iscsi_snack_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t rsvd2[14];
+ uint32_t itt;
+ uint32_t begrun;
+ uint32_t runlength;
+ uint32_t expstatsn;
+ uint32_t rsvd3;
+ uint32_t expdatasn;
+ uint8_t rsvd6[8];
+} iscsi_snack_hdr_t;
+
+/* SNACK PDU flags */
+#define ISCSI_FLAG_SNACK_TYPE_MASK 0x0F /* 4 bits */
+
+/* Reject Message Header */
+typedef struct _iscsi_reject_rsp_hdr {
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t reason;
+ uint8_t rsvd2;
+ uint8_t rsvd3;
+ uint8_t dlength[3];
+ uint8_t rsvd4[16];
+ uint32_t statsn;
+ uint32_t expcmdsn;
+ uint32_t maxcmdsn;
+ uint32_t datasn;
+ uint8_t rsvd5[8];
+ /*
+ * Text - Rejected hdr
+ */
+} iscsi_reject_rsp_hdr_t;
+
+/* Reason for Reject */
+#define ISCSI_REJECT_CMD_BEFORE_LOGIN 1
+#define ISCSI_REJECT_DATA_DIGEST_ERROR 2
+#define ISCSI_REJECT_SNACK_REJECT 3
+#define ISCSI_REJECT_PROTOCOL_ERROR 4
+#define ISCSI_REJECT_CMD_NOT_SUPPORTED 5
+#define ISCSI_REJECT_IMM_CMD_REJECT 6
+#define ISCSI_REJECT_TASK_IN_PROGRESS 7
+#define ISCSI_REJECT_INVALID_DATA_ACK 8
+#define ISCSI_REJECT_INVALID_PDU_FIELD 9
+#define ISCSI_REJECT_LONG_OPERATION_REJECT 10
+#define ISCSI_REJECT_NEGOTIATION_RESET 11
+#define ISCSI_REJECT_WAITING_FOR_LOGOUT 12
+
+/* Defaults as defined by the iSCSI specification */
+#define ISCSI_DEFAULT_IMMEDIATE_DATA TRUE
+#define ISCSI_DEFAULT_INITIALR2T TRUE
+#define ISCSI_DEFAULT_FIRST_BURST_LENGTH (64 * 1024) /* 64kbytes */
+#define ISCSI_DEFAULT_MAX_BURST_LENGTH (256 * 1024) /* 256kbytes */
+#define ISCSI_DEFAULT_DATA_PDU_IN_ORDER TRUE
+#define ISCSI_DEFAULT_DATA_SEQUENCE_IN_ORDER TRUE
+#define ISCSI_DEFAULT_TIME_TO_WAIT 2 /* 2 seconds */
+#define ISCSI_DEFAULT_TIME_TO_RETAIN 20 /* 20 seconds */
+#define ISCSI_DEFAULT_HEADER_DIGEST ISCSI_DIGEST_NONE
+#define ISCSI_DEFAULT_DATA_DIGEST ISCSI_DIGEST_NONE
+#define ISCSI_DEFAULT_MAX_RECV_SEG_LEN (64 * 1024)
+#define ISCSI_DEFAULT_MAX_XMIT_SEG_LEN (8 * 1024)
+#define ISCSI_DEFAULT_MAX_CONNECTIONS 1
+#define ISCSI_DEFAULT_MAX_OUT_R2T 1
+#define ISCSI_DEFAULT_ERROR_RECOVERY_LEVEL 0
+#define ISCSI_DEFAULT_IFMARKER FALSE
+#define ISCSI_DEFAULT_OFMARKER FALSE
+
+/*
+ * Maximum values from the iSCSI specification
+ */
+#define ISCSI_MAX_HEADER_DIGEST 3
+#define ISCSI_MAX_DATA_DIGEST 3
+#define ISCSI_MAX_TIME2RETAIN 3600
+#define ISCSI_MAX_TIME2WAIT 3600
+#define ISCSI_MAX_ERROR_RECOVERY_LEVEL 2
+#define ISCSI_MAX_FIRST_BURST_LENGTH 0xffffff
+#define ISCSI_MAX_BURST_LENGTH 0xffffff
+#define ISCSI_MAX_CONNECTIONS 65535
+#define ISCSI_MAX_OUTSTANDING_R2T 65535
+#define ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH 0xffffff
+#define ISCSI_MAX_TPGT_VALUE 65535 /* 16 bit numeric */
+
+/*
+ * iqn and eui name prefixes and related defines
+ */
+#define ISCSI_IQN_NAME_PREFIX "iqn"
+#define ISCSI_EUI_NAME_PREFIX "eui"
+#define ISCSI_EUI_NAME_LEN 20 /* eui. plus 16 octets */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ISCSI_PROTOCOL_H */
diff --git a/usr/src/uts/common/sys/scsi/generic/commands.h b/usr/src/uts/common/sys/scsi/generic/commands.h
index 8d3c373d81..1829959a93 100644
--- a/usr/src/uts/common/sys/scsi/generic/commands.h
+++ b/usr/src/uts/common/sys/scsi/generic/commands.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -323,8 +323,10 @@ extern "C" {
*/
#define SCMD_GROUP5 0xA0
#define SCMD_REPORT_LUNS 0xA0
+#define SCMD_REPORT_TARGET_PORT_GROUPS 0xA3
#define SCMD_READ_G5 0xA8
#define SCMD_WRITE_G5 0xAA
+#define SCMD_READ_MEDIA_SERIAL 0xAB
#define SCMD_GET_PERFORMANCE 0xAC
diff --git a/usr/src/uts/common/sys/scsi/generic/inquiry.h b/usr/src/uts/common/sys/scsi/generic/inquiry.h
index fa2676747d..6d49fce3e3 100644
--- a/usr/src/uts/common/sys/scsi/generic/inquiry.h
+++ b/usr/src/uts/common/sys/scsi/generic/inquiry.h
@@ -63,7 +63,7 @@ struct scsi_inquiry {
/* byte 3 */
uchar_t inq_rdf : 4, /* response data format */
- : 1, /* reserved */
+ inq_hisup : 1, /* Hierarchial support */
inq_normaca : 1, /* setting NACA bit supported */
inq_trmiop : 1, /* TERMINATE I/O PROC msg */
inq_aenc : 1; /* async event notification cap. */
@@ -72,8 +72,9 @@ struct scsi_inquiry {
uchar_t inq_len; /* additional length */
- uchar_t : 8; /* reserved */
-
+ uchar_t : 4, /* reserved */
+ inq_tpgs : 1, /* supports Target Port Group set */
+ : 3;
uchar_t inq_addr16 : 1, /* supports 16 bit wide SCSI addr */
inq_addr32 : 1, /* supports 32 bit wide SCSI addr */
inq_ackqreqq : 1, /* data tranfer on Q cable */
@@ -140,14 +141,16 @@ struct scsi_inquiry {
uchar_t inq_aenc : 1, /* async event notification cap. */
inq_trmiop : 1, /* supports TERMINATE I/O PROC msg */
inq_normaca : 1, /* setting NACA bit supported */
- : 1, /* reserved */
+ inq_hisup : 1, /* hierachial support */
inq_rdf : 4; /* response data format */
/* bytes 4-7 */
uchar_t inq_len; /* additional length */
- uchar_t : 8; /* reserved */
+ uchar_t : 3, /* reserved */
+ inq_tpgs : 1, /* supports Target Port Group Set */
+ : 4;
uchar_t : 2, /* reserved */
inq_port : 1, /* port receiving inquiry cmd */
@@ -292,6 +295,51 @@ struct scsi_inquiry {
#define RDF_CCS 0x01
#define RDF_SCSI2 0x02
+/*
+ * SPC-3 revision 21c, section 7.6.4.1
+ * Table 289 -- Device Identification VPD page
+ */
+struct vpd_hdr {
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t device_type : 4,
+ periph_qual : 4;
+#elif defined(_BIT_FIELDS_HTOL)
+ uchar_t periph_qual : 4,
+ device_type : 4;
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+ uchar_t page_code,
+ page_len[2];
+};
+
+/*
+ * SPC-3 revision 21c, section 7.6.4.1
+ * Table 290 -- Identification descriptor
+ */
+struct vpd_desc {
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t code_set : 4,
+ proto_id : 4;
+ uchar_t id_type : 4,
+ association : 2,
+ : 1,
+ piv : 1;
+#elif defined(_BIT_FIELDS_HTOL)
+ uchar_t proto_id : 4,
+ code_set : 4;
+ uchar_t piv : 1,
+ : 1,
+ association : 2,
+ id_type : 4;
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+ uchar_t resrv1;
+ uchar_t len;
+ /* ---- data follows ---- */
+};
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/scsi/impl/commands.h b/usr/src/uts/common/sys/scsi/impl/commands.h
index 91ea8e6233..059f6b90b6 100644
--- a/usr/src/uts/common/sys/scsi/impl/commands.h
+++ b/usr/src/uts/common/sys/scsi/impl/commands.h
@@ -340,8 +340,10 @@ union scsi_cdb { /* scsi command description block */
(cdb)->g0_addr1 = ((addr) >> 8) & 0xFF; \
(cdb)->g0_addr0 = (addr) & 0xFF
-#define GETG0ADDR(cdb) (((cdb)->g0_addr2 & 0x1F) << 16) + \
- ((cdb)->g0_addr1 << 8) + ((cdb)->g0_addr0)
+#define GETG0COUNT(cdb) (cdb)->g0_count0
+
+#define GETG0ADDR(cdb) ((((cdb)->g0_addr2 & 0x1F) << 16) + \
+ ((cdb)->g0_addr1 << 8) + ((cdb)->g0_addr0))
#define GETG0TAG(cdb) ((cdb)->g0_addr2)
@@ -357,10 +359,12 @@ union scsi_cdb { /* scsi command description block */
(cdb)->g1_addr1 = ((addr) >> 8) & 0xFF; \
(cdb)->g1_addr0 = (addr) & 0xFF
-#define GETG1ADDR(cdb) ((cdb)->g1_addr3 << 24) + \
+#define GETG1COUNT(cdb) (((cdb)->g1_count1 << 8) + ((cdb)->g1_count0))
+
+#define GETG1ADDR(cdb) (((cdb)->g1_addr3 << 24) + \
((cdb)->g1_addr2 << 16) + \
((cdb)->g1_addr1 << 8) + \
- ((cdb)->g1_addr0)
+ ((cdb)->g1_addr0))
#define GETG1TAG(cdb) (cdb)->g1_reladdr
@@ -385,6 +389,20 @@ union scsi_cdb { /* scsi command description block */
(cdb)->g4_addtl_cdb_data0 = \
(addr) & 0xFF
+#define GETG4COUNT(cdb) (((cdb)->g4_count3 << 24) + \
+ ((cdb)->g4_count2 << 16) + \
+ ((cdb)->g4_count1 << 8) + \
+ ((cdb)->g4_count0))
+
+#define GETG4LONGADDR(cdb) (((diskaddr_t)(cdb)->g4_addr3 << 56) + \
+ ((diskaddr_t)(cdb)->g4_addr2 << 48) + \
+ ((diskaddr_t)(cdb)->g4_addr1 << 40) + \
+ ((diskaddr_t)(cdb)->g4_addr0 << 32) + \
+ ((diskaddr_t)(cdb)->g4_addtl_cdb_data3 << 24) + \
+ ((diskaddr_t)(cdb)->g4_addtl_cdb_data2 << 16) + \
+ ((diskaddr_t)(cdb)->g4_addtl_cdb_data1 << 8) + \
+ ((diskaddr_t)(cdb)->g4_addtl_cdb_data0))
+
#define FORMG4ADDR(cdb, addr) (cdb)->g4_addr3 = (addr) >> 24; \
(cdb)->g4_addr2 = ((addr) >> 16) & 0xFF; \
(cdb)->g4_addr1 = ((addr) >> 8) & 0xFF; \