summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Christie <michaelc@cs.wisc.edu>2010-02-19 19:19:22 -0600
committerMike Christie <michaelc@cs.wisc.edu>2010-03-22 17:32:08 -0500
commit22610b42dea26f17d9be3f5bd23fed0f32436932 (patch)
tree93f5d9f26e9a9ee0d1476984e83734be7e7e24aa
parent7fef761e193dc3a1ff3c65b7eb9f8d3942d69bbb (diff)
downloadopen-iscsi-22610b42dea26f17d9be3f5bd23fed0f32436932.tar.gz
iscsi tools: use open-isns services
This replaces the native isns code with open-isns's libisns. I included the open-isns code in the open-iscsi tarball to make distribution easier since some distros use different isns clients and may not want to carry open-isns. This is based on open-isns commit 5e09f36d3446e41de0b8361601ffec4cd140d513. Changes in iSNS behavior/use: - To do discovery you must pass the ip and optionally the port to iscsiadm: iscsiadm -m discovery -t st -p 10.15.0.9 This command accepts the same ops as sendtargets so you can add/remove/update the node records that are created. It also supports ifaces properly now. - isns.address and isns.port in iscsid.conf are no longer used. - ESI is temporarily not supported. This will be fixed in the next patch when SCNs support is added. - The iscsiadm isns discovery command is not marked as stable. Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
-rw-r--r--Makefile2
-rw-r--r--README10
-rw-r--r--doc/iscsiadm.88
-rw-r--r--etc/iscsid.conf7
-rw-r--r--include/list.h4
-rw-r--r--usr/Makefile13
-rw-r--r--usr/config.h4
-rw-r--r--usr/discovery.c244
-rw-r--r--usr/discovery.h5
-rw-r--r--usr/discoveryd.c2
-rw-r--r--usr/event_poll.c17
-rw-r--r--usr/event_poll.h3
-rw-r--r--usr/host.c2
-rw-r--r--usr/idbm.c61
-rw-r--r--usr/idbm.h3
-rw-r--r--usr/initiator.c2
-rw-r--r--usr/initiator.h6
-rw-r--r--usr/iscsi_util.c (renamed from usr/util.c)3
-rw-r--r--usr/iscsi_util.h (renamed from usr/util.h)0
-rw-r--r--usr/iscsiadm.c69
-rw-r--r--usr/iscsid.c7
-rw-r--r--usr/iscsid_req.c2
-rw-r--r--usr/iscsistart.c4
-rw-r--r--usr/isns.c744
-rw-r--r--usr/isns_proto.h200
-rw-r--r--usr/log.c2
-rw-r--r--usr/mgmt_ipc.c8
-rw-r--r--usr/session_mgmt.c2
-rw-r--r--usr/transport.c2
-rw-r--r--utils/open-isns/COPYING504
-rw-r--r--utils/open-isns/ChangeLog50
-rw-r--r--utils/open-isns/HACKING30
-rw-r--r--utils/open-isns/Makefile.in82
-rw-r--r--utils/open-isns/README173
-rw-r--r--utils/open-isns/TODO100
-rw-r--r--utils/open-isns/aclocal/config.guess1499
-rw-r--r--utils/open-isns/aclocal/config.sub1570
-rw-r--r--utils/open-isns/aclocal/install-sh251
-rw-r--r--utils/open-isns/attrs.c1618
-rw-r--r--utils/open-isns/attrs.h262
-rw-r--r--utils/open-isns/authblock.c62
-rw-r--r--utils/open-isns/bitvector.c651
-rw-r--r--utils/open-isns/buffer.c407
-rw-r--r--utils/open-isns/buffer.h141
-rw-r--r--utils/open-isns/callback.c148
-rw-r--r--utils/open-isns/client.c203
-rw-r--r--utils/open-isns/compat/my_getopt.c271
-rw-r--r--utils/open-isns/compat/my_getopt.h69
-rw-r--r--utils/open-isns/config.c278
-rw-r--r--utils/open-isns/config.h.in103
-rw-r--r--utils/open-isns/configure6727
-rw-r--r--utils/open-isns/configure.ac118
-rw-r--r--utils/open-isns/db-file.c615
-rw-r--r--utils/open-isns/db-policy.c185
-rw-r--r--utils/open-isns/db.c994
-rw-r--r--utils/open-isns/db.h147
-rw-r--r--utils/open-isns/dd.c1307
-rw-r--r--utils/open-isns/deregister.c271
-rw-r--r--utils/open-isns/doc/isns_config.5387
-rw-r--r--utils/open-isns/doc/isnsadm.8672
-rw-r--r--utils/open-isns/doc/isnsd.893
-rw-r--r--utils/open-isns/doc/isnsdd.875
-rw-r--r--utils/open-isns/doc/rfc2608.txt3027
-rw-r--r--utils/open-isns/doc/rfc3279.txt1515
-rw-r--r--utils/open-isns/doc/rfc3720.txt14395
-rw-r--r--utils/open-isns/doc/rfc3722.txt451
-rw-r--r--utils/open-isns/doc/rfc4018.txt1291
-rw-r--r--utils/open-isns/doc/rfc4171.txt6891
-rw-r--r--utils/open-isns/domain.c208
-rw-r--r--utils/open-isns/entity.c127
-rw-r--r--utils/open-isns/error.c65
-rw-r--r--utils/open-isns/esi.c575
-rw-r--r--utils/open-isns/etc/isnsadm.conf73
-rw-r--r--utils/open-isns/etc/isnsd.conf129
-rw-r--r--utils/open-isns/etc/isnsdd.conf72
-rw-r--r--utils/open-isns/etc/openisns.init71
-rw-r--r--utils/open-isns/export.c547
-rw-r--r--utils/open-isns/getnext.c257
-rw-r--r--utils/open-isns/internal.h16
-rw-r--r--utils/open-isns/isns-proto.h258
-rw-r--r--utils/open-isns/isns.h670
-rw-r--r--utils/open-isns/isnsadm.c1149
-rw-r--r--utils/open-isns/isnsd.c299
-rw-r--r--utils/open-isns/isnsdd.c1153
-rw-r--r--utils/open-isns/isnssetup52
-rw-r--r--utils/open-isns/local.c353
-rw-r--r--utils/open-isns/logging.c228
-rw-r--r--utils/open-isns/mdebug.c295
-rw-r--r--utils/open-isns/message.c681
-rw-r--r--utils/open-isns/message.h196
-rw-r--r--utils/open-isns/objects.c1320
-rw-r--r--utils/open-isns/objects.h167
-rw-r--r--utils/open-isns/parser.c134
-rw-r--r--utils/open-isns/paths.h22
-rw-r--r--utils/open-isns/pidfile.c98
-rw-r--r--utils/open-isns/pki.c536
-rw-r--r--utils/open-isns/policy.c577
-rw-r--r--utils/open-isns/portal-group.c307
-rw-r--r--utils/open-isns/query.c238
-rw-r--r--utils/open-isns/register.c934
-rw-r--r--utils/open-isns/relation.c281
-rw-r--r--utils/open-isns/scn.c926
-rw-r--r--utils/open-isns/scope.c513
-rw-r--r--utils/open-isns/security.c437
-rw-r--r--utils/open-isns/security.h180
-rw-r--r--utils/open-isns/server.c236
-rw-r--r--utils/open-isns/simple.c725
-rw-r--r--utils/open-isns/slp.c242
-rw-r--r--utils/open-isns/socket.c2289
-rw-r--r--utils/open-isns/socket.h95
-rw-r--r--utils/open-isns/source.h32
-rw-r--r--utils/open-isns/storage-node.c202
-rw-r--r--utils/open-isns/sysdep-unix.c132
-rw-r--r--utils/open-isns/tags.c740
-rw-r--r--utils/open-isns/tests/.cvsignore2
-rw-r--r--utils/open-isns/tests/Makefile40
-rw-r--r--utils/open-isns/tests/client.conf8
-rw-r--r--utils/open-isns/tests/data/test01/01-enroll18
-rw-r--r--utils/open-isns/tests/data/test01/02-registration42
-rw-r--r--utils/open-isns/tests/data/test01/03-query20
-rw-r--r--utils/open-isns/tests/data/test01/03-registration20
-rw-r--r--utils/open-isns/tests/data/test01/99-unregistration18
-rw-r--r--utils/open-isns/tests/data/test02/01-enroll18
-rw-r--r--utils/open-isns/tests/data/test02/02-enroll24
-rw-r--r--utils/open-isns/tests/data/test02/03-registration72
-rw-r--r--utils/open-isns/tests/data/test02/04-query20
-rw-r--r--utils/open-isns/tests/data/test02/05-query20
-rw-r--r--utils/open-isns/tests/data/test02/06-dd-registration81
-rw-r--r--utils/open-isns/tests/data/test02/07-query40
-rw-r--r--utils/open-isns/tests/data/test02/08-query40
-rw-r--r--utils/open-isns/tests/data/test02/09-query20
-rw-r--r--utils/open-isns/tests/data/test02/10-dd-registration87
-rw-r--r--utils/open-isns/tests/data/test02/11-query10
-rw-r--r--utils/open-isns/tests/data/test02/12-dd-deregistration85
-rw-r--r--utils/open-isns/tests/data/test02/13-dd-deregistration83
-rw-r--r--utils/open-isns/tests/data/test02/14-dd-registration85
-rw-r--r--utils/open-isns/tests/data/test02/15-dd-deregistration76
-rw-r--r--utils/open-isns/tests/data/test03/01-enroll18
-rw-r--r--utils/open-isns/tests/data/test03/02-registration42
-rw-r--r--utils/open-isns/tests/data/test03/03-unregistration42
-rw-r--r--utils/open-isns/tests/data/test03/04-unregistration18
-rw-r--r--utils/open-isns/tests/data/test03/99-unregistration13
-rw-r--r--utils/open-isns/tests/data/test04/01-enroll18
-rw-r--r--utils/open-isns/tests/data/test04/02-registration42
-rw-r--r--utils/open-isns/tests/data/test04/03-restart42
-rw-r--r--utils/open-isns/tests/data/test04/04-query20
-rw-r--r--utils/open-isns/tests/data/test05/01-enroll18
-rw-r--r--utils/open-isns/tests/data/test05/02-registration42
-rw-r--r--utils/open-isns/tests/data/test05/03-expired18
-rw-r--r--utils/open-isns/tests/data/test06/01-enroll18
-rw-r--r--utils/open-isns/tests/data/test06/02-registration42
-rw-r--r--utils/open-isns/tests/data/test06/03-registration42
-rw-r--r--utils/open-isns/tests/data/test06/04-registration42
-rw-r--r--utils/open-isns/tests/data/test06/05-dd-registration49
-rw-r--r--utils/open-isns/tests/data/test06/06-registration49
-rw-r--r--utils/open-isns/tests/data/test06/07-dd-registration52
-rw-r--r--utils/open-isns/tests/data/test06/08-registration64
-rw-r--r--utils/open-isns/tests/data/test06/09-registration64
-rw-r--r--utils/open-isns/tests/data/test06/10-unregistration37
-rw-r--r--utils/open-isns/tests/data/test06/11-registration52
-rw-r--r--utils/open-isns/tests/data/test07/01-enroll19
-rw-r--r--utils/open-isns/tests/data/test07/02-registration45
-rw-r--r--utils/open-isns/tests/data/test07/03-expired19
-rw-r--r--utils/open-isns/tests/data/test07/04-registration57
-rw-r--r--utils/open-isns/tests/data/test07/05-expired19
-rw-r--r--utils/open-isns/tests/data/test08/01-pauw1100
-rw-r--r--utils/open-isns/tests/data/test09/01-pauw231
-rw-r--r--utils/open-isns/tests/data/test10/01-pauw331
-rw-r--r--utils/open-isns/tests/data/test10/02-expired31
-rw-r--r--utils/open-isns/tests/data/test10/03-pauw331
-rw-r--r--utils/open-isns/tests/data/test10/04-expired31
-rw-r--r--utils/open-isns/tests/data/test11/01-pauw432
-rw-r--r--utils/open-isns/tests/genkey175
-rw-r--r--utils/open-isns/tests/harness.pl929
-rw-r--r--utils/open-isns/tests/pauw1.c179
-rw-r--r--utils/open-isns/tests/pauw2.c212
-rw-r--r--utils/open-isns/tests/pauw3.c139
-rw-r--r--utils/open-isns/tests/pauw4.c137
-rw-r--r--utils/open-isns/tests/server.conf11
-rw-r--r--utils/open-isns/tests/test01.pl30
-rw-r--r--utils/open-isns/tests/test02.pl58
-rw-r--r--utils/open-isns/tests/test03.pl27
-rw-r--r--utils/open-isns/tests/test04.pl30
-rw-r--r--utils/open-isns/tests/test05.pl25
-rw-r--r--utils/open-isns/tests/test06.pl50
-rw-r--r--utils/open-isns/tests/test07.pl37
-rw-r--r--utils/open-isns/tests/test08.pl23
-rw-r--r--utils/open-isns/tests/test09.pl23
-rw-r--r--utils/open-isns/tests/test10.pl33
-rw-r--r--utils/open-isns/tests/test11.pl23
-rw-r--r--utils/open-isns/timer.c126
-rw-r--r--utils/open-isns/types.h57
-rw-r--r--utils/open-isns/util.c263
-rw-r--r--utils/open-isns/util.h288
-rw-r--r--utils/open-isns/vendor.c41
-rw-r--r--utils/open-isns/vendor.h56
196 files changed, 71961 insertions, 1136 deletions
diff --git a/Makefile b/Makefile
index 7bd1e2f..9203044 100644
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,7 @@ IFACEFILES = etc/iface.example
all: user kernel
user: ;
+ cd utils/open-isns; ./configure; $(MAKE)
$(MAKE) -C utils/sysdeps
$(MAKE) -C utils/fwparam_ibft
$(MAKE) -C usr
@@ -52,6 +53,7 @@ kernel: force
force: ;
clean:
+ $(MAKE) -C utils/open-isns clean
$(MAKE) -C utils/sysdeps clean
$(MAKE) -C utils/fwparam_ibft clean
$(MAKE) -C utils clean
diff --git a/README b/README
index c2aa7fa..b6fd1eb 100644
--- a/README
+++ b/README
@@ -913,14 +913,10 @@ iscsiadm -m discovery -t sendtargets -p ip:port
where "ip" is the address of the portal and port is the port.
-Or you can you perform discovery using iSNS by setting the address
-of the iSNS server in iscsid.conf with the "isns.address" value and
-running:
+To use iSNS you can run the discovery command with the type as "isns"
+and pass in the ip:port:
-iscsiadm -m discovery -t isns
-
-*** Warning *** iSNS support is experimental in this release. The command
-line options for it will change in future releases.
+iscsiadm -m discovery -t isns -p ip:port
Both commands will print out the list of all discovered targets and their
portals:
diff --git a/doc/iscsiadm.8 b/doc/iscsiadm.8
index 1c60216..b2bad47 100644
--- a/doc/iscsiadm.8
+++ b/doc/iscsiadm.8
@@ -248,12 +248,8 @@ information about available targets.
.B
iSNS
iSNS (Internet Storage Name Service) records information about storage
-volumes within a larger network. To utilize iSNS, the address of the
-iSNS server must be set in iscsid.conf using the "isns.address" value,
-and iscsiadm must be run in discovery mode with the "isns" discovery type.
-
-iSNS support in open-iscsi is experimental. The iscsid.conf settings,
-iscsiadm syntax and node DB layout may change.
+volumes within a larger network. To utilize iSNS, pass the address and
+optionally the port of the iSNS server to do discovery to.
.TP
.B
diff --git a/etc/iscsid.conf b/etc/iscsid.conf
index e3a13cb..5fa0aca 100644
--- a/etc/iscsid.conf
+++ b/etc/iscsid.conf
@@ -7,13 +7,6 @@
# and man page for iscsiadm for details on the --op command.
#
-################
-# iSNS settings
-################
-# Address of iSNS server
-#isns.address = 192.168.0.1
-#isns.port = 3205
-
######################
# iscsid daemon config
######################
diff --git a/include/list.h b/include/list.h
index bbf3425..a59ca00 100644
--- a/include/list.h
+++ b/include/list.h
@@ -11,7 +11,7 @@
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
-#define container_of(ptr, type, member) ({ \
+#define list_container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
@@ -36,7 +36,7 @@ static inline int list_empty(const struct list_head *head)
}
#define list_entry(ptr, type, member) \
- container_of(ptr, type, member)
+ list_container_of(ptr, type, member)
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
diff --git a/usr/Makefile b/usr/Makefile
index f533177..e9d6bd1 100644
--- a/usr/Makefile
+++ b/usr/Makefile
@@ -30,17 +30,18 @@ endif
OPTFLAGS ?= -O2 -g
WARNFLAGS ?= -Wall -Wstrict-prototypes
-CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -I../include -I. -D$(OSNAME) $(IPC_CFLAGS)
+CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -I../include -I. -I../utils/open-isns \
+ -D$(OSNAME) $(IPC_CFLAGS)
PROGRAMS = iscsid iscsiadm iscsistart
# libc compat files
SYSDEPS_SRCS = $(wildcard ../utils/sysdeps/*.o)
# sources shared between iscsid, iscsiadm and iscsistart
-ISCSI_LIB_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o iface.o idbm.o \
- sysfs.o host.o session_info.o iscsi_sysfs.o iscsi_net_util.o \
+ISCSI_LIB_SRCS = iscsi_util.o io.o auth.o login.o log.o md5.o sha1.o iface.o \
+ idbm.o sysfs.o host.o session_info.o iscsi_sysfs.o iscsi_net_util.o \
iscsid_req.o $(SYSDEPS_SRCS)
# core initiator files
-INITIATOR_SRCS = initiator.o scsi.o actor.o event_poll.o mgmt_ipc.o isns.o \
+INITIATOR_SRCS = initiator.o scsi.o actor.o event_poll.o mgmt_ipc.o \
transport.o cxgb3i.o be2iscsi.o
# fw boot files
FW_BOOT_SRCS = $(wildcard ../utils/fwparam_ibft/*.o)
@@ -52,10 +53,10 @@ all: $(PROGRAMS)
iscsid: $(ISCSI_LIB_SRCS) $(IPC_OBJ) $(INITIATOR_SRCS) $(DISCOVERY_SRCS) \
iscsid.o session_mgmt.o discoveryd.o
- $(CC) $(CFLAGS) $^ -o $@
+ $(CC) $(CFLAGS) $^ -o $@ -L../utils/open-isns -lisns -lcrypto
iscsiadm: $(ISCSI_LIB_SRCS) $(DISCOVERY_SRCS) iscsiadm.o session_mgmt.o
- $(CC) $(CFLAGS) $^ -o $@
+ $(CC) $(CFLAGS) $^ -o $@ -L../utils/open-isns -lisns -lcrypto
iscsistart: $(IPC_OBJ) $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(FW_BOOT_SRCS) \
iscsistart.o statics.o
diff --git a/usr/config.h b/usr/config.h
index 2ade266..c3c1c7f 100644
--- a/usr/config.h
+++ b/usr/config.h
@@ -142,6 +142,9 @@ struct iscsi_sendtargets_config {
struct iscsi_conn_operational_config iscsi;
};
+struct iscsi_isns_config {
+};
+
struct iscsi_slp_config {
char *scopes;
char *interfaces; /* for multicast, list of interfaces names,
@@ -231,6 +234,7 @@ typedef struct discovery_rec {
union {
struct iscsi_sendtargets_config sendtargets;
struct iscsi_slp_config slp;
+ struct iscsi_isns_config isns;
} u;
} discovery_rec_t;
diff --git a/usr/discovery.c b/usr/discovery.c
index 4fc0205..a121ddc 100644
--- a/usr/discovery.c
+++ b/usr/discovery.c
@@ -42,6 +42,11 @@
#include "sysdeps.h"
#include "fw_context.h"
#include "iscsid_req.h"
+#include "iscsi_util.h"
+/* libisns includes */
+#include "isns.h"
+#include "paths.h"
+#include "message.h"
#ifdef SLP_ENABLE
#include "iscsi-slp-discovery.h"
@@ -54,6 +59,211 @@ static int rediscover = 0;
static char initiator_name[TARGET_NAME_MAXLEN + 1];
static char initiator_alias[TARGET_NAME_MAXLEN + 1];
+static int request_initiator_name(void)
+{
+ int rc;
+ iscsiadm_req_t req;
+ iscsiadm_rsp_t rsp;
+
+ memset(initiator_name, 0, sizeof(initiator_name));
+ initiator_name[0] = '\0';
+ memset(initiator_alias, 0, sizeof(initiator_alias));
+ initiator_alias[0] = '\0';
+
+ memset(&req, 0, sizeof(req));
+ req.command = MGMT_IPC_CONFIG_INAME;
+
+ rc = iscsid_exec_req(&req, &rsp, 1);
+ if (rc)
+ return EIO;
+
+ if (rsp.u.config.var[0] != '\0')
+ strcpy(initiator_name, rsp.u.config.var);
+
+ memset(&req, 0, sizeof(req));
+ req.command = MGMT_IPC_CONFIG_IALIAS;
+
+ rc = iscsid_exec_req(&req, &rsp, 0);
+ if (rc)
+ /* alias is optional so return ok */
+ return 0;
+
+ if (rsp.u.config.var[0] != '\0')
+ strcpy(initiator_alias, rsp.u.config.var);
+ return 0;
+}
+
+int discovery_isns(struct discovery_rec *drec, struct iface_rec *iface,
+ struct list_head *rec_list)
+{
+ isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
+ isns_simple_t *qry;
+ isns_client_t *clnt;
+ isns_attr_list_t *attrs;
+ char *server;
+ uint32_t status;
+ int rc, i;
+
+ isns_config.ic_security = 0;
+
+ if (iface && strlen(iface->iname))
+ isns_assign_string(&isns_config.ic_source_name, iface->iname);
+ else {
+ if (request_initiator_name() || initiator_name[0] == '\0') {
+ log_error("Cannot perform discovery. Initiatorname "
+ "required.");
+ return EINVAL;
+ }
+ isns_assign_string(&isns_config.ic_source_name, initiator_name);
+ }
+
+ if (drec->port > USHRT_MAX) {
+ log_error("Invalid port %d\n", drec->port);
+ rc = EINVAL;
+ goto free_mem;
+ }
+
+ /* 5 for port and 1 for colon and 1 for null */
+ i = strlen(drec->address) + 7;
+ server = calloc(1, i);
+ if (!server) {
+ rc = ENOMEM;
+ goto free_mem;
+ }
+
+ snprintf(server, i, "%s:%d", drec->address, drec->port);
+ isns_assign_string(&isns_config.ic_server_name, server);
+ free(server);
+
+ clnt = isns_create_default_client(NULL);
+ if (!clnt) {
+ rc = ENOMEM;
+ goto free_mem;
+ }
+
+ /* do not retry forever */
+ isns_socket_set_disconnect_fatal(clnt->ic_socket);
+
+ qry = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_QUERY,
+ clnt->ic_source, NULL);
+ if (!qry) {
+ rc = ENOMEM;
+ goto free_clnt;
+ }
+
+ attrs = &qry->is_message_attrs;
+ isns_attr_list_append_uint32(attrs, ISNS_TAG_ISCSI_NODE_TYPE,
+ ISNS_ISCSI_TARGET_MASK);
+
+ attrs = &qry->is_operating_attrs;
+ isns_attr_list_append_nil(attrs, ISNS_TAG_ISCSI_NAME);
+ isns_attr_list_append_nil(attrs, ISNS_TAG_ISCSI_NODE_TYPE);
+ isns_attr_list_append_nil(attrs, ISNS_TAG_PORTAL_IP_ADDRESS);
+ isns_attr_list_append_nil(attrs, ISNS_TAG_PORTAL_TCP_UDP_PORT);
+ isns_attr_list_append_nil(attrs, ISNS_TAG_PG_ISCSI_NAME);
+ isns_attr_list_append_nil(attrs, ISNS_TAG_PG_PORTAL_IP_ADDR);
+ isns_attr_list_append_nil(attrs, ISNS_TAG_PG_PORTAL_TCP_UDP_PORT);
+ isns_attr_list_append_nil(attrs, ISNS_TAG_PG_TAG);
+
+ status = isns_client_call(clnt, &qry);
+ if (status != ISNS_SUCCESS) {
+ log_error("iSNS discovery failed: %s", isns_strerror(status));
+ rc = EIO;
+ goto free_query;
+ }
+
+ status = isns_query_response_get_objects(qry, &objects);
+ if (status) {
+ log_error("Unable to extract object list from query "
+ "response: %s\n", isns_strerror(status));
+ rc = EIO;
+ goto free_query;
+ }
+
+ for (i = 0; i < objects.iol_count; ++i) {
+ isns_object_t *obj = objects.iol_data[i];
+ const char *pg_tgt = NULL;
+ struct in6_addr in_addr;
+ uint32_t pg_port = ISCSI_LISTEN_PORT;
+ uint32_t pg_tag = PORTAL_GROUP_TAG_UNKNOWN;
+ char pg_addr[INET6_ADDRSTRLEN + 1];
+ struct node_rec *rec;
+
+ if (!isns_object_is_pg(obj))
+ continue;
+
+ if (!isns_object_get_string(obj, ISNS_TAG_PG_ISCSI_NAME,
+ &pg_tgt)) {
+ log_debug(1, "Missing target name");
+ continue;
+ }
+
+ if (!isns_object_get_ipaddr(obj, ISNS_TAG_PG_PORTAL_IP_ADDR,
+ &in_addr)) {
+ log_debug(1, "Missing addr");
+ continue;
+ }
+ if (IN6_IS_ADDR_V4MAPPED(&in_addr) ||
+ IN6_IS_ADDR_V4COMPAT(&in_addr)) {
+ struct in_addr ipv4;
+
+ ipv4.s_addr = in_addr.s6_addr32[3];
+ inet_ntop(AF_INET, &ipv4, pg_addr, sizeof(pg_addr));
+ } else
+ inet_ntop(AF_INET6, &in_addr, pg_addr, sizeof(pg_addr));
+
+ if (!isns_object_get_uint32(obj,
+ ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
+ &pg_port)) {
+ log_debug(1, "Missing port");
+ continue;
+ }
+
+ if (!isns_object_get_uint32(obj, ISNS_TAG_PG_TAG, &pg_tag)) {
+ log_debug(1, "Missing tag");
+ continue;
+ }
+
+ rec = calloc(1, sizeof(*rec));
+ if (!rec) {
+ rc = ENOMEM;
+ goto destroy_list;
+ }
+
+ idbm_node_setup_from_conf(rec);
+ rec->disc_type = drec->type;
+ rec->disc_port = drec->port;
+ strcpy(rec->disc_address, drec->address);
+
+ strlcpy(rec->name, pg_tgt, TARGET_NAME_MAXLEN);
+ rec->tpgt = pg_tag;
+ rec->conn[0].port = pg_port;
+ strlcpy(rec->conn[0].address, pg_addr, NI_MAXHOST);
+
+ list_add_tail(&rec->list, rec_list);
+ }
+ rc = 0;
+
+ isns_flush_events();
+destroy_list:
+ isns_object_list_destroy(&objects);
+free_query:
+ isns_simple_free(qry);
+free_clnt:
+ isns_client_destroy(clnt);
+free_mem:
+ if (isns_config.ic_source_name)
+ free(isns_config.ic_source_name);
+ isns_config.ic_source_name = NULL;
+
+ if (isns_config.ic_server_name)
+ free(isns_config.ic_server_name);
+ isns_config.ic_server_name = NULL;
+ return rc;
+}
+
+
+
int discovery_fw(struct discovery_rec *drec, struct iface_rec *iface,
struct list_head *rec_list)
{
@@ -549,40 +759,6 @@ msecs_until(struct timeval *timer)
return msecs;
}
-static int request_initiator_name(void)
-{
- int rc;
- iscsiadm_req_t req;
- iscsiadm_rsp_t rsp;
-
- memset(initiator_name, 0, sizeof(initiator_name));
- initiator_name[0] = '\0';
- memset(initiator_alias, 0, sizeof(initiator_alias));
- initiator_alias[0] = '\0';
-
- memset(&req, 0, sizeof(req));
- req.command = MGMT_IPC_CONFIG_INAME;
-
- rc = iscsid_exec_req(&req, &rsp, 1);
- if (rc)
- return EIO;
-
- if (rsp.u.config.var[0] != '\0')
- strcpy(initiator_name, rsp.u.config.var);
-
- memset(&req, 0, sizeof(req));
- req.command = MGMT_IPC_CONFIG_IALIAS;
-
- rc = iscsid_exec_req(&req, &rsp, 0);
- if (rc)
- /* alias is optional so return ok */
- return 0;
-
- if (rsp.u.config.var[0] != '\0')
- strcpy(initiator_alias, rsp.u.config.var);
- return 0;
-}
-
static iscsi_session_t *
init_new_session(struct iscsi_sendtargets_config *config,
struct iface_rec *iface)
diff --git a/usr/discovery.h b/usr/discovery.h
index b75ee7c..3d65fb7 100644
--- a/usr/discovery.h
+++ b/usr/discovery.h
@@ -27,8 +27,9 @@ struct iface_rec;
struct node_rec;
struct boot_context;
-extern int discovery_fw(struct discovery_rec *drec,
- struct iface_rec *iface,
+extern int discovery_isns(struct discovery_rec *drec, struct iface_rec *iface,
+ struct list_head *rec_list);
+extern int discovery_fw(struct discovery_rec *drec, struct iface_rec *iface,
struct list_head *rec_list);
extern int discovery_sendtargets(struct discovery_rec *drec,
struct iface_rec *iface,
diff --git a/usr/discoveryd.c b/usr/discoveryd.c
index cae228b..2986122 100644
--- a/usr/discoveryd.c
+++ b/usr/discoveryd.c
@@ -34,7 +34,7 @@
#include "sysdeps.h"
#include "log.h"
#include "session_mgmt.h"
-#include "util.h"
+#include "iscsi_util.h"
#include "event_poll.h"
#include "iface.h"
diff --git a/usr/event_poll.c b/usr/event_poll.c
index 691645b..8e6d3d2 100644
--- a/usr/event_poll.c
+++ b/usr/event_poll.c
@@ -63,8 +63,7 @@ static void reaper(void)
#define POLL_CTRL 0
#define POLL_IPC 1
-#define POLL_ISNS 2
-#define POLL_MAX 3
+#define POLL_MAX 2
static int event_loop_stop;
@@ -73,8 +72,7 @@ void event_loop_exit(void)
event_loop_stop = 1;
}
-void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd,
- int isns_fd)
+void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd)
{
struct pollfd poll_array[POLL_MAX];
int res;
@@ -84,13 +82,6 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd,
poll_array[POLL_IPC].fd = mgmt_ipc_fd;
poll_array[POLL_IPC].events = POLLIN;
- if (isns_fd < 0)
- poll_array[POLL_ISNS].fd = poll_array[POLL_ISNS].events = 0;
- else {
- poll_array[POLL_ISNS].fd = isns_fd;
- poll_array[POLL_ISNS].events = POLLIN;
- }
-
event_loop_stop = 0;
while (!event_loop_stop) {
res = poll(poll_array, POLL_MAX, ACTOR_RESOLUTION);
@@ -101,10 +92,6 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd,
if (poll_array[POLL_IPC].revents)
mgmt_ipc_handle(mgmt_ipc_fd);
-
- if (poll_array[POLL_ISNS].revents)
- isns_handle(isns_fd);
-
} else if (res < 0) {
if (errno == EINTR) {
log_debug(1, "event_loop interrupted");
diff --git a/usr/event_poll.h b/usr/event_poll.h
index 02137e8..d1e0285 100644
--- a/usr/event_poll.h
+++ b/usr/event_poll.h
@@ -22,8 +22,7 @@
struct iscsi_ipc;
void need_reap(void);
-void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd,
- int isns_fd);
+void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd);
void event_loop_exit(void);
#endif
diff --git a/usr/host.c b/usr/host.c
index 1f6ffb2..4ce837c 100644
--- a/usr/host.c
+++ b/usr/host.c
@@ -22,7 +22,7 @@
#include <errno.h>
#include "list.h"
-#include "util.h"
+#include "iscsi_util.h"
#include "log.h"
#include "iscsi_sysfs.h"
#include "version.h"
diff --git a/usr/idbm.c b/usr/idbm.c
index bf6401e..3bb30e4 100644
--- a/usr/idbm.c
+++ b/usr/idbm.c
@@ -33,7 +33,7 @@
#include "idbm.h"
#include "idbm_fields.h"
#include "log.h"
-#include "util.h"
+#include "iscsi_util.h"
#include "iscsi_settings.h"
#include "transport.h"
#include "iscsi_sysfs.h"
@@ -1744,65 +1744,6 @@ int idbm_bind_ifaces_to_nodes(idbm_disc_nodes_fn *disc_node_fn,
return 0;
}
-/*
- * remove this when isns is converted
- */
-int idbm_add_nodes(node_rec_t *newrec, discovery_rec_t *drec,
- struct list_head *ifaces, int update)
-{
- struct iface_rec *iface, *tmp;
- struct iscsi_transport *t;
- int rc = 0, found = 0;
-
- if (!ifaces || list_empty(ifaces)) {
- struct list_head def_ifaces;
-
- INIT_LIST_HEAD(&def_ifaces);
- iface_link_ifaces(&def_ifaces);
-
- list_for_each_entry_safe(iface, tmp, &def_ifaces, list) {
- list_del(&iface->list);
- t = iscsi_sysfs_get_transport_by_name(iface->transport_name);
- /* only auto bind to software iscsi */
- if (!t || strcmp(t->name, DEFAULT_TRANSPORT) ||
- !strcmp(iface->name, DEFAULT_IFACENAME)) {
- free(iface);
- continue;
- }
-
- iface_copy(&newrec->iface, iface);
- rc = idbm_add_node(newrec, drec, update);
- free(iface);
- if (rc)
- return rc;
- found = 1;
- }
-
- /* create default iface with old/default behavior */
- if (!found) {
- iface_setup_defaults(&newrec->iface);
- return idbm_add_node(newrec, drec, update);
- }
- } else {
- list_for_each_entry(iface, ifaces, list) {
- if (strcmp(iface->name, DEFAULT_IFACENAME) &&
- !iface_is_valid(iface)) {
- log_error("iface %s is not valid. Will not "
- "bind node to it. Iface settings "
- iface_fmt, iface->name,
- iface_str(iface));
- continue;
- }
-
- iface_copy(&newrec->iface, iface);
- rc = idbm_add_node(newrec, drec, update);
- if (rc)
- return rc;
- }
- }
- return 0;
-}
-
static void idbm_rm_disc_node_links(char *disc_dir)
{
char *target = NULL, *tpgt = NULL, *port = NULL;
diff --git a/usr/idbm.h b/usr/idbm.h
index f390315..b1ed347 100644
--- a/usr/idbm.h
+++ b/usr/idbm.h
@@ -128,9 +128,6 @@ extern int idbm_bind_ifaces_to_nodes(idbm_disc_nodes_fn *disc_node_fn,
struct discovery_rec *drec,
struct list_head *ifaces,
struct list_head *bound_recs);
-extern int idbm_add_nodes(node_rec_t *newrec,
- discovery_rec_t *drec, struct list_head *ifaces,
- int overwrite);
extern int idbm_add_discovery(discovery_rec_t *newrec);
extern void idbm_sendtargets_defaults(struct iscsi_sendtargets_config *cfg);
extern void idbm_slp_defaults(struct iscsi_slp_config *cfg);
diff --git a/usr/initiator.c b/usr/initiator.c
index f8df8e6..416e1bc 100644
--- a/usr/initiator.c
+++ b/usr/initiator.c
@@ -40,7 +40,7 @@
#include "iscsi_ipc.h"
#include "idbm.h"
#include "log.h"
-#include "util.h"
+#include "iscsi_util.h"
#include "scsi.h"
#include "iscsi_sysfs.h"
#include "iscsi_settings.h"
diff --git a/usr/initiator.h b/usr/initiator.h
index 8ee7138..2cf3f11 100644
--- a/usr/initiator.h
+++ b/usr/initiator.h
@@ -346,10 +346,4 @@ extern void iscsi_async_session_creation(uint32_t host_no, uint32_t sid);
extern void iscsi_async_session_destruction(uint32_t host_no, uint32_t sid);
extern void free_initiator(void);
-/* isns.c */
-extern int isns_init(void);
-extern void isns_handle(int);
-extern void isns_exit(void);
-extern int isns_dev_attr_query_task(queue_task_t *qtask);
-
#endif /* INITIATOR_H */
diff --git a/usr/util.c b/usr/iscsi_util.c
index 552845b..63cef8f 100644
--- a/usr/util.c
+++ b/usr/iscsi_util.c
@@ -86,8 +86,7 @@ str_to_ipport(char *str, int *port, int *tpgt)
*sport++ = '\0';
*port = strtoul(sport, NULL, 10);
str = sport;
- } else
- *port = ISCSI_LISTEN_PORT;
+ }
if ((stpgt = strchr(str, ','))) {
*stpgt++ = '\0';
diff --git a/usr/util.h b/usr/iscsi_util.h
index 87b2bb2..87b2bb2 100644
--- a/usr/util.h
+++ b/usr/iscsi_util.h
diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
index 09b6bad..50d63c4 100644
--- a/usr/iscsiadm.c
+++ b/usr/iscsiadm.c
@@ -33,7 +33,7 @@
#include "log.h"
#include "mgmt_ipc.h"
#include "idbm.h"
-#include "util.h"
+#include "iscsi_util.h"
#include "transport.h"
#include "version.h"
#include "iscsi_sysfs.h"
@@ -997,24 +997,38 @@ sw_st:
op);
}
-static int isns_dev_attr_query(discovery_rec_t *drec,
- int info_level)
+static int do_isns(discovery_rec_t *drec, struct list_head *ifaces,
+ int info_level, int do_login, int op)
{
- iscsiadm_req_t req;
- iscsiadm_rsp_t rsp;
- int err;
+ struct list_head rec_list;
+ struct node_rec *rec, *tmp;
+ int rc;
+
+ INIT_LIST_HEAD(&rec_list);
+ /*
+ * compat: if the user did not pass any op then we do all
+ * ops for them
+ */
+ if (!op)
+ op = OP_NEW | OP_DELETE | OP_UPDATE;
- memset(&req, 0, sizeof(iscsiadm_req_t));
- req.command = MGMT_IPC_ISNS_DEV_ATTR_QUERY;
+ drec->type = DISCOVERY_TYPE_ISNS;
- err = iscsid_exec_req(&req, &rsp, 1);
- if (err) {
- iscsid_handle_error(err);
- return EIO;
- } else {
- idbm_print_discovered(drec, info_level);
- return 0;
+ rc = idbm_bind_ifaces_to_nodes(discovery_isns, drec, ifaces,
+ &rec_list);
+ if (rc) {
+ log_error("Could not perform iSNS discovery.");
+ return rc;
}
+
+ rc = exec_disc_op_on_recs(drec, &rec_list, info_level, do_login, op);
+
+ list_for_each_entry_safe(rec, tmp, &rec_list, list) {
+ list_del(&rec->list);
+ free(rec);
+ }
+
+ return rc;
}
static int
@@ -1632,6 +1646,9 @@ main(int argc, char **argv)
goto free_ifaces;
}
+ if (mode != MODE_DISCOVERY && ip)
+ port = ISCSI_LISTEN_PORT;
+
switch (mode) {
case MODE_HOST:
if ((rc = verify_mode_params(argc, argv, "HdmP", 0))) {
@@ -1673,7 +1690,10 @@ main(int argc, char **argv)
}
switch (type) {
case DISCOVERY_TYPE_SENDTARGETS:
- if (ip == NULL || port < 0) {
+ if (port < 0)
+ port = ISCSI_LISTEN_PORT;
+
+ if (ip == NULL) {
log_error("please specify right portal as "
"<ipaddr>[:<ipport>]");
rc = -1;
@@ -1698,10 +1718,23 @@ main(int argc, char **argv)
rc = -1;
break;
case DISCOVERY_TYPE_ISNS:
- drec.type = DISCOVERY_TYPE_ISNS;
+ if (!ip) {
+ log_error("please specify right portal as "
+ "<ipaddr>:[<ipport>]");
+ rc = -1;
+ goto out;
+ }
- if (isns_dev_attr_query(&drec, info_level))
+ strlcpy(drec.address, ip, sizeof(drec.address));
+ if (port < 0)
+ drec.port = 3205;
+ else
+ drec.port = port;
+
+ if (do_isns(&drec, &ifaces, info_level, do_login, op)) {
rc = -1;
+ goto out;
+ }
break;
case DISCOVERY_TYPE_FW:
drec.type = DISCOVERY_TYPE_FW;
diff --git a/usr/iscsid.c b/usr/iscsid.c
index 29fcc9e..147b00d 100644
--- a/usr/iscsid.c
+++ b/usr/iscsid.c
@@ -37,7 +37,7 @@
#include "event_poll.h"
#include "iscsi_ipc.h"
#include "log.h"
-#include "util.h"
+#include "iscsi_util.h"
#include "initiator.h"
#include "transport.h"
#include "idbm.h"
@@ -333,7 +333,6 @@ int main(int argc, char *argv[])
char *initiatorname_file = INITIATOR_NAME_FILE;
char *pid_file = PID_FILE;
int ch, longindex;
- int isns_fd;
uid_t uid = 0;
struct sigaction sa_old;
struct sigaction sa_new;
@@ -512,12 +511,10 @@ int main(int argc, char *argv[])
}
actor_init();
- isns_fd = isns_init();
- event_loop(ipc, control_fd, mgmt_ipc_fd, isns_fd);
+ event_loop(ipc, control_fd, mgmt_ipc_fd);
idbm_terminate();
sysfs_cleanup();
- isns_exit();
ipc->ctldev_close();
mgmt_ipc_close(mgmt_ipc_fd);
if (daemon_config.initiator_name)
diff --git a/usr/iscsid_req.c b/usr/iscsid_req.c
index 4f3b439..5280a0a 100644
--- a/usr/iscsid_req.c
+++ b/usr/iscsid_req.c
@@ -29,7 +29,7 @@
#include "initiator.h"
#include "log.h"
#include "mgmt_ipc.h"
-#include "util.h"
+#include "iscsi_util.h"
#include "config.h"
static void iscsid_startup(void)
diff --git a/usr/iscsistart.c b/usr/iscsistart.c
index 63d3e5f..94a9601 100644
--- a/usr/iscsistart.c
+++ b/usr/iscsistart.c
@@ -38,7 +38,7 @@
#include "event_poll.h"
#include "transport.h"
#include "log.h"
-#include "util.h"
+#include "iscsi_util.h"
#include "idbm.h"
#include "version.h"
#include "iscsi_sysfs.h"
@@ -422,7 +422,7 @@ int main(int argc, char *argv[])
* Start Main Event Loop
*/
actor_init();
- event_loop(ipc, control_fd, mgmt_ipc_fd, -1);
+ event_loop(ipc, control_fd, mgmt_ipc_fd);
ipc->ctldev_close();
mgmt_ipc_close(mgmt_ipc_fd);
free_initiator();
diff --git a/usr/isns.c b/usr/isns.c
deleted file mode 100644
index dec7aac..0000000
--- a/usr/isns.c
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * iSNS functions
- *
- * Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-#include <sys/poll.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include "initiator.h"
-#include "idbm.h"
-#include "log.h"
-#include "util.h"
-#include "isns_proto.h"
-#include "sysdeps.h"
-
-enum isns_task_state {
- ISNS_TASK_WAIT_CONN,
- ISNS_TASK_SEND_PDU,
- ISNS_TASK_RECV_PDU,
-};
-
-struct isns_task {
- int state;
- int fd;
- int len;
- char data[ISCSI_DEF_MAX_RECV_SEG_LEN];
- int transaction;
- int done;
- int retry;
- queue_task_t *qtask;
- struct actor actor;
-};
-
-static struct sockaddr_storage ss;
-static uint16_t transaction;
-
-static char isns_address[NI_MAXHOST];
-static int isns_port = 3205, isns_listen_port, max_retry = 10000;
-
-static void isns_poll(void *data);
-
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-
-#define get_hdr_param(hdr, function, length, flags, transaction, sequence) \
-{ \
- function = ntohs(hdr->function); \
- length = ntohs(hdr->length); \
- flags = ntohs(hdr->flags); \
- transaction = ntohs(hdr->transaction); \
- sequence = ntohs(hdr->sequence); \
-}
-
-/* use io.c */
-static int set_non_blocking(int fd)
-{
- int res = fcntl(fd, F_GETFL);
-
- if (res == -1)
- log_warning("unable to get fd flags %m");
- else {
- res = fcntl(fd, F_SETFL, res | O_NONBLOCK);
- if (res)
- log_warning("unable to set fd flags %m");
- }
-
- return res;
-}
-
-static void
-isns_hdr_init(struct isns_hdr *hdr, uint16_t function, uint16_t length,
- uint16_t flags, uint16_t trans, uint16_t sequence)
-{
- hdr->version = htons(0x0001);
- hdr->function = htons(function);
- hdr->length = htons(length);
- hdr->flags = htons(flags);
- hdr->transaction = htons(trans);
- hdr->sequence = htons(sequence);
-}
-
-static int
-isns_tlv_set(struct isns_tlv **tlv, uint32_t tag, uint32_t length, void *value)
-{
- if (length)
- memcpy((*tlv)->value, value, length);
- if (length % ISNS_ALIGN)
- length += (ISNS_ALIGN - (length % ISNS_ALIGN));
-
- (*tlv)->tag = htonl(tag);
- (*tlv)->length = htonl(length);
-
- length += sizeof(struct isns_tlv);
- *tlv = (struct isns_tlv *) ((char *) *tlv + length);
-
- return length;
-}
-
-static void build_dev_reg_req(struct isns_task *task)
-{
- struct isns_hdr *hdr = (struct isns_hdr *) task->data;
- struct isns_tlv *tlv = (struct isns_tlv *) hdr->pdu;
- struct sockaddr_storage lss;
- static uint8_t ip[16];
- char eid[NI_MAXHOST];
- char *name = dconfig->initiator_name;
- char *alias = dconfig->initiator_alias;
- socklen_t slen = sizeof(lss);
- int i;
- uint16_t flags = 0, length = 0;
- uint32_t addr;
- uint32_t port;
- uint32_t node = htonl(ISNS_NODE_INITIATOR);
- uint32_t type = htonl(2);
-
- memset(hdr, 0, sizeof(task->data));
-
- getsockname(task->fd, (struct sockaddr *) &lss, &slen);
- getnameinfo((struct sockaddr *) &lss, sizeof(lss), eid, sizeof(eid),
- NULL, 0, 0);
-
- switch (lss.ss_family) {
- case AF_INET:
- addr = (((struct sockaddr_in *) &lss)->sin_addr.s_addr);
-
- ip[10] = ip[11] = 0xff;
- ip[15] = 0xff & (addr >> 24);
- ip[14] = 0xff & (addr >> 16);
- ip[13] = 0xff & (addr >> 8);
- ip[12] = 0xff & addr;
- port = ((struct sockaddr_in *) &lss)->sin_port;
- break;
- case AF_INET6:
- for (i = 0; i < ARRAY_SIZE(ip); i++)
- ip[i] = ((struct sockaddr_in6 *) &lss)->sin6_addr.s6_addr[i];
- break;
- port = ((struct sockaddr_in6 *) &lss)->sin6_port;
- }
-
- port = htonl(ntohs(port));
-
- length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name), name);
- length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER,
- strlen(eid), eid);
- length += isns_tlv_set(&tlv, 0, 0, 0);
- length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER,
- strlen(eid), eid);
-
- length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_PROTOCOL,
- sizeof(type), &type);
- length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_IP_ADDRESS,
- sizeof(ip), &ip);
- length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_PORT,
- sizeof(port), &port);
- flags = ISNS_FLAG_REPLACE;
-
- port = htonl(isns_listen_port);
- length += isns_tlv_set(&tlv, ISNS_ATTR_ESI_PORT,
- sizeof(port), &port);
-
- length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name), name);
- length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE,
- sizeof(node), &node);
- if(alias)
- length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_ALIAS,
- strlen(alias), alias);
-
- flags |= ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
- task->transaction = ++transaction;
- isns_hdr_init(hdr, ISNS_FUNC_DEV_ATTR_REG, length, flags,
- task->transaction, 0);
-
- task->len = length + sizeof(*hdr);
-}
-
-static int isns_connect(void)
-{
- int err;
- int fd;
-
- fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
- if (fd < 0) {
- log_error("can't create socket %m");
- return -errno;
- }
-
- err = set_non_blocking(fd);
- if (err) {
- log_error("can't set non-blocking %m");
- close(fd);
- return -errno;
- }
-
- err = connect(fd, (struct sockaddr *) &ss, sizeof(ss));
- if (err && errno != EINPROGRESS) {
- log_error("can't connect %m");
- close(fd);
- return -errno;
- }
- return fd;
-}
-
-static int isns_send_pdu(struct isns_task *task)
-{
- int err;
-
- err = write(task->fd, task->data + task->done, task->len - task->done);
- if (err < 0) {
- if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) {
- log_error("send fail %m");
- return -1;
- }
- } else
- task->done += err;
-
- return 0;
-}
-static void isns_free_task(struct isns_task *task)
-{
- close(task->fd);
- free(task);
-}
-
-static int isns_recv_pdu(struct isns_task *task)
-{
- struct isns_hdr *hdr = (struct isns_hdr *) task->data;
- uint16_t function, length, flags, transaction, sequence;
- int err, size;
-
- if (task->done < sizeof(*hdr))
- size = sizeof(*hdr) - task->done;
- else
- size = task->len + sizeof(*hdr) - task->done;
-
- err = read(task->fd, task->data + task->done, size);
- if (err <= 0) {
- if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) {
- log_error("send fail %m");
- return -1;
- }
- } else {
- task->done += err;
-
- if (task->done == sizeof(*hdr)) {
- get_hdr_param(hdr, function, length, flags, transaction,
- sequence);
- task->len = length;
- }
- }
- return 0;
-}
-
-static char *isns_get_config_file(void)
-{
- return dconfig->config_file;
-}
-
-static void add_new_target_node(char *targetname, uint8_t *ip, int port,
- int tag)
-{
- int err;
- node_rec_t rec;
- discovery_rec_t drec;
- char dst[INET6_ADDRSTRLEN];
-
- memset(dst, 0, sizeof(dst));
- /*
- * some servers are sending compat instead of mapped
- */
- if (IN6_IS_ADDR_V4MAPPED(ip) || IN6_IS_ADDR_V4COMPAT(ip))
- inet_ntop(AF_INET, ip + 12, dst, sizeof(dst));
- else
- inet_ntop(AF_INET6, ip, dst, sizeof(dst));
-
- log_debug(1, "add a new target node:%s %s,%d %d",
- targetname, dst, port, tag);
-
- if (idbm_init(isns_get_config_file)) {
- log_error("Could not add new target node:%s %s,%d",
- targetname, dst, port);
- return;
- }
- idbm_node_setup_from_conf(&rec);
- strlcpy(rec.name, targetname, TARGET_NAME_MAXLEN);
- rec.conn[0].port = port;
- rec.tpgt = tag;
- strlcpy(rec.conn[0].address, dst, NI_MAXHOST);
-
- /* TODO?: shoudl we set the address and port of the server ? */
- memset(&drec, 0, sizeof(discovery_rec_t));
- drec.type = DISCOVERY_TYPE_ISNS;
- err = idbm_add_nodes(&rec, &drec, NULL, 0);
- if (err)
- log_error("Could not add new target node:%s %s,%d",
- targetname, dst, port);
-
- idbm_terminate();
-}
-
-static int qry_rsp_handle(struct isns_hdr *hdr)
-{
- struct isns_tlv *tlv;
- uint16_t function, length, flags, transaction, sequence;
- uint32_t port, tag, status;
- uint8_t *addr;
- char *name;
-
- get_hdr_param(hdr, function, length, flags, transaction, sequence);
-
- status = (uint32_t) (*hdr->pdu);
- if (status)
- return status;
-
- /* skip status */
- tlv = (struct isns_tlv *) ((char *) hdr->pdu + 4);
- length -= 4;
-
- /* check node type in the message key*/
- if ((ntohl(tlv->tag) != ISNS_ATTR_ISCSI_NODE_TYPE) ||
- ntohl(*(tlv->value)) != ISNS_NODE_TARGET)
- return EINVAL;
-
- /* 12 + 8 bytes */
- length -= (sizeof(*tlv) + 4 + 8);
- if (length <= 0) {
- log_error("No target found.");
- return EINVAL;
- }
-
- tlv = (struct isns_tlv *) ((char *) tlv + 20);
-
- name = NULL;
- addr = NULL;
- port = tag = 0;
-
- /* FIXME: this assume the exact order. */
- while (length) {
- uint32_t vlen = ntohl(tlv->length);
-
- switch (ntohl(tlv->tag)) {
- case ISNS_ATTR_PG_ISCSI_NAME:
- if (name && addr) {
- add_new_target_node(name, addr, port, tag);
- name = NULL;
- addr = NULL;
- }
- name = (char *) tlv->value;
- break;
- case ISNS_ATTR_ISCSI_NODE_TYPE:
- if (ntohl(*(tlv->value)) != ISNS_NODE_TARGET)
- name = NULL;
- break;
- case ISNS_ATTR_PG_PORTAL_IP_ADDRESS:
- addr = (uint8_t *) tlv->value;
- break;
- case ISNS_ATTR_PG_PORTAL_PORT:
- port = ntohl(tlv->value[0]);
- break;
- case ISNS_ATTR_PG_TAG:
- tag = ntohl(tlv->value[0]);
- break;
- case ISNS_ATTR_ISCSI_NAME:
- case ISNS_ATTR_PORTAL_IP_ADDRESS:
- case ISNS_ATTR_PORTAL_PORT:
- break;
- default:
- log_error("unexpected type %d", ntohl(tlv->tag));
- break;
- }
-
- length -= (sizeof(*tlv) + vlen);
- tlv = (struct isns_tlv *) ((char *) tlv->value + vlen);
- }
-
- if (name && addr)
- add_new_target_node(name, addr, port, tag);
-
- return 0;
-}
-
-static void send_mgmt_rsp(struct isns_task *task, int err)
-{
- mgmt_ipc_write_rsp(task->qtask,
- err ? MGMT_IPC_ERR_ISNS_UNAVAILABLE : MGMT_IPC_OK);
-}
-
-static int isns_task_done(struct isns_task *task)
-{
- struct isns_hdr *hdr = (struct isns_hdr *) task->data;
- uint16_t function, length, flags, transaction, sequence;
- uint32_t status = (uint32_t) (*hdr->pdu);
- char *payload = (char *) hdr + sizeof(*hdr);
- int finished = 1;
-
- get_hdr_param(hdr, function, length, flags, transaction,
- sequence);
-
- if (function & 0x8000 && status)
- log_error("error isns response %x %x", function, status);
-
- switch (function) {
- case ISNS_FUNC_DEV_ATTR_REG_RSP:
- break;
- case ISNS_FUNC_DEV_ATTR_QRY_RSP:
- if (!status)
- qry_rsp_handle((struct isns_hdr *)task->data);
- send_mgmt_rsp(task, status);
- break;
- case ISNS_FUNC_ESI:
- memmove(payload + 4, payload, length);
- *((uint32_t *) payload) = 0;
-
- length += 4;
- flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU |
- ISNS_FLAG_FIRST_PDU;
- isns_hdr_init(hdr, ISNS_FUNC_ESI_RSP, length, flags,
- transaction, 0);
- task->state = ISNS_TASK_SEND_PDU;
- task->len = length + sizeof(*hdr);
- task->done = 0;
-
- actor_new(&task->actor, isns_poll, task);
- actor_schedule(&task->actor);
- finished = 0;
- break;
- default:
- log_error("unexpected function %d", function);
- break;
- }
-
- return finished;
-}
-
-int isns_dev_attr_query_task(queue_task_t *qtask)
-{
- int fd;
- struct isns_hdr *hdr;
- struct isns_tlv *tlv;
- char *name = dconfig->initiator_name;
- uint16_t flags, length = 0;
- uint32_t node = htonl(ISNS_NODE_TARGET);
- struct isns_task *task;
-
- if (!strlen(isns_address))
- return MGMT_IPC_ERR_ISNS_UNAVAILABLE;
-
- fd = isns_connect();
- if (fd < 0) {
- log_error("%s %m", __FUNCTION__);
- return MGMT_IPC_ERR_ISNS_UNAVAILABLE;
- }
-
- task = malloc(sizeof(*task));
- if (!task) {
- log_error("%s %m", __FUNCTION__);
- close(fd);
- return MGMT_IPC_ERR_NOMEM;
- }
- memset(task, 0, sizeof(*task));
-
- task->qtask = qtask;
- task->fd = fd;
-
- hdr = (struct isns_hdr *) task->data;
- tlv = (struct isns_tlv *) hdr->pdu;
-
- memset(hdr, 0, sizeof(task->data));
-
- length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name), name);
- length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE,
- sizeof(node), &node);
- length += isns_tlv_set(&tlv, 0, 0, 0);
- length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, 0, 0);
- length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE, 0, 0);
- length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_IP_ADDRESS, 0, 0);
- length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_PORT, 0, 0);
- length += isns_tlv_set(&tlv, ISNS_ATTR_PG_ISCSI_NAME, 0, 0);
- length += isns_tlv_set(&tlv, ISNS_ATTR_PG_PORTAL_IP_ADDRESS, 0, 0);
- length += isns_tlv_set(&tlv, ISNS_ATTR_PG_PORTAL_PORT, 0, 0);
- length += isns_tlv_set(&tlv, ISNS_ATTR_PG_TAG, 0, 0);
-
- flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
- task->transaction = ++transaction;
- isns_hdr_init(hdr, ISNS_FUNC_DEV_ATTR_QRY, length, flags,
- task->transaction, 0);
-
- task->len = length + sizeof(*hdr);
- task->state = ISNS_TASK_SEND_PDU;
-
- qtask->rsp.command = MGMT_IPC_ISNS_DEV_ATTR_QUERY;
-
- actor_new(&task->actor, isns_poll, task);
- actor_schedule(&task->actor);
-
- return MGMT_IPC_OK;
-}
-
-void isns_handle(int listen_fd)
-{
- struct sockaddr_storage from;
- socklen_t slen = sizeof(from);
- int fd;
- struct isns_task *task;
-
- fd = accept(listen_fd, (struct sockaddr *) &from, &slen);
- if (fd < 0) {
- log_error("%s: accept error %m", __FUNCTION__);
- return;
- }
-
- task = malloc(sizeof(*task));
- if (!task) {
- log_error("%s %m", __FUNCTION__);
- close(fd);
- return;
- }
-
- memset(task, 0, sizeof(*task));
- task->state = ISNS_TASK_RECV_PDU;
- task->fd = fd;
-
- actor_new(&task->actor, isns_poll, task);
- actor_schedule(&task->actor);
-}
-
-static void isns_poll(void *data)
-{
- int err, finished;
- struct pollfd pfd;
- struct isns_task *task = data;
- struct isns_hdr *hdr = (struct isns_hdr *) task->data;
- uint16_t function = ntohs(hdr->function);
-
- pfd.fd = task->fd;
- switch (task->state) {
- case ISNS_TASK_WAIT_CONN:
- case ISNS_TASK_SEND_PDU:
- pfd.events = POLLOUT;
- break;
- case ISNS_TASK_RECV_PDU:
- pfd.events = POLLIN;
- }
-
- err = poll(&pfd, 1, 1);
- if (err > 0) {
- switch (task->state) {
- case ISNS_TASK_WAIT_CONN:
- task->state = ISNS_TASK_SEND_PDU;
- case ISNS_TASK_SEND_PDU:
- err = isns_send_pdu(task);
- if (err)
- goto abort_task;
- else {
-
- if (task->done == task->len) {
- task->state = ISNS_TASK_RECV_PDU;
- task->done = task->len = 0;
-
- if (function == ISNS_FUNC_ESI_RSP)
- goto free_task;
- }
-
- actor_new(&task->actor, isns_poll, task);
- actor_schedule(&task->actor);
- }
- break;
- case ISNS_TASK_RECV_PDU:
- err = isns_recv_pdu(task);
- if (err)
- goto abort_task;
- else {
- if (task->done ==
- task->len + sizeof(struct isns_hdr)) {
- finished = isns_task_done(task);
- if (finished)
- goto free_task;
- } else {
- /* need to read more */
- actor_new(&task->actor, isns_poll,
- task);
- actor_schedule(&task->actor);
- }
- }
- }
- } else if (!err) {
- /* FIXME */
- if (task->retry++ > max_retry) {
- log_error("abort task");
- goto abort_task;
- } else {
- actor_new(&task->actor, isns_poll, task);
- actor_schedule(&task->actor);
- }
- }
-
- return;
-abort_task:
- if (task->qtask)
- send_mgmt_rsp(task, 1);
-free_task:
- isns_free_task(task);
-}
-
-static int isns_dev_register(void)
-{
- struct isns_task *task;
-
- task = malloc(sizeof(*task));
- if (!task)
- return -ENOMEM;
- memset(task, 0, sizeof(*task));
-
- task->fd = isns_connect();
- if (task->fd < 0) {
- free(task);
- return -ENOMEM;
- }
-
- task->state = ISNS_TASK_WAIT_CONN;
- build_dev_reg_req(task);
-
- actor_new(&task->actor, isns_poll, task);
- actor_schedule(&task->actor);
-
- return 0;
-}
-
-static int isns_listen_init(int *listen_fd)
-{
- int fd, opt, err;
- struct sockaddr_storage lss;
- socklen_t slen;
-
- fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
- if (fd < 0) {
- log_error("%s %m", __FUNCTION__);
- return -errno;
- }
-
- opt = 1;
- if (ss.ss_family == AF_INET6) {
- err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt,
- sizeof(opt));
- if (err)
- log_error("%s %m", __FUNCTION__);
- goto out;
- }
-
- err = listen(fd, 5);
- if (err) {
- log_error("%s %m", __FUNCTION__);
- goto out;
- }
-
- slen = sizeof(lss);
- err = getsockname(fd, (struct sockaddr *) &lss, &slen);
- if (err) {
- log_error("%s %m", __FUNCTION__);
- goto out;
- }
-
- if (lss.ss_family == AF_INET6)
- isns_listen_port = ((struct sockaddr_in6 *) &lss)->sin6_port;
- else
- isns_listen_port = ((struct sockaddr_in *) &lss)->sin_port;
-
- isns_listen_port = ntohs(isns_listen_port);
-out:
- if (err) {
- close(fd);
- return -1;
- } else {
- *listen_fd = fd;
- return 0;
- }
-}
-
-int isns_init(void)
-{
- char buf[2048], port[NI_MAXSERV];
- int fd = -1, err;
- FILE *f;
-
- f = fopen(isns_get_config_file(), "r");
- if (!f)
- return -EIO;
-
- while (fgets(buf, sizeof(buf), f)) {
- /* FIXME */
- if (buf[strlen(buf) - 1] == '\n')
- buf[strlen(buf) - 1] = '\0';
- if (!strncmp(buf, "isns.address = ", 15))
- strncpy(isns_address, buf + 15, sizeof(isns_address));
- else if (!strncmp(buf, "isns.port = ", 12))
- isns_port = atoi(buf + 12);
- }
-
- fclose(f);
-
- if (!strlen(isns_address))
- return -1;
-
- snprintf(port, sizeof(port), "%d", isns_port);
- err = resolve_address(isns_address, port, &ss);
- if (err) {
- log_error("can't resolve address %m, %s", isns_address);
- return err;
- }
-
- err = isns_listen_init(&fd);
- if (err)
- return err;
-
- isns_dev_register();
- return fd;
-}
-
-void isns_exit(void)
-{
- /* do nothing for now */
- ;
-}
diff --git a/usr/isns_proto.h b/usr/isns_proto.h
deleted file mode 100644
index a070d03..0000000
--- a/usr/isns_proto.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * iSNS protocol data types
- *
- * Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef ISNS_PROTO_H
-#define ISNS_PROTO_H
-
-#define ISNS_PORT 3205
-#define ISNS_ALIGN 4
-
-struct isns_hdr {
- uint16_t version;
- uint16_t function;
- uint16_t length;
- uint16_t flags;
- uint16_t transaction;
- uint16_t sequence;
- uint32_t pdu[0];
-} __attribute__ ((packed));
-
-struct isns_tlv {
- uint32_t tag;
- uint32_t length;
- uint32_t value[0];
-} __attribute__ ((packed));
-
-/* Commands and responses (4.1.3) */
-#define ISNS_FUNC_DEV_ATTR_REG 0x0001
-#define ISNS_FUNC_DEV_ATTR_QRY 0x0002
-#define ISNS_FUNC_DEV_GET_NEXT 0x0003
-#define ISNS_FUNC_DEV_DEREG 0x0004
-#define ISNS_FUNC_SCN_REG 0x0005
-#define ISNS_FUNC_SCN_DEREG 0x0006
-#define ISNS_FUNC_SCN_EVENT 0x0007
-#define ISNS_FUNC_SCN 0x0008
-#define ISNS_FUNC_DD_REG 0x0009
-#define ISNS_FUNC_DD_DEREG 0x000a
-#define ISNS_FUNC_DDS_REG 0x000b
-#define ISNS_FUNC_DDS_DEREG 0x000c
-#define ISNS_FUNC_ESI 0x000d
-#define ISNS_FUNC_HEARTBEAT 0x000e
-
-#define ISNS_FUNC_DEV_ATTR_REG_RSP 0x8001
-#define ISNS_FUNC_DEV_ATTR_QRY_RSP 0x8002
-#define ISNS_FUNC_DEV_GET_NEXT_RSP 0x8003
-#define ISNS_FUNC_DEV_DEREG_RSP 0x8004
-#define ISNS_FUNC_SCN_REG_RSP 0x8005
-#define ISNS_FUNC_SCN_DEREG_RSP 0x8006
-#define ISNS_FUNC_SCN_EVENT_RSP 0x8007
-#define ISNS_FUNC_SCN_RSP 0x8008
-#define ISNS_FUNC_DD_REG_RSP 0x8009
-#define ISNS_FUNC_DD_DEREG_RSP 0x800a
-#define ISNS_FUNC_DDS_REG_RSP 0x800b
-#define ISNS_FUNC_DDS_DEREG_RSP 0x800c
-#define ISNS_FUNC_ESI_RSP 0x800d
-
-/* iSNSP flags (5.1.4) */
-#define ISNS_FLAG_CLIENT (1U << 15)
-#define ISNS_FLAG_SERVER (1U << 14)
-#define ISNS_FLAG_AUTH (1U << 13)
-#define ISNS_FLAG_REPLACE (1U << 12)
-#define ISNS_FLAG_LAST_PDU (1U << 11)
-#define ISNS_FLAG_FIRST_PDU (1U << 10)
-
-/* Response Status Codes (5.4) */
-#define ISNS_STATUS_SUCCESS 0
-#define ISNS_STATUS_UNKNOWN_ERROR 1
-#define ISNS_STATUS_FORMAT_ERROR 2
-#define ISNS_STATUS_INVALID_REGISTRATION 3
-#define ISNS_STATUS_RESERVED 4
-#define ISNS_STATUS_INVALID_QUERY 5
-#define ISNS_STATUS_SOURCE_UNKNOWN 6
-#define ISNS_STATUS_SOURCE_ABSENT 7
-#define ISNS_STATUS_SOURCE_UNAUTHORIZED 8
-#define ISNS_STATUS_NO_SUCH_ENTRY 9
-#define ISNS_STATUS_VERSION_NOT_SUPPORTED 10
-#define ISNS_STATUS_INTERNAL_ERROR 11
-#define ISNS_STATUS_BUSY 12
-#define ISNS_STATUS_OPTION_NOT_UNDERSTOOD 13
-#define ISNS_STATUS_INVALID_UPDATE 14
-#define ISNS_STATUS_MESSAGE_NOT_SUPPORTED 15
-#define ISNS_STATUS_SCN_EVENT_REJECTED 16
-#define ISNS_STATUS_SCN_REGISTRATION_REJECTED 17
-#define ISNS_STATUS_ATTRIBUTE_NOT_IMPLEMENTED 18
-#define ISNS_STATUS_FC_DOMAIN_ID_NOT_AVAILABLE 19
-#define ISNS_STATUS_FC_DOMAIN_ID_NOT_ALLOCATED 20
-#define ISNS_STATUS_ESI_NOT_AVAILABLE 21
-#define ISNS_STATUS_INVALIDE_DEREGISTRATION 22
-#define ISNS_STATUS_REGISTRATION_NOT_SUPPORTED 23
-
-/* Node type (5.4.2) */
-#define ISNS_NODE_CONTROL (1U << 2)
-#define ISNS_NODE_INITIATOR (1U << 1)
-#define ISNS_NODE_TARGET (1U << 0)
-
-/* Attributes (6.1) */
-#define ISNS_ATTR_DELIMITER 0
-#define ISNS_ATTR_ENTITY_IDENTIFIER 1
-#define ISNS_ATTR_ENTITY_PROTOCOL 2
-#define ISNS_ATTR_MANAGEMENT_IP_ADDRESS 3
-#define ISNS_ATTR_TIMESTAMP 4
-#define ISNS_ATTR_PROTOCOL_VERSION_RANGE 5
-#define ISNS_ATTR_REGISTRATION_PERIOD 6
-#define ISNS_ATTR_ENTITY_INDEX 7
-#define ISNS_ATTR_ENTITY_NEXT_INDEX 8
-#define ISNS_ATTR_ISAKMP_PHASE1 11
-#define ISNS_ATTR_CERTIFICATE 12
-#define ISNS_ATTR_PORTAL_IP_ADDRESS 16
-#define ISNS_ATTR_PORTAL_PORT 17
-#define ISNS_ATTR_PORTAL_SYMBOLIC_NAME 18
-#define ISNS_ATTR_ESI_INTERVAL 19
-#define ISNS_ATTR_ESI_PORT 20
-#define ISNS_ATTR_PORTAL_INDEX 22
-#define ISNS_ATTR_SCN_PORT 23
-#define ISNS_ATTR_PORTAL_NEXT_INDEX 24
-#define ISNS_ATTR_PORTAL_SECURITY_BITMAP 27
-#define ISNS_ATTR_PORTAL_ISAKMP_PHASE1 28
-#define ISNS_ATTR_PORTAL_ISAKMP_PHASE2 29
-#define ISNS_ATTR_PORTAL_CERTIFICATE 31
-#define ISNS_ATTR_ISCSI_NAME 32
-#define ISNS_ATTR_ISCSI_NODE_TYPE 33
-#define ISNS_ATTR_ISCSI_ALIAS 34
-#define ISNS_ATTR_ISCSI_SCN_BITMAP 35
-#define ISNS_ATTR_ISCSI_NODE_INDEX 36
-#define ISNS_ATTR_WWNN_TOKEN 37
-#define ISNS_ATTR_ISCSI_NODE_NEXT_INDEX 38
-#define ISNS_ATTR_ISCSI_AUTHMETHOD 42
-#define ISNS_ATTR_PG_ISCSI_NAME 48
-#define ISNS_ATTR_PG_PORTAL_IP_ADDRESS 49
-#define ISNS_ATTR_PG_PORTAL_PORT 50
-#define ISNS_ATTR_PG_TAG 51
-#define ISNS_ATTR_PG_INDEX 52
-#define ISNS_ATTR_PG_NEXT_INDEX 53
-#define ISNS_ATTR_FC_PORT_NAME_WWPN 64
-#define ISNS_ATTR_PORT_ID 65
-#define ISNS_ATTR_PORT_TYPE 66
-#define ISNS_ATTR_SYMBOLIC_PORT_NAME 67
-#define ISNS_ATTR_FABRIC_PORT_NAME 68
-#define ISNS_ATTR_HARD_ADDRESS 69
-#define ISNS_ATTR_PORT_IP_ADDRESS 70
-#define ISNS_ATTR_CLASS_OF_SERVICE 71
-#define ISNS_ATTR_FC4_TYPES 72
-#define ISNS_ATTR_FC4_DESCRIPOTR 73
-#define ISNS_ATTR_FC4_FEATURES 74
-#define ISNS_ATTR_IFCP_SCN_BITMAP 75
-#define ISNS_ATTR_PORT_ROLE 76
-#define ISNS_ATTR_PERMANENT_PORT_NAME 77
-#define ISNS_ATTR_FC4_TYPE_CODE 95
-#define ISNS_ATTR_FC_NODE_NAME_WWNN 96
-#define ISNS_ATTR_SYMBOLIC_NODE_NAME 97
-#define ISNS_ATTR_NODE_IP_ADDRESS 98
-#define ISNS_ATTR_NODE_IPA 99
-#define ISNS_ATTR_PORXY_ISCSI_NAME 101
-#define ISNS_ATTR_SWITCH_NAME 128
-#define ISNS_ATTR_PREFERRED_ID 129
-#define ISNS_ATTR_ASSIGNED_ID 130
-#define ISNS_ATTR_VIRTUAL_FABRIC_ID 131
-#define ISNS_ATTR_ISNS_SERVER_VENDOR_OUI 256
-#define ISNS_ATTR_DD_SET_ID 2049
-#define ISNS_ATTR_DD_SET_SYM_NAME 2050
-#define ISNS_ATTR_DD_SET_STATUS 2051
-#define ISNS_ATTR_DD_SET_NEXT_ID 2052
-#define ISNS_ATTR_DD_ID 2065
-#define ISNS_ATTR_DD_SYMBOLIC_NAME 2066
-#define ISNS_ATTR_DD_MEMBER_ISCSI_INDEX 2067
-#define ISNS_ATTR_DD_MEMBER_ISCSI_NAME 2068
-#define ISNS_ATTR_DD_MEMBER_FC_PORT_NAME 2069
-#define ISNS_ATTR_DD_MEMBER_PORTAL_INDEX 2070
-#define ISNS_ATTR_DD_MEMBER_IP_ADDR 2071
-#define ISNS_ATTR_DD_MEMBER_TCP_UDP 2072
-#define ISNS_ATTR_DD_FEATURES 2078
-#define ISNS_ATTR_DD_ID_NEXT_ID 2079
-
-/* SCN flags (6.4.4) */
-#define ISNS_SCN_FLAG_INITIATOR (1U << 24)
-#define ISNS_SCN_FLAG_TARGET (1U << 25)
-#define ISNS_SCN_FLAG_MANAGEMENT (1U << 26)
-#define ISNS_SCN_FLAG_OBJECT_REMOVE (1U << 27)
-#define ISNS_SCN_FLAG_OBJECT_ADDED (1U << 28)
-#define ISNS_SCN_FLAG_OBJECT_UPDATED (1U << 29)
-#define ISNS_SCN_FLAG_DD_REMOVED (1U << 30)
-#define ISNS_SCN_FLAG_DD_ADDED (1U << 31)
-#endif
diff --git a/usr/log.c b/usr/log.c
index 908aac1..7dd6de8 100644
--- a/usr/log.c
+++ b/usr/log.c
@@ -19,7 +19,7 @@
#include <sys/types.h>
#include <sys/wait.h>
-#include "util.h"
+#include "iscsi_util.h"
#include "log.h"
#define SEMKEY 0xA7L
diff --git a/usr/mgmt_ipc.c b/usr/mgmt_ipc.c
index e784bca..9343a76 100644
--- a/usr/mgmt_ipc.c
+++ b/usr/mgmt_ipc.c
@@ -202,13 +202,6 @@ mgmt_ipc_conn_remove(queue_task_t *qtask)
}
static mgmt_ipc_err_e
-mgmt_ipc_isns_dev_attr_query(queue_task_t *qtask)
-{
- return isns_dev_attr_query_task(qtask);
-}
-
-
-static mgmt_ipc_err_e
mgmt_ipc_host_set_param(queue_task_t *qtask)
{
struct ipc_msg_set_host_param *hp = &qtask->req.u.set_host_param;
@@ -518,7 +511,6 @@ static mgmt_ipc_fn_t * mgmt_ipc_functions[__MGMT_IPC_MAX_COMMAND] = {
[MGMT_IPC_CONFIG_IALIAS] = mgmt_ipc_cfg_initiatoralias,
[MGMT_IPC_CONFIG_FILE] = mgmt_ipc_cfg_filename,
[MGMT_IPC_IMMEDIATE_STOP] = mgmt_ipc_immediate_stop,
-[MGMT_IPC_ISNS_DEV_ATTR_QUERY] = mgmt_ipc_isns_dev_attr_query,
[MGMT_IPC_SET_HOST_PARAM] = mgmt_ipc_host_set_param,
[MGMT_IPC_NOTIFY_ADD_NODE] = mgmt_ipc_notify_add_node,
[MGMT_IPC_NOTIFY_DEL_NODE] = mgmt_ipc_notify_del_node,
diff --git a/usr/session_mgmt.c b/usr/session_mgmt.c
index 2ff974e..f02eeb8 100644
--- a/usr/session_mgmt.c
+++ b/usr/session_mgmt.c
@@ -25,7 +25,7 @@
#include "idbm.h"
#include "list.h"
-#include "util.h"
+#include "iscsi_util.h"
#include "mgmt_ipc.h"
#include "session_info.h"
#include "iscsi_sysfs.h"
diff --git a/usr/transport.c b/usr/transport.c
index 75709ee..c0789bb 100644
--- a/usr/transport.c
+++ b/usr/transport.c
@@ -23,7 +23,7 @@
#include "initiator.h"
#include "transport.h"
#include "log.h"
-#include "util.h"
+#include "iscsi_util.h"
#include "iscsi_sysfs.h"
#include "cxgb3i.h"
#include "be2iscsi.h"
diff --git a/utils/open-isns/COPYING b/utils/open-isns/COPYING
new file mode 100644
index 0000000..b1e3f5a
--- /dev/null
+++ b/utils/open-isns/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/utils/open-isns/ChangeLog b/utils/open-isns/ChangeLog
new file mode 100644
index 0000000..36d6506
--- /dev/null
+++ b/utils/open-isns/ChangeLog
@@ -0,0 +1,50 @@
+Under development:
+
+2007-09-27:
+ Fixed a serious interoperability bug
+ Added SLP support (using openslp)
+ Init script (courtesy Albert Pauw)
+
+2007-09-18:
+ Fixed a number of bugs
+ Added more test cases
+ Implemented default DD
+ Support autoconf, and building with/without openssl
+
+2007-08-24:
+ Improved discovery domain handling
+ Implemented DD deregistration
+ Backward compat fixes for older openssl versions
+ Made SCN more robust, SCN state now persists across server restarts
+ More regression tests
+
+2007-07-27:
+ Implemented SCN and ESI
+ Created iSNS discovery daemon (isnsdd)
+ Rewrote the policy handling a bit
+ Started to write some regression test code
+ Better manpages
+
+2007-07-12:
+ DevGetNext support
+ You can now define policies linking authentication
+ to permitted storage node names, permitted
+ entity names, etc.
+ Implemented DDReg
+ Queries and GetNext are now scoped to discovery domains
+ Lots of little bits and pieces for RFC conformance
+
+2005-07-18:
+ Public snapshot released
+ DSA based authentication
+ Deregistration
+ Simple file backed storage for the iSNS database
+ Entity Registration Period + Timestamp support (server side),
+ and entity expiration
+ isnsd now writes a pid file
+ Improved manual pages
+ DevGetNext support under development
+
+2007-05-11:
+ First public release, supporting register/query
+
diff --git a/utils/open-isns/HACKING b/utils/open-isns/HACKING
new file mode 100644
index 0000000..95b330c
--- /dev/null
+++ b/utils/open-isns/HACKING
@@ -0,0 +1,30 @@
+
+When hacking on open-isns, or when trying to locate a problem,
+the following information may be useful:
+
+ - You can start the daemon using the -f option, which
+ prevents it from backgrounding itself. Crucial if
+ you want to run it in a debugger, or under strace.
+
+ This option works for isnsd and isnsdd
+
+ - All tools support the "-d" option to enable debugging.
+ In general, you want to use "-d all" to turn on all
+ debugging options. However, you can select individual
+ debug facilities - check out the manpages and/or
+ the source code in logging.c
+
+ - If isnsd crashes, and you suspect memory corruption,
+ you can compile open-isns with memory debugging enabled. Re-run
+ the configure script and add the option --enable-memdebug. Then
+ run "make clean all" to rebuild everything.
+
+ Memory debugging can be chosen at run-time by setting the
+ ISNS_MDEBUG environment variable, and re-starting the application:
+
+ export ISNS_MDEBUG=1
+ ./isnsd -f -d all
+
+ Memory debugging works for all memory allocations done by the
+ Open-iSNS code, but does not affect memory allocations by other
+ libraries (such as glibc or openssl).
diff --git a/utils/open-isns/Makefile.in b/utils/open-isns/Makefile.in
new file mode 100644
index 0000000..744fe8b
--- /dev/null
+++ b/utils/open-isns/Makefile.in
@@ -0,0 +1,82 @@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+sbindir = @sbindir@
+mandir = @mandir@
+etcdir = /etc
+vardir = /var/lib/isns
+
+SBINDIR = $(INSTALL_ROOT)$(sbindir)
+ETCDIR = $(INSTALL_ROOT)$(etcdir)
+CFGDIR = $(ETCDIR)/isns
+MANDIR = $(INSTALL_ROOT)$(mandir)
+VARDIR = $(INSTALL_ROOT)$(vardir)
+
+CC = @CC@
+CPPFLAGS= @CPPFLAGS@
+CFLAGS = @CFLAGS@ -I.
+LDFLAGS = @LDFLAGS@
+
+LIB = libisns.a
+LIBOBJS = server.o \
+ client.o \
+ objects.o \
+ callback.o \
+ timer.o \
+ vendor.o \
+ db.o \
+ db-file.o \
+ db-policy.o \
+ relation.o \
+ scope.o \
+ message.o \
+ security.o \
+ authblock.o \
+ policy.o \
+ pki.o \
+ register.o \
+ query.o \
+ getnext.o \
+ deregister.o \
+ esi.o \
+ scn.o \
+ dd.o \
+ entity.o \
+ portal-group.o \
+ storage-node.o \
+ domain.o \
+ simple.o \
+ tags.o \
+ attrs.o \
+ export.o \
+ socket.o \
+ slp.o \
+ error.o \
+ logging.o \
+ config.o \
+ parser.o \
+ buffer.o \
+ pidfile.o \
+ sysdep-unix.o \
+ util.o \
+ bitvector.o \
+ mdebug.o
+SECLINK = @SECLIBS@
+SLPLINK = @SLPLIBS@
+SLPLIN = @SLPLIBS@
+
+all: $(LIB)
+
+clean distclean::
+ rm -f *.o $(LIB) *~
+
+distclean::
+ rm -f config.h Makefile config.status config.log
+ rm -rf autom4te.cache
+
+$(LIB): $(LIBOBJS)
+ ar cr $@ $(LIBOBJS)
+
+depend:
+ gcc $(CFLAGS) -M `ls *.c` > .depend
+
+-include .depend
diff --git a/utils/open-isns/README b/utils/open-isns/README
new file mode 100644
index 0000000..acff29b
--- /dev/null
+++ b/utils/open-isns/README
@@ -0,0 +1,173 @@
+
+Welcome to Open-iSNS
+====================
+
+This is a partial implementation of iSNS, according to RFC4171.
+The implementation is still somewhat incomplete, but I'm releasing
+it for your reading pleasure.
+
+The distribution comprises
+
+ isnsd
+ This is the iSNS server, supporting persistent storage
+ of registrations in a file based database.
+
+ isnsadm
+ A command line utility for querying the iSNS database,
+ and for registering/deregistering nodes and portals
+
+ isnsdd
+ An iSNS Discovery Daemon, which is still very much work
+ in progress. The daemon is supposed to handle all the
+ bit banging and server communications required to register
+ a node, its portals, and to maintain the registration.
+ It is also supposed to use the iSNS State Change Notification
+ framework to learn of new targets or initiators coming online,
+ and inform local services (such as the iSCSI initiator daemon)
+ about these changes.
+
+Thanks!
+-------
+
+Many thanks to Albert Pauw for his fearless testing of snapshots,
+and his copious feedback!
+
+What works, after a fashion:
+----------------------------
+
+ - For now, I've been focusing on getting the iSCSI part to
+ work. There is some very basic support for FC objects, but
+ this will be hardly useful yet.
+
+ - Registration, deregistration, query, getnext
+ You can use isnsadm to register iSCSI nodes, and portals.
+ isnsadm also illustrates how this is supposed to be used from
+ the client perspective.
+
+ - Discovery domains are supported mostly. The administrator
+ can create discovery domains using isnsadm, and place storage
+ nodes in domains. Queries by clients are scoped by their
+ discovery domains membership, so that they will be unable to
+ see nodes not part of a shared DD.
+
+ Open-iSNS currently does not allow clients to place themselves
+ in a DD.
+
+ Optionally, storage nodes that are not in any discovery domain
+ will be placed in a "default DD" (see the DefaultDiscoveryDomain
+ in isnsd.conf).
+
+ - ESI, supported both by the server and the discovery daemon
+
+ - SCN, supported by the server and the discovery daemon
+
+What is still missing
+---------------------
+
+ - Better documentation (esp. a HOWTO on getting started with iSNS)
+ - DD Sets
+ - Various bits and pieces of the protocol
+ - FC support
+
+
+Building Open-iSNS
+------------------
+
+The Open-iSNS build is now based on autoconf. The distributed tarball
+should include a configure script and a config.h.in file generated
+from configure.ac. If these are missing, you can generate them
+by running
+
+ autoconf
+ autoheader
+
+For most people, it should be sufficient to run configure without any
+arguments, or at most with the option --prefix. If run without --prefix,
+program files, manpages etc will be installed below /usr/local. To have
+everything installed /usr/bin, /usr/share/man etc, run it as
+
+ ./configure --prefix=/usr
+
+Dependencies:
+
+ - If you want to build Open-iSNS with support for authentication,
+ you need the OpenSSL libraries and header files installed.
+
+ The configure script should pick up the presence of these
+ libraries, and enable security support automatically. To disable
+ this explicitly in your build, pass the --without-security option
+ to configure.
+
+ - If you want to build Open-iSNS with SLP support, you need the
+ OpenSLP library and header file installed.
+
+ The configure script should pick up the presence of this library,
+ and enable SLP support automatically. To disable this explicitly
+ in your build, pass the --without-slp option to configure.
+
+When configure is run, it checks for a the presence of a number of
+headers and libraries in your system (the results of most of these checks
+are currently ignored :-). Then, it creates a Makefile and a config.h
+include file. With these in place, you can build the binaries and libraries:
+
+ make
+ make install
+
+Getting started
+---------------
+
+On the iSNS server, you need to generate a server key and install it. The
+simplest way is probably to use the isnssetup script included in the
+source package.
+
+For each client you wish to use, you should then
+
+iSNS Security
+-------------
+
+This implementation of iSNS supports authentication, as descibed in RFC
+4171. In order to use it, you have to create DSA keys for the server and
+all clients.
+
+iSNS uses conceptually the same security mechanism as SLP, and identifies
+principals by a "Security Parameter Index", which is essentially a string
+identifying a key.
+
+Open-iSNS fully supports DSA based security, and offers a flexible
+policy mechanism that ties an SPI to a network entity and the storage
+node names it is allowed to use. For an introduction to the security
+model used by Open-iSNS, refer to the isns_config(5) manual page. An
+overview on setting up the iSNS server for authentication is given in
+the EXAMPLES section of the isnsadm(8) manual page.
+
+Downloading Open-iSNS
+---------------------
+
+Open-iSNS is available for download from
+
+ http://oss.oracle.com/~okir/open-isns
+
+You have to grab the latest tarball and compile it; fancy things such
+as RPMs are not available yet.
+
+------------------------------------------------------------------
+
+
+ COPYRIGHT NOTICE
+
+ Copyright (C) 2007 Olaf Kirch.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
diff --git a/utils/open-isns/TODO b/utils/open-isns/TODO
new file mode 100644
index 0000000..2ddf008
--- /dev/null
+++ b/utils/open-isns/TODO
@@ -0,0 +1,100 @@
+Documentation:
+ - Add HOWTO
+
+isnsd:
+ - When registering a node, use the default EID given in its
+ policy (avoid the isns.control trap)
+ - make PGs children of the iSCSI storage node they're associated
+ with?
+ - Implement missing functions
+
+isnsadm:
+ - support iSNS server discovery through DNS SRV
+ records, and SLP
+
+isnsdd:
+ - support iSNS server discovery through DNS SRV
+ records, and SLP
+ - At startup, query the server for the list of
+ visible nodes/portals
+ - When receiving an SCN, query for the node's
+ portals, authmethod and such, and compare that
+ to what we have cached
+ - At regular intervals, repeat the query for
+ all visible nodes/portals, and do a diff with
+ our shadow DB
+ - At regular intervals, check whether the portals
+ we registered for ESI are seeing the server's
+ ESI messages.
+
+DevAttrReg:
+ - Refuse registration of nodes inside the CONTROL
+ entity, unless it's a control node.
+ - If the client uses REPLACE, is it okay for the
+ entity's index to change?
+ - security: optionally validate the IP addresses
+ a client registers (either against a static policy,
+ or using DNS).
+ - relaxed security model: require privilege
+ for registration of targets; anyone can register
+ an initiator?
+ - Gracefully handle registrations where the client
+ specifies an index attribute, as long as it matches
+ the next_index
+
+DevAttrQuery:
+ - fix --local --query policy-index=iqn.1969-12.brummo
+ and write test case
+ - fix the way we enumerate related objects
+ - ensure DD discovery works (5.6.5.2):
+ DD membership can be discovered through the DevAttrQry message
+ by including either DD member attributes (i.e., DD Member
+ iSCSI Index, DD Member iSCSI Node, DD Member iFCP Node, DD
+ Member Portal Index, DD Member Portal IP Addr, and DD Member
+ Portal TCP/UDP) or the object key of the Storage Node or
+ Portal (i.e., iSCSI Name, iSCSI Index, Portal IP Addr, Portal
+ TCP/UDP Port, and Portal Index) in the Operating Attributes.
+ Using DD member attributes SHALL return both registered and
+ unregistered member Storage Nodes and/or Portals of a DD.
+ DevAttrQry messages using the Storage Node and/or Portal
+ object key SHALL return only member Storage Nodes or Portals
+ that are currently registered in the iSNS database.
+
+DevAttrDereg:
+ - PG Removal code: ignore nodes/portal that are dead
+ - review security
+ - cancel any SCN/ESI callbacks
+
+SCN:
+ - Trigger a mgmt reg SCN when accepting a mgmt registration
+
+SCNEvent:
+ - Implement
+
+ESI:
+ - Right now the way we re-establish ESI state after database
+ reload is awkward.
+
+DDReg:
+ - Write test cases
+
+DDDereg:
+ - Write test cases
+
+DDSReg/DDSDereg:
+ - Implement
+
+Heartbeat:
+ - Implement message send
+ - Implement failover?
+
+Security:
+ - Allow policies without key?
+ - Implement simple default policies linking client IP +
+ hostname (network entity) + storage node names
+
+Renaming
+ - Add isns_ prefix to all visible functions
+
+Socket code:
+ - impose upper limit on the reassembly buffer
diff --git a/utils/open-isns/aclocal/config.guess b/utils/open-isns/aclocal/config.guess
new file mode 100644
index 0000000..6d71f75
--- /dev/null
+++ b/utils/open-isns/aclocal/config.guess
@@ -0,0 +1,1499 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+timestamp='2005-05-27'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ amd64:OpenBSD:*:*)
+ echo x86_64-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ cats:OpenBSD:*:*)
+ echo arm-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ luna88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ sgi:OpenBSD:*:*)
+ echo mips64-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm:riscos:*:*|arm:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ x86:Interix*:[34]*)
+ echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+ exit ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo crisv32-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo frv-unknown-linux-gnu
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && {
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit
+ }
+ test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ *86) UNAME_PROCESSOR=i686 ;;
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NSE-?:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
+and
+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/utils/open-isns/aclocal/config.sub b/utils/open-isns/aclocal/config.sub
new file mode 100644
index 0000000..519f2cd
--- /dev/null
+++ b/utils/open-isns/aclocal/config.sub
@@ -0,0 +1,1570 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+timestamp='2005-05-12'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
+ kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | msp430 \
+ | ns16k | ns32k \
+ | openrisc | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | msp430-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \
+ | xstormy16-* | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16c)
+ basic_machine=cr16c-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ or32 | or32-*)
+ basic_machine=or32-unknown
+ os=-coff
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* | -skyos*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/utils/open-isns/aclocal/install-sh b/utils/open-isns/aclocal/install-sh
new file mode 100644
index 0000000..220abbf
--- /dev/null
+++ b/utils/open-isns/aclocal/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/utils/open-isns/attrs.c b/utils/open-isns/attrs.c
new file mode 100644
index 0000000..12517c1
--- /dev/null
+++ b/utils/open-isns/attrs.c
@@ -0,0 +1,1618 @@
+/*
+ * Handle iSNS attributes and attribute lists
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "util.h"
+#include "vendor.h"
+#include "attrs.h"
+#include "isns.h"
+
+/* Implementation limit - sanity checking */
+#define ISNS_ATTR_MAX_LEN 8192
+
+static void __isns_attr_set_value(isns_attr_t *, const isns_value_t *);
+
+/*
+ * Allocate an attribute
+ */
+isns_attr_t *
+isns_attr_alloc(uint32_t tag, const isns_tag_type_t *tag_type, const isns_value_t *value)
+{
+ isns_attr_t *attr;
+
+ if (tag_type == NULL)
+ tag_type = isns_tag_type_by_id(tag);
+
+ attr = isns_calloc(1, sizeof(*attr));
+ if (!attr)
+ isns_fatal("Out of memory!\n");
+
+ attr->ia_users = 1;
+ attr->ia_tag_id = tag;
+ attr->ia_tag = tag_type;
+
+ __isns_attr_set_value(attr, value);
+ return attr;
+}
+
+isns_attr_t *
+isns_attr_get(isns_attr_t *attr)
+{
+ if (attr) {
+ isns_assert(attr->ia_users);
+ attr->ia_users++;
+ }
+ return attr;
+}
+
+void
+isns_attr_release(isns_attr_t *attr)
+{
+ const isns_attr_type_t *type;
+
+ isns_assert(attr->ia_users);
+ if (--(attr->ia_users))
+ return;
+
+ type = attr->ia_value.iv_type;
+ if (type->it_destroy)
+ type->it_destroy(&attr->ia_value);
+ isns_free(attr);
+}
+
+/*
+ * Assign a value to an attribute
+ */
+void
+__isns_attr_set_value(isns_attr_t *attr, const isns_value_t *new_value)
+{
+ const isns_attr_type_t *type, *old_type;
+ isns_value_t *old_value;
+
+ old_value = &attr->ia_value;
+ if (old_value == new_value)
+ return;
+
+ old_type = old_value->iv_type;
+ if (old_type && old_type->it_destroy)
+ old_type->it_destroy(old_value);
+
+ if (!new_value || !(type = new_value->iv_type))
+ type = attr->ia_tag->it_type;
+
+ /* When assigning the value to the attr, check
+ * whether it needs special attention. */
+ if (new_value) {
+ if (type->it_assign) {
+ type->it_assign(&attr->ia_value, new_value);
+ } else {
+ attr->ia_value = *new_value;
+ }
+ }
+ attr->ia_value.iv_type = type;
+}
+
+/*
+ * Compare two attributes.
+ * Returns non-null when attributes are the same, else 0.
+ */
+int
+isns_attr_match(const isns_attr_t *a, const isns_attr_t *b)
+{
+ const isns_attr_type_t *type;
+
+ if (a->ia_tag_id != b->ia_tag_id)
+ return 0;
+
+ /* NIL acts as a wildcard */
+ if (a->ia_value.iv_type == &isns_attr_type_nil
+ || b->ia_value.iv_type == &isns_attr_type_nil)
+ return 1;
+
+ if (a->ia_value.iv_type != b->ia_value.iv_type)
+ return 0;
+ type = a->ia_value.iv_type;
+
+ if (type->it_match)
+ return type->it_match(&a->ia_value, &b->ia_value);
+
+ return !memcmp(&a->ia_value, &b->ia_value, sizeof(isns_value_t));
+}
+
+/*
+ * Lexicographical comparison of two attributes.
+ * Returns -1 when a is less than b, +1 when a is greater than
+ * b, and 0 if equal.
+ */
+int
+isns_attr_compare(const isns_attr_t *a, const isns_attr_t *b)
+{
+ const isns_attr_type_t *type = a->ia_value.iv_type;
+
+ isns_assert(a->ia_tag_id == b->ia_tag_id);
+
+ if (type != b->ia_value.iv_type) {
+ /* One of them must be NIL */
+ if (type == &isns_attr_type_nil)
+ return -1;
+ return 1;
+ }
+
+ /* If both are NIL, consider them equal */
+ if (type == &isns_attr_type_nil)
+ return 0;
+
+ /* A few types need special comparison functions, but
+ * most don't. The reason is, we don't care whether the
+ * ordering this creates is the "canonical" ordering for
+ * this type, eg for integers. All that matters is that
+ * there is some consistent ordering suitable for
+ * DevGetNext.
+ */
+ if (type->it_compare)
+ return type->it_compare(&a->ia_value, &b->ia_value);
+
+ return memcmp(&a->ia_value, &b->ia_value, sizeof(isns_value_t));
+}
+
+/*
+ * Convert a string to an attribute
+ */
+isns_attr_t *
+isns_attr_from_string(uint32_t tag, const char *string)
+{
+ const isns_tag_type_t *tag_type;
+ int (*parse)(isns_value_t *, const char *);
+ isns_value_t value;
+
+ memset(&value, 0, sizeof(value));
+
+ tag_type = isns_tag_type_by_id(tag);
+ if (!tag_type)
+ return NULL;
+
+ parse = tag_type->it_parse;
+ if (parse == NULL)
+ parse = tag_type->it_type->it_parse;
+
+ if (!parse || !parse(&value, string))
+ return NULL;
+
+ return isns_attr_alloc(tag, tag_type, &value);
+}
+
+/*
+ * Initialize an attribute list.
+ */
+void
+isns_attr_list_init(isns_attr_list_t *list)
+{
+ memset(list, 0, sizeof(*list));
+}
+
+static inline void
+__isns_attr_list_resize(isns_attr_list_t *list, unsigned int count)
+{
+ unsigned int max;
+
+ max = (list->ial_count + 15) & ~15;
+ if (count < max)
+ return;
+
+ count = (count + 15) & ~15;
+ list->ial_data = isns_realloc(list->ial_data, count * sizeof(isns_attr_t *));
+ if (!list->ial_data)
+ isns_fatal("Out of memory!\n");
+}
+
+void
+isns_attr_list_append_list(isns_attr_list_t *dst,
+ const isns_attr_list_t *src)
+{
+ unsigned int i, j;
+
+ __isns_attr_list_resize(dst, dst->ial_count + src->ial_count);
+ j = dst->ial_count;
+ for (i = 0; i < src->ial_count; ++i, ++j) {
+ isns_attr_t *attr = src->ial_data[i];
+
+ dst->ial_data[j] = attr;
+ attr->ia_users++;
+ }
+ dst->ial_count = j;
+}
+
+void
+isns_attr_list_copy(isns_attr_list_t *dst,
+ const isns_attr_list_t *src)
+{
+ isns_attr_list_destroy(dst);
+ isns_attr_list_append_list(dst, src);
+}
+
+void
+isns_attr_list_destroy(isns_attr_list_t *list)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->ial_count; ++i) {
+ isns_attr_t *attr = list->ial_data[i];
+
+ isns_attr_release(attr);
+ }
+
+ if (list->ial_data)
+ isns_free(list->ial_data);
+ memset(list, 0, sizeof(*list));
+}
+
+int
+isns_attr_list_remove_tag(isns_attr_list_t *list, uint32_t tag)
+{
+ unsigned int i = 0, j = 0, removed = 0;
+
+ for (i = 0; i < list->ial_count; ++i) {
+ isns_attr_t *attr = list->ial_data[i];
+
+ if (attr->ia_tag_id == tag) {
+ isns_attr_release(attr);
+ removed++;
+ } else {
+ list->ial_data[j++] = attr;
+ }
+ }
+ list->ial_count = j;
+ return removed;
+}
+
+/*
+ * Locate the given attribute in the list, remove it
+ * and any following attributes that have a tag from the
+ * @subordinate_tags list. This is used by the DDDereg
+ * code to remove DD members.
+ */
+int
+isns_attr_list_remove_member(isns_attr_list_t *list,
+ const isns_attr_t *match,
+ const uint32_t *subordinate_tags)
+{
+ unsigned int i = 0, j = 0, k, removed = 0, purging = 0;
+
+ while (i < list->ial_count) {
+ isns_attr_t *attr = list->ial_data[i++];
+
+ if (purging && subordinate_tags) {
+ for (k = 0; subordinate_tags[k]; ++k) {
+ if (attr->ia_tag_id == subordinate_tags[k])
+ goto purge_attr;
+ }
+ }
+ purging = 0;
+
+ if (!isns_attr_match(attr, match)) {
+ list->ial_data[j++] = attr;
+ continue;
+ }
+
+purge_attr:
+ isns_attr_release(attr);
+ purging = 1;
+ removed++;
+ }
+ list->ial_count = j;
+ return removed;
+}
+
+/*
+ * Find the first attribute with the given tag
+ */
+static inline isns_attr_t *
+__isns_attr_list_find(const isns_attr_list_t *list, uint32_t tag)
+{
+ isns_attr_t *attr;
+ unsigned int i;
+
+ for (i = 0; i < list->ial_count; ++i) {
+ attr = list->ial_data[i];
+
+ if (attr->ia_tag_id == tag)
+ return attr;
+ }
+
+ return NULL;
+}
+
+/*
+ * Add a new attribute at the end of the list
+ */
+static inline void
+__isns_attr_list_append_attr(isns_attr_list_t *list, isns_attr_t *attr)
+{
+ __isns_attr_list_resize(list, list->ial_count + 1);
+ list->ial_data[list->ial_count++] = attr;
+}
+
+void
+isns_attr_list_append_attr(isns_attr_list_t *list, isns_attr_t *attr)
+{
+ attr->ia_users++;
+ __isns_attr_list_append_attr(list, attr);
+}
+
+/*
+ * Append an element to an attribute list
+ */
+static void
+__isns_attr_list_append(isns_attr_list_t *list,
+ uint32_t tag, const isns_tag_type_t *tag_type,
+ const isns_value_t *value)
+{
+ isns_attr_t *attr;
+
+ if (tag_type == NULL)
+ tag_type = isns_tag_type_by_id(tag);
+ if (value->iv_type != &isns_attr_type_nil
+ && value->iv_type != tag_type->it_type) {
+ isns_warning("Using wrong type (%s) "
+ "when encoding attribute %04x (%s) - should be %s\n",
+ value->iv_type->it_name,
+ tag, tag_type->it_name,
+ tag_type->it_type->it_name);
+ }
+
+ attr = isns_attr_alloc(tag, tag_type, value);
+ __isns_attr_list_append_attr(list, attr);
+}
+
+/*
+ * Update an element to an attribute list
+ */
+static void
+__isns_attr_list_update(isns_attr_list_t *list,
+ uint32_t tag, const isns_tag_type_t *tag_type,
+ const isns_value_t *value)
+{
+ const isns_attr_type_t *type = value->iv_type;
+ isns_attr_t *attr;
+
+ if (tag_type == NULL)
+ tag_type = isns_tag_type_by_id(tag);
+ if (type != &isns_attr_type_nil
+ && type != tag_type->it_type) {
+ isns_warning("Using wrong type (%s) "
+ "when encoding attribute %04x (%s) - should be %s\n",
+ type->it_name,
+ tag, tag_type->it_name,
+ tag_type->it_type->it_name);
+ }
+
+ if (tag_type->it_multiple
+ || (attr = __isns_attr_list_find(list, tag)) == NULL) {
+ attr = isns_attr_alloc(tag, tag_type, NULL);
+ __isns_attr_list_append_attr(list, attr);
+ }
+
+ __isns_attr_set_value(attr, value);
+}
+
+/*
+ * Append an element to an attribute list - public interface
+ */
+void
+isns_attr_list_append_value(isns_attr_list_t *list,
+ uint32_t tag, const isns_tag_type_t *tag_type,
+ const isns_value_t *value)
+{
+ __isns_attr_list_append(list, tag, tag_type, value);
+}
+
+/*
+ * Update an element of an attribute list - public interface
+ */
+void
+isns_attr_list_update_value(isns_attr_list_t *list,
+ uint32_t tag, const isns_tag_type_t *tag_type,
+ const isns_value_t *value)
+{
+ __isns_attr_list_update(list, tag, tag_type, value);
+}
+
+void
+isns_attr_list_update_attr(isns_attr_list_t *list,
+ const isns_attr_t *attr)
+{
+ __isns_attr_list_update(list, attr->ia_tag_id,
+ attr->ia_tag, &attr->ia_value);
+}
+
+/*
+ * Replace an attribute on a list
+ */
+int
+isns_attr_list_replace_attr(isns_attr_list_t *list,
+ isns_attr_t *attr)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->ial_count; ++i) {
+ isns_attr_t *other = list->ial_data[i];
+
+ if (other->ia_tag_id == attr->ia_tag_id) {
+ list->ial_data[i] = attr;
+ attr->ia_users++;
+ isns_attr_release(other);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Retrieve an element of an attribute list
+ */
+int
+isns_attr_list_get_attr(const isns_attr_list_t *list,
+ uint32_t tag, isns_attr_t **result)
+{
+ *result = __isns_attr_list_find(list, tag);
+ return *result != NULL;
+}
+
+int
+isns_attr_list_get_value(const isns_attr_list_t *list,
+ uint32_t tag, isns_value_t *value)
+{
+ isns_attr_t *attr;
+
+ if (!(attr = __isns_attr_list_find(list, tag)))
+ return 0;
+
+ *value = attr->ia_value;
+ return 1;
+}
+
+int
+isns_attr_list_get_uint32(const isns_attr_list_t *list,
+ uint32_t tag, uint32_t *value)
+{
+ isns_attr_t *attr;
+
+ if (!(attr = __isns_attr_list_find(list, tag))
+ || !ISNS_ATTR_IS_UINT32(attr))
+ return 0;
+
+ *value = attr->ia_value.iv_uint32;
+ return 1;
+}
+
+int
+isns_attr_list_get_ipaddr(const isns_attr_list_t *list,
+ uint32_t tag, struct in6_addr *value)
+{
+ isns_attr_t *attr;
+
+ if (!(attr = __isns_attr_list_find(list, tag))
+ || !ISNS_ATTR_IS_IPADDR(attr))
+ return 0;
+
+ *value = attr->ia_value.iv_ipaddr;
+ return 1;
+}
+
+int
+isns_attr_list_get_string(const isns_attr_list_t *list,
+ uint32_t tag, const char **value)
+{
+ isns_attr_t *attr;
+
+ if (!(attr = __isns_attr_list_find(list, tag))
+ || !ISNS_ATTR_IS_STRING(attr))
+ return 0;
+
+ *value = attr->ia_value.iv_string;
+ return 1;
+}
+
+int
+isns_attr_list_contains(const isns_attr_list_t *list,
+ uint32_t tag)
+{
+ return __isns_attr_list_find(list, tag) != NULL;
+}
+
+/*
+ * Some attribute types have an implied ordering,
+ * which is needed for GetNext. This is used to
+ * compare two lists.
+ */
+
+/*
+ * Typed versions of isns_attr_list_append
+ */
+void
+isns_attr_list_append_nil(isns_attr_list_t *list, uint32_t tag)
+{
+ isns_value_t var = ISNS_VALUE_INIT(nil, 0);
+
+ __isns_attr_list_append(list, tag, NULL, &var);
+}
+
+void
+isns_attr_list_append_string(isns_attr_list_t *list,
+ uint32_t tag, const char *value)
+{
+ isns_value_t var = ISNS_VALUE_INIT(string, (char *) value);
+
+ __isns_attr_list_append(list, tag, NULL, &var);
+}
+
+void
+isns_attr_list_append_uint32(isns_attr_list_t *list,
+ uint32_t tag, uint32_t value)
+{
+ isns_value_t var = ISNS_VALUE_INIT(uint32, value);
+
+ __isns_attr_list_append(list, tag, NULL, &var);
+}
+
+void
+isns_attr_list_append_int32(isns_attr_list_t *list,
+ uint32_t tag, int32_t value)
+{
+ isns_value_t var = ISNS_VALUE_INIT(int32, value);
+
+ __isns_attr_list_append(list, tag, NULL, &var);
+}
+
+void
+isns_attr_list_append_uint64(isns_attr_list_t *list,
+ uint32_t tag, int64_t value)
+{
+ isns_value_t var = ISNS_VALUE_INIT(uint64, value);
+
+ __isns_attr_list_append(list, tag, NULL, &var);
+}
+
+void
+isns_attr_list_append_ipaddr(isns_attr_list_t *list,
+ uint32_t tag, const struct in6_addr *value)
+{
+ isns_value_t var = ISNS_VALUE_INIT(ipaddr, *value);
+
+ __isns_attr_list_append(list, tag, NULL, &var);
+}
+
+/*
+ * Untyped version of isns_attr_list_append and isns_attr_list_update.
+ * The caller must make sure that the type of @data matches the tag's type.
+ */
+int
+isns_attr_list_append(isns_attr_list_t *list, uint32_t tag, const void *data)
+{
+ const isns_tag_type_t *tag_type;
+ isns_value_t var;
+
+ if (!(tag_type = isns_tag_type_by_id(tag)))
+ return 0;
+
+ var.iv_type = tag_type->it_type;
+ if (!var.iv_type->it_set(&var, data))
+ return 0;
+
+ __isns_attr_list_append(list, tag, tag_type, &var);
+ return 1;
+}
+
+int
+isns_attr_list_update(isns_attr_list_t *list, uint32_t tag, const void *data)
+{
+ const isns_tag_type_t *tag_type;
+ isns_attr_type_t *type;
+ isns_value_t var;
+
+ if (!(tag_type = isns_tag_type_by_id(tag)))
+ return 0;
+
+ type = tag_type->it_type;
+ var.iv_type = type;
+ if (!type->it_set(&var, data))
+ return 0;
+
+ __isns_attr_list_update(list, tag, tag_type, &var);
+ return 1;
+}
+
+/*
+ * Validate the attribute list.
+ */
+int
+isns_attr_validate(const isns_attr_t *attr,
+ const isns_policy_t *policy)
+{
+ const isns_tag_type_t *tag_type;
+
+ tag_type = attr->ia_tag;
+ if (tag_type->it_validate == NULL)
+ return 1;
+ return tag_type->it_validate(&attr->ia_value, policy);
+}
+
+int
+isns_attr_list_validate(const isns_attr_list_t *list,
+ const isns_policy_t *policy,
+ unsigned int function)
+{
+ DECLARE_BITMAP(seen, __ISNS_TAG_MAX);
+ unsigned int i;
+
+ for (i = 0; i < list->ial_count; ++i) {
+ const isns_tag_type_t *tag_type;
+ isns_attr_t *attr = list->ial_data[i];
+ uint32_t tag = attr->ia_tag_id;
+ unsigned int bit;
+
+ if (attr == NULL)
+ return ISNS_INTERNAL_ERROR;
+
+ tag_type = attr->ia_tag;
+ if (tag_type == NULL)
+ return ISNS_INTERNAL_ERROR;
+
+ bit = tag;
+ if (OPENISNS_IS_PRIVATE_ATTR(tag))
+ bit -= OPENISNS_VENDOR_PREFIX;
+ if (bit >= __ISNS_TAG_MAX)
+ goto invalid;
+
+ if (attr->ia_value.iv_type == &isns_attr_type_nil) {
+ if (test_bit(seen, bit))
+ goto invalid;
+ } else
+ if (attr->ia_value.iv_type == tag_type->it_type) {
+ if (!tag_type->it_multiple && test_bit(seen, bit))
+ goto invalid;
+
+ if (!isns_attr_validate(attr, policy))
+ goto invalid;
+ } else {
+ return ISNS_INTERNAL_ERROR;
+ }
+
+ if (function == ISNS_DEVICE_ATTRIBUTE_REGISTER
+ && tag_type->it_readonly)
+ goto invalid;
+
+ set_bit(seen, bit);
+ }
+
+ return ISNS_SUCCESS;
+
+invalid:
+ switch (function) {
+ case ISNS_DEVICE_ATTRIBUTE_REGISTER:
+ return ISNS_INVALID_REGISTRATION;
+
+ case ISNS_DEVICE_DEREGISTER:
+ return ISNS_INVALID_DEREGISTRATION;
+
+ case ISNS_DEVICE_ATTRIBUTE_QUERY:
+ case ISNS_DEVICE_GET_NEXT:
+ return ISNS_INVALID_QUERY;
+ }
+ return ISNS_ATTRIBUTE_NOT_IMPLEMENTED;
+}
+
+/*
+ * Debug helper: print attribute list
+ */
+void
+isns_attr_list_print(const isns_attr_list_t *list, isns_print_fn_t *fn)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->ial_count; ++i)
+ isns_attr_print(list->ial_data[i], fn);
+}
+
+char *
+isns_attr_print_value(const isns_attr_t *attr, char *buffer, size_t size)
+{
+ const isns_tag_type_t *tag_type = attr->ia_tag;
+ const isns_attr_type_t *type = attr->ia_value.iv_type;
+
+ if (tag_type->it_print && type == tag_type->it_type)
+ tag_type->it_print(&attr->ia_value, buffer, size);
+ else
+ type->it_print(&attr->ia_value, buffer, size);
+ return buffer;
+}
+
+void
+isns_attr_print(const isns_attr_t *attr, isns_print_fn_t *fn)
+{
+ const isns_tag_type_t *tag_type = attr->ia_tag;
+ const isns_attr_type_t *type = attr->ia_value.iv_type;
+ uint32_t tag;
+ char value[512], *vspec = "";
+
+ tag = attr->ia_tag_id;
+ if (OPENISNS_IS_PRIVATE_ATTR(tag)) {
+ tag -= OPENISNS_VENDOR_PREFIX;
+ vspec = "v";
+ }
+
+ fn(" %04x%1s %-12s: %s = %s\n",
+ tag, vspec,
+ type->it_name,
+ tag_type? tag_type->it_name : "Unknown Attribute",
+ isns_attr_print_value(attr, value, sizeof(value)));
+}
+
+/*
+ * TLV encode a single attribute
+ */
+int
+isns_attr_encode(buf_t *bp, const isns_attr_t *attr)
+{
+ const isns_value_t *value = &attr->ia_value;
+ const isns_attr_type_t *type = value->iv_type;
+
+ if (!buf_put32(bp, attr->ia_tag_id)
+ || !type->it_encode(bp, value))
+ return ISNS_INTERNAL_ERROR;
+
+ return ISNS_SUCCESS;
+}
+
+/*
+ * TLV decode a single attribute
+ */
+int
+isns_attr_decode(buf_t *bp, isns_attr_t **result)
+{
+ isns_attr_t *attr = NULL;
+ isns_value_t *value;
+ uint32_t tag, len;
+
+ if (!buf_get32(bp, &tag)
+ || !buf_get32(bp, &len))
+ goto msg_fmt_error;
+
+ /* Attributes MUST be word aligned */
+ if (len & 3)
+ goto msg_fmt_error;
+
+ if (len > ISNS_ATTR_MAX_LEN)
+ goto msg_fmt_error;
+
+ /* Allocate the attribute */
+ attr = isns_attr_alloc(tag, NULL, NULL);
+
+ value = &attr->ia_value;
+ if (len == 0)
+ value->iv_type = &isns_attr_type_nil;
+
+ if (!value->iv_type->it_decode(bp, len, value))
+ goto msg_fmt_error;
+
+ *result = attr;
+ return ISNS_SUCCESS;
+
+msg_fmt_error:
+ isns_error("Error decoding attribute, tag=0x%04x, len=%u\n",
+ tag, len);
+ if (attr)
+ isns_attr_release(attr);
+ return ISNS_MESSAGE_FORMAT_ERROR;
+}
+
+
+/*
+ * Decode the list of TLV encoded attributes inside an
+ * iSNS message.
+ */
+static int
+__isns_attr_list_decode(buf_t *bp, isns_attr_list_t *list, int delimited)
+{
+ int status;
+
+ while (buf_avail(bp)) {
+ isns_attr_t *attr;
+
+ status = isns_attr_decode(bp, &attr);
+ if (status != ISNS_SUCCESS)
+ return status;
+
+ if (delimited && attr->ia_tag_id == ISNS_TAG_DELIMITER) {
+ isns_attr_release(attr);
+ break;
+ }
+
+ __isns_attr_list_append_attr(list, attr);
+ }
+
+ return ISNS_SUCCESS;
+}
+
+int
+isns_attr_list_decode(buf_t *bp, isns_attr_list_t *list)
+{
+ return __isns_attr_list_decode(bp, list, 0);
+}
+
+int
+isns_attr_list_decode_delimited(buf_t *bp, isns_attr_list_t *list)
+{
+ return __isns_attr_list_decode(bp, list, 1);
+}
+
+/*
+ * Remove all attributes from a list save those matching
+ * the given tags.
+ */
+void
+isns_attr_list_prune(isns_attr_list_t *list,
+ const uint32_t *tags, unsigned int num_tags)
+{
+ unsigned int i, j, k;
+
+ for (i = j = 0; i < list->ial_count; ++i) {
+ isns_attr_t *attr = list->ial_data[i];
+
+ for (k = 0; k < num_tags; ++k) {
+ if (attr->ia_tag_id == tags[k]) {
+ list->ial_data[j++] = attr;
+ goto next;
+ }
+ }
+
+ isns_attr_release(attr);
+
+next: ;
+ }
+
+ list->ial_count = j;
+}
+
+/*
+ * TLV ecode the list of attributes to go with
+ * iSNS message.
+ */
+int
+isns_attr_list_encode(buf_t *bp, const isns_attr_list_t *list)
+{
+ unsigned int i, status = ISNS_SUCCESS;
+
+ for (i = 0; i < list->ial_count; ++i) {
+ struct isns_attr *attr = list->ial_data[i];
+
+ status = isns_attr_encode(bp, attr);
+ if (status)
+ break;
+ }
+ return status;
+}
+
+/*
+ * Encode the delimiter attribute
+ */
+int
+isns_encode_delimiter(buf_t *bp)
+{
+ uint32_t tag = 0, len = 0;
+
+ if (!buf_put32(bp, tag)
+ || !buf_put32(bp, len))
+ return ISNS_INTERNAL_ERROR;
+
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Padded encoding
+ */
+static inline int
+isns_encode_padded(buf_t *bp, const void *ptr, size_t len)
+{
+ if (!buf_put(bp, ptr, len))
+ return 0;
+
+ if ((len & 3) == 0)
+ return 1;
+
+ return buf_put(bp, "\0\0\0", 4 - (len & 3));
+}
+
+/*
+ * Helper functions to deal with portal information
+ */
+void
+isns_portal_init(isns_portal_info_t *portal,
+ const struct sockaddr *saddr, int proto)
+{
+ const struct sockaddr_in *sin;
+
+ memset(portal, 0, sizeof(*portal));
+ switch (saddr->sa_family) {
+ case AF_INET6:
+ portal->addr = *(const struct sockaddr_in6 *) saddr;
+ break;
+
+ case AF_INET:
+ sin = (const struct sockaddr_in *) saddr;
+ portal->addr.sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
+ portal->addr.sin6_port = sin->sin_port;
+ portal->addr.sin6_family = AF_INET6;
+ break;
+ default:
+ isns_warning("Unknown address family in isns_portal_init\n");
+ return;
+ }
+
+ portal->proto = proto;
+}
+
+int
+isns_portal_from_attr_list(isns_portal_info_t *portal,
+ uint32_t addr_tag, uint32_t port_tag,
+ const isns_attr_list_t *list)
+{
+ const isns_attr_t *addr_attr = NULL, *port_attr = NULL;
+ unsigned int i;
+
+ for (i = 0; i + 1 < list->ial_count; ++i) {
+ const isns_attr_t *attr = list->ial_data[i];
+
+ if (!ISNS_ATTR_IS_IPADDR(attr))
+ continue;
+ if (addr_tag && attr->ia_tag_id != addr_tag)
+ continue;
+ addr_attr = attr;
+ if (port_tag == 0) {
+ port_attr = list->ial_data[i + 1];
+ goto extract_portal;
+ }
+ break;
+ }
+
+ /* We have a specific port tag. */
+ while (++i < list->ial_count) {
+ const isns_attr_t *attr = list->ial_data[i];
+
+ if (attr->ia_tag_id == port_tag) {
+ port_attr = attr;
+ goto extract_portal;
+ }
+ }
+
+ return 0;
+
+extract_portal:
+ return isns_portal_from_attr_pair(portal,
+ addr_attr, port_attr);
+}
+
+int
+isns_portal_from_attr_pair(isns_portal_info_t *portal,
+ const isns_attr_t *addr_attr,
+ const isns_attr_t *port_attr)
+{
+ uint32_t portspec;
+
+ memset(portal, 0, sizeof(*portal));
+ portal->addr.sin6_family = AF_INET6;
+
+ if (!ISNS_ATTR_IS_IPADDR(addr_attr)
+ || !ISNS_ATTR_IS_UINT32(port_attr))
+ return 0;
+
+ portal->addr.sin6_addr = addr_attr->ia_value.iv_ipaddr;
+
+ portspec = port_attr->ia_value.iv_uint32;
+ portal->addr.sin6_port = htons(portspec & 0xffff);
+ portal->proto = (portspec & ISNS_PORTAL_PORT_UDP_MASK)? IPPROTO_UDP : IPPROTO_TCP;
+
+ return 1;
+}
+
+int
+isns_portal_to_attr_list(const isns_portal_info_t *portal,
+ uint32_t addr_tag, uint32_t port_tag,
+ isns_attr_list_t *list)
+{
+ uint32_t portspec;
+
+ portspec = htons(portal->addr.sin6_port);
+ if (portal->proto == IPPROTO_UDP)
+ portspec |= ISNS_PORTAL_PORT_UDP_MASK;
+
+ {
+ isns_value_t addr_value = ISNS_VALUE_INIT(ipaddr, portal->addr.sin6_addr);
+ isns_value_t port_value = ISNS_VALUE_INIT(uint32, portspec);
+
+ isns_attr_list_update_value(list, addr_tag, NULL, &addr_value);
+ isns_attr_list_update_value(list, port_tag, NULL, &port_value);
+ }
+
+ return 1;
+}
+
+const char *
+isns_portal_string(const isns_portal_info_t *portal)
+{
+ const struct sockaddr_in6 *six = &portal->addr;
+ static char buffer[128];
+ char abuf[128];
+
+ inet_ntop(six->sin6_family, &six->sin6_addr, abuf, sizeof(abuf));
+ snprintf(buffer, sizeof(buffer), "[%s]:%d/%s",
+ abuf, ntohs(six->sin6_port),
+ (portal->proto == IPPROTO_UDP)? "udp" : "tcp");
+ return buffer;
+}
+
+int
+isns_portal_is_wildcard(const isns_portal_info_t *portal)
+{
+ return !memcmp(&portal->addr.sin6_addr,
+ &in6addr_any,
+ sizeof(struct in6_addr));
+}
+
+int
+isns_portal_equal(const isns_portal_info_t *a,
+ const isns_portal_info_t *b)
+{
+ if (a->proto != b->proto)
+ return 0;
+ return !memcmp(&a->addr, &b->addr, sizeof(a->addr));
+}
+
+uint32_t
+isns_portal_tcpudp_port(const isns_portal_info_t *portal)
+{
+ uint32_t port;
+
+ port = isns_addr_get_port((const struct sockaddr *) &portal->addr);
+ if (portal->proto == IPPROTO_UDP)
+ port |= ISNS_PORTAL_PORT_UDP_MASK;
+ return port;
+}
+
+int
+isns_portal_parse(isns_portal_info_t *portal,
+ const char *spec,
+ const char *default_port)
+{
+ struct sockaddr_storage addr;
+ char *copy, *psp;
+ int alen, proto = IPPROTO_TCP, sock_type = SOCK_STREAM;
+
+ if (spec[0] == '/') {
+ isns_warning("%s: no AF_LOCAL addresses for portals!\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ /* Look at trailing /tcp or /udp */
+ copy = isns_strdup(spec);
+ if ((psp = strrchr(copy, '/')) != NULL) {
+ if (!strcasecmp(psp, "/udp")) {
+ sock_type = SOCK_DGRAM;
+ proto = IPPROTO_UDP;
+ *psp = '\0';
+ } else
+ if (!strcasecmp(psp, "/tcp")) {
+ sock_type = SOCK_STREAM;
+ proto = IPPROTO_TCP;
+ *psp = '\0';
+ }
+ }
+
+ alen = isns_get_address(&addr, copy, default_port, 0, sock_type, 0);
+ isns_free(copy);
+
+ if (alen < 0)
+ return 0;
+
+ isns_portal_init(portal, (struct sockaddr *) &addr, proto);
+ return 1;
+}
+
+/*
+ * Attribute type NIL
+ */
+static int
+isns_attr_type_nil_encode(buf_t *bp, const isns_value_t *value)
+{
+ return buf_put32(bp, 0);
+}
+
+static int
+isns_attr_type_nil_decode(buf_t *bp, size_t len, isns_value_t *value)
+{
+ return len == 0;
+}
+
+static void
+isns_attr_type_nil_print(const isns_value_t *value, char *buf, size_t size)
+{
+ snprintf(buf, size, "<empty>");
+}
+
+static int
+isns_attr_type_nil_parse(isns_value_t *value, const char *string)
+{
+ if (string && *string)
+ return 0;
+ return 1;
+}
+
+isns_attr_type_t isns_attr_type_nil = {
+ .it_id = ISNS_ATTR_TYPE_NIL,
+ .it_name = "nil",
+ .it_encode = isns_attr_type_nil_encode,
+ .it_decode = isns_attr_type_nil_decode,
+ .it_print = isns_attr_type_nil_print,
+ .it_parse = isns_attr_type_nil_parse,
+};
+
+/*
+ * Attribute type UINT32
+ */
+static int
+isns_attr_type_uint32_encode(buf_t *bp, const isns_value_t *value)
+{
+ return buf_put32(bp, 4) && buf_put32(bp, value->iv_uint32);
+}
+
+static int
+isns_attr_type_uint32_decode(buf_t *bp, size_t len, isns_value_t *value)
+{
+ if (len != 4)
+ return 0;
+ return buf_get32(bp, &value->iv_uint32);
+}
+
+static void
+isns_attr_type_uint32_print(const isns_value_t *value, char *buf, size_t size)
+{
+ snprintf(buf, size, "%u", value->iv_uint32);
+}
+
+static int
+isns_attr_type_uint32_parse(isns_value_t *value, const char *string)
+{
+ char *end;
+
+ value->iv_uint32 = strtoul(string, &end, 0);
+ return *end == '\0';
+}
+
+static void
+isns_attr_type_int32_print(const isns_value_t *value, char *buf, size_t size)
+{
+ snprintf(buf, size, "%d", value->iv_uint32);
+}
+
+static int
+isns_attr_type_int32_parse(isns_value_t *value, const char *string)
+{
+ char *end;
+
+ value->iv_int32 = strtol(string, &end, 0);
+ return *end == '\0';
+}
+
+isns_attr_type_t isns_attr_type_uint32 = {
+ .it_id = ISNS_ATTR_TYPE_UINT32,
+ .it_name = "uint32",
+ .it_encode = isns_attr_type_uint32_encode,
+ .it_decode = isns_attr_type_uint32_decode,
+ .it_print = isns_attr_type_uint32_print,
+ .it_parse = isns_attr_type_uint32_parse,
+};
+
+isns_attr_type_t isns_attr_type_int32 = {
+ .it_id = ISNS_ATTR_TYPE_INT32,
+ .it_name = "int32",
+ .it_encode = isns_attr_type_uint32_encode,
+ .it_decode = isns_attr_type_uint32_decode,
+ .it_print = isns_attr_type_int32_print,
+ .it_parse = isns_attr_type_int32_parse,
+};
+
+/*
+ * 16bit min/max
+ */
+static int
+isns_attr_type_range16_encode(buf_t *bp, const isns_value_t *value)
+{
+ uint32_t word;
+
+ word = (value->iv_range.max << 16) | value->iv_range.min;
+ return buf_put32(bp, 4) && buf_put32(bp, word);
+}
+
+static int
+isns_attr_type_range16_decode(buf_t *bp, size_t len, isns_value_t *value)
+{
+ uint32_t word;
+
+ if (len != 4)
+ return 0;
+ if (!buf_get32(bp, &word))
+ return 0;
+ value->iv_range.max = word >> 16;
+ value->iv_range.min = word & 0xFFFF;
+ return 1;
+}
+
+static void
+isns_attr_type_range16_print(const isns_value_t *value, char *buf, size_t size)
+{
+ snprintf(buf, size, "[%u, %u]", value->iv_range.min, value->iv_range.max);
+}
+
+isns_attr_type_t isns_attr_type_range16 = {
+ .it_id = ISNS_ATTR_TYPE_RANGE16,
+ .it_name = "range16",
+ .it_encode = isns_attr_type_range16_encode,
+ .it_decode = isns_attr_type_range16_decode,
+ .it_print = isns_attr_type_range16_print,
+// .it_parse = isns_attr_type_range16_parse,
+};
+
+
+/*
+ * 64bit integers
+ */
+static int
+isns_attr_type_uint64_encode(buf_t *bp, const isns_value_t *value)
+{
+ return buf_put32(bp, 8) && buf_put64(bp, value->iv_uint64);
+}
+
+static int
+isns_attr_type_uint64_decode(buf_t *bp, size_t len, isns_value_t *value)
+{
+ if (len != 8)
+ return 0;
+ return buf_get64(bp, &value->iv_uint64);
+}
+
+static void
+isns_attr_type_uint64_print(const isns_value_t *value, char *buf, size_t size)
+{
+ snprintf(buf, size, "%Lu", (unsigned long long) value->iv_uint64);
+}
+
+static int
+isns_attr_type_uint64_parse(isns_value_t *value, const char *string)
+{
+ char *end;
+
+ value->iv_uint64 = strtoull(string, &end, 0);
+ return *end == '\0';
+}
+
+isns_attr_type_t isns_attr_type_uint64 = {
+ .it_id = ISNS_ATTR_TYPE_UINT64,
+ .it_name = "uint64",
+ .it_encode = isns_attr_type_uint64_encode,
+ .it_decode = isns_attr_type_uint64_decode,
+ .it_print = isns_attr_type_uint64_print,
+ .it_parse = isns_attr_type_uint64_parse,
+};
+
+/*
+ * Attribute type STRING
+ */
+static void
+isns_attr_type_string_destroy(isns_value_t *value)
+{
+ isns_free(value->iv_string);
+ value->iv_string = NULL;
+}
+
+static int
+isns_attr_type_string_match(const isns_value_t *a, const isns_value_t *b)
+{
+ if (a->iv_string && b->iv_string)
+ return !strcmp(a->iv_string, b->iv_string);
+
+ return a->iv_string == b->iv_string;
+}
+
+static int
+isns_attr_type_string_compare(const isns_value_t *a, const isns_value_t *b)
+{
+ if (a->iv_string && b->iv_string)
+ return strcmp(a->iv_string, b->iv_string);
+
+ return a->iv_string? 1 : -1;
+}
+
+static int
+isns_attr_type_string_encode(buf_t *bp, const isns_value_t *value)
+{
+ uint32_t len;
+
+ len = value->iv_string? strlen(value->iv_string) + 1 : 0;
+
+ if (!buf_put32(bp, ISNS_PAD(len)))
+ return 0;
+
+ if (len && !isns_encode_padded(bp, value->iv_string, len))
+ return 0;
+
+ return 1;
+}
+
+static int
+isns_attr_type_string_decode(buf_t *bp, size_t len, isns_value_t *value)
+{
+ /* Is this legal? */
+ if (len == 0)
+ return 1;
+
+ /* The string should be NUL terminated, but
+ * better be safe than sorry. */
+ value->iv_string = isns_malloc(len + 1);
+ if (!buf_get(bp, value->iv_string, len)) {
+ isns_free(value->iv_string);
+ return 0;
+ }
+ value->iv_string[len] = '\0';
+ return 1;
+}
+
+static void
+isns_attr_type_string_print(const isns_value_t *value, char *buf, size_t size)
+{
+ if (!value->iv_string)
+ snprintf(buf, size, "(empty)");
+ else
+ snprintf(buf, size, "\"%s\"", value->iv_string);
+}
+
+static int
+isns_attr_type_string_parse(isns_value_t *value, const char *string)
+{
+ value->iv_string = isns_strdup(string);
+ return 1;
+}
+
+static void
+isns_attr_type_string_assign(isns_value_t *value, const isns_value_t *new_value)
+{
+ isns_assert(!value->iv_string);
+ if (new_value->iv_string)
+ value->iv_string = isns_strdup(new_value->iv_string);
+}
+
+isns_attr_type_t isns_attr_type_string = {
+ .it_id = ISNS_ATTR_TYPE_STRING,
+ .it_name = "string",
+ .it_assign = isns_attr_type_string_assign,
+ .it_destroy = isns_attr_type_string_destroy,
+ .it_match = isns_attr_type_string_match,
+ .it_compare = isns_attr_type_string_compare,
+ .it_encode = isns_attr_type_string_encode,
+ .it_decode = isns_attr_type_string_decode,
+ .it_print = isns_attr_type_string_print,
+ .it_parse = isns_attr_type_string_parse,
+};
+
+/*
+ * Attribute type IPADDR
+ */
+static int
+isns_attr_type_ipaddr_encode(buf_t *bp, const isns_value_t *value)
+{
+ if (!buf_put32(bp, 16)
+ || !buf_put(bp, &value->iv_ipaddr, 16))
+ return 0;
+
+ return 1;
+}
+
+static int
+isns_attr_type_ipaddr_decode(buf_t *bp, size_t len, isns_value_t *value)
+{
+ if (len != 16)
+ return 0;
+
+ return buf_get(bp, &value->iv_ipaddr, 16);
+}
+
+static void
+isns_attr_type_ipaddr_print(const isns_value_t *value, char *buf, size_t size)
+{
+ const struct in6_addr *addr = &value->iv_ipaddr;
+ char buffer[INET6_ADDRSTRLEN + 1];
+
+ /* The standard requires IPv4 mapping, but
+ * some oldish implementations seem to use
+ * IPv4 compatible addresss. */
+ if (IN6_IS_ADDR_V4MAPPED(addr) || IN6_IS_ADDR_V4COMPAT(addr)) {
+ struct in_addr ipv4;
+
+ ipv4.s_addr = addr->s6_addr32[3];
+ inet_ntop(AF_INET, &ipv4, buffer, sizeof(buffer));
+ } else {
+ inet_ntop(AF_INET6, addr, buffer, sizeof(buffer));
+ }
+ snprintf(buf, size, "%s", buffer);
+}
+
+static int
+isns_attr_type_ipaddr_parse(isns_value_t *value, const char *string)
+{
+ struct in_addr addr4;
+
+ if (inet_pton(AF_INET, string, &addr4)) {
+ value->iv_ipaddr = in6addr_any;
+ value->iv_ipaddr.s6_addr32[3] = addr4.s_addr;
+ return 1;
+ }
+
+ return inet_pton(AF_INET6, string, &value->iv_ipaddr);
+}
+
+isns_attr_type_t isns_attr_type_ipaddr = {
+ .it_id = ISNS_ATTR_TYPE_IPADDR,
+ .it_name = "ipaddr",
+ .it_encode = isns_attr_type_ipaddr_encode,
+ .it_decode = isns_attr_type_ipaddr_decode,
+ .it_print = isns_attr_type_ipaddr_print,
+ .it_parse = isns_attr_type_ipaddr_parse,
+};
+
+/*
+ * Attribute type OPAQUE
+ */
+static void
+isns_attr_type_opaque_assign(isns_value_t *value, const isns_value_t *new_value)
+{
+ size_t new_len = new_value->iv_opaque.len;
+ isns_assert(value->iv_opaque.len == 0);
+ if (new_len) {
+ value->iv_opaque.ptr = isns_malloc(new_len);
+ value->iv_opaque.len = new_len;
+ memcpy(value->iv_opaque.ptr,
+ new_value->iv_opaque.ptr,
+ new_len);
+ }
+}
+
+static void
+isns_attr_type_opaque_destroy(isns_value_t *value)
+{
+ isns_free(value->iv_opaque.ptr);
+ value->iv_opaque.ptr = NULL;
+ value->iv_opaque.len = 0;
+}
+
+static int
+isns_attr_type_opaque_match(const isns_value_t *a, const isns_value_t *b)
+{
+ if (a->iv_opaque.len != b->iv_opaque.len)
+ return 0;
+ return !memcmp(a->iv_opaque.ptr, b->iv_opaque.ptr, a->iv_opaque.len);
+}
+
+static int
+isns_attr_type_opaque_compare(const isns_value_t *a, const isns_value_t *b)
+{
+ long delta;
+
+ delta = a->iv_opaque.len - b->iv_opaque.len;
+ if (delta)
+ return delta;
+
+ return memcmp(a->iv_opaque.ptr, b->iv_opaque.ptr, a->iv_opaque.len);
+}
+
+static int
+isns_attr_type_opaque_encode(buf_t *bp, const isns_value_t *value)
+{
+ uint32_t len;
+
+ len = value->iv_opaque.len;
+ if (len & 3)
+ return 0;
+
+ if (!buf_put32(bp, len)
+ || !buf_put(bp, value->iv_opaque.ptr, len))
+ return 0;
+
+ return 1;
+}
+
+static int
+isns_attr_type_opaque_decode(buf_t *bp, size_t len, isns_value_t *value)
+{
+ value->iv_opaque.ptr = isns_malloc(len);
+ if (!buf_get(bp, value->iv_opaque.ptr, len)) {
+ isns_free(value->iv_opaque.ptr);
+ return 0;
+ }
+
+ value->iv_opaque.len = len;
+ return 1;
+}
+
+static void
+isns_attr_type_opaque_print(const isns_value_t *value, char *buf, size_t size)
+{
+ unsigned char *data = value->iv_opaque.ptr;
+ unsigned int i, len;
+
+ /* There must be room for "<...>\0" */
+ if (size < 6)
+ return;
+ size -= 6;
+
+ if ((len = value->iv_opaque.len) > 20)
+ len = 20;
+ if (size < 3 * len)
+ len = size / 3;
+
+ *buf++ = '<';
+ for (i = 0; i < len; ++i) {
+ if (i)
+ *buf++ = ' ';
+ sprintf(buf, "%02x", data[i]);
+ buf += 2;
+ }
+ if (len < value->iv_opaque.len) {
+ strcat(buf, "...");
+ buf += 4;
+ }
+ *buf++ = '>';
+ *buf++ = '\0';
+}
+
+isns_attr_type_t isns_attr_type_opaque = {
+ .it_id = ISNS_ATTR_TYPE_OPAQUE,
+ .it_name = "opaque",
+ .it_assign = isns_attr_type_opaque_assign,
+ .it_destroy = isns_attr_type_opaque_destroy,
+ .it_match = isns_attr_type_opaque_match,
+ .it_compare = isns_attr_type_opaque_compare,
+ .it_encode = isns_attr_type_opaque_encode,
+ .it_decode = isns_attr_type_opaque_decode,
+ .it_print = isns_attr_type_opaque_print,
+};
+
+/*
+ * Map attribute type IDs to attribute types
+ */
+static isns_attr_type_t *
+isns_attr_types_builtin[__ISNS_ATTR_TYPE_BUILTIN_MAX] = {
+[ISNS_ATTR_TYPE_NIL] = &isns_attr_type_nil,
+[ISNS_ATTR_TYPE_OPAQUE] = &isns_attr_type_opaque,
+[ISNS_ATTR_TYPE_STRING] = &isns_attr_type_string,
+[ISNS_ATTR_TYPE_INT32] = &isns_attr_type_int32,
+[ISNS_ATTR_TYPE_UINT32] = &isns_attr_type_uint32,
+[ISNS_ATTR_TYPE_UINT64] = &isns_attr_type_uint64,
+[ISNS_ATTR_TYPE_IPADDR] = &isns_attr_type_ipaddr,
+[ISNS_ATTR_TYPE_RANGE16] = &isns_attr_type_range16,
+};
+
+const isns_attr_type_t *
+isns_attr_type_by_id(unsigned int id)
+{
+ if (id < __ISNS_ATTR_TYPE_BUILTIN_MAX)
+ return isns_attr_types_builtin[id];
+
+ /* TODO: handle dynamic registration of attrtypes
+ * for vendor extensions. */
+ return NULL;
+}
diff --git a/utils/open-isns/attrs.h b/utils/open-isns/attrs.h
new file mode 100644
index 0000000..1d3667e
--- /dev/null
+++ b/utils/open-isns/attrs.h
@@ -0,0 +1,262 @@
+/*
+ * iSNS object attributes
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_ATTRS_H
+#define ISNS_ATTRS_H
+
+#include <netinet/in.h>
+#include "buffer.h"
+#include "isns.h"
+
+/*
+ * Type identifier
+ */
+enum {
+ ISNS_ATTR_TYPE_NIL = 0,
+ ISNS_ATTR_TYPE_OPAQUE,
+ ISNS_ATTR_TYPE_STRING,
+ ISNS_ATTR_TYPE_INT32,
+ ISNS_ATTR_TYPE_UINT32,
+ ISNS_ATTR_TYPE_UINT64,
+ ISNS_ATTR_TYPE_IPADDR,
+ ISNS_ATTR_TYPE_RANGE16,
+
+ __ISNS_ATTR_TYPE_BUILTIN_MAX
+};
+
+/*
+ * Union holding an attribute value
+ */
+typedef struct isns_value {
+ const struct isns_attr_type * iv_type;
+
+ /* Data is stuffed into an anonymous union */
+ union {
+ uint32_t iv_nil;
+ struct __isns_opaque {
+ void * ptr;
+ size_t len;
+ } iv_opaque;
+ char * iv_string;
+ int32_t iv_int32;
+ uint32_t iv_uint32;
+ uint64_t iv_uint64;
+ struct in6_addr iv_ipaddr;
+ struct {
+ uint16_t min, max;
+ } iv_range;
+ };
+} isns_value_t;
+
+#define __ISNS_ATTRTYPE(type) isns_attr_type_##type
+#define __ISNS_MEMBER(type) iv_##type
+#define ISNS_VALUE_INIT(type, value) \
+ (isns_value_t) { .iv_type = &__ISNS_ATTRTYPE(type), \
+ { .__ISNS_MEMBER(type) = (value) } }
+
+#define isns_attr_initialize(attrp, tag, type, value) do { \
+ isns_attr_t *__attr = (attrp); \
+ uint32_t __tag = (tag); \
+ __attr->ia_users = 1; \
+ __attr->ia_tag_id = (__tag); \
+ __attr->ia_tag = isns_tag_type_by_id(__tag); \
+ __attr->ia_value = ISNS_VALUE_INIT(type, value); \
+ } while (0)
+#define ISNS_ATTR_INIT(tag, type, value) (isns_attr_t) { \
+ .ia_users = 1, \
+ .ia_tag_id = (tag), \
+ .ia_tag = isns_tag_type_by_id(tag), \
+ .ia_value = ISNS_VALUE_INIT(type, value) \
+ }
+
+/*
+ * Attribute type
+ */
+typedef struct isns_attr_type {
+ uint32_t it_id;
+ const char * it_name;
+
+ void (*it_assign)(isns_value_t *, const isns_value_t *);
+ int (*it_set)(isns_value_t *, const void *);
+ int (*it_get)(isns_value_t *, void *);
+ int (*it_match)(const isns_value_t *, const isns_value_t *);
+ int (*it_compare)(const isns_value_t *, const isns_value_t *);
+ int (*it_encode)(buf_t *, const isns_value_t *);
+ int (*it_decode)(buf_t *, size_t, isns_value_t *);
+ void (*it_destroy)(isns_value_t *);
+ void (*it_print)(const isns_value_t *, char *, size_t);
+ int (*it_parse)(isns_value_t *, const char *);
+} isns_attr_type_t;
+
+/*
+ * Tag info: for each tag, provides a printable name,
+ * and the attribute type associated with it.
+ */
+struct isns_tag_type {
+ uint32_t it_id;
+ const char * it_name;
+ unsigned int it_multiple : 1,
+ it_readonly : 1;
+ isns_attr_type_t *it_type;
+
+ int (*it_validate)(const isns_value_t *,
+ const isns_policy_t *);
+ void (*it_print)(const isns_value_t *, char *, size_t);
+ int (*it_parse)(isns_value_t *, const char *);
+ const char * (*it_help)(void);
+};
+
+/*
+ * Attribute
+ */
+struct isns_attr {
+ unsigned int ia_users;
+ uint32_t ia_tag_id;
+ const isns_tag_type_t * ia_tag;
+ isns_value_t ia_value;
+};
+
+extern isns_attr_type_t isns_attr_type_nil;
+extern isns_attr_type_t isns_attr_type_opaque;
+extern isns_attr_type_t isns_attr_type_string;
+extern isns_attr_type_t isns_attr_type_int32;
+extern isns_attr_type_t isns_attr_type_uint32;
+extern isns_attr_type_t isns_attr_type_uint64;
+extern isns_attr_type_t isns_attr_type_ipaddr;
+extern isns_attr_type_t isns_attr_type_range16;
+
+extern isns_attr_t * isns_attr_alloc(uint32_t, const isns_tag_type_t *,
+ const isns_value_t *);
+
+extern void isns_attr_list_append_value(isns_attr_list_t *,
+ uint32_t tag, const isns_tag_type_t *,
+ const isns_value_t *);
+extern void isns_attr_list_update_value(isns_attr_list_t *,
+ uint32_t tag, const isns_tag_type_t *,
+ const isns_value_t *);
+extern int isns_attr_list_get_value(const isns_attr_list_t *,
+ uint32_t tag,
+ isns_value_t *);
+extern int isns_attr_list_get_uint32(const isns_attr_list_t *,
+ uint32_t tag,
+ uint32_t *);
+extern int isns_attr_list_get_string(const isns_attr_list_t *,
+ uint32_t tag,
+ const char **);
+
+extern int isns_attr_list_validate(const isns_attr_list_t *,
+ const isns_policy_t *,
+ unsigned int function);
+extern int isns_attr_validate(const isns_attr_t *,
+ const isns_policy_t *);
+
+extern void isns_attr_list_prune(isns_attr_list_t *,
+ const uint32_t *,
+ unsigned int);
+extern int isns_attr_list_remove_member(isns_attr_list_t *,
+ const isns_attr_t *,
+ const uint32_t *);
+extern void isns_attr_list_update_attr(isns_attr_list_t *,
+ const isns_attr_t *);
+
+extern int isns_attr_decode(buf_t *, isns_attr_t **);
+extern int isns_attr_encode(buf_t *, const isns_attr_t *);
+
+extern int isns_attr_list_decode(buf_t *, isns_attr_list_t *);
+extern int isns_attr_list_decode_delimited(buf_t *, isns_attr_list_t *);
+extern int isns_attr_list_encode(buf_t *, const isns_attr_list_t *);
+extern int isns_encode_delimiter(buf_t *);
+
+extern const isns_tag_type_t *isns_tag_type_by_id(unsigned int);
+extern const isns_attr_type_t *isns_attr_type_by_id(unsigned int);
+
+typedef struct isns_quick_attr_list isns_quick_attr_list_t;
+struct isns_quick_attr_list {
+ isns_attr_list_t iqa_list;
+ isns_attr_t * iqa_attrs[1];
+ isns_attr_t iqa_attr;
+};
+#define ISNS_QUICK_ATTR_LIST_DECLARE(qlist, tag, type, value) \
+ isns_quick_attr_list_t qlist = { \
+ .iqa_list = (isns_attr_list_t) { \
+ .ial_data = qlist.iqa_attrs, \
+ .ial_count = 1 \
+ }, \
+ .iqa_attrs = { &qlist.iqa_attr }, \
+ .iqa_attr = ISNS_ATTR_INIT(tag, type, value), \
+ }
+
+/*
+ * The following is used to chop up an incoming attr list as
+ * given in eg. a DevAttrReg message into separate chunks,
+ * following the ordering constraints laid out in the RFC.
+ *
+ * isns_attr_list_scanner_init initializes the scanner state.
+ *
+ * isns_attr_list_scanner_next advances to the next object in
+ * the list, returning the keys and attrs for one object.
+ *
+ * The isns_attr_list_scanner struct should really be opaque, but
+ * we put it here so you can declare a scanner variable on the
+ * stack.
+ */
+struct isns_attr_list_scanner {
+ isns_source_t * source;
+ isns_policy_t * policy;
+ isns_object_t * key_obj;
+ isns_attr_list_t orig_attrs;
+ unsigned int pos;
+
+ isns_attr_list_t keys;
+ isns_attr_list_t attrs;
+ isns_object_template_t *tmpl;
+ unsigned int num_key_attrs;
+
+ unsigned int entities;
+
+ uint32_t pgt_next_attr;
+ uint32_t pgt_value;
+ const char * pgt_iscsi_name;
+ isns_portal_info_t pgt_portal_info;
+ isns_object_t * pgt_base_object;
+
+ unsigned int index_acceptable : 1;
+};
+
+extern void isns_attr_list_scanner_init(struct isns_attr_list_scanner *,
+ isns_object_t *key_obj,
+ const isns_attr_list_t *attrs);
+extern int isns_attr_list_scanner_next(struct isns_attr_list_scanner *);
+extern void isns_attr_list_scanner_destroy(struct isns_attr_list_scanner *);
+
+/*
+ * The following is used to parse attribute lists given as
+ * a bunch of strings.
+ */
+struct isns_attr_list_parser {
+ struct isns_tag_prefix *prefix;
+ const char * default_port;
+
+ unsigned int multi_type_permitted : 1,
+ nil_permitted : 1;
+
+ isns_attr_t * (*load_key)(const char *);
+ isns_attr_t * (*generate_key)(void);
+};
+
+extern int isns_attr_list_split(char *line, char **argv, unsigned int argc_max);
+extern void isns_attr_list_parser_init(struct isns_attr_list_parser *,
+ isns_object_template_t *);
+extern int isns_parse_attrs(unsigned int, char **,
+ isns_attr_list_t *, struct isns_attr_list_parser *);
+extern int isns_parse_query_attrs(unsigned int, char **,
+ isns_attr_list_t *, isns_attr_list_t *,
+ struct isns_attr_list_parser *);
+extern void isns_attr_list_parser_help(struct isns_attr_list_parser *);
+extern isns_object_template_t *isns_attr_list_parser_context(const struct isns_attr_list_parser *);
+extern int isns_print_attrs(isns_object_t *, char **, unsigned int);
+
+#endif /* ISNS_ATTRS_H */
diff --git a/utils/open-isns/authblock.c b/utils/open-isns/authblock.c
new file mode 100644
index 0000000..76d35b4
--- /dev/null
+++ b/utils/open-isns/authblock.c
@@ -0,0 +1,62 @@
+/*
+ * iSNS authentication functions
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "attrs.h"
+#include "message.h"
+#include "util.h"
+
+/* We impose an artificial limit on the size of
+ * the size of the authenticator
+ */
+#define ISNS_SPISTR_MAX 512
+
+int
+isns_authblock_decode(buf_t *bp, struct isns_authblk *auth)
+{
+ unsigned int avail = buf_avail(bp);
+
+ if (!buf_get32(bp, &auth->iab_bsd)
+ || !buf_get32(bp, &auth->iab_length)
+ || !buf_get64(bp, &auth->iab_timestamp)
+ || !buf_get32(bp, &auth->iab_spi_len))
+ return 0;
+
+ /* Make sure the length specified by the auth block
+ * is reasonable. */
+ if (auth->iab_length < ISNS_AUTHBLK_SIZE
+ || auth->iab_length > avail)
+ return 0;
+
+ /* This chops off any data trailing the auth block.
+ * It also makes sure that we detect if iab_length
+ * exceeds the amount of available data. */
+ if (!buf_truncate(bp, auth->iab_length - ISNS_AUTHBLK_SIZE))
+ return 0;
+
+ auth->iab_spi = buf_head(bp);
+ if (!buf_pull(bp, auth->iab_spi_len))
+ return 0;
+
+ auth->iab_sig = buf_head(bp);
+ auth->iab_sig_len = buf_avail(bp);
+ return 1;
+}
+
+int
+isns_authblock_encode(buf_t *bp, const struct isns_authblk *auth)
+{
+ if (!buf_put32(bp, auth->iab_bsd)
+ || !buf_put32(bp, auth->iab_length)
+ || !buf_put64(bp, auth->iab_timestamp)
+ || !buf_put32(bp, auth->iab_spi_len)
+ || !buf_put(bp, auth->iab_spi, auth->iab_spi_len)
+ || !buf_put(bp, auth->iab_sig, auth->iab_sig_len))
+ return 0;
+ return 1;
+}
diff --git a/utils/open-isns/bitvector.c b/utils/open-isns/bitvector.c
new file mode 100644
index 0000000..3e23f26
--- /dev/null
+++ b/utils/open-isns/bitvector.c
@@ -0,0 +1,651 @@
+/*
+ * Handle bit vector as a run length encoded array of
+ * 32bit words.
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "util.h"
+
+struct isns_bitvector {
+ unsigned int ib_count;
+ uint32_t * ib_words;
+};
+
+void
+isns_bitvector_init(isns_bitvector_t *bv)
+{
+ memset(bv, 0, sizeof(*bv));
+}
+
+void
+isns_bitvector_destroy(isns_bitvector_t *bv)
+{
+ isns_free(bv->ib_words);
+ memset(bv, 0, sizeof(*bv));
+}
+
+isns_bitvector_t *
+isns_bitvector_alloc(void)
+{
+ return isns_calloc(1, sizeof(isns_bitvector_t));
+}
+
+void
+isns_bitvector_free(isns_bitvector_t *bv)
+{
+ if (bv) {
+ isns_free(bv->ib_words);
+ memset(bv, 0xa5, sizeof(*bv));
+ isns_free(bv);
+ }
+}
+
+/*
+ * Helper function to locate bit
+ */
+uint32_t *
+__isns_bitvector_find_word(const isns_bitvector_t *bv, unsigned int bit)
+{
+ uint32_t *wp, *end;
+
+ if (bv->ib_words == NULL)
+ return NULL;
+
+ wp = bv->ib_words;
+ end = wp + bv->ib_count;
+ while (wp < end) {
+ unsigned int base, rlen;
+
+ base = wp[0];
+ rlen = wp[1];
+
+ isns_assert(!(base % 32));
+ if (base <= bit && bit < base + rlen * 32)
+ return wp + 2 + ((bit - base) / 32);
+
+ wp += 2 + rlen;
+ isns_assert(wp <= end);
+ }
+
+ return NULL;
+}
+
+/*
+ * Insert words in the middle of the array
+ */
+static inline void
+__isns_bitvector_insert_words(isns_bitvector_t *bv,
+ unsigned int offset, unsigned int count)
+{
+ bv->ib_words = isns_realloc(bv->ib_words,
+ (bv->ib_count + count) * sizeof(uint32_t));
+
+ /* If we insert in the middle, shift out the tail
+ * to make room for the new range. */
+ isns_assert(offset <= bv->ib_count);
+ if (offset < bv->ib_count) {
+ memmove(bv->ib_words + offset + count,
+ bv->ib_words + offset,
+ (bv->ib_count - offset) * sizeof(uint32_t));
+ }
+
+ memset(bv->ib_words + offset, 0, count * sizeof(uint32_t));
+ bv->ib_count += count;
+}
+
+/*
+ * Insert a new range
+ */
+static inline uint32_t *
+__isns_bitvector_insert_range(isns_bitvector_t *bv,
+ unsigned int offset, unsigned int base)
+{
+ uint32_t *pos;
+
+ __isns_bitvector_insert_words(bv, offset, 3);
+
+ pos = bv->ib_words + offset;
+
+ *pos++ = base & ~31;
+ *pos++ = 1;
+
+ return pos;
+}
+
+/*
+ * Extend an existing range
+ * @offset marks the beginning of the existing range.
+ */
+static inline uint32_t *
+__isns_bitvector_extend_range(isns_bitvector_t *bv,
+ unsigned int offset, unsigned int count)
+{
+ uint32_t *pos, rlen;
+
+ /* Find the end of the range */
+ pos = bv->ib_words + offset;
+ rlen = pos[1];
+
+ __isns_bitvector_insert_words(bv, offset + 2 + rlen, count);
+
+ pos = bv->ib_words + offset;
+ pos[1] += count;
+
+ /* Return pointer to the last word of the new range. */
+ return pos + 2 + rlen + count - 1;
+}
+
+/*
+ * Find a suitable range for insertion
+ */
+static uint32_t *
+__isns_bitvector_find_insert_word(isns_bitvector_t *bv, unsigned int bit)
+{
+ uint32_t *wp, *end;
+
+ if (bv->ib_words == NULL)
+ return __isns_bitvector_insert_range(bv, 0, bit);
+
+ wp = bv->ib_words;
+ end = wp + bv->ib_count;
+ while (wp < end) {
+ unsigned int base, rlen, distance;
+
+ base = wp[0];
+ rlen = wp[1];
+
+ isns_assert(!(base % 32));
+
+ if (bit < base) {
+ return __isns_bitvector_insert_range(bv,
+ wp - bv->ib_words, bit);
+ }
+
+ distance = (bit - base) / 32;
+ if (distance < rlen) {
+ /* This bit is within range */
+ return wp + 2 + distance;
+ }
+
+ /* Is it efficient to extend this range?
+ * The break even point is if we have to add
+ * 3 words to extend the range, because a new
+ * range would be at least that much.
+ */
+ if (distance + 1 <= rlen + 3) {
+ return __isns_bitvector_extend_range(bv,
+ wp - bv->ib_words,
+ distance + 1 - rlen);
+ }
+
+ wp += 2 + rlen;
+ isns_assert(wp <= end);
+ }
+
+ /* No suitable range found. Append one at the end */
+ return __isns_bitvector_insert_range(bv,
+ bv->ib_count, bit);
+}
+
+/*
+ * After clearing a bit, check if the bitvector can be
+ * compacted.
+ */
+static void
+__isns_bitvector_compact(isns_bitvector_t *bv)
+{
+ uint32_t *src, *dst, *end;
+ unsigned int dst_base = 0, dst_len = 0;
+
+ if (bv->ib_words == NULL)
+ return;
+
+ src = dst = bv->ib_words;
+ end = src + bv->ib_count;
+ while (src < end) {
+ unsigned int base, rlen;
+
+ base = *src++;
+ rlen = *src++;
+
+ /* Consume leading NUL words */
+ while (rlen && *src == 0) {
+ base += 32;
+ src++;
+ rlen--;
+ }
+
+ /* Consume trailing NUL words */
+ while (rlen && src[rlen-1] == 0)
+ rlen--;
+
+ if (rlen != 0) {
+ if (dst_len && dst_base + 32 * dst_len == base) {
+ /* We can extend the previous run */
+ } else {
+ /* New run. Close off the previous one,
+ * if we had one. */
+ if (dst_len != 0) {
+ dst[0] = dst_base;
+ dst[1] = dst_len;
+ dst += 2 + dst_len;
+ }
+
+ dst_base = base;
+ dst_len = 0;
+ }
+
+ while (rlen--)
+ dst[2 + dst_len++] = *src++;
+ }
+
+ isns_assert(src <= end);
+ }
+
+
+ if (dst_len != 0) {
+ dst[0] = dst_base;
+ dst[1] = dst_len;
+ dst += 2 + dst_len;
+ }
+
+ bv->ib_count = dst - bv->ib_words;
+ if (bv->ib_count == 0)
+ isns_bitvector_destroy(bv);
+}
+
+/*
+ * Test the value of a single bit
+ */
+int
+isns_bitvector_test_bit(const isns_bitvector_t *bv, unsigned int bit)
+{
+ const uint32_t *pos;
+ uint32_t mask;
+
+ pos = __isns_bitvector_find_word(bv, bit);
+ if (pos == NULL)
+ return 0;
+
+ mask = 1 << (bit % 32);
+ return !!(*pos & mask);
+}
+
+int
+isns_bitvector_clear_bit(isns_bitvector_t *bv, unsigned int bit)
+{
+ uint32_t *pos, oldval, mask;
+
+ pos = __isns_bitvector_find_word(bv, bit);
+ if (pos == NULL)
+ return 0;
+
+ mask = 1 << (bit % 32);
+ oldval = *pos;
+ *pos &= ~mask;
+
+ __isns_bitvector_compact(bv);
+ return !!(oldval & mask);
+}
+
+int
+isns_bitvector_set_bit(isns_bitvector_t *bv, unsigned int bit)
+{
+ uint32_t *pos, oldval = 0, mask;
+
+ mask = 1 << (bit % 32);
+
+ pos = __isns_bitvector_find_insert_word(bv, bit);
+ if (pos != NULL) {
+ oldval = *pos;
+ *pos |= mask;
+
+ return !!(oldval & mask);
+ }
+
+ return 0;
+}
+
+int
+isns_bitvector_is_empty(const isns_bitvector_t *bv)
+{
+ uint32_t *wp, *end;
+
+ if (bv == NULL || bv->ib_count == 0)
+ return 1;
+
+ /* In theory, we should never have a non-compacted
+ * empty bitvector, as the only way to get one
+ * is through clear_bit.
+ * Better safe than sorry...
+ */
+
+ wp = bv->ib_words;
+ end = wp + bv->ib_count;
+ while (wp < end) {
+ unsigned int base, rlen;
+
+ base = *wp++;
+ rlen = *wp++;
+
+ while (rlen--) {
+ if (*wp++)
+ return 0;
+ }
+ isns_assert(wp <= end);
+ }
+
+ return 1;
+}
+
+int
+isns_bitvector_intersect(const isns_bitvector_t *a,
+ const isns_bitvector_t *b,
+ isns_bitvector_t *result)
+{
+ const uint32_t *runa, *runb, *enda, *endb;
+ const uint32_t *wpa = NULL, *wpb = NULL;
+ uint32_t bita = 0, lena = 0, bitb = 0, lenb = 0;
+ int found = -1;
+
+ if (a == NULL || b == NULL)
+ return -1;
+
+ /* Returning the intersect is not implemented yet. */
+ isns_assert(result == NULL);
+
+ runa = a->ib_words;
+ enda = runa + a->ib_count;
+ runb = b->ib_words;
+ endb = runb + b->ib_count;
+
+ while (1) {
+ unsigned int skip;
+
+ if (lena == 0) {
+next_a:
+ if (runa >= enda)
+ break;
+ bita = *runa++;
+ lena = *runa++;
+ wpa = runa;
+ runa += lena;
+ lena *= 32;
+ }
+
+ if (lenb == 0) {
+next_b:
+ if (runb >= endb)
+ break;
+ bitb = *runb++;
+ lenb = *runb++;
+ wpb = runb;
+ runb += lenb;
+ lenb *= 32;
+ }
+
+ if (bita < bitb) {
+ skip = bitb - bita;
+
+ /* range A ends before range B starts.
+ * Proceed to next run in vector A. */
+ if (skip >= lena)
+ goto next_a;
+
+ bita += skip;
+ lena -= skip;
+ wpa += skip / 32;
+ } else
+ if (bitb < bita) {
+ skip = bita - bitb;
+
+ /* range B ends before range A starts.
+ * Proceed to next run in vector B. */
+ if (skip >= lenb)
+ goto next_b;
+
+ bitb += skip;
+ lenb -= skip;
+ wpb += skip / 32;
+ }
+
+ isns_assert(bita == bitb);
+
+ while (lena && lenb) {
+ uint32_t intersect;
+
+ intersect = *wpa & *wpb;
+
+ if (!intersect)
+ goto next_word;
+
+ /* Find the bit */
+ if (found < 0) {
+ uint32_t mask = intersect;
+
+ found = bita;
+ while (!(mask & 1)) {
+ found++;
+ mask >>= 1;
+ }
+ }
+
+ if (result == NULL)
+ return found;
+
+ /* Append to result vector */
+ /* FIXME: TBD */
+
+next_word:
+ bita += 32; lena -= 32; wpa++;
+ bitb += 32; lenb -= 32; wpb++;
+ }
+ }
+
+ return found;
+}
+
+/*
+ * Iterate over the bit vector
+ */
+void
+isns_bitvector_foreach(const isns_bitvector_t *bv,
+ int (*cb)(uint32_t, void *),
+ void *user_data)
+{
+ uint32_t *wp, *end;
+
+ wp = bv->ib_words;
+ end = wp + bv->ib_count;
+ while (wp < end) {
+ unsigned int base, rlen, bits;
+
+ base = wp[0];
+ rlen = wp[1];
+ bits = rlen * 32;
+ wp += 2;
+
+ while (rlen--) {
+ uint32_t mask, word;
+
+ word = *wp++;
+ for (mask = 1; mask; mask <<= 1, ++base) {
+ if (word & mask)
+ cb(base, user_data);
+ }
+ }
+ isns_assert(wp <= end);
+ }
+}
+
+void
+isns_bitvector_dump(const isns_bitvector_t *bv, isns_print_fn_t *fn)
+{
+ uint32_t *wp, *end;
+
+ fn("Bit Vector %p (%u words):", bv, bv->ib_count);
+
+ wp = bv->ib_words;
+ end = wp + bv->ib_count;
+ while (wp < end) {
+ unsigned int base, rlen, bits;
+
+ base = wp[0];
+ rlen = wp[1];
+ bits = rlen * 32;
+ wp += 2;
+
+ fn(" <%u:", base);
+ while (rlen--)
+ fn(" 0x%x", *wp++);
+ fn(">");
+
+ isns_assert(wp <= end);
+ }
+
+ if (bv->ib_count == 0)
+ fn("<empty>");
+ fn("\n");
+}
+
+static inline void
+__isns_bitvector_print_next(uint32_t first, uint32_t last,
+ isns_print_fn_t *fn)
+{
+ switch (last - first) {
+ case 0:
+ return;
+ case 1:
+ fn(", %u", last);
+ break;
+ default:
+ fn("-%u", last);
+ break;
+ }
+}
+
+void
+isns_bitvector_print(const isns_bitvector_t *bv,
+ isns_print_fn_t *fn)
+{
+ uint32_t *wp, *end, first = 0, next = 0;
+ const char *sepa = "";
+
+ wp = bv->ib_words;
+ end = wp + bv->ib_count;
+ while (wp < end) {
+ unsigned int base, rlen, bits;
+
+ base = wp[0];
+ rlen = wp[1];
+ bits = rlen * 32;
+ wp += 2;
+
+ while (rlen--) {
+ uint32_t mask, word;
+
+ word = *wp++;
+ for (mask = 1; mask; mask <<= 1, ++base) {
+ if (word & mask) {
+ if (next++)
+ continue;
+ fn("%s%u", sepa, base);
+ sepa = ", ";
+ first = base;
+ next = base + 1;
+ } else {
+ if (next)
+ __isns_bitvector_print_next(first, next - 1, fn);
+ first = next = 0;
+ }
+ }
+ }
+ isns_assert(wp <= end);
+ }
+
+ if (next)
+ __isns_bitvector_print_next(first, next - 1, fn);
+
+ if (*sepa == '\0')
+ fn("<empty>");
+ fn("\n");
+}
+
+#ifdef TEST
+int
+main(void)
+{
+ isns_bitvector_t a, b;
+ int i;
+
+ isns_bitvector_init(&a);
+ isns_bitvector_set_bit(&a, 0);
+ isns_bitvector_dump(&a, isns_print_stdout);
+ isns_bitvector_set_bit(&a, 1);
+ isns_bitvector_set_bit(&a, 16);
+ isns_bitvector_set_bit(&a, 32);
+ isns_bitvector_set_bit(&a, 64);
+ isns_bitvector_dump(&a, isns_print_stdout);
+ isns_bitvector_set_bit(&a, 8192);
+ isns_bitvector_set_bit(&a, 8196);
+ isns_bitvector_set_bit(&a, 8194);
+ isns_bitvector_dump(&a, isns_print_stdout);
+ isns_bitvector_set_bit(&a, 2052);
+ isns_bitvector_set_bit(&a, 2049);
+ isns_bitvector_set_bit(&a, 2051);
+ isns_bitvector_set_bit(&a, 2050);
+ isns_bitvector_dump(&a, isns_print_stdout);
+ isns_bitvector_print(&a, isns_print_stdout);
+ isns_bitvector_destroy(&a);
+
+ isns_bitvector_init(&a);
+ for (i = 127; i >= 0; --i)
+ isns_bitvector_set_bit(&a, i);
+ isns_bitvector_dump(&a, isns_print_stdout);
+ printf("[Compacting]\n");
+ __isns_bitvector_compact(&a);
+ isns_bitvector_dump(&a, isns_print_stdout);
+ isns_bitvector_print(&a, isns_print_stdout);
+ isns_bitvector_destroy(&a);
+
+ isns_bitvector_init(&a);
+ for (i = 0; i < 128; ++i)
+ isns_bitvector_set_bit(&a, i);
+ isns_bitvector_dump(&a, isns_print_stdout);
+ isns_bitvector_print(&a, isns_print_stdout);
+ isns_bitvector_destroy(&a);
+
+ isns_bitvector_init(&a);
+ isns_bitvector_init(&b);
+ isns_bitvector_set_bit(&a, 0);
+ isns_bitvector_set_bit(&a, 77);
+ isns_bitvector_set_bit(&a, 249);
+ isns_bitvector_set_bit(&a, 102);
+
+ isns_bitvector_set_bit(&b, 1);
+ isns_bitvector_set_bit(&b, 76);
+ isns_bitvector_set_bit(&b, 250);
+ isns_bitvector_set_bit(&b, 102);
+ i = isns_bitvector_intersect(&a, &b, NULL);
+ if (i != 102)
+ fprintf(stderr, "*** BAD: Intersect should return 102 (got %d)! ***\n", i);
+ else
+ printf("Intersect okay: %d\n", i);
+ isns_bitvector_destroy(&a);
+ isns_bitvector_destroy(&b);
+
+ isns_bitvector_init(&a);
+ isns_bitvector_set_bit(&a, 0);
+ isns_bitvector_set_bit(&a, 1);
+ isns_bitvector_clear_bit(&a, 1);
+ isns_bitvector_clear_bit(&a, 0);
+ isns_bitvector_dump(&a, isns_print_stdout);
+ isns_bitvector_print(&a, isns_print_stdout);
+ isns_bitvector_destroy(&a);
+ return 0;
+}
+#endif
diff --git a/utils/open-isns/buffer.c b/utils/open-isns/buffer.c
new file mode 100644
index 0000000..279ab76
--- /dev/null
+++ b/utils/open-isns/buffer.c
@@ -0,0 +1,407 @@
+/*
+ * Buffer handling functions
+ *
+ * Copyright (C) 2003-2007, Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+#include <unistd.h>
+#include <netinet/in.h> /* ntohl&htonl */
+#include "buffer.h"
+#include "util.h" /* htonll */
+
+static int buf_drain(buf_t *bp);
+
+buf_t *
+buf_alloc(size_t size)
+{
+ buf_t *bp;
+
+ bp = isns_calloc(1, sizeof(*bp));
+ buf_init_empty(bp, size);
+
+ return bp;
+}
+
+buf_t *
+buf_open(const char *filename, int flags)
+{
+ static const unsigned int buflen = 4096;
+ buf_t *bp;
+ int oerr;
+
+ if (!(bp = isns_calloc(1, sizeof(*bp) + buflen)))
+ return NULL;
+ buf_init(bp, (bp + 1), buflen);
+
+ switch (flags & O_ACCMODE) {
+ case O_RDONLY:
+ bp->write_mode = 0;
+ break;
+
+ case O_WRONLY:
+ bp->write_mode = 1;
+ break;
+
+ default:
+ errno = EINVAL;
+ goto failed;
+ }
+
+ if (!filename || !strcmp(filename, "-")) {
+ bp->fd = dup(bp->write_mode? 1 : 0);
+ } else {
+ bp->fd = open(filename, flags, 0666);
+ }
+
+ if (bp->fd < 0)
+ goto failed;
+
+ return bp;
+
+failed: oerr = errno;
+ isns_free(bp);
+ errno = oerr;
+ return NULL;
+}
+
+buf_t *
+buf_dup(const buf_t *src)
+{
+ buf_t *bp;
+
+ bp = buf_alloc(src->max_size);
+ buf_put(bp, src->base + src->head, src->tail - src->head);
+
+ bp->addr = src->addr;
+ bp->addrlen = src->addrlen;
+ return bp;
+}
+
+void
+buf_close(buf_t *bp)
+{
+ if (bp->write_mode)
+ buf_drain(bp);
+ if (bp->fd >= 0)
+ close(bp->fd);
+ bp->fd = -1;
+ isns_free(bp);
+}
+
+void
+buf_free(buf_t *bp)
+{
+ if (!bp)
+ return;
+ if (bp->allocated)
+ isns_free(bp->base);
+ isns_free(bp);
+}
+
+void
+buf_list_free(buf_t *bp)
+{
+ buf_t *next;
+
+ while (bp) {
+ next = bp->next;
+ buf_free(bp);
+ bp = next;
+ }
+}
+
+void
+buf_init(buf_t *bp, void *mem, size_t len)
+{
+ memset(bp, 0, sizeof(*bp));
+ bp->base = (unsigned char *) mem;
+ bp->size = len;
+ bp->max_size = len;
+ bp->fd = -1;
+}
+
+void
+buf_init_empty(buf_t *bp, size_t len)
+{
+ memset(bp, 0, sizeof(*bp));
+ bp->max_size = len;
+ bp->fd = -1;
+}
+
+void
+buf_set(buf_t *bp, void *mem, size_t len)
+{
+ buf_init(bp, mem, len);
+ bp->tail = len;
+}
+
+void
+buf_clear(buf_t *bp)
+{
+ bp->head = bp->tail = 0;
+}
+
+int
+buf_fill(buf_t *bp)
+{
+ int n;
+
+ if (bp->head || bp->tail)
+ buf_compact(bp);
+
+ if (bp->write_mode || bp->fd < 0)
+ return 0;
+
+ n = read(bp->fd, bp->base + bp->tail, buf_tailroom(bp));
+ if (n < 0) {
+ warn("read error");
+ return 0;
+ }
+
+ bp->tail += n;
+ return n;
+}
+
+int
+buf_drain(buf_t *bp)
+{
+ int n;
+
+ if (!bp->write_mode || bp->fd < 0)
+ return 0;
+
+ n = write(bp->fd, bp->base + bp->head, buf_avail(bp));
+ if (n < 0) {
+ warn("write error");
+ return 0;
+ }
+
+ bp->head += n;
+ return n;
+}
+
+int
+__buf_resize(buf_t *bp, size_t new_size)
+{
+ void *new_base;
+
+ if (new_size > bp->max_size)
+ return 0;
+ isns_assert(bp->allocated || bp->base == NULL);
+
+ new_size = (new_size + 127) & ~127;
+ if (new_size > bp->max_size)
+ new_size = bp->max_size;
+
+ new_base = isns_realloc(bp->base, new_size);
+ if (new_base == NULL)
+ return 0;
+
+ bp->base = new_base;
+ bp->size = new_size;
+ bp->allocated = 1;
+ return new_size;
+}
+
+buf_t *
+buf_split(buf_t **to_split, size_t size)
+{
+ buf_t *old = *to_split, *new;
+ size_t avail;
+
+ avail = buf_avail(old);
+ if (size > avail)
+ return NULL;
+
+ if (size == avail) {
+ *to_split = NULL;
+ return old;
+ }
+
+ new = buf_alloc(size);
+ buf_put(new, buf_head(old), size);
+ buf_pull(old, size);
+
+ return new;
+}
+
+int
+buf_seek(buf_t *bp, off_t offset)
+{
+ if (bp->write_mode && !buf_drain(bp))
+ return 0;
+ if (lseek(bp->fd, offset, SEEK_SET) < 0) {
+ warn("cannot seek to offset %ld", (long) offset);
+ return 0;
+ }
+ return 1;
+}
+
+int
+buf_get(buf_t *bp, void *mem, size_t len)
+{
+ caddr_t dst = (caddr_t) mem;
+ unsigned int total = len, copy;
+
+ while (len) {
+ if ((copy = buf_avail(bp)) > len)
+ copy = len;
+ if (copy == 0) {
+ if (!buf_fill(bp))
+ return 0;
+ continue;
+ }
+ if (dst) {
+ memcpy(dst, bp->base + bp->head, copy);
+ dst += copy;
+ }
+ bp->head += copy;
+ len -= copy;
+ }
+ return total;
+}
+
+int
+buf_get32(buf_t *bp, uint32_t *vp)
+{
+ if (!buf_get(bp, vp, 4))
+ return 0;
+ *vp = ntohl(*vp);
+ return 1;
+}
+
+int
+buf_get64(buf_t *bp, uint64_t *vp)
+{
+ if (!buf_get(bp, vp, 8))
+ return 0;
+ *vp = ntohll(*vp);
+ return 1;
+}
+
+int
+buf_gets(buf_t *bp, char *stringbuf, size_t size)
+{
+ uint32_t len, copy;
+
+ if (size == 0)
+ return 0;
+
+ if (!buf_get32(bp, &len))
+ return 0;
+
+ if ((copy = len) >= size)
+ copy = size - 1;
+
+ if (!buf_get(bp, stringbuf, copy))
+ return 0;
+ stringbuf[copy] = '\0';
+
+ /* Pull remaining bytes */
+ if (copy != len && !buf_pull(bp, len - copy))
+ return 0;
+
+ return copy + 1;
+}
+
+int
+buf_put(buf_t *bp, const void *mem, size_t len)
+{
+ caddr_t src = (caddr_t) mem;
+ unsigned int total = len, copy;
+
+ while (len) {
+ if ((copy = bp->size - bp->tail) > len)
+ copy = len;
+ if (copy == 0) {
+ if (buf_drain(bp)) {
+ buf_compact(bp);
+ continue;
+ }
+ if (__buf_resize(bp, bp->tail + len)) {
+ buf_compact(bp);
+ continue;
+ }
+ return 0;
+ }
+ if (src) {
+ memcpy(bp->base + bp->tail, src, copy);
+ src += copy;
+ }
+ bp->tail += copy;
+ len -= copy;
+ }
+ return total;
+}
+
+int
+buf_putc(buf_t *bp, int byte)
+{
+ unsigned char c = byte;
+
+ return buf_put(bp, &c, 1);
+}
+
+int
+buf_put32(buf_t *bp, uint32_t val)
+{
+ val = htonl(val);
+ if (!buf_put(bp, &val, 4))
+ return 0;
+ return 1;
+}
+
+int
+buf_put64(buf_t *bp, uint64_t val)
+{
+ val = htonll(val);
+ return buf_put(bp, &val, 8);
+}
+
+int
+buf_puts(buf_t *bp, const char *sp)
+{
+ uint32_t len = 0;
+
+ if (sp)
+ len = strlen(sp);
+ return buf_put32(bp, len) && buf_put(bp, sp, len);
+}
+
+void
+buf_compact(buf_t *bp)
+{
+ unsigned int count;
+
+ if (bp->head == 0)
+ return;
+
+ count = bp->tail - bp->head;
+ memmove(bp->base, bp->base + bp->head, count);
+ bp->tail -= bp->head;
+ bp->head = 0;
+}
+
+void
+buf_list_append(buf_t **list, buf_t *bp)
+{
+ bp->next = NULL;
+ while (*list)
+ list = &(*list)->next;
+ *list = bp;
+}
+
+int
+buf_truncate(buf_t *bp, size_t len)
+{
+ if (bp->head + len > bp->tail)
+ return 0;
+
+ bp->tail = bp->head + len;
+ return 1;
+}
diff --git a/utils/open-isns/buffer.h b/utils/open-isns/buffer.h
new file mode 100644
index 0000000..75ba910
--- /dev/null
+++ b/utils/open-isns/buffer.h
@@ -0,0 +1,141 @@
+/*
+ * Buffer handling functions
+ *
+ * Copyright (C) 2003-2006, Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdint.h>
+
+typedef struct isns_buf {
+ struct isns_buf * next;
+ unsigned char * base;
+ unsigned int head, tail, size, max_size;
+ unsigned int write_mode : 1,
+ allocated : 1;
+ int fd;
+
+ /* Anonymous union for misc stuff */
+ union {
+ struct {
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ };
+ };
+} buf_t;
+
+extern buf_t * buf_open(const char *, int);
+extern buf_t * buf_alloc(size_t);
+extern buf_t * buf_dup(const buf_t *);
+extern void buf_init(buf_t *, void *, size_t);
+extern void buf_init_empty(buf_t *, size_t);
+extern void buf_set(buf_t *, void *, size_t);
+
+extern void buf_clear(buf_t *);
+extern void buf_close(buf_t *);
+extern void buf_destroy(buf_t *);
+extern void buf_free(buf_t *);
+extern void buf_list_free(buf_t *);
+
+extern int buf_get(buf_t *, void *, size_t);
+extern int buf_get32(buf_t *, uint32_t *);
+extern int buf_get64(buf_t *, uint64_t *);
+extern int buf_gets(buf_t *, char *, size_t);
+extern int buf_put(buf_t *, const void *, size_t);
+extern int buf_put32(buf_t *, uint32_t);
+extern int buf_put64(buf_t *, uint64_t);
+extern int buf_puts(buf_t *, const char *);
+extern int buf_putc(buf_t *, int);
+extern int buf_read(buf_t *, int);
+extern int buf_seek(buf_t *bp, off_t offset);
+extern int buf_truncate(buf_t *, size_t);
+extern void buf_compact(buf_t *);
+extern buf_t * buf_split(buf_t **to_split, size_t len);
+extern int __buf_resize(buf_t *, size_t);
+
+extern void buf_list_append(buf_t **, buf_t *);
+
+static inline size_t
+buf_avail(const buf_t *bp)
+{
+ return bp->tail - bp->head;
+}
+
+static inline size_t
+buf_tailroom(const buf_t *bp)
+{
+ return bp->max_size - bp->tail;
+}
+
+static inline size_t
+buf_size(const buf_t *bp)
+{
+ return bp->size;
+}
+
+static inline void *
+buf_head(const buf_t *bp)
+{
+ return bp->base + bp->head;
+}
+
+static inline void *
+buf_tail(const buf_t *bp)
+{
+ return bp->base + bp->tail;
+}
+
+static inline int
+buf_reserve(buf_t *bp, size_t len)
+{
+ if (bp->head != bp->tail)
+ return 0;
+ if (bp->max_size - bp->head < len)
+ return 0;
+ bp->head += len;
+ bp->tail += len;
+ return 1;
+}
+
+static inline int
+buf_pull(buf_t *bp, size_t len)
+{
+ if (len > buf_avail(bp))
+ return 0;
+ bp->head += len;
+ return 1;
+}
+
+static inline void *
+buf_push(buf_t *bp, size_t len)
+{
+ if (bp->max_size - bp->tail < len)
+ return NULL;
+
+ if (bp->tail + len > bp->size
+ && !__buf_resize(bp, bp->tail + len))
+ return NULL;
+
+ bp->tail += len;
+ return bp->base + bp->tail - len;
+}
+
+static inline void *
+buf_push_head(buf_t *bp, size_t len)
+{
+ if (bp->head < len)
+ return NULL;
+
+ if (bp->tail > bp->size
+ && !__buf_resize(bp, bp->tail))
+ return NULL;
+
+ bp->head -= len;
+ return bp->base + bp->head;
+}
+
+#endif /* BUFFER_H */
diff --git a/utils/open-isns/callback.c b/utils/open-isns/callback.c
new file mode 100644
index 0000000..ecdabd7
--- /dev/null
+++ b/utils/open-isns/callback.c
@@ -0,0 +1,148 @@
+/*
+ * iSNS object callbacks for SCN and other stuff
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "objects.h"
+#include "vendor.h"
+#include "attrs.h"
+#include "util.h"
+
+typedef struct isns_object_notifier isns_object_notifier_t;
+struct isns_object_notifier {
+ isns_list_t list;
+ isns_db_callback_t * func;
+ void * data;
+};
+
+typedef struct isns_cb_event isns_cb_event_t;
+struct isns_cb_event {
+ isns_list_t list;
+ isns_db_event_t info;
+};
+
+static ISNS_LIST_DECLARE(notifiers);
+static ISNS_LIST_DECLARE(events);
+
+static inline void
+__isns_db_event(isns_object_t *dst,
+ isns_object_t *obj,
+ unsigned int bits,
+ isns_object_t *trigger)
+{
+ isns_cb_event_t *ev;
+
+ ev = isns_calloc(1, sizeof(*ev));
+ ev->info.ie_recipient = isns_object_get(dst);
+ ev->info.ie_object = isns_object_get(obj);
+ ev->info.ie_bits = bits;
+ ev->info.ie_trigger = isns_object_get(trigger);
+ isns_list_append(&events, &ev->list);
+}
+
+void
+isns_object_event(isns_object_t *obj,
+ unsigned int bits,
+ isns_object_t *trigger)
+{
+ __isns_db_event(NULL, obj, bits, trigger);
+}
+
+void
+isns_unicast_event(isns_object_t *dst,
+ isns_object_t *obj,
+ unsigned int bits,
+ isns_object_t *trigger)
+{
+ __isns_db_event(dst, obj, bits, trigger);
+}
+
+/*
+ * Given an object pair and an event bitmask,
+ * invoke all callbacks
+ */
+static inline void
+isns_call_callbacks(isns_db_event_t *ev)
+{
+ isns_object_t *obj = ev->ie_object;
+ isns_list_t *pos, *next;
+
+ ev->ie_bits |= obj->ie_scn_bits;
+ if (ev->ie_bits == 0)
+ return;
+ isns_list_foreach(&notifiers, pos, next) {
+ isns_object_notifier_t *not;
+
+ not = isns_list_item(isns_object_notifier_t, list, pos);
+ not->func(ev, not->data);
+ }
+ obj->ie_scn_bits = 0;
+}
+
+void
+isns_flush_events(void)
+{
+ while (!isns_list_empty(&events)) {
+ isns_cb_event_t *ev = isns_list_item(isns_cb_event_t, list, events.next);
+
+ isns_call_callbacks(&ev->info);
+ isns_object_release(ev->info.ie_recipient);
+ isns_object_release(ev->info.ie_object);
+ isns_object_release(ev->info.ie_trigger);
+ isns_list_del(&ev->list);
+ isns_free(ev);
+ }
+}
+
+void
+isns_register_callback(isns_db_callback_t *func,
+ void *user_data)
+{
+ isns_object_notifier_t *not;
+
+ not = isns_calloc(1, sizeof(*not));
+ not->func = func;
+ not->data = user_data;
+
+ isns_list_append(&notifiers, &not->list);
+}
+
+const char *
+isns_event_string(unsigned int bits)
+{
+ static const char *names[16] = {
+ [ISNS_SCN_DD_MEMBER_ADDED] = "member added",
+ [ISNS_SCN_DD_MEMBER_REMOVED] = "member removed",
+ [ISNS_SCN_OBJECT_UPDATED] = "updated",
+ [ISNS_SCN_OBJECT_ADDED] = "added",
+ [ISNS_SCN_OBJECT_REMOVED] = "removed",
+ [ISNS_SCN_MANAGEMENT_REGISTRATION]= "mgmt registration",
+ [ISNS_SCN_TARGET_AND_SELF_ONLY] = "target+self",
+ [ISNS_SCN_INITIATOR_AND_SELF_ONLY]= "initiator+self",
+ };
+ static char buffer[128];
+ unsigned int pos = 0, i;
+
+
+ for (i = 0; i < 16; ++i, bits >>= 1) {
+ if (!(bits & 1))
+ continue;
+
+ if (names[i]) {
+ snprintf(buffer + pos, sizeof(buffer) - pos,
+ "%s%s", pos? ", " : "", names[i]);
+ } else {
+ snprintf(buffer + pos, sizeof(buffer) - pos,
+ "%sevent %u", pos? ", " : "", i);
+ }
+ pos = strlen(buffer);
+ }
+ if (pos == 0)
+ return "<no event>";
+
+ return buffer;
+}
diff --git a/utils/open-isns/client.c b/utils/open-isns/client.c
new file mode 100644
index 0000000..44e07b8
--- /dev/null
+++ b/utils/open-isns/client.c
@@ -0,0 +1,203 @@
+/*
+ * Client functions
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <isns.h>
+#include "security.h"
+#include "util.h"
+#include "internal.h"
+#include "config.h"
+
+static isns_client_t *
+__isns_create_default_client(isns_socket_t *sock, isns_security_t *ctx,
+ const char *source_name)
+{
+ isns_client_t *clnt;
+
+ clnt = isns_calloc(1, sizeof(*clnt));
+
+ if (!source_name)
+ source_name = isns_config.ic_source_name;
+
+ clnt->ic_source = isns_source_create_iscsi(source_name);
+ clnt->ic_socket = sock;
+
+ isns_socket_set_security_ctx(clnt->ic_socket, ctx);
+
+ return clnt;
+}
+
+isns_client_t *
+isns_create_client(isns_security_t *ctx, const char *source_name)
+{
+ isns_socket_t *sock;
+ const char *server_name;
+
+ server_name = isns_config.ic_server_name;
+ if (!strcasecmp(server_name, "SLP:")
+ && !(server_name = isns_slp_find())) {
+ isns_error("Unable to locate iSNS server through SLP\n");
+ return NULL;
+ }
+
+ sock = isns_create_bound_client_socket(
+ isns_config.ic_bind_address,
+ server_name,
+ "isns", 0, SOCK_STREAM);
+ if (sock == NULL) {
+ isns_error("Unable to create socket for host \"%s\"\n",
+ isns_config.ic_server_name);
+ return NULL;
+ }
+
+ return __isns_create_default_client(sock,
+ ctx? : isns_default_security_context(0),
+ source_name);
+}
+
+isns_client_t *
+isns_create_default_client(isns_security_t *ctx)
+{
+ return isns_create_client(ctx, isns_config.ic_source_name);
+}
+
+isns_client_t *
+isns_create_local_client(isns_security_t *ctx, const char *source_name)
+{
+ isns_socket_t *sock;
+
+ if (isns_config.ic_control_socket == NULL)
+ isns_fatal("Cannot use local mode: no local control socket\n");
+
+ sock = isns_create_client_socket(isns_config.ic_control_socket,
+ NULL, 0, SOCK_STREAM);
+ if (sock == NULL) {
+ isns_error("Unable to create control socket (%s)\n",
+ isns_config.ic_control_socket);
+ return NULL;
+ }
+
+ return __isns_create_default_client(sock, ctx, source_name);
+}
+
+int
+isns_client_call(isns_client_t *clnt,
+ isns_simple_t **inout)
+{
+ return isns_simple_call(clnt->ic_socket, inout);
+}
+
+void
+isns_client_destroy(isns_client_t *clnt)
+{
+ if (clnt->ic_socket)
+ isns_socket_free(clnt->ic_socket);
+ if (clnt->ic_source)
+ isns_source_release(clnt->ic_source);
+ isns_free(clnt);
+}
+
+/*
+ * Get the local address
+ */
+int
+isns_client_get_local_address(const isns_client_t *clnt,
+ isns_portal_info_t *portal_info)
+{
+ return isns_socket_get_portal_info(clnt->ic_socket, portal_info);
+}
+
+/*
+ * Create a security context
+ */
+static isns_security_t *
+__create_security_context(const char *name, const char *auth_key,
+ const char *server_key)
+{
+ isns_security_t *ctx;
+ isns_principal_t *princ;
+
+ if (!isns_config.ic_security)
+ return NULL;
+
+#ifndef WITH_SECURITY
+ isns_error("Cannot create security context: security disabled at build time\n");
+ return NULL;
+#else /* WITH_SECURITY */
+ ctx = isns_create_dsa_context();
+ if (ctx == NULL)
+ isns_fatal("Unable to create security context\n");
+
+ /* Load my own key */
+ princ = isns_security_load_privkey(ctx, auth_key);
+ if (!princ)
+ isns_fatal("Unable to load private key from %s\n",
+ auth_key);
+
+ isns_principal_set_name(princ, name);
+ isns_security_set_identity(ctx, princ);
+
+ if (server_key) {
+ /* We're a client, and we want to load the
+ * server's public key in order to authenticate
+ * the server's responses.
+ */
+ princ = isns_security_load_pubkey(ctx, server_key);
+ if (!princ)
+ isns_fatal("Unable to load public key from %s\n",
+ server_key);
+
+ /* Do *not* set a name for this principal -
+ * this will be the default principal used when
+ * verifying the server's reply, which is a good thing
+ * because we don't know what SPI the server will
+ * be using. */
+ isns_add_principal(ctx, princ);
+
+ /* But set a policy for the server which allows it
+ to send ESI and SCN messages */
+ isns_principal_set_policy(princ, isns_policy_server());
+ }
+
+ return ctx;
+#endif /* WITH_SECURITY */
+}
+
+/*
+ * Create the default security context
+ */
+isns_security_t *
+isns_default_security_context(int server_only)
+{
+ static isns_security_t *ctx;
+
+ if (ctx == NULL)
+ ctx = __create_security_context(isns_config.ic_auth_name,
+ isns_config.ic_auth_key_file,
+ server_only? NULL : isns_config.ic_server_key_file);
+ return ctx;
+}
+
+/*
+ * Create the control security context
+ */
+isns_security_t *
+isns_control_security_context(int server_only)
+{
+ static isns_security_t *ctx;
+
+ if (ctx == NULL)
+ ctx = __create_security_context(isns_config.ic_control_name,
+ isns_config.ic_control_key_file,
+ server_only? NULL : isns_config.ic_server_key_file);
+ return ctx;
+}
diff --git a/utils/open-isns/compat/my_getopt.c b/utils/open-isns/compat/my_getopt.c
new file mode 100644
index 0000000..512b0ae
--- /dev/null
+++ b/utils/open-isns/compat/my_getopt.c
@@ -0,0 +1,271 @@
+/*
+ * my_getopt.c - my re-implementation of getopt.
+ * Copyright 1997, 2000, 2001, 2002, Benjamin Sittler
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "my_getopt.h"
+
+int my_optind=1, my_opterr=1, my_optopt=0;
+char *my_optarg=0;
+
+/* this is the plain old UNIX getopt, with GNU-style extensions. */
+/* if you're porting some piece of UNIX software, this is all you need. */
+/* this supports GNU-style permution and optional arguments */
+
+int my_getopt(int argc, char * argv[], const char *opts)
+{
+ static int charind=0;
+ const char *s;
+ char mode, colon_mode;
+ int off = 0, opt = -1;
+
+ if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+';
+ else {
+ if((colon_mode = *opts) == ':') off ++;
+ if(((mode = opts[off]) == '+') || (mode == '-')) {
+ off++;
+ if((colon_mode != ':') && ((colon_mode = opts[off]) == ':'))
+ off ++;
+ }
+ }
+ my_optarg = 0;
+ if(charind) {
+ my_optopt = argv[my_optind][charind];
+ for(s=opts+off; *s; s++) if(my_optopt == *s) {
+ charind++;
+ if((*(++s) == ':') || ((my_optopt == 'W') && (*s == ';'))) {
+ if(argv[my_optind][charind]) {
+ my_optarg = &(argv[my_optind++][charind]);
+ charind = 0;
+ } else if(*(++s) != ':') {
+ charind = 0;
+ if(++my_optind >= argc) {
+ if(my_opterr) fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ argv[0], my_optopt);
+ opt = (colon_mode == ':') ? ':' : '?';
+ goto my_getopt_ok;
+ }
+ my_optarg = argv[my_optind++];
+ }
+ }
+ opt = my_optopt;
+ goto my_getopt_ok;
+ }
+ if(my_opterr) fprintf(stderr,
+ "%s: illegal option -- %c\n",
+ argv[0], my_optopt);
+ opt = '?';
+ if(argv[my_optind][++charind] == '\0') {
+ my_optind++;
+ charind = 0;
+ }
+ my_getopt_ok:
+ if(charind && ! argv[my_optind][charind]) {
+ my_optind++;
+ charind = 0;
+ }
+ } else if((my_optind >= argc) ||
+ ((argv[my_optind][0] == '-') &&
+ (argv[my_optind][1] == '-') &&
+ (argv[my_optind][2] == '\0'))) {
+ my_optind++;
+ opt = -1;
+ } else if((argv[my_optind][0] != '-') ||
+ (argv[my_optind][1] == '\0')) {
+ char *tmp;
+ int i, j, k;
+
+ if(mode == '+') opt = -1;
+ else if(mode == '-') {
+ my_optarg = argv[my_optind++];
+ charind = 0;
+ opt = 1;
+ } else {
+ for(i=j=my_optind; i<argc; i++) if((argv[i][0] == '-') &&
+ (argv[i][1] != '\0')) {
+ my_optind=i;
+ opt=my_getopt(argc, argv, opts);
+ while(i > j) {
+ tmp=argv[--i];
+ for(k=i; k+1<my_optind; k++) argv[k]=argv[k+1];
+ argv[--my_optind]=tmp;
+ }
+ break;
+ }
+ if(i == argc) opt = -1;
+ }
+ } else {
+ charind++;
+ opt = my_getopt(argc, argv, opts);
+ }
+ if (my_optind > argc) my_optind = argc;
+ return opt;
+}
+
+/* this is the extended getopt_long{,_only}, with some GNU-like
+ * extensions. Implements _getopt_internal in case any programs
+ * expecting GNU libc getopt call it.
+ */
+
+int _my_getopt_internal(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only)
+{
+ char mode, colon_mode = *shortopts;
+ int shortoff = 0, opt = -1;
+
+ if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+';
+ else {
+ if((colon_mode = *shortopts) == ':') shortoff ++;
+ if(((mode = shortopts[shortoff]) == '+') || (mode == '-')) {
+ shortoff++;
+ if((colon_mode != ':') && ((colon_mode = shortopts[shortoff]) == ':'))
+ shortoff ++;
+ }
+ }
+ my_optarg = 0;
+ if((my_optind >= argc) ||
+ ((argv[my_optind][0] == '-') &&
+ (argv[my_optind][1] == '-') &&
+ (argv[my_optind][2] == '\0'))) {
+ my_optind++;
+ opt = -1;
+ } else if((argv[my_optind][0] != '-') ||
+ (argv[my_optind][1] == '\0')) {
+ char *tmp;
+ int i, j, k;
+
+ opt = -1;
+ if(mode == '+') return -1;
+ else if(mode == '-') {
+ my_optarg = argv[my_optind++];
+ return 1;
+ }
+ for(i=j=my_optind; i<argc; i++) if((argv[i][0] == '-') &&
+ (argv[i][1] != '\0')) {
+ my_optind=i;
+ opt=_my_getopt_internal(argc, argv, shortopts,
+ longopts, longind,
+ long_only);
+ while(i > j) {
+ tmp=argv[--i];
+ for(k=i; k+1<my_optind; k++)
+ argv[k]=argv[k+1];
+ argv[--my_optind]=tmp;
+ }
+ break;
+ }
+ } else if((!long_only) && (argv[my_optind][1] != '-'))
+ opt = my_getopt(argc, argv, shortopts);
+ else {
+ int charind, offset;
+ int found = 0, ind, hits = 0;
+
+ if(((my_optopt = argv[my_optind][1]) != '-') && ! argv[my_optind][2]) {
+ int c;
+
+ ind = shortoff;
+ while((c = shortopts[ind++])) {
+ if(((shortopts[ind] == ':') ||
+ ((c == 'W') && (shortopts[ind] == ';'))) &&
+ (shortopts[++ind] == ':'))
+ ind ++;
+ if(my_optopt == c) return my_getopt(argc, argv, shortopts);
+ }
+ }
+ offset = 2 - (argv[my_optind][1] != '-');
+ for(charind = offset;
+ (argv[my_optind][charind] != '\0') &&
+ (argv[my_optind][charind] != '=');
+ charind++);
+ for(ind = 0; longopts[ind].name && !hits; ind++)
+ if((strlen(longopts[ind].name) == (size_t) (charind - offset)) &&
+ (strncmp(longopts[ind].name,
+ argv[my_optind] + offset, charind - offset) == 0))
+ found = ind, hits++;
+ if(!hits) for(ind = 0; longopts[ind].name; ind++)
+ if(strncmp(longopts[ind].name,
+ argv[my_optind] + offset, charind - offset) == 0)
+ found = ind, hits++;
+ if(hits == 1) {
+ opt = 0;
+
+ if(argv[my_optind][charind] == '=') {
+ if(longopts[found].has_arg == 0) {
+ opt = '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], longopts[found].name);
+ } else {
+ my_optarg = argv[my_optind] + ++charind;
+ charind = 0;
+ }
+ } else if(longopts[found].has_arg == 1) {
+ if(++my_optind >= argc) {
+ opt = (colon_mode == ':') ? ':' : '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: option `--%s' requires an argument\n",
+ argv[0], longopts[found].name);
+ } else my_optarg = argv[my_optind];
+ }
+ if(!opt) {
+ if (longind) *longind = found;
+ if(!longopts[found].flag) opt = longopts[found].val;
+ else *(longopts[found].flag) = longopts[found].val;
+ }
+ my_optind++;
+ } else if(!hits) {
+ if(offset == 1) opt = my_getopt(argc, argv, shortopts);
+ else {
+ opt = '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: unrecognized option `%s'\n",
+ argv[0], argv[my_optind++]);
+ }
+ } else {
+ opt = '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: option `%s' is ambiguous\n",
+ argv[0], argv[my_optind++]);
+ }
+ }
+ if (my_optind > argc) my_optind = argc;
+ return opt;
+}
+
+int my_getopt_long(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind)
+{
+ return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 0);
+}
+
+int my_getopt_long_only(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind)
+{
+ return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 1);
+}
diff --git a/utils/open-isns/compat/my_getopt.h b/utils/open-isns/compat/my_getopt.h
new file mode 100644
index 0000000..34fcfe7
--- /dev/null
+++ b/utils/open-isns/compat/my_getopt.h
@@ -0,0 +1,69 @@
+/*
+ * my_getopt.h - interface to my re-implementation of getopt.
+ * Copyright 1997, 2000, 2001, 2002, Benjamin Sittler
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef MY_GETOPT_H_INCLUDED
+#define MY_GETOPT_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* UNIX-style short-argument parser */
+extern int my_getopt(int argc, char * argv[], const char *opts);
+
+extern int my_optind, my_opterr, my_optopt;
+extern char *my_optarg;
+
+struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* human-readable values for has_arg */
+#undef no_argument
+#define no_argument 0
+#undef required_argument
+#define required_argument 1
+#undef optional_argument
+#define optional_argument 2
+
+/* GNU-style long-argument parsers */
+extern int my_getopt_long(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind);
+
+extern int my_getopt_long_only(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind);
+
+extern int _my_getopt_internal(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MY_GETOPT_H_INCLUDED */
diff --git a/utils/open-isns/config.c b/utils/open-isns/config.c
new file mode 100644
index 0000000..cc470a4
--- /dev/null
+++ b/utils/open-isns/config.c
@@ -0,0 +1,278 @@
+/*
+ * Config file reader
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "isns.h"
+#include "util.h"
+#include "paths.h"
+
+/*
+ * iSNS configuration
+ */
+struct isns_config isns_config = {
+ /* Security parameters */
+ .ic_security = -1,
+ .ic_auth_key_file = ISNS_ETCDIR "/auth_key",
+ .ic_server_key_file = ISNS_ETCDIR "/server_key.pub",
+ .ic_client_keystore = "DB:",
+ .ic_control_socket = ISNS_RUNDIR "/isnsctl",
+ .ic_pidfile = ISNS_RUNDIR "/isnsd.pid",
+ .ic_local_registry_file = ISNS_DEFAULT_LOCAL_REGISTRY,
+
+ .ic_control_name = "isns.control",
+ .ic_control_key_file = ISNS_ETCDIR "/control.key",
+
+ .ic_registration_period = 3600, /* 1 hour */
+ .ic_scn_timeout = 60,
+ .ic_scn_retries = 3,
+
+ .ic_esi_max_interval = 600, /* 10 minutes */
+ .ic_esi_min_interval = 60, /* 1 minute */
+ .ic_esi_retries = 3,
+
+ .ic_auth = {
+ .replay_window = 300, /* 5 min clock skew */
+ .timestamp_jitter = 1, /* 1 sec timestamp jitter */
+ .allow_unknown_peers = 1,
+ },
+ .ic_network = {
+ .max_sockets = 1024,
+ .connect_timeout = 5,
+ .reconnect_timeout = 10,
+ .call_timeout = 60,
+ .udp_retrans_timeout = 10,
+ .tcp_retrans_timeout = 60,
+ .idle_timeout = 300,
+ },
+ .ic_dsa = {
+ .param_file = ISNS_ETCDIR "/dsa.params",
+ },
+};
+
+/*
+ * Default string values need to be dup'ed,
+ * so that later assignment does't try to free
+ * these strings.
+ */
+static inline void
+__isns_config_defaults(void)
+{
+ static int defaults_init = 1;
+
+ if (!defaults_init)
+ return;
+
+#define DUP(member) \
+ if (isns_config.member) \
+ isns_config.member = isns_strdup(isns_config.member)
+
+ DUP(ic_source_name);
+ DUP(ic_database);
+ DUP(ic_server_name);
+ DUP(ic_bind_address);
+ DUP(ic_auth_key_file);
+ DUP(ic_server_key_file);
+ DUP(ic_client_keystore);
+ DUP(ic_control_socket);
+ DUP(ic_pidfile);
+ DUP(ic_control_name);
+ DUP(ic_control_key_file);
+ DUP(ic_local_registry_file);
+ DUP(ic_dsa.param_file);
+
+#undef DUP
+
+ defaults_init = 0;
+}
+
+/*
+ * Read the iSNS configuration file
+ */
+int
+isns_read_config(const char *filename)
+{
+ FILE *fp;
+ char *name, *pos;
+
+ __isns_config_defaults();
+
+ if ((fp = fopen(filename, "r")) == NULL) {
+ perror(filename);
+ return -1;
+ }
+
+ while ((pos = parser_get_next_line(fp)) != NULL) {
+ pos[strcspn(pos, "#")] = '\0';
+
+ if (!(name = parser_get_next_word(&pos)))
+ continue;
+
+ isns_config_set(name, pos);
+ }
+
+ fclose(fp);
+
+ /* Massage the config file */
+ if (isns_config.ic_security < 0) {
+ /* By default, we will enable authentication
+ * whenever we find our private key, and
+ * the server's public key. */
+ if (access(isns_config.ic_auth_key_file, R_OK) == 0
+ && access(isns_config.ic_server_key_file, R_OK) == 0)
+ isns_config.ic_security = 1;
+ else
+ isns_config.ic_security = 0;
+ }
+
+ isns_init_names();
+
+ return 0;
+}
+
+int
+isns_config_set(const char *name, char *pos)
+{
+ char *value;
+
+ value = parser_get_rest_of_line(&pos);
+ if (value)
+ while (isspace(*value) || *value == '=')
+ ++value;
+ if (!strcasecmp(name, "HostName")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_host_name, value);
+ } else if (!strcasecmp(name, "SourceName")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_source_name, value);
+ } else if (!strcasecmp(name, "AuthName")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_auth_name, value);
+ } else if (!strcasecmp(name, "Database")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_database, value);
+ } else if (!strcasecmp(name, "ServerAddress")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_server_name, value);
+ } else if (!strcasecmp(name, "BindAddress")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_bind_address, value);
+ } else if (!strcasecmp(name, "ControlSocket")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_control_socket, value);
+ } else if (!strcasecmp(name, "PIDFile")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_pidfile, value);
+ } else if (!strcasecmp(name, "LocalRegistry")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_local_registry_file, value);
+ } else if (!strcasecmp(name, "RegistrationPeriod")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_registration_period = parse_timeout(value);
+ } else if (!strcasecmp(name, "SCNTimeout")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_scn_timeout = parse_timeout(value);
+ } else if (!strcasecmp(name, "SCNRetries")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_scn_retries = parse_int(value);
+ } else if (!strcasecmp(name, "SCNCallout")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_scn_callout, value);
+ } else if (!strcasecmp(name, "ESIMinInterval")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_esi_min_interval = parse_timeout(value);
+ } else if (!strcasecmp(name, "ESIMaxInterval")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_esi_max_interval = parse_timeout(value);
+ } else if (!strcasecmp(name, "ESIRetries")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_esi_retries = parse_int(value);
+ } else if (!strcasecmp(name, "DefaultDiscoveryDomain")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_use_default_domain = parse_int(value);
+ } else if (!strcasecmp(name, "SLPRegister")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_slp_register = parse_int(value);
+ } else if (!strcasecmp(name, "Security")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_security = parse_int(value);
+ } else if (!strcasecmp(name, "AuthKeyFile")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_auth_key_file, value);
+ } else if (!strcasecmp(name, "ServerKeyFile")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_server_key_file, value);
+ } else if (!strcasecmp(name, "ClientKeyStore")
+ || !strcasecmp(name, "KeyStore")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_client_keystore, value);
+ } else if (!strcasecmp(name, "Control.SourceName")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_control_name, value);
+ } else if (!strcasecmp(name, "Control.AuthKeyFile")) {
+ if (!value)
+ goto no_value;
+ isns_assign_string(&isns_config.ic_control_key_file, value);
+ } else if (!strcasecmp(name, "Auth.ReplayWindow")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_auth.replay_window = parse_timeout(value);
+ } else if (!strcasecmp(name, "Auth.TimestampJitter")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_auth.timestamp_jitter = parse_timeout(value);
+ } else if (!strcasecmp(name, "Network.MaxSockets")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_network.max_sockets = parse_timeout(value);
+ } else if (!strcasecmp(name, "Network.ConnectTimeout")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_network.connect_timeout = parse_timeout(value);
+ } else if (!strcasecmp(name, "Network.ReconnectTimeout")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_network.reconnect_timeout = parse_timeout(value);
+ } else if (!strcasecmp(name, "Network.CallTimeout")) {
+ if (!value)
+ goto no_value;
+ isns_config.ic_network.call_timeout = parse_timeout(value);
+ } else {
+ fprintf(stderr, "Unknown config item %s=%s\n", name, value);
+ }
+ return 0;
+
+no_value:
+ fprintf(stderr,
+ "*** Missing value in configuration assignment for %s ***\n",
+ name);
+ return -1;
+}
diff --git a/utils/open-isns/config.h.in b/utils/open-isns/config.h.in
new file mode 100644
index 0000000..b560bb0
--- /dev/null
+++ b/utils/open-isns/config.h.in
@@ -0,0 +1,103 @@
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <getopt.h> header file. */
+#undef HAVE_GETOPT_H
+
+/* Define if you have the `getopt_long' function. */
+#undef HAVE_GETOPT_LONG
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <openssl/crypto.h> header file. */
+#undef HAVE_OPENSSL_CRYPTO_H
+
+/* Define to 1 if you have the <slp.h> header file. */
+#undef HAVE_SLP_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define if you want to support iSNS authentication */
+#undef WITH_SECURITY
+
+/* Define if you want to support SLP discovery */
+#undef WITH_SLP
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+# undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
diff --git a/utils/open-isns/configure b/utils/open-isns/configure
new file mode 100644
index 0000000..2d1054b
--- /dev/null
+++ b/utils/open-isns/configure
@@ -0,0 +1,6727 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.63 for open-isns 0.90.
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+if test "x$CONFIG_SHELL" = x; then
+ if (eval ":") 2>/dev/null; then
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+
+ if test $as_have_required = yes && (eval ":
+(as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=\$LINENO
+ as_lineno_2=\$LINENO
+ test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
+ test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
+") 2> /dev/null; then
+ :
+else
+ as_candidate_shells=
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ case $as_dir in
+ /*)
+ for as_base in sh bash ksh sh5; do
+ as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
+ done;;
+ esac
+done
+IFS=$as_save_IFS
+
+
+ for as_shell in $as_candidate_shells $SHELL; do
+ # Try only shells that exist, to save several forks.
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { ("$as_shell") 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+_ASEOF
+}; then
+ CONFIG_SHELL=$as_shell
+ as_have_required=yes
+ if { "$as_shell" 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+(as_func_return () {
+ (exit $1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = "$1" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test $exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
+
+_ASEOF
+}; then
+ break
+fi
+
+fi
+
+ done
+
+ if test "x$CONFIG_SHELL" != x; then
+ for as_var in BASH_ENV ENV
+ do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ done
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+
+ if test $as_have_required = no; then
+ echo This script requires a shell more modern than all the
+ echo shells that I found on your system. Please install a
+ echo modern shell, or manually run the script under such a
+ echo shell if you do have one.
+ { (exit 1); exit 1; }
+fi
+
+
+fi
+
+fi
+
+
+
+(eval "as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0") || {
+ echo No shell found that supports shell functions.
+ echo Please tell bug-autoconf@gnu.org about your system,
+ echo including any error possibly output before this message.
+ echo This can help us improve future autoconf versions.
+ echo Configuration will now proceed without shell functions.
+}
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+exec 7<&0 </dev/null 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Identity of this package.
+PACKAGE_NAME='open-isns'
+PACKAGE_TARNAME='open-isns'
+PACKAGE_VERSION='0.90'
+PACKAGE_STRING='open-isns 0.90'
+PACKAGE_BUGREPORT=''
+
+ac_unique_file="isnsd.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+OPTIMIZE
+SLPLIBS
+SECLIBS
+GETOPTSRC
+SH
+SET_MAKE
+LN_S
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+EGREP
+GREP
+CPP
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+with_security
+with_slp
+enable_memdebug
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { $as_echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { $as_echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2
+ { (exit 1); exit 1; }; } ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; }
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ { $as_echo "$as_me: error: working directory cannot be determined" >&2
+ { (exit 1); exit 1; }; }
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ { $as_echo "$as_me: error: pwd does not report name of working directory" >&2
+ { (exit 1); exit 1; }; }
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2
+ { (exit 1); exit 1; }; }
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures open-isns 0.90 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/open-isns]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of open-isns 0.90:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-memdebug Enable malloc debugging
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-security Enable iSNS authentication - requires OpenSSL
+ --with-slp Enable SLP for server discovery - requires OpenSLP
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+open-isns configure 0.90
+generated by GNU Autoconf 2.63
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by open-isns $as_me 0.90, which was
+generated by GNU Autoconf 2.63. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args '$ac_arg'"
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ ac_site_file1=$CONFIG_SITE
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test -r "$ac_site_file"; then
+ { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:$LINENO: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:$LINENO: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_aux_dir=
+for ac_dir in aclocal "$srcdir"/aclocal; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in aclocal \"$srcdir\"/aclocal" >&5
+$as_echo "$as_me: error: cannot find install-sh or install.sh in aclocal \"$srcdir\"/aclocal" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:$LINENO: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compiler --version >&5") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compiler -v >&5") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compiler -V >&5") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { (ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+if test -z "$ac_file"; then
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if test "${ac_cv_objext+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_compiler_gnu=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ CFLAGS=""
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_c89=$ac_arg
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:$LINENO: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:$LINENO: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ { { $as_echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5
+$as_echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;}
+ { (exit 1); exit 1; }; }
+
+{ $as_echo "$as_me:$LINENO: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if test "${ac_cv_build+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ { { $as_echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+$as_echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+ { (exit 1); exit 1; }; }
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical build" >&5
+$as_echo "$as_me: error: invalid value of canonical build" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:$LINENO: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if test "${ac_cv_host+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical host" >&5
+$as_echo "$as_me: error: invalid value of canonical host" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:$LINENO: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:$LINENO: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_stdc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ eval "$as_ac_Header=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+ { $as_echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
+$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
+if test "${ac_cv_c_bigendian+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_bigendian=unknown
+ # See if we're dealing with a universal compiler.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifndef __APPLE_CC__
+ not a universal capable compiler
+ #endif
+ typedef int dummy;
+
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+
+ # Check for potential -arch flags. It is not universal unless
+ # there are some -arch flags. Note that *ppc* also matches
+ # ppc64. This check is also rather less than ideal.
+ case "${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS}" in #(
+ *-arch*ppc*|*-arch*i386*|*-arch*x86_64*) ac_cv_c_bigendian=universal;;
+ esac
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ if test $ac_cv_c_bigendian = unknown; then
+ # See if sys/param.h defines the BYTE_ORDER macro.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ #include <sys/param.h>
+
+int
+main ()
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
+ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
+ && LITTLE_ENDIAN)
+ bogus endian macros
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ # It does; now see whether it defined to BIG_ENDIAN or not.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ #include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_c_bigendian=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_c_bigendian=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ fi
+ if test $ac_cv_c_bigendian = unknown; then
+ # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <limits.h>
+
+int
+main ()
+{
+#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
+ bogus endian macros
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ # It does; now see whether it defined to _BIG_ENDIAN or not.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <limits.h>
+
+int
+main ()
+{
+#ifndef _BIG_ENDIAN
+ not big endian
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_c_bigendian=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_c_bigendian=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ fi
+ if test $ac_cv_c_bigendian = unknown; then
+ # Compile a test program.
+ if test "$cross_compiling" = yes; then
+ # Try to guess by grepping values from an object file.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+short int ascii_mm[] =
+ { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+ short int ascii_ii[] =
+ { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+ int use_ascii (int i) {
+ return ascii_mm[i] + ascii_ii[i];
+ }
+ short int ebcdic_ii[] =
+ { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+ short int ebcdic_mm[] =
+ { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+ int use_ebcdic (int i) {
+ return ebcdic_mm[i] + ebcdic_ii[i];
+ }
+ extern int foo;
+
+int
+main ()
+{
+return use_ascii (foo) == use_ebcdic (foo);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
+ ac_cv_c_bigendian=yes
+ fi
+ if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+ if test "$ac_cv_c_bigendian" = unknown; then
+ ac_cv_c_bigendian=no
+ else
+ # finding both strings is unlikely to happen, but who knows?
+ ac_cv_c_bigendian=unknown
+ fi
+ fi
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+ u.l = 1;
+ return u.c[sizeof (long int) - 1] == 1;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_c_bigendian=no
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_bigendian=yes
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5
+$as_echo "$ac_cv_c_bigendian" >&6; }
+ case $ac_cv_c_bigendian in #(
+ yes)
+ cat >>confdefs.h <<\_ACEOF
+#define WORDS_BIGENDIAN 1
+_ACEOF
+;; #(
+ no)
+ ;; #(
+ universal)
+
+cat >>confdefs.h <<\_ACEOF
+#define AC_APPLE_UNIVERSAL_BUILD 1
+_ACEOF
+
+ ;; #(
+ *)
+ { { $as_echo "$as_me:$LINENO: error: unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" >&5
+$as_echo "$as_me: error: unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
+ { (exit 1); exit 1; }; } ;;
+ esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:$LINENO: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:$LINENO: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+# Extract the first word of "sh", so it can be a program name with args.
+set dummy sh; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_SH+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $SH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SH="$SH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SH="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+SH=$ac_cv_path_SH
+if test -n "$SH"; then
+ { $as_echo "$as_me:$LINENO: result: $SH" >&5
+$as_echo "$SH" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for inline" >&5
+$as_echo_n "checking for inline... " >&6; }
+if test "${ac_cv_c_inline+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_inline=no
+for ac_kw in inline __inline__ __inline; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifndef __cplusplus
+typedef int foo_t;
+static $ac_kw foo_t static_foo () {return 0; }
+$ac_kw foo_t foo () {return 0; }
+#endif
+
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_c_inline=$ac_kw
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_inline" != no && break
+done
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5
+$as_echo "$ac_cv_c_inline" >&6; }
+
+
+case $ac_cv_c_inline in
+ inline | yes) ;;
+ *)
+ case $ac_cv_c_inline in
+ no) ac_val=;;
+ *) ac_val=$ac_cv_c_inline;;
+ esac
+ cat >>confdefs.h <<_ACEOF
+#ifndef __cplusplus
+#define inline $ac_val
+#endif
+_ACEOF
+ ;;
+esac
+
+if test "$GCC" = "yes"; then
+ CFLAGS="-Wall -fno-strict-aliasing $CFLAGS"
+ CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_stdc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5
+$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if test "${ac_cv_header_sys_wait_h+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+ int s;
+ wait (&s);
+ s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_sys_wait_h=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_sys_wait_h=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5
+$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SYS_WAIT_H 1
+_ACEOF
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+for ac_header in errno.h fcntl.h malloc.h stdlib.h string.h strings.h sys/time.h unistd.h locale.h getopt.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+# Check if socket() is in libsocket
+{ $as_echo "$as_me:$LINENO: checking for socket in -lsocket" >&5
+$as_echo_n "checking for socket in -lsocket... " >&6; }
+if test "${ac_cv_lib_socket_socket+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsocket $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_socket_socket=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_socket_socket=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_socket_socket" >&5
+$as_echo "$ac_cv_lib_socket_socket" >&6; }
+if test "x$ac_cv_lib_socket_socket" = x""yes; then
+ LIBS="$LIBS -lsocket"
+fi
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for getopt_long" >&5
+$as_echo_n "checking for getopt_long... " >&6; }
+if test "${ac_cv_func_getopt_long+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define getopt_long to an innocuous variant, in case <limits.h> declares getopt_long.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define getopt_long innocuous_getopt_long
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char getopt_long (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef getopt_long
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char getopt_long ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_getopt_long || defined __stub___getopt_long
+choke me
+#endif
+
+int
+main ()
+{
+return getopt_long ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_func_getopt_long=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_func_getopt_long=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_getopt_long" >&5
+$as_echo "$ac_cv_func_getopt_long" >&6; }
+if test "x$ac_cv_func_getopt_long" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GETOPT_LONG 1
+_ACEOF
+
+else
+ GETOPTSRC="$GETOPTSRC \$(top_srcdir)/compat/my_getopt.c"
+ CPPFLAGS="-I\$(top_srcdir)/compat/ $CPPFLAGS"
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GETOPT_H 1
+_ACEOF
+
+fi
+
+
+WITH_SECURITY=maybe
+
+# Check whether --with-security was given.
+if test "${with_security+set}" = set; then
+ withval=$with_security;
+ if test "x$withval" = "xno" -o "x$withval" = "xyes"; then
+ WITH_SECURITY=$withval
+ else
+ WITH_SECURITY=yes
+ CPPFLAGS="$CPPFLAGS -I${withval}"
+ LDFLAGS="$LDFLAGS -L${withval}"
+ fi
+
+
+fi
+
+
+if test "x$WITH_SECURITY" != "xno" ; then
+ # Check for openssl support - very primitive, we just
+ # check for the presence of crypto.h
+
+for ac_header in openssl/crypto.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ have_libcrypto=no
+fi
+
+done
+
+ { $as_echo "$as_me:$LINENO: checking for EVP_PKEY_new in -lcrypto" >&5
+$as_echo_n "checking for EVP_PKEY_new in -lcrypto... " >&6; }
+if test "${ac_cv_lib_crypto_EVP_PKEY_new+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcrypto $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char EVP_PKEY_new ();
+int
+main ()
+{
+return EVP_PKEY_new ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_crypto_EVP_PKEY_new=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_crypto_EVP_PKEY_new=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_crypto_EVP_PKEY_new" >&5
+$as_echo "$ac_cv_lib_crypto_EVP_PKEY_new" >&6; }
+if test "x$ac_cv_lib_crypto_EVP_PKEY_new" = x""yes; then
+ SECLIBS="-lcrypto"
+else
+ have_libcrypto=no
+fi
+
+
+ if test "x$have_libcrypto" != "xno" ; then
+
+cat >>confdefs.h <<\_ACEOF
+#define WITH_SECURITY 1
+_ACEOF
+
+ else
+ if test "x$WITH_SECURITY" = "xyes" ; then
+ { { $as_echo "$as_me:$LINENO: error: Security requested, but unable to find libcrypto" >&5
+$as_echo "$as_me: error: Security requested, but unable to find libcrypto" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+
+
+WITH_SLP=maybe
+
+# Check whether --with-slp was given.
+if test "${with_slp+set}" = set; then
+ withval=$with_slp;
+ if test "x$withval" = "xno" -o "x$withval" = "xyes"; then
+ WITH_SLP=$withval
+ else
+ WITH_SLP=yes
+ CPPFLAGS="$CPPFLAGS -I${withval}"
+ LDFLAGS="$LDFLAGS -L${withval}"
+ fi
+
+
+fi
+
+
+if test "x$WITH_SLP" != "xno" ; then
+ # Check for openslp support - very primitive
+
+for ac_header in slp.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ have_openslp=no
+fi
+
+done
+
+ { $as_echo "$as_me:$LINENO: checking for SLPOpen in -lslp" >&5
+$as_echo_n "checking for SLPOpen in -lslp... " >&6; }
+if test "${ac_cv_lib_slp_SLPOpen+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lslp $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SLPOpen ();
+int
+main ()
+{
+return SLPOpen ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_slp_SLPOpen=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_slp_SLPOpen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_slp_SLPOpen" >&5
+$as_echo "$ac_cv_lib_slp_SLPOpen" >&6; }
+if test "x$ac_cv_lib_slp_SLPOpen" = x""yes; then
+ SLPLIBS="-lslp"
+else
+ have_openslp=no
+fi
+
+
+ if test "x$have_openslp" != "xno" ; then
+
+cat >>confdefs.h <<\_ACEOF
+#define WITH_SLP 1
+_ACEOF
+
+ else
+ if test "x$WITH_SLP" = "xyes" ; then
+ { { $as_echo "$as_me:$LINENO: error: SLP requested, but unable to find openslp" >&5
+$as_echo "$as_me: error: SLP requested, but unable to find openslp" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+
+
+MEMDEBUG=
+# Check whether --enable-memdebug was given.
+if test "${enable_memdebug+set}" = set; then
+ enableval=$enable_memdebug;
+ if test "x$enableval" = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -DMEMDEBUG"
+ fi
+
+
+fi
+
+
+
+ac_config_files="$ac_config_files Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ test "x$cache_file" != "x/dev/null" &&
+ { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ cat confcache >$cache_file
+ else
+ { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+
+# Save the log message, to keep $[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by open-isns $as_me 0.90, which was
+generated by GNU Autoconf 2.63. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTION]... [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_version="\\
+open-isns config.status 0.90
+configured by $0, generated by GNU Autoconf 2.63,
+ with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ CONFIG_FILES="$CONFIG_FILES '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ { $as_echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { $as_echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+ *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp=
+ trap 'exit_status=$?
+ { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} ||
+{
+ $as_echo "$as_me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=' '
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\).*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\).*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
+ || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5
+$as_echo "$as_me: error: could not setup config files machinery" >&2;}
+ { (exit 1); exit 1; }; }
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[ ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_t=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_t"; then
+ break
+ elif $ac_last_try; then
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5
+$as_echo "$as_me: error: could not setup config headers machinery" >&2;}
+ { (exit 1); exit 1; }; }
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS "
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5
+$as_echo "$as_me: error: invalid tag $ac_tag" >&2;}
+ { (exit 1); exit 1; }; };;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
+$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ ac_file_inputs="$ac_file_inputs '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:$LINENO: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$tmp/stdin" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; } ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ { as_dir="$ac_dir"
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+$as_echo "$as_me: error: cannot create directory $as_dir" >&2;}
+ { (exit 1); exit 1; }; }; }
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p
+'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined." >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined." >&2;}
+
+ rm -f "$tmp/stdin"
+ case $ac_file in
+ -) cat "$tmp/out" && rm -f "$tmp/out";;
+ *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+ esac \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
+ } >"$tmp/config.h" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+ if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$tmp/config.h" "$ac_file" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5
+$as_echo "$as_me: error: could not create -" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ ;;
+
+
+ esac
+
+done # for ac_tag
+
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/utils/open-isns/configure.ac b/utils/open-isns/configure.ac
new file mode 100644
index 0000000..a02915b
--- /dev/null
+++ b/utils/open-isns/configure.ac
@@ -0,0 +1,118 @@
+AC_INIT(open-isns, [0.90])
+AC_CONFIG_SRCDIR([isnsd.c])
+AC_CONFIG_AUX_DIR([aclocal])
+
+AC_CONFIG_HEADER(config.h)
+
+AC_PROG_CC
+AC_CANONICAL_HOST
+AC_C_BIGENDIAN
+
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_PATH_PROG(SH, sh)
+
+dnl C Compiler features
+AC_C_INLINE
+if test "$GCC" = "yes"; then
+ CFLAGS="-Wall -fno-strict-aliasing $CFLAGS"
+ CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
+fi
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS([errno.h fcntl.h malloc.h stdlib.h string.h strings.h sys/time.h unistd.h locale.h getopt.h])
+
+# Check if socket() is in libsocket
+AC_CHECK_LIB(socket, socket, [LIBS="$LIBS -lsocket"])
+
+
+AC_SUBST(GETOPTSRC)
+AC_CHECK_FUNC(getopt_long, AC_DEFINE(HAVE_GETOPT_LONG, 1, [Define if you have the `getopt_long' function.]),
+ [GETOPTSRC="$GETOPTSRC \$(top_srcdir)/compat/my_getopt.c"
+ CPPFLAGS="-I\$(top_srcdir)/compat/ $CPPFLAGS"
+ AC_DEFINE(HAVE_GETOPT_H, 1, [Define if you have the <getopt.h> header file.])])
+
+WITH_SECURITY=maybe
+AC_ARG_WITH(security,
+ [ --with-security Enable iSNS authentication - requires OpenSSL],
+ [
+ if test "x$withval" = "xno" -o "x$withval" = "xyes"; then
+ WITH_SECURITY=$withval
+ else
+ WITH_SECURITY=yes
+ CPPFLAGS="$CPPFLAGS -I${withval}"
+ LDFLAGS="$LDFLAGS -L${withval}"
+ fi
+ ]
+)
+
+if test "x$WITH_SECURITY" != "xno" ; then
+ # Check for openssl support - very primitive, we just
+ # check for the presence of crypto.h
+ AC_CHECK_HEADERS([openssl/crypto.h],
+ ,
+ [have_libcrypto=no])
+ AC_CHECK_LIB(crypto, EVP_PKEY_new,
+ [SECLIBS="-lcrypto"],
+ [have_libcrypto=no])
+
+ if test "x$have_libcrypto" != "xno" ; then
+ AC_DEFINE(WITH_SECURITY, 1,
+ [Define if you want to support iSNS authentication])
+ else
+ if test "x$WITH_SECURITY" = "xyes" ; then
+ AC_MSG_ERROR([Security requested, but unable to find libcrypto])
+ fi
+ fi
+fi
+AC_SUBST(SECLIBS)
+
+WITH_SLP=maybe
+AC_ARG_WITH(slp,
+ [ --with-slp Enable SLP for server discovery - requires OpenSLP],
+ [
+ if test "x$withval" = "xno" -o "x$withval" = "xyes"; then
+ WITH_SLP=$withval
+ else
+ WITH_SLP=yes
+ CPPFLAGS="$CPPFLAGS -I${withval}"
+ LDFLAGS="$LDFLAGS -L${withval}"
+ fi
+ ]
+)
+
+if test "x$WITH_SLP" != "xno" ; then
+ # Check for openslp support - very primitive
+ AC_CHECK_HEADERS([slp.h],,
+ [have_openslp=no])
+ AC_CHECK_LIB(slp, SLPOpen,
+ [SLPLIBS="-lslp"],
+ [have_openslp=no])
+
+ if test "x$have_openslp" != "xno" ; then
+ AC_DEFINE(WITH_SLP, 1,
+ [Define if you want to support SLP discovery])
+ else
+ if test "x$WITH_SLP" = "xyes" ; then
+ AC_MSG_ERROR([SLP requested, but unable to find openslp])
+ fi
+ fi
+fi
+AC_SUBST(SLPLIBS)
+
+MEMDEBUG=
+AC_ARG_ENABLE(memdebug,
+ [ --enable-memdebug Enable malloc debugging],
+ [
+ if test "x$enableval" = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -DMEMDEBUG"
+ fi
+ ]
+)
+AC_SUBST(OPTIMIZE)
+
+AC_OUTPUT(Makefile)
diff --git a/utils/open-isns/db-file.c b/utils/open-isns/db-file.c
new file mode 100644
index 0000000..8adee06
--- /dev/null
+++ b/utils/open-isns/db-file.c
@@ -0,0 +1,615 @@
+/*
+ * iSNS object database
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "isns.h"
+#include "objects.h"
+#include "message.h"
+#include "util.h"
+#include "db.h"
+
+#define DBE_FILE_VERSION 1
+
+struct isns_db_file_info {
+ uint32_t db_version;
+ uint32_t db_last_eid;
+ uint32_t db_last_index;
+};
+
+struct isns_db_object_info {
+ uint32_t db_version;
+ char db_type[64];
+ uint32_t db_parent;
+ uint32_t db_state;
+ uint32_t db_flags;
+ uint32_t db_scn_mask;
+ /* reserved bytes */
+ uint32_t __db_reserved[15];
+};
+
+static int isns_dbe_file_sync(isns_db_t *);
+static int isns_dbe_file_reload(isns_db_t *);
+static int isns_dbe_file_store(isns_db_t *,
+ const isns_object_t *);
+static int isns_dbe_file_remove(isns_db_t *,
+ const isns_object_t *);
+static int __dbe_file_load_all(const char *,
+ isns_object_list_t *);
+
+/*
+ * Helper functions
+ */
+static const char *
+__path_concat(const char *dirname, const char *basename)
+{
+ static char pathname[PATH_MAX];
+
+ snprintf(pathname, sizeof(pathname), "%s/%s",
+ dirname, basename);
+ return pathname;
+}
+
+static const char *
+__print_index(uint32_t index)
+{
+ static char namebuf[32];
+
+ snprintf(namebuf, sizeof(namebuf), "%08x", index);
+ return namebuf;
+}
+
+static int
+__get_index(const char *name, uint32_t *result)
+{
+ char *end;
+
+ *result = strtoul(name, &end, 16);
+ if (*end)
+ return ISNS_INTERNAL_ERROR;
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Build path names for an object
+ */
+static const char *
+__dbe_file_object_path(const char *dirname, const isns_object_t *obj)
+{
+ return __path_concat(dirname, __print_index(obj->ie_index));
+}
+
+/*
+ * Build a path name for a temporary file.
+ * Cannot use __path_concat, because we need both names
+ * when storing objects
+ */
+static const char *
+__dbe_file_object_temp(const char *dirname, const isns_object_t *obj)
+{
+ static char pathname[PATH_MAX];
+
+ snprintf(pathname, sizeof(pathname), "%s/.%s",
+ dirname, __print_index(obj->ie_index));
+ return pathname;
+}
+
+/*
+ * Recursively create a directory
+ */
+static int
+__dbe_mkdir_path(const char *dirname)
+{
+ unsigned int true_len = strlen(dirname);
+ char *copy, *s;
+
+ copy = isns_strdup(dirname);
+
+ /* Walk up until we find a directory that exists */
+ while (1) {
+ s = strrchr(copy, '/');
+ if (s == NULL)
+ break;
+
+ *s = '\0';
+ if (access(copy, F_OK) == 0)
+ break;
+ }
+
+ while (strcmp(dirname, copy)) {
+ unsigned int len = strlen(copy);
+
+ /* Better safe than sorry */
+ isns_assert(len < true_len);
+
+ /* Put the next slash back in */
+ copy[len] = '/';
+
+ /* and try to create the directory */
+ if (mkdir(copy, 0700) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Write an object to a file
+ */
+static int
+__dbe_file_store_object(const char *dirname, const isns_object_t *obj)
+{
+ struct isns_db_object_info info;
+ const char *path = __dbe_file_object_path(dirname, obj);
+ const char *temp = __dbe_file_object_temp(dirname, obj);
+ buf_t *bp = NULL;
+ int status = ISNS_INTERNAL_ERROR;
+
+ isns_debug_state("DB: Storing object %u -> %s\n", obj->ie_index, path);
+ if (access(dirname, F_OK) < 0
+ && (errno != ENOENT || __dbe_mkdir_path(dirname) < 0)) {
+ isns_error("DB: Unable to create %s: %m\n",
+ dirname);
+ goto out;
+ }
+
+ bp = buf_open(temp, O_CREAT|O_TRUNC|O_WRONLY);
+ if (bp == NULL) {
+ isns_error("Unable to open %s: %m\n", temp);
+ goto out;
+ }
+
+ /* Encode the header info ... */
+ memset(&info, 0, sizeof(info));
+ info.db_version = htonl(DBE_FILE_VERSION);
+ info.db_state = htonl(obj->ie_state);
+ info.db_flags = htonl(obj->ie_flags);
+ info.db_scn_mask = htonl(obj->ie_scn_mask);
+ strcpy(info.db_type, obj->ie_template->iot_name);
+ if (obj->ie_container)
+ info.db_parent = htonl(obj->ie_container->ie_index);
+
+ if (!buf_put(bp, &info, sizeof(info)))
+ goto out;
+
+ /* ... and attributes */
+ status = isns_attr_list_encode(bp, &obj->ie_attrs);
+ if (status != ISNS_SUCCESS)
+ goto out;
+
+ /* Renaming an open file. NFS will hate this */
+ if (rename(temp, path) < 0) {
+ isns_error("Cannot rename %s -> %s: %m\n",
+ temp, path);
+ unlink(temp);
+ status = ISNS_INTERNAL_ERROR;
+ }
+
+out:
+ if (bp)
+ buf_close(bp);
+ return status;
+}
+
+/*
+ * Store all children of an object
+ */
+static int
+__dbe_file_store_children(const char *dirname, const isns_object_t *obj)
+{
+ int status = ISNS_SUCCESS;
+ unsigned int i;
+
+ for (i = 0; i < obj->ie_children.iol_count; ++i) {
+ isns_object_t *child;
+
+ child = obj->ie_children.iol_data[i];
+ status = __dbe_file_store_object(dirname, child);
+ if (status)
+ break;
+ status = __dbe_file_store_children(dirname, child);
+ if (status)
+ break;
+ }
+
+ return status;
+}
+
+/*
+ * Remove object and children
+ */
+static int
+__dbe_file_remove_object(const char *dirname, const isns_object_t *obj)
+{
+ const char *path = __dbe_file_object_path(dirname, obj);
+
+ isns_debug_state("DB: Purging object %u (%s)\n", obj->ie_index, path);
+ if (unlink(path) < 0)
+ isns_error("DB: Cannot remove %s: %m\n", path);
+ return ISNS_SUCCESS;
+}
+
+static int
+__dbe_file_remove_children(const char *dirname, const isns_object_t *obj)
+{
+ const isns_object_list_t *list = &obj->ie_children;
+ unsigned int i;
+
+ for (i = 0; i < list->iol_count; ++i)
+ __dbe_file_remove_object(dirname, list->iol_data[i]);
+
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Load an object from file
+ */
+static int
+__dbe_file_load_object(const char *filename, const char *basename,
+ isns_object_list_t *result)
+{
+ struct isns_db_object_info info;
+ isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
+ isns_object_template_t *tmpl;
+ isns_object_t *obj = NULL;
+ buf_t *bp = NULL;
+ uint32_t index;
+ int status;
+
+ bp = buf_open(filename, O_RDONLY);
+ if (bp == NULL) {
+ isns_error("Unable to open %s: %m\n", filename);
+ goto internal_error;
+ }
+
+ /* Decode the header ... */
+ if (!buf_get(bp, &info, sizeof(info)))
+ goto internal_error;
+ if (info.db_version != htonl(DBE_FILE_VERSION)) {
+ /* If we ever have to deal with a DB version
+ * upgrade, we could do it here. */
+ isns_fatal("Found iSNS database version %u; not supported\n",
+ ntohl(info.db_version));
+ }
+
+ /* ... and attributes */
+ status = isns_attr_list_decode(bp, &attrs);
+ if (status != ISNS_SUCCESS)
+ goto out;
+
+ /* Get the index from the file name */
+ status = __get_index(basename, &index);
+ if (status != ISNS_SUCCESS)
+ goto out;
+
+ tmpl = isns_object_template_by_name(info.db_type);
+ if (tmpl == NULL) {
+ isns_error("DB: Bad type name \"%s\" in object file\n",
+ info.db_type);
+ goto internal_error;
+ }
+
+ obj = isns_create_object(tmpl, &attrs, NULL);
+ if (obj == NULL)
+ goto internal_error;
+
+ obj->ie_state = ntohl(info.db_state);
+ obj->ie_flags = ntohl(info.db_flags) & ~(ISNS_OBJECT_DIRTY);
+ obj->ie_scn_mask = ntohl(info.db_scn_mask);
+ obj->ie_index = index;
+
+ /* Stash away the parent's index; we resolve them later on
+ * once we've loaded all objects */
+ obj->ie_container = (isns_object_t *) ntohl(info.db_parent);
+
+ isns_object_list_append(result, obj);
+
+out:
+ if (bp)
+ buf_close(bp);
+ if (obj)
+ isns_object_release(obj);
+ isns_attr_list_destroy(&attrs);
+ return status;
+
+internal_error:
+ isns_error("Unable to load %s: Internal error\n",
+ filename);
+ status = ISNS_INTERNAL_ERROR;
+ goto out;
+}
+
+/*
+ * Load contents of directory into our database.
+ *
+ * We take two passes over the directory. In the first pass, we load
+ * all regular files containing objects. The file names correspond to
+ * the DB index.
+ *
+ * In the second pass, we load all directories, containing children of
+ * an object. The directories names are formed by the object's index,
+ * with ".d" appended to it.
+ */
+static int
+__dbe_file_load_all(const char *dirpath, isns_object_list_t *result)
+{
+ struct dirent *dp;
+ DIR *dir;
+ int status = ISNS_SUCCESS;
+
+ if ((dir = opendir(dirpath)) == NULL) {
+ isns_error("DB: cannot open %s: %m\n", dirpath);
+ return ISNS_INTERNAL_ERROR;
+ }
+
+ while ((dp = readdir(dir)) != NULL) {
+ struct stat stb;
+ const char *path;
+
+ if (dp->d_name[0] == '.'
+ || !strcmp(dp->d_name, "DB"))
+ continue;
+
+ path = __path_concat(dirpath, dp->d_name);
+ if (lstat(path, &stb) < 0) {
+ isns_error("DB: cannot stat %s: %m\n", path);
+ status = ISNS_INTERNAL_ERROR;
+ } else
+ if (S_ISREG(stb.st_mode)) {
+ status = __dbe_file_load_object(path,
+ dp->d_name, result);
+ } else {
+ isns_debug_state("DB: ignoring %s\n", path);
+ }
+
+ if (status != ISNS_SUCCESS)
+ break;
+ }
+
+ closedir(dir);
+ return status;
+}
+
+/*
+ * Load and store DB metadata
+ */
+static int
+__dbe_file_write_info(isns_db_t *db)
+{
+ isns_db_backend_t *back = db->id_backend;
+ const char *path;
+ buf_t *bp;
+ int status = ISNS_INTERNAL_ERROR;
+
+ path = __path_concat(back->idb_name, "DB");
+ if ((bp = buf_open(path, O_CREAT|O_TRUNC|O_WRONLY)) == NULL) {
+ isns_error("Unable to write %s: %m\n", path);
+ goto out;
+ }
+
+ if (buf_put32(bp, DBE_FILE_VERSION)
+ && buf_put32(bp, db->id_last_eid)
+ && buf_put32(bp, db->id_last_index))
+ status = ISNS_SUCCESS;
+
+out:
+ if (bp)
+ buf_close(bp);
+ return status;
+}
+
+static int
+__dbe_file_load_info(isns_db_t *db)
+{
+ isns_db_backend_t *back = db->id_backend;
+ struct isns_db_file_info info;
+ const char *path;
+ buf_t *bp = NULL;
+ int status;
+
+ path = __path_concat(back->idb_name, "DB");
+ if ((bp = buf_open(path, O_RDONLY)) == NULL) {
+ status = ISNS_NO_SUCH_ENTRY;
+ goto out;
+ }
+
+ status = ISNS_INTERNAL_ERROR;
+ if (!buf_get32(bp, &info.db_version))
+ goto out;
+
+ if (info.db_version != DBE_FILE_VERSION) {
+ isns_error("DB file from unsupported version %04x\n",
+ info.db_version);
+ goto out;
+ }
+
+ if (buf_get32(bp, &info.db_last_eid)
+ && buf_get32(bp, &info.db_last_index)) {
+ db->id_last_eid = info.db_last_eid;
+ db->id_last_index = info.db_last_index;
+ status = ISNS_SUCCESS;
+ }
+
+out:
+ if (bp)
+ buf_close(bp);
+ return status;
+}
+
+/*
+ * Find object with the given index.
+ */
+static isns_object_t *
+__dbe_find_object(isns_object_list_t *list, uint32_t index)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ if (obj->ie_index == index)
+ return obj;
+ }
+ return NULL;
+}
+
+int
+isns_dbe_file_reload(isns_db_t *db)
+{
+ isns_db_backend_t *back = db->id_backend;
+ int status;
+ unsigned int i;
+
+ isns_debug_state("DB: loading all objects from %s\n",
+ back->idb_name);
+
+ if (access(back->idb_name, R_OK) < 0) {
+ if (errno == ENOENT) {
+ /* Empty database is okay */
+ return ISNS_NO_SUCH_ENTRY;
+ }
+ isns_error("Cannot open database %s: %m\n", back->idb_name);
+ return ISNS_INTERNAL_ERROR;
+ }
+
+ status = __dbe_file_load_info(db);
+ if (status)
+ return status;
+
+ status = __dbe_file_load_all(back->idb_name, db->id_objects);
+ if (status)
+ return status;
+
+ /* Resolve parent/child relationship for all nodes */
+ for (i = 0; i < db->id_objects->iol_count; ++i) {
+ isns_object_t *obj = db->id_objects->iol_data[i];
+ uint32_t index = (uint32_t) obj->ie_container;
+ isns_object_t *parent;
+
+ if (index == 0)
+ continue;
+
+ obj->ie_container = NULL;
+
+ parent = __dbe_find_object(db->id_objects, index);
+ if (parent == NULL) {
+ isns_warning("DB: object %u references "
+ "unknown container %u\n",
+ obj->ie_index,
+ index);
+ } else {
+ isns_object_attach(obj, parent);
+ }
+ }
+
+ /* Add objects to the appropriate lists */
+ for (i = 0; i < db->id_objects->iol_count; ++i) {
+ isns_object_template_t *tmpl;
+ isns_object_t *obj = db->id_objects->iol_data[i];
+
+ switch (obj->ie_state) {
+ case ISNS_OBJECT_STATE_MATURE:
+ isns_scope_add(db->id_global_scope, obj);
+ obj->ie_references++;
+
+ tmpl = obj->ie_template;
+ if (tmpl->iot_build_relation
+ && !tmpl->iot_build_relation(db, obj, NULL))
+ isns_warning("DB: cannot build relation for "
+ "object %u\n",
+ obj->ie_index);
+
+ if (obj->ie_relation)
+ isns_relation_add(db->id_relations,
+ obj->ie_relation);
+
+ if (ISNS_IS_ENTITY(obj))
+ isns_esi_register(obj);
+ break;
+
+ case ISNS_OBJECT_STATE_LIMBO:
+ isns_object_list_append(&db->id_limbo, obj);
+ break;
+
+ default:
+ isns_error("Unexpected object state %d in object %u "
+ "loaded from %s\n",
+ obj->ie_state, obj->ie_index,
+ back->idb_name);
+ }
+
+ /* Clear the dirty flag, which will be set when the
+ object is created. */
+ obj->ie_flags &= ~ISNS_OBJECT_DIRTY;
+ }
+
+ return ISNS_SUCCESS;
+}
+
+int
+isns_dbe_file_sync(isns_db_t *db)
+{
+ return __dbe_file_write_info(db);
+}
+
+int
+isns_dbe_file_store(isns_db_t *db, const isns_object_t *obj)
+{
+ isns_db_backend_t *back = db->id_backend;
+ int status;
+
+ if (obj->ie_index == 0) {
+ isns_error("DB: Refusing to store object with index 0\n");
+ return ISNS_INTERNAL_ERROR;
+ }
+
+ status = __dbe_file_store_object(back->idb_name, obj);
+ if (status == ISNS_SUCCESS)
+ status = __dbe_file_store_children(back->idb_name, obj);
+
+ return status;
+}
+
+int
+isns_dbe_file_remove(isns_db_t *db, const isns_object_t *obj)
+{
+ isns_db_backend_t *back = db->id_backend;
+ int status;
+
+ status = __dbe_file_remove_object(back->idb_name, obj);
+ if (status == ISNS_SUCCESS)
+ status = __dbe_file_remove_children(back->idb_name, obj);
+
+ return status;
+}
+
+/*
+ * Create the file backend
+ */
+isns_db_backend_t *
+isns_create_file_db_backend(const char *pathname)
+{
+ isns_db_backend_t *back;
+
+ isns_debug_state("Creating file DB backend (%s)\n", pathname);
+
+ back = isns_calloc(1, sizeof(*back));
+ back->idb_name = isns_strdup(pathname);
+ back->idb_reload = isns_dbe_file_reload;
+ back->idb_sync = isns_dbe_file_sync;
+ back->idb_store = isns_dbe_file_store;
+ back->idb_remove = isns_dbe_file_remove;
+
+ return back;
+}
+
diff --git a/utils/open-isns/db-policy.c b/utils/open-isns/db-policy.c
new file mode 100644
index 0000000..7f09cba
--- /dev/null
+++ b/utils/open-isns/db-policy.c
@@ -0,0 +1,185 @@
+/*
+ * Use database as policy and keystore
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include "isns.h"
+#include "security.h"
+#include "objects.h"
+#include "vendor.h"
+#include "util.h"
+#include "config.h"
+
+/*
+ * DB keystore
+ */
+typedef struct isns_db_keystore isns_db_keystore_t;
+struct isns_db_keystore {
+ isns_keystore_t sd_base;
+ isns_db_t * sd_db;
+ isns_object_t * sd_control;
+};
+
+/*
+ * Look up the policy object given its SPI
+ */
+isns_object_t *
+__isns_db_keystore_lookup(isns_db_keystore_t *store,
+ const char *name, size_t namelen)
+{
+ isns_attr_list_t keys = ISNS_ATTR_LIST_INIT;
+ char namebuf[256];
+
+ if (namelen >= sizeof(namebuf))
+ return NULL;
+ memcpy(namebuf, name, namelen);
+ namebuf[namelen] = '\0';
+
+ isns_attr_list_append_string(&keys,
+ OPENISNS_TAG_POLICY_SPI,
+ namebuf);
+ return isns_db_lookup(store->sd_db, NULL, &keys);
+}
+
+/*
+ * Load a DSA key from the DB store
+ */
+static EVP_PKEY *
+__isns_db_keystore_find(isns_keystore_t *store_base,
+ const char *name, size_t namelen)
+{
+#ifdef WITH_SECURITY
+ isns_db_keystore_t *store = (isns_db_keystore_t *) store_base;
+ isns_object_t *obj;
+ const void *key_data;
+ size_t key_size;
+
+ obj = __isns_db_keystore_lookup(store, name, namelen);
+ if (obj == NULL)
+ return NULL;
+
+ if (!isns_object_get_opaque(obj, OPENISNS_TAG_POLICY_KEY,
+ &key_data, &key_size))
+ return NULL;
+
+ return isns_dsa_decode_public(key_data, key_size);
+#else
+ return NULL;
+#endif
+}
+
+/*
+ * Retrieve policy from database
+ */
+static void
+__isns_db_keystore_copy_policy_string(isns_object_t *obj,
+ uint32_t tag, char **var)
+{
+ const char *value;
+
+ if (!isns_object_get_string(obj, tag, &value))
+ return;
+ isns_assign_string(var, value);
+}
+
+static void
+__isns_db_keystore_copy_policy_strings(isns_object_t *obj,
+ uint32_t tag, struct string_array *array)
+{
+ isns_attr_list_t *attrs = &obj->ie_attrs;
+ unsigned int i;
+
+ for (i = 0; i < attrs->ial_count; ++i) {
+ isns_attr_t *attr = attrs->ial_data[i];
+
+ if (attr->ia_tag_id != tag
+ || !ISNS_ATTR_IS_STRING(attr))
+ continue;
+ isns_string_array_append(array, attr->ia_value.iv_string);
+ }
+}
+
+static isns_policy_t *
+__isns_db_keystore_get_policy(isns_keystore_t *store_base,
+ const char *name, size_t namelen)
+{
+ isns_db_keystore_t *store = (isns_db_keystore_t *) store_base;
+ isns_policy_t *policy;
+ isns_object_t *obj;
+ uint32_t intval;
+
+ obj = __isns_db_keystore_lookup(store, name, namelen);
+ if (obj == NULL)
+ return NULL;
+
+ policy = __isns_policy_alloc(name, namelen);
+
+ /* retrieve policy bits from object */
+#if 0
+ __isns_db_keystore_copy_policy_string(obj,
+ OPENISNS_TAG_POLICY_SOURCE_NAME,
+ &policy->ip_source);
+#endif
+ __isns_db_keystore_copy_policy_string(obj,
+ OPENISNS_TAG_POLICY_ENTITY,
+ &policy->ip_entity);
+ __isns_db_keystore_copy_policy_string(obj,
+ OPENISNS_TAG_POLICY_DEFAULT_DD,
+ &policy->ip_dd_default);
+ __isns_db_keystore_copy_policy_strings(obj,
+ OPENISNS_TAG_POLICY_NODE_NAME,
+ &policy->ip_node_names);
+
+ if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_OBJECT_TYPE, &intval))
+ policy->ip_object_types = intval;
+ if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_NODE_TYPE, &intval))
+ policy->ip_node_types = intval;
+ if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_FUNCTIONS, &intval))
+ policy->ip_functions = intval;
+
+ return policy;
+}
+
+void
+__isns_db_keystore_change_notify(const isns_db_event_t *ev, void *handle)
+{
+ isns_db_keystore_t *store = handle;
+ isns_object_t *obj = ev->ie_object;
+
+ if (isns_object_get_entity(obj) == store->sd_control) {
+ isns_debug_auth("DB keystore: policy data was modified\n");
+ store->sd_base.ic_generation++;
+ }
+}
+
+isns_keystore_t *
+isns_create_db_keystore(isns_db_t *db)
+{
+ isns_db_keystore_t *store;
+ isns_object_t *entity;
+
+ isns_debug_auth("Creating DB keystore\n");
+ if (!(entity = isns_db_get_control(db))) {
+ isns_error("Could not create control entity in database\n");
+ return NULL;
+ }
+ isns_debug_auth("Control entity is 0x%08x\n", entity->ie_index);
+
+ store = isns_calloc(1, sizeof(*store));
+ store->sd_base.ic_name = "database key store";
+ store->sd_base.ic_find = __isns_db_keystore_find;
+ store->sd_base.ic_get_policy = __isns_db_keystore_get_policy;
+ store->sd_control = entity;
+ store->sd_db = db;
+
+ isns_register_callback(__isns_db_keystore_change_notify, store);
+
+ return (isns_keystore_t *) store;
+}
+
diff --git a/utils/open-isns/db.c b/utils/open-isns/db.c
new file mode 100644
index 0000000..c66dfbb
--- /dev/null
+++ b/utils/open-isns/db.c
@@ -0,0 +1,994 @@
+/*
+ * iSNS object database
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+
+#include "isns.h"
+#include "objects.h"
+#include "db.h"
+#include "util.h"
+
+enum {
+ IDT_INSERT,
+ IDT_REMOVE,
+ IDT_UPDATE
+};
+struct isns_db_trans {
+ struct isns_db_trans * idt_next;
+ int idt_action;
+ isns_object_t * idt_object;
+};
+
+/* Internal helpers */
+static int isns_db_sanity_check(isns_db_t *);
+static int isns_db_get_key_tags(const isns_attr_list_t *,
+ uint32_t *, unsigned int);
+static int isns_db_keyed_compare(const isns_object_t *,
+ const isns_attr_list_t *,
+ const uint32_t *, unsigned int);
+
+/*
+ * Open a database
+ */
+static isns_db_t *
+isns_db_create(isns_db_backend_t *backend)
+{
+ isns_db_t *db;
+
+ db = isns_calloc(1, sizeof(*db));
+ db->id_last_index = 1;
+ db->id_last_eid = 1;
+ db->id_backend = backend;
+ db->id_global_scope = isns_scope_alloc(db);
+ db->id_relations = isns_relation_soup_alloc();
+ db->id_objects = &db->__id_objects;
+
+ if (backend && backend->idb_reload) {
+ int status;
+
+ status = backend->idb_reload(db);
+ /* "No such entry" is returned when the DB
+ * is still empty. */
+ if (status != ISNS_SUCCESS
+ && status != ISNS_NO_SUCH_ENTRY) {
+ isns_error("Error loading database: %s\n",
+ isns_strerror(status));
+ /* FIXME: isns_db_free(db); */
+ return NULL;
+ }
+
+ isns_db_sanity_check(db);
+ }
+
+ return db;
+}
+
+isns_db_t *
+isns_db_open(const char *location)
+{
+ isns_db_backend_t *backend;
+
+ if (location == NULL) {
+ isns_debug_state("Using in-memory DB\n");
+ return isns_db_create(NULL);
+ }
+
+ if (location[0] == '/') {
+ backend = isns_create_file_db_backend(location);
+ } else
+ if (!strncmp(location, "file:", 5)) {
+ backend = isns_create_file_db_backend(location + 5);
+ } else {
+ isns_error("Unsupported database type \"%s\"\n",
+ location);
+ return NULL;
+ }
+
+ return isns_db_create(backend);
+}
+
+isns_db_t *
+isns_db_open_shadow(isns_object_list_t *list)
+{
+ isns_db_t *db;
+
+ if ((db = isns_db_create(NULL)) != NULL)
+ db->id_objects = list;
+ return db;
+}
+
+int
+isns_db_sanity_check(isns_db_t *db)
+{
+ unsigned int i;
+
+ i = 0;
+ while (i < db->id_objects->iol_count) {
+ isns_object_t *obj = db->id_objects->iol_data[i];
+
+ switch (obj->ie_state) {
+ case ISNS_OBJECT_STATE_MATURE:
+ /* Nothing yet. */
+ break;
+
+ case ISNS_OBJECT_STATE_LIMBO:
+ if (!ISNS_IS_ISCSI_NODE(obj)
+ && !ISNS_IS_PORTAL(obj)) {
+ isns_error("Unexpected object %u (%s) in limbo\n",
+ obj->ie_index,
+ obj->ie_template->iot_name);
+ isns_db_remove(db, obj);
+ }
+ break;
+
+ default:
+ isns_error("Unexpected object state %d in object %u (%s)\n",
+ obj->ie_state, obj->ie_index,
+ obj->ie_template->iot_name);
+ isns_db_remove(db, obj);
+ break;
+ }
+
+ i += 1;
+ }
+
+ return 1;
+}
+
+isns_object_t *
+isns_db_lookup(isns_db_t *db,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *keys)
+{
+ return isns_object_list_lookup(db->id_objects, tmpl, keys);
+}
+
+int
+isns_db_gang_lookup(isns_db_t *db,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *keys,
+ isns_object_list_t *result)
+{
+ return isns_object_list_gang_lookup(db->id_objects,
+ tmpl, keys, result);
+}
+
+/*
+ * Look up the storage node for the given source.
+ */
+isns_object_t *
+isns_db_lookup_source_node(isns_db_t *db,
+ const isns_source_t *source)
+{
+ isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
+ isns_object_t *node;
+
+ isns_attr_list_append_attr(&attrs, isns_source_attr(source));
+ node = isns_db_lookup(db, NULL, &attrs);
+ isns_attr_list_destroy(&attrs);
+
+ return node;
+}
+
+isns_object_t *
+isns_db_vlookup(isns_db_t *db,
+ isns_object_template_t *tmpl,
+ ...)
+{
+ isns_attr_list_t keys = ISNS_ATTR_LIST_INIT;
+ isns_object_t *obj = NULL;
+ va_list ap;
+
+ va_start(ap, tmpl);
+ while (1) {
+ const isns_tag_type_t *tag_type;
+ isns_value_t value;
+ uint32_t tag;
+
+ tag = va_arg(ap, unsigned int);
+ if (tag == 0)
+ break;
+
+ tag_type = isns_tag_type_by_id(tag);
+ if (tag_type == NULL) {
+ isns_error("isns_db_vlookup: unknown tag %u\n", tag);
+ goto out;
+ }
+
+ memset(&value, 0, sizeof(value));
+ value.iv_type = tag_type->it_type;
+ switch (tag_type->it_type->it_id) {
+ case ISNS_ATTR_TYPE_STRING:
+ value.iv_string = va_arg(ap, char *);
+ break;
+
+ case ISNS_ATTR_TYPE_INT32:
+ value.iv_int32 = va_arg(ap, int32_t);
+ break;
+
+ case ISNS_ATTR_TYPE_UINT32:
+ value.iv_int32 = va_arg(ap, uint32_t);
+ break;
+
+ case ISNS_ATTR_TYPE_IPADDR:
+ value.iv_ipaddr = *va_arg(ap, struct in6_addr *);
+ break;
+
+ default:
+ isns_error("isns_db_vlookup: unsupported tag type %s\n",
+ value.iv_type->it_name);
+ goto out;
+ }
+
+ isns_attr_list_append_value(&keys, tag, tag_type, &value);
+ }
+
+ obj = isns_db_lookup(db, tmpl, &keys);
+
+out:
+ isns_attr_list_destroy(&keys);
+ va_end(ap);
+ return obj;
+}
+
+/*
+ * Find the next matching object
+ *
+ * This implementation could be a lot simpler if the
+ * RFC didn't make things so awfully complicated.
+ * It could simply have mandated the use of the object
+ * index attribute, period.
+ */
+isns_object_t *
+__isns_db_get_next(const isns_object_list_t *list,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *current,
+ const isns_attr_list_t *scope)
+{
+ isns_object_t *next = NULL;
+ uint32_t tags[16];
+ unsigned int i;
+ int num_tags;
+
+ if (!tmpl)
+ return NULL;
+
+ /* Get the search attribute tags, and sort them.
+ * Note, these don't have to be the standard key
+ * attributes for a given object type; the RFC
+ * also permits index attributes.
+ */
+ num_tags = isns_db_get_key_tags(current, tags, 16);
+ if (num_tags < 0)
+ return NULL;
+
+ /*
+ * 5.6.5.3.
+ * If the TLV length of the Message Key Attribute(s) is zero,
+ * then the first object entry in the iSNS database matching the
+ * Message Key type SHALL be returned in the Message Key of the
+ * corresponding DevGetNextRsp message.
+ */
+ for (i = 0; i < current->ial_count; ++i) {
+ isns_attr_t *attr = current->ial_data[i];
+
+ if (!ISNS_ATTR_IS_NIL(attr))
+ goto non_nil;
+ }
+ current = NULL;
+non_nil:
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ if (obj->ie_template != tmpl)
+ continue;
+ if (scope && !isns_object_match(obj, scope))
+ continue;
+
+ /* compare returns -1 if the first list
+ * is "before" the second list, in terms of
+ * implicit ordering. */
+ if (current
+ && isns_db_keyed_compare(obj, current, tags, num_tags) <= 0) {
+ /* obj less than or equal to current */
+ continue;
+ }
+
+ if (next == NULL
+ || isns_db_keyed_compare(obj, &next->ie_attrs, tags, num_tags) < 0)
+ next = obj;
+ }
+
+ if (next)
+ isns_object_get(next);
+ return next;
+}
+
+isns_object_t *
+isns_db_get_next(isns_db_t *db,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *current,
+ const isns_attr_list_t *scope,
+ const isns_source_t *source)
+{
+ return __isns_db_get_next(db->id_objects,
+ tmpl, current, scope);
+}
+
+/*
+ * Get the search key tags
+ */
+static int
+isns_db_get_key_tags(const isns_attr_list_t *keys,
+ uint32_t *tags, unsigned int max_tags)
+{
+ unsigned int i;
+
+ /* Get the search attribute tags, and sort them */
+ for (i = 0; i < keys->ial_count; ++i) {
+ if (i >= 16)
+ return -1;
+ tags[i] = keys->ial_data[i]->ia_tag_id;
+ }
+
+ /* FIXME: qsort the list */
+ return i;
+}
+
+/*
+ * Helper function for GetNext
+ */
+static int
+isns_db_keyed_compare(const isns_object_t *obj,
+ const isns_attr_list_t *attrs,
+ const uint32_t *tags, unsigned int num_tags)
+{
+ int ind = 0;
+ unsigned int i;
+
+ for (i = 0; i < num_tags; ++i) {
+ isns_attr_t *attr1, *attr2;
+ uint32_t tag = tags[i];
+
+ if (!isns_attr_list_get_attr(&obj->ie_attrs, tag, &attr1))
+ attr1 = NULL;
+ if (!isns_attr_list_get_attr(attrs, tag, &attr2))
+ attr2 = NULL;
+ if (attr1 == attr2) {
+ ind = 0;
+ } else if (attr1 && attr2) {
+ ind = isns_attr_compare(attr1, attr2);
+ } else if (attr1 == NULL) {
+ ind = -1;
+ } else {
+ ind = 1;
+ }
+ if (ind)
+ break;
+ }
+ return ind;
+}
+
+uint32_t
+isns_db_allocate_index(isns_db_t *db)
+{
+ return db->id_last_index++;
+}
+
+/*
+ * Insert an object into the database.
+ */
+void
+__isns_db_insert(isns_db_t *db, isns_object_t *obj, unsigned int state)
+{
+ uint32_t idx_tag = obj->ie_template->iot_index;
+
+ switch (obj->ie_state) {
+ case ISNS_OBJECT_STATE_LIMBO:
+ /* The object was in limbo; now it goes
+ * live (again). It should have an index,
+ * and it should be on the global id_objects
+ * list too.
+ */
+ isns_assert(state == ISNS_OBJECT_STATE_MATURE);
+ isns_assert(obj->ie_index);
+ isns_assert(obj->ie_users > 1);
+ isns_object_list_remove(&db->id_limbo, obj);
+ break;
+
+ case ISNS_OBJECT_STATE_DEAD:
+ /* A DevAttrReg with the F_REPLACE bit set will cause
+ * the key object to be removed from the DB, which may
+ * kill it for good.
+ * The subsequent call to db_insert will assign a new
+ * index, and re-add it to the database.
+ */
+
+ case ISNS_OBJECT_STATE_LARVAL:
+ /* Larval objects can go either to mature or
+ * limbo state. */
+ obj->ie_index = db->id_last_index++;
+
+ if (idx_tag)
+ isns_object_set_uint32(obj,
+ idx_tag,
+ obj->ie_index);
+
+ isns_object_list_append(db->id_objects, obj);
+ break;
+
+ case ISNS_OBJECT_STATE_MATURE:
+ /* If we call db_insert on a mature object, treat
+ this as a NOP. */
+ isns_assert(state == ISNS_OBJECT_STATE_MATURE);
+ return;
+
+ default:
+ isns_error("Internal error: unexpected object %u (%s) "
+ "state %u in db_insert\n",
+ obj->ie_index,
+ obj->ie_template->iot_name,
+ obj->ie_state);
+ return;
+ }
+
+ obj->ie_state = state;
+
+ /* Add it to the global scope */
+ if (state == ISNS_OBJECT_STATE_MATURE) {
+ isns_scope_add(db->id_global_scope, obj);
+ obj->ie_references++;
+
+ /* See if this object represents a relationship
+ * (eg a portal group). */
+ if (obj->ie_template->iot_relation_type) {
+ if (!obj->ie_relation) {
+ isns_warning("DB: inserting %s object "
+ "without relation\n",
+ obj->ie_template->iot_name);
+ } else {
+ isns_relation_add(db->id_relations,
+ obj->ie_relation);
+ }
+ }
+
+ isns_mark_object(obj, ISNS_SCN_OBJECT_ADDED);
+ }
+
+ isns_debug_state("DB: added object %u (%s) state %u\n",
+ obj->ie_index,
+ obj->ie_template->iot_name,
+ obj->ie_state);
+
+ if (db->id_backend) {
+ db->id_backend->idb_store(db, obj);
+ db->id_backend->idb_sync(db);
+ }
+}
+
+void
+isns_db_insert(isns_db_t *db, isns_object_t *obj)
+{
+ __isns_db_insert(db, obj, ISNS_OBJECT_STATE_MATURE);
+}
+
+void
+isns_db_insert_limbo(isns_db_t *db, isns_object_t *obj)
+{
+ isns_assert(obj->ie_state == ISNS_OBJECT_STATE_LARVAL);
+ __isns_db_insert(db, obj, ISNS_OBJECT_STATE_LIMBO);
+}
+
+/*
+ * Save an object after updating it
+ */
+void
+isns_db_sync(isns_db_t *db)
+{
+ isns_object_list_t *list = db->id_objects;
+ unsigned int i, saved = 0;
+
+ if (!db->id_backend)
+ return;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ if (obj->ie_flags & ISNS_OBJECT_DIRTY) {
+ db->id_backend->idb_store(db, obj);
+ obj->ie_flags &= ~ISNS_OBJECT_DIRTY;
+ saved++;
+ }
+ }
+ if (saved)
+ db->id_backend->idb_sync(db);
+}
+
+/*
+ * Remove an object from the database.
+ * This is slow and inefficient, due to the use
+ * of an object array. We should at least use
+ * a linked list, or maybe even a hash one day.
+ */
+static void
+__isns_db_prepare_removal(isns_db_t *db, isns_object_t *obj)
+{
+ isns_object_t *child;
+
+ obj->ie_flags |= ISNS_OBJECT_DEAD;
+ isns_object_get(obj);
+
+ /* The node is dead; it's no longer interested in SCNs */
+ obj->ie_scn_mask = 0;
+
+ /* Trigger an SCN event. */
+ if (obj->ie_state == ISNS_OBJECT_STATE_MATURE)
+ isns_mark_object(obj, ISNS_SCN_OBJECT_REMOVED);
+
+ /* If the object represents a relation between
+ * two other objects, sever that relationship.
+ */
+ if (obj->ie_relation) {
+ isns_relation_remove(db->id_relations,
+ obj->ie_relation);
+ isns_relation_sever(obj->ie_relation);
+ isns_relation_release(obj->ie_relation);
+ obj->ie_relation = NULL;
+ }
+
+ /* Detach the object from its container */
+ isns_object_detach(obj);
+
+ /* Remove it from the database */
+ if (isns_scope_remove(db->id_global_scope, obj)) {
+ obj->ie_references--;
+ } else {
+ isns_warning("Unable to remove object from scope\n");
+ }
+
+ /* Recursively remove all children */
+ while (obj->ie_children.iol_count) {
+ child = obj->ie_children.iol_data[0];
+ __isns_db_prepare_removal(db, child);
+ }
+
+ isns_debug_state("DB: removed object %u (%s)\n",
+ obj->ie_index,
+ obj->ie_template->iot_name);
+
+ isns_object_list_append(&db->id_deferred, obj);
+ isns_object_release(obj);
+}
+
+int
+isns_db_remove(isns_db_t *db, isns_object_t *obj)
+{
+ isns_object_t *entity;
+ unsigned int i;
+
+ /* Don't even bother if the object was never added */
+ if (obj->ie_index == 0)
+ goto out;
+
+ /* Obtain the containing entity before removal */
+ entity = isns_object_get_entity(obj);
+
+ /* We don't remove the object for real yet;
+ * this will happen later during db_purge */
+ __isns_db_prepare_removal(db, obj);
+
+ /*
+ * 5.6.5.4.
+ * If all Nodes and Portals associated with a Network Entity are
+ * deregistered, then the Network Entity SHALL also be removed.
+ *
+ * If both the Portal and iSCSI Storage Node objects associated
+ * with a Portal Group object are removed, then that Portal Group
+ * object SHALL also be removed. The Portal Group object SHALL
+ * remain registered as long as either of its associated Portal
+ * or iSCSI Storage Node objects remain registered. If a deleted
+ * Storage Node or Portal object is subsequently re-registered,
+ * then a relationship between the re- registered object and
+ * an existing Portal or Storage Node object registration,
+ * indicated by the PG object, SHALL be restored.
+ */
+ if (ISNS_IS_ENTITY(obj))
+ goto out;
+
+ if (entity == NULL || !ISNS_IS_ENTITY(entity))
+ goto out;
+
+ /* Don't do this for the CONTROL entity. */
+ if (entity->ie_flags & ISNS_OBJECT_PRIVATE)
+ goto out;
+
+ /* Step 1: Purge all relationship objects (read: portal groups)
+ * where both referenced objects are dead.
+ */
+ for (i = 0; i < entity->ie_children.iol_count; ) {
+ isns_object_t *child = entity->ie_children.iol_data[i];
+
+ if (child->ie_relation
+ && isns_relation_is_dead(child->ie_relation)) {
+ __isns_db_prepare_removal(db, child);
+ continue;
+ }
+
+ i += 1;
+ }
+
+ /* Step 2: If all portals, nodes and PGs have been unregistered,
+ * the list of children should be empty. */
+ if (entity->ie_children.iol_count == 0) {
+ isns_debug_state("Last portal/node unregistered, removing entity\n");
+ __isns_db_prepare_removal(db, entity);
+ }
+
+out:
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Purge deregistered objects.
+ * If we find they're still part of some discovery
+ * domain, they're moved to id_limbo; otherwise we'll
+ * destroy them for good.
+ */
+void
+isns_db_purge(isns_db_t *db)
+{
+ isns_object_list_t *list = &db->id_deferred;
+ unsigned int i;
+
+ while (list->iol_count) {
+ isns_object_t *obj = list->iol_data[0];
+
+ if (obj->ie_references == 0) {
+ isns_debug_state("DB: destroying object %u (%s)\n",
+ obj->ie_index,
+ obj->ie_template->iot_name);
+
+ if (db->id_backend) {
+ db->id_backend->idb_remove(db, obj);
+ /* db->id_backend->idb_sync(db); */
+ }
+
+ isns_object_list_remove(db->id_objects, obj);
+ obj->ie_state = ISNS_OBJECT_STATE_DEAD;
+ } else if (obj->ie_state != ISNS_OBJECT_STATE_LIMBO) {
+ isns_debug_state("DB: moving object %u (%s) to purgatory - "
+ "%u references left\n",
+ obj->ie_index,
+ obj->ie_template->iot_name,
+ obj->ie_references);
+
+ isns_object_list_append(&db->id_limbo, obj);
+ obj->ie_state = ISNS_OBJECT_STATE_LIMBO;
+ isns_object_prune_attrs(obj);
+
+ if (db->id_backend) {
+ db->id_backend->idb_store(db, obj);
+ db->id_backend->idb_sync(db);
+ }
+ }
+
+ isns_object_list_remove(list, obj);
+ }
+
+ /* Brute force - look at all objects in limbo and kill those
+ * that went out of scope */
+ for (i = 0; i < db->id_limbo.iol_count; ) {
+ isns_object_t *obj = db->id_limbo.iol_data[i];
+
+ if (obj->ie_references == 0) {
+ isns_debug_state("DB: destroying object %u (%s)\n",
+ obj->ie_index,
+ obj->ie_template->iot_name);
+
+ if (db->id_backend) {
+ db->id_backend->idb_remove(db, obj);
+ /* db->id_backend->idb_sync(db); */
+ }
+
+ obj->ie_state = ISNS_OBJECT_STATE_DEAD;
+ isns_object_list_remove(&db->id_limbo, obj);
+ isns_object_list_remove(db->id_objects, obj);
+ continue;
+ }
+
+ i += 1;
+ }
+}
+
+/*
+ * Expire old entities
+ *
+ * This code is still rather simple, but once we start
+ * using ESI things get rather complex quickly.
+ */
+time_t
+isns_db_expire(isns_db_t *db)
+{
+ isns_object_list_t *list = db->id_objects;
+ time_t now = time(NULL), next_timeout;
+ unsigned int i = 0;
+
+ next_timeout = now + 3600;
+ if (isns_config.ic_registration_period == 0)
+ return next_timeout;
+
+ while (i < list->iol_count) {
+ isns_object_t *obj;
+ uint64_t stamp;
+ uint32_t period;
+
+ obj = list->iol_data[i];
+ if (!ISNS_IS_ENTITY(obj))
+ goto next;
+
+ if (!isns_object_get_uint32(obj,
+ ISNS_TAG_REGISTRATION_PERIOD,
+ &period)) {
+ isns_debug_state("No registration period for entity %u\n",
+ obj->ie_index);
+ goto next;
+ }
+
+ if (!isns_object_get_uint64(obj,
+ ISNS_TAG_TIMESTAMP,
+ &stamp)) {
+ isns_debug_state("No timestamp for entity %u\n",
+ obj->ie_index);
+ goto next;
+ }
+
+ stamp += period;
+ if (stamp <= now) {
+ /* removing the object will move one
+ * object from the tail to the free
+ * slot in the list. So don't increment
+ * the index here. */
+ isns_debug_state("Expiring entity %u\n", obj->ie_index);
+ isns_db_remove(db, obj);
+ goto next;
+ } else {
+ isns_debug_state("Entity %u will expire in %u sec\n",
+ obj->ie_index, (int) (stamp - now));
+ if (stamp < next_timeout)
+ next_timeout = stamp;
+ }
+
+next:
+ i += 1;
+ }
+
+ /* Send out SCN notifications.
+ * This makes sure we won't have extraneous references
+ * on expired objects when we reach db_purge. */
+ isns_flush_events();
+
+ return next_timeout;
+}
+
+/*
+ * Very special function to make sure we always have a
+ * CONTROL entity.
+ */
+isns_object_t *
+isns_db_get_control(isns_db_t *db)
+{
+ isns_attr_list_t keys = ISNS_ATTR_LIST_INIT;
+ isns_object_list_t *list = db->id_objects;
+ isns_object_t *found = NULL;
+ unsigned int i;
+
+ isns_attr_list_append_string(&keys,
+ ISNS_TAG_ENTITY_IDENTIFIER,
+ ISNS_ENTITY_CONTROL);
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj;
+
+ obj = list->iol_data[i];
+ if (!ISNS_IS_ENTITY(obj))
+ continue;
+ if (isns_object_match(obj, &keys)) {
+ obj->ie_users++;
+ found = obj;
+ goto done;
+ }
+ }
+
+ found = isns_create_object(&isns_entity_template,
+ &keys, NULL);
+ found->ie_flags |= ISNS_OBJECT_PRIVATE;
+ isns_db_insert(db, found);
+ isns_db_sync(db);
+
+done:
+ return found;
+}
+
+void
+isns_db_get_domainless(isns_db_t *db,
+ isns_object_template_t *tmpl,
+ isns_object_list_t *result)
+{
+ isns_object_list_t *list = db->id_objects;
+ unsigned int i;
+
+ if (!tmpl)
+ return;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ if (obj->ie_template == tmpl
+ && isns_bitvector_is_empty(obj->ie_membership))
+ isns_object_list_append(result, obj);
+ }
+}
+
+/*
+ * Create a relationship and store it in the DB
+ */
+void
+isns_db_create_relation(isns_db_t *db,
+ isns_object_t *relating_object,
+ unsigned int relation_type,
+ isns_object_t *subordinate_object1,
+ isns_object_t *subordinate_object2)
+{
+ isns_relation_t *rel;
+
+ rel = isns_create_relation(relating_object,
+ relation_type,
+ subordinate_object1,
+ subordinate_object2);
+ if (rel) {
+ isns_relation_add(db->id_relations, rel);
+ isns_relation_release(rel);
+ }
+}
+
+/*
+ * Get all objects related to @left through a relation
+ * of type @type.
+ */
+void
+isns_db_get_relationship_objects(isns_db_t *db,
+ const isns_object_t *left,
+ unsigned int relation_type,
+ isns_object_list_t *result)
+{
+ isns_relation_get_edge_objects(db->id_relations,
+ left, relation_type,
+ result);
+}
+
+/*
+ * Get the object relating left and right.
+ * Usually called to find the portal group connecting
+ * a portal and a storage node, or a DD connecting
+ * two storage nodes.
+ */
+isns_object_t *
+isns_db_get_relationship_object(isns_db_t *db,
+ const isns_object_t *left,
+ const isns_object_t *right,
+ unsigned int relation_type)
+{
+ isns_relation_t *rel;
+
+ /* Find a relation of the given type, connecting
+ * the two objects. */
+ rel = isns_relation_find_edge(db->id_relations,
+ left, right, relation_type);
+
+ if (rel == NULL)
+ return NULL;
+
+ return isns_object_get(rel->ir_object);
+}
+
+/*
+ * See if a relationship exists
+ */
+int
+isns_db_relation_exists(isns_db_t *db,
+ const isns_object_t *relating_object,
+ const isns_object_t *left,
+ const isns_object_t *right,
+ unsigned int relation_type)
+{
+ return isns_relation_exists(db->id_relations,
+ relating_object,
+ left, right, relation_type);
+}
+
+/*
+ * Debug helper
+ */
+void
+isns_db_print(isns_db_t *db, isns_print_fn_t *fn)
+{
+ const isns_object_list_t *list = db->id_objects;
+ unsigned int i;
+
+ fn("Dumping database contents\n"
+ "Backend: %s\n"
+ "Last EID: %u\n"
+ "Last Index: %u\n"
+ ,
+ db->id_backend->idb_name,
+ db->id_last_eid,
+ db->id_last_index);
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ fn("--------------\n"
+ "Object: index=%u type=<%s> state=%s",
+ obj->ie_index,
+ obj->ie_template->iot_name,
+ isns_object_state_string(obj->ie_state));
+ if (obj->ie_container)
+ fn(" parent=%u", obj->ie_container->ie_index);
+ if (obj->ie_flags & ISNS_OBJECT_DIRTY)
+ fn(" DIRTY");
+ if (obj->ie_flags & ISNS_OBJECT_PRIVATE)
+ fn(" PRIVATE");
+ fn("\n");
+ isns_attr_list_print(&obj->ie_attrs, fn);
+ }
+}
+
+/*
+ * Generate a "random" entity identifier. This is used when
+ * a DevAttrReg request does not specify an entity, and the
+ * client's policy doesn't specify one either.
+ */
+const char *
+isns_db_generate_eid(isns_db_t *db, char *buf, size_t size)
+{
+ snprintf(buf, size, "isns.entity.%04d", db->id_last_eid);
+ db->id_last_eid++;
+ return buf;
+}
+
+/*
+ * Highly primitive transaction handling.
+ * This is really just a hack for the iSNS server code,
+ * which wants to go along creating objects, and back out
+ * if something goes wrong.
+ */
+void
+isns_db_begin_transaction(isns_db_t *db)
+{
+ if (db->id_in_transaction) {
+ isns_error("isns_db_begin_transaction: running into pending transaction\n");
+ isns_db_rollback(db);
+ }
+ db->id_in_transaction = 1;
+}
+
+void
+isns_db_commit(isns_db_t *db)
+{
+ /* Nothing yet */
+ db->id_in_transaction = 0;
+}
+
+void
+isns_db_rollback(isns_db_t *db)
+{
+ /* Nothing yet */
+ db->id_in_transaction = 0;
+}
diff --git a/utils/open-isns/db.h b/utils/open-isns/db.h
new file mode 100644
index 0000000..148d930
--- /dev/null
+++ b/utils/open-isns/db.h
@@ -0,0 +1,147 @@
+/*
+ * iSNS object database
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_DB_H
+#define ISNS_DB_H
+
+#include "attrs.h"
+
+typedef struct isns_db_backend isns_db_backend_t;
+
+/*
+ * In-memory portion of object database.
+ * Stable storage is provided by different
+ * backends.
+ */
+struct isns_db {
+ isns_object_list_t * id_objects;
+ isns_object_list_t __id_objects;
+
+ isns_relation_soup_t * id_relations;
+
+ uint32_t id_last_eid;
+ uint32_t id_last_index;
+
+ isns_scope_t * id_global_scope;
+ isns_scope_t * id_default_scope;
+
+ isns_db_backend_t * id_backend;
+
+ unsigned int id_in_transaction : 1;
+ struct isns_db_trans * id_transact;
+
+ /* This is for objects in limbo. When a client
+ * calls DevAttrDereg, the object will first be
+ * placed on the id_deferred list.
+ * When we're done processing the message, we
+ * invoke isns_db_purge, which looks at these
+ * objects.
+ * - if the reference count is 1, the object
+ * is deleted.
+ * - otherwise, we assume the object is referenced
+ * by a discovery domain. In this case, we prune
+ * the attribute list down to the key attr(s)
+ * plus the index attribute, and move it to
+ * the id_limbo list.
+ */
+ isns_object_list_t id_deferred;
+ isns_object_list_t id_limbo;
+};
+
+
+struct isns_db_backend {
+ char * idb_name;
+
+ int (*idb_reload)(isns_db_t *);
+ int (*idb_sync)(isns_db_t *);
+ int (*idb_store)(isns_db_t *,
+ const isns_object_t *);
+ int (*idb_remove)(isns_db_t *,
+ const isns_object_t *);
+};
+
+extern isns_db_backend_t *isns_create_file_db_backend(const char *);
+extern isns_object_t * __isns_db_get_next(const isns_object_list_t *,
+ isns_object_template_t *,
+ const isns_attr_list_t *,
+ const isns_attr_list_t *);
+
+extern isns_relation_soup_t *isns_relation_soup_alloc(void);
+extern isns_relation_t *isns_create_relation(isns_object_t *relating_object,
+ unsigned int relation_type,
+ isns_object_t *subordinate_object1,
+ isns_object_t *subordinate_object2);
+extern void isns_relation_sever(isns_relation_t *);
+extern void isns_relation_release(isns_relation_t *);
+extern void isns_relation_add(isns_relation_soup_t *,
+ isns_relation_t *);
+extern void isns_relation_remove(isns_relation_soup_t *,
+ isns_relation_t *);
+extern isns_object_t * isns_relation_get_other(const isns_relation_t *,
+ const isns_object_t *);
+extern isns_relation_t *isns_relation_find_edge(isns_relation_soup_t *,
+ const isns_object_t *,
+ const isns_object_t *,
+ unsigned int);
+extern void isns_relation_halfspace(isns_relation_soup_t *,
+ const isns_object_t *,
+ unsigned int,
+ isns_object_list_t *);
+extern void isns_relation_get_edge_objects(isns_relation_soup_t *,
+ const isns_object_t *,
+ unsigned int,
+ isns_object_list_t *);
+extern int isns_relation_exists(isns_relation_soup_t *,
+ const isns_object_t *relating_object,
+ const isns_object_t *left,
+ const isns_object_t *right,
+ unsigned int relation_type);
+extern int isns_relation_is_dead(const isns_relation_t *);
+
+extern void isns_db_create_relation(isns_db_t *db,
+ isns_object_t *relating_object,
+ unsigned int relation_type,
+ isns_object_t *subordinate_object1,
+ isns_object_t *subordinate_object2);
+extern void isns_db_get_relationship_objects(isns_db_t *,
+ const isns_object_t *,
+ unsigned int relation_type,
+ isns_object_list_t *);
+extern isns_object_t * isns_db_get_relationship_object(isns_db_t *,
+ const isns_object_t *,
+ const isns_object_t *,
+ unsigned int relation_type);
+extern int isns_db_relation_exists(isns_db_t *db,
+ const isns_object_t *relating_object,
+ const isns_object_t *left,
+ const isns_object_t *right,
+ unsigned int relation_type);
+extern int isns_db_create_pg_relation(isns_db_t *,
+ isns_object_t *);
+
+extern isns_scope_t * isns_scope_for_call(isns_db_t *, const isns_simple_t *);
+extern isns_scope_t * isns_scope_alloc(isns_db_t *);
+extern void isns_scope_release(isns_scope_t *);
+extern void isns_scope_add(isns_scope_t *,
+ isns_object_t *);
+extern int isns_scope_remove(isns_scope_t *,
+ isns_object_t *);
+extern int isns_scope_gang_lookup(isns_scope_t *,
+ isns_object_template_t *,
+ const isns_attr_list_t *,
+ isns_object_list_t *);
+extern isns_object_t * isns_scope_get_next(isns_scope_t *,
+ isns_object_template_t *,
+ const isns_attr_list_t *current,
+ const isns_attr_list_t *scope);
+extern void isns_scope_get_related(isns_scope_t *,
+ const isns_object_t *,
+ unsigned int,
+ isns_object_list_t *);
+extern isns_db_t * isns_scope_get_db(const isns_scope_t *);
+
+
+#endif /* ISNS_DB_H */
diff --git a/utils/open-isns/dd.c b/utils/open-isns/dd.c
new file mode 100644
index 0000000..b392036
--- /dev/null
+++ b/utils/open-isns/dd.c
@@ -0,0 +1,1307 @@
+/*
+ * Handle DD registration/deregistration
+ *
+ * Discovery domains are weird, even in the context of
+ * iSNS. For once thing, all other objects have unique
+ * attributes; DDs attributes can appear several times.
+ * They should really have made each DD member an object
+ * in its own right.
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "attrs.h"
+#include "objects.h"
+#include "message.h"
+#include "security.h"
+#include "util.h"
+#include "db.h"
+
+#define DD_DEBUG
+
+enum {
+ ISNS_DD_MEMBER_ISCSI_NODE = 1,
+ ISNS_DD_MEMBER_IFCP_NODE,
+ ISNS_DD_MEMBER_PORTAL,
+};
+/* Must be zero/one: */
+enum {
+ NOTIFY_MEMBER_ADDED = 0,
+ NOTIFY_MEMBER_REMOVED = 1
+};
+
+typedef struct isns_dd isns_dd_t;
+typedef struct isns_dd_list isns_dd_list_t;
+typedef struct isns_dd_member isns_dd_member_t;
+
+struct isns_dd {
+ uint32_t dd_id;
+ char * dd_name;
+ uint32_t dd_features;
+ isns_dd_member_t * dd_members;
+
+ unsigned int dd_inserted : 1;
+
+ isns_object_t * dd_object;
+};
+
+struct isns_dd_member {
+ isns_dd_member_t * ddm_next;
+ unsigned int ddm_type;
+ isns_object_ref_t ddm_object;
+
+ unsigned int ddm_added : 1;
+ union {
+ uint32_t ddm_index;
+
+ /* Index must be first in all structs below.
+ * Yeah, I know. Aliasing is bad. */
+ struct isns_dd_portal {
+ uint32_t index;
+ isns_portal_info_t info;
+ } ddm_portal;
+ struct isns_dd_iscsi_node {
+ uint32_t index;
+ char * name;
+ } ddm_iscsi_node;
+ struct isns_dd_ifcp_node {
+ uint32_t index;
+ char * name;
+ } ddm_ifcp_node;
+ };
+};
+
+struct isns_dd_list {
+ unsigned int ddl_count;
+ isns_dd_t ** ddl_data;
+};
+
+/*
+ * List of all discovery domains.
+ * This duplicates the DD information from the database,
+ * but unfortunately this can't be helped - we need to
+ * have fast algorithms to compute the membership of a
+ * node, and the relative visibility of two nodes.
+ */
+static int isns_dd_list_initialized = 0;
+static isns_dd_list_t isns_dd_list;
+static uint32_t isns_dd_next_id = 1;
+
+static isns_dd_t * isns_dd_alloc(void);
+static isns_dd_t * isns_dd_clone(const isns_dd_t *);
+static void isns_dd_release(isns_dd_t *);
+static int isns_dd_parse_attrs(isns_dd_t *,
+ isns_db_t *, const isns_attr_list_t *,
+ const isns_dd_t *, int);
+static int isns_dd_remove_members(isns_dd_t *,
+ isns_db_t *,
+ isns_dd_t *);
+static void isns_dd_notify(const isns_dd_t *,
+ isns_dd_member_t *,
+ isns_dd_member_t *,
+ int);
+static void isns_dd_add_members(isns_dd_t *,
+ isns_db_t *,
+ isns_dd_t *);
+static void isns_dd_store(isns_db_t *, const isns_dd_t *, int);
+static void isns_dd_destroy(isns_db_t *, isns_dd_t *);
+static void isns_dd_insert(isns_dd_t *);
+static isns_dd_t * isns_dd_by_id(uint32_t);
+static isns_dd_t * isns_dd_by_name(const char *);
+static isns_dd_member_t * isns_dd_create_member(isns_object_t *);
+static inline void isns_dd_member_free(isns_dd_member_t *);
+static int isns_dd_remove_member(isns_dd_t *, isns_object_t *);
+static void isns_dd_list_resize(isns_dd_list_t *, unsigned int);
+static void isns_dd_list_insert(isns_dd_list_t *, isns_dd_t *);
+static void isns_dd_list_remove(isns_dd_list_t *, isns_dd_t *);
+
+static isns_object_t * isns_dd_get_member_object(isns_db_t *,
+ const isns_attr_t *, const isns_attr_t *,
+ int);
+
+/*
+ * Create DDReg messages
+ */
+isns_simple_t *
+isns_create_dd_registration(isns_client_t *clnt, const isns_attr_list_t *attrs)
+{
+ isns_simple_t *msg;
+ isns_attr_t *id_attr;
+
+ msg = isns_simple_create(ISNS_DD_REGISTER, clnt->ic_source, NULL);
+ if (msg == NULL)
+ return NULL;
+
+ /* If the caller specified a DD_ID, use it in the
+ * message key. */
+ if (isns_attr_list_get_attr(attrs, ISNS_TAG_DD_ID, &id_attr))
+ isns_attr_list_append_attr(&msg->is_message_attrs, id_attr);
+
+ isns_attr_list_copy(&msg->is_operating_attrs, attrs);
+ return msg;
+}
+
+isns_simple_t *
+isns_create_dd_deregistration(isns_client_t *clnt,
+ uint32_t dd_id, const isns_attr_list_t *attrs)
+{
+ isns_simple_t *msg;
+
+ msg = isns_simple_create(ISNS_DD_DEREGISTER, clnt->ic_source, NULL);
+ if (msg == NULL)
+ return NULL;
+
+ isns_attr_list_append_uint32(&msg->is_message_attrs,
+ ISNS_TAG_DD_ID, dd_id);
+
+ isns_attr_list_copy(&msg->is_operating_attrs, attrs);
+ return msg;
+}
+
+/*
+ * Process a DD registration
+ */
+int
+isns_process_dd_registration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
+{
+ isns_simple_t *reply = NULL;
+ isns_attr_list_t *keys = &call->is_message_attrs;
+ isns_attr_list_t *attrs = &call->is_operating_attrs;
+ isns_db_t *db = srv->is_db;
+ isns_dd_t *dd = NULL, *temp_dd = NULL;
+ isns_attr_t *attr;
+ uint32_t id = 0;
+ int status;
+
+ /*
+ * 5.6.5.9.
+ * The Message Key, if used, contains the DD_ID of the Discovery
+ * Domain to be registered. If the Message Key contains a DD_ID
+ * of an existing DD entry in the iSNS database, then the DDReg
+ * message SHALL attempt to update the existing entry. If the
+ * DD_ID in the Message Key (if used) does not match an existing
+ * DD entry, then the iSNS server SHALL reject the DDReg message
+ * with a status code of 3 (Invalid Registration).
+ */
+ switch (keys->ial_count) {
+ case 0:
+ /* Security: check if the client is allowed to
+ * create a discovery domain */
+ if (!isns_policy_validate_object_creation(call->is_policy,
+ call->is_source,
+ &isns_dd_template,
+ keys, attrs,
+ call->is_function))
+ goto unauthorized;
+ break;
+
+ case 1:
+ attr = keys->ial_data[0];
+ if (attr->ia_tag_id != ISNS_TAG_DD_ID)
+ goto reject;
+ if (ISNS_ATTR_IS_NIL(attr))
+ break;
+ if (!ISNS_ATTR_IS_UINT32(attr))
+ goto reject;
+
+ id = attr->ia_value.iv_uint32;
+ if (id == 0)
+ goto reject;
+
+ dd = isns_dd_by_id(id);
+ if (dd == NULL) {
+ isns_debug_state("DDReg for unknown ID=%u\n", id);
+ goto reject;
+ }
+
+ /* Security: check if the client is allowed to
+ * mess with this DD. */
+ isns_assert(dd->dd_object);
+ if (!isns_policy_validate_object_update(call->is_policy,
+ call->is_source,
+ dd->dd_object, attrs,
+ call->is_function))
+ goto unauthorized;
+
+ break;
+
+ default:
+ goto reject;
+ }
+
+ temp_dd = isns_dd_alloc();
+
+ /* Parse the attributes and build a DD object. */
+ status = isns_dd_parse_attrs(temp_dd, db, attrs, dd, 1);
+ if (status != ISNS_SUCCESS)
+ goto out;
+
+ if (dd == NULL) {
+ /* Create the DD, and copy the general information
+ * such asn features and symbolic name from temp_dd */
+ dd = isns_dd_clone(temp_dd);
+
+ /* Don't assign the attrs to the DD right away.
+ * First and foremost, they may be unsorted. Second,
+ * we really want to hand-pick through them due to
+ * the weird semantics mandated by the RFC. */
+ dd->dd_object = isns_create_object(&isns_dd_template, NULL, NULL);
+ if (dd->dd_object == NULL)
+ goto reject;
+
+ /* Insert new domain into database */
+ isns_db_insert(db, dd->dd_object);
+
+ /* Add it to the internal list. Assign DD_ID and
+ * symbolic name if none were given.
+ */
+ isns_dd_insert(dd);
+ } else {
+ if (!dd->dd_id)
+ dd->dd_id = temp_dd->dd_id;
+ dd->dd_features = temp_dd->dd_features;
+ isns_assign_string(&dd->dd_name, temp_dd->dd_name);
+ }
+
+ /* Send notifications. This must be done before merging
+ * the list of new members into the DD.
+ */
+ isns_dd_notify(dd, dd->dd_members, temp_dd->dd_members,
+ NOTIFY_MEMBER_ADDED);
+
+ /* Update the DD */
+ isns_dd_add_members(dd, db, temp_dd);
+
+ /* And add it to the database. */
+ isns_dd_store(db, dd, 0);
+
+ reply = isns_simple_create(ISNS_DD_REGISTER, srv->is_source, NULL);
+ isns_object_extract_all(dd->dd_object, &reply->is_operating_attrs);
+
+ status = ISNS_SUCCESS;
+
+out:
+ isns_dd_release(temp_dd);
+ isns_dd_release(dd);
+ *result = reply;
+ return status;
+
+reject:
+ status = ISNS_INVALID_REGISTRATION;
+ goto out;
+
+unauthorized:
+ status = ISNS_SOURCE_UNAUTHORIZED;
+ goto out;
+}
+
+/*
+ * Process a DD deregistration
+ */
+int
+isns_process_dd_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
+{
+ isns_simple_t *reply = NULL;
+ isns_attr_list_t *keys = &call->is_message_attrs;
+ isns_attr_list_t *attrs = &call->is_operating_attrs;
+ isns_db_t *db = srv->is_db;
+ isns_dd_t *dd = NULL, *temp_dd = NULL;
+ isns_attr_t *attr;
+ uint32_t id = 0;
+ int status;
+
+ /*
+ * 5.6.5.10.
+ * The Message Key Attribute for a DDDereg message is the DD
+ * ID for the Discovery Domain being removed or having members
+ * removed.
+ */
+ if (keys->ial_count != 1)
+ goto reject;
+
+ attr = keys->ial_data[0];
+ if (attr->ia_tag_id != ISNS_TAG_DD_ID
+ || ISNS_ATTR_IS_NIL(attr)
+ || !ISNS_ATTR_IS_UINT32(attr))
+ goto reject;
+
+ id = attr->ia_value.iv_uint32;
+ if (id == 0)
+ goto reject;
+
+ dd = isns_dd_by_id(id);
+ if (dd == NULL)
+ goto reject;
+
+ /* Security: check if the client is permitted to
+ * modify the DD object.
+ */
+ if (!isns_policy_validate_object_update(call->is_policy,
+ call->is_source,
+ dd->dd_object, attrs,
+ call->is_function))
+ goto unauthorized;
+
+ /*
+ * 5.6.5.10.
+ * If the DD ID matches an existing DD and there are
+ * no Operating Attributes, then the DD SHALL be removed and a
+ * success Status Code returned. Any existing members of that
+ * DD SHALL remain in the iSNS database without membership in
+ * the just-removed DD.
+ */
+ if (attrs->ial_count == 0) {
+ isns_dd_member_t *mp;
+
+ /* Zap the membership bit */
+ for (mp = dd->dd_members; mp; mp = mp->ddm_next) {
+ isns_object_t *obj = mp->ddm_object.obj;
+
+ isns_object_clear_membership(obj, dd->dd_id);
+ }
+
+ /* Notify all DD members that they will lose the other
+ * nodes. */
+ isns_dd_notify(dd, NULL, dd->dd_members, NOTIFY_MEMBER_REMOVED);
+
+ isns_dd_destroy(db, dd);
+ } else {
+ /* Parse the attributes and build a temporary DD object. */
+ temp_dd = isns_dd_alloc();
+ status = isns_dd_parse_attrs(temp_dd, db, attrs, dd, 0);
+ if (status != ISNS_SUCCESS)
+ goto out;
+
+ /* Update the DD object */
+ status = isns_dd_remove_members(dd, db, temp_dd);
+ if (status != ISNS_SUCCESS)
+ goto out;
+
+ /* Send notifications. This must be done before after
+ * updating the DD.
+ */
+ isns_dd_notify(dd, dd->dd_members, temp_dd->dd_members,
+ NOTIFY_MEMBER_REMOVED);
+
+ /* Store it in the database. */
+ isns_dd_store(db, dd, 1);
+ }
+
+ reply = isns_simple_create(ISNS_DD_DEREGISTER, srv->is_source, NULL);
+ status = ISNS_SUCCESS;
+
+out:
+ isns_dd_release(temp_dd);
+ isns_dd_release(dd);
+ *result = reply;
+ return status;
+
+reject:
+ status = ISNS_INVALID_DEREGISTRATION;
+ goto out;
+
+unauthorized:
+ status = ISNS_SOURCE_UNAUTHORIZED;
+ goto out;
+}
+
+static isns_dd_t *
+isns_dd_alloc(void)
+{
+ return isns_calloc(1, sizeof(isns_dd_t));
+}
+
+/*
+ * Allocate a clone of the orig_dd, but without
+ * copying the members.
+ */
+static isns_dd_t *
+isns_dd_clone(const isns_dd_t *orig_dd)
+{
+ isns_dd_t *dd;
+
+ dd = isns_dd_alloc();
+
+ dd->dd_id = orig_dd->dd_id;
+ dd->dd_features = orig_dd->dd_features;
+ dd->dd_object = isns_object_get(orig_dd->dd_object);
+ isns_assign_string(&dd->dd_name, orig_dd->dd_name);
+
+ return dd;
+}
+
+static void
+isns_dd_release(isns_dd_t *dd)
+{
+ isns_dd_member_t *member;
+
+ if (dd == NULL || dd->dd_inserted)
+ return;
+
+ while ((member = dd->dd_members) != NULL) {
+ dd->dd_members = member->ddm_next;
+ isns_dd_member_free(member);
+ }
+
+ if (dd->dd_object)
+ isns_object_release(dd->dd_object);
+
+ isns_free(dd->dd_name);
+ isns_free(dd);
+}
+
+static isns_dd_member_t *
+isns_dd_create_member(isns_object_t *obj)
+{
+ isns_dd_member_t *new;
+
+ new = isns_calloc(1, sizeof(*new));
+ new->ddm_added = 1;
+
+ if (ISNS_IS_ISCSI_NODE(obj))
+ new->ddm_type = ISNS_DD_MEMBER_ISCSI_NODE;
+ else if (ISNS_IS_PORTAL(obj))
+ new->ddm_type = ISNS_DD_MEMBER_PORTAL;
+ else if (ISNS_IS_FC_NODE(obj))
+ new->ddm_type = ISNS_DD_MEMBER_IFCP_NODE;
+ else {
+ isns_free(new);
+ return NULL;
+ }
+
+ isns_object_reference_set(&new->ddm_object, obj);
+ return new;
+}
+
+static inline void
+isns_dd_member_free(isns_dd_member_t *member)
+{
+ switch (member->ddm_type) {
+ case ISNS_DD_MEMBER_ISCSI_NODE:
+ isns_free(member->ddm_iscsi_node.name);
+ break;
+
+ case ISNS_DD_MEMBER_IFCP_NODE:
+ isns_free(member->ddm_ifcp_node.name);
+ break;
+ }
+
+ isns_object_reference_drop(&member->ddm_object);
+ isns_free(member);
+}
+
+void
+isns_dd_get_members(uint32_t dd_id, isns_object_list_t *list, int active_only)
+{
+ isns_dd_t *dd;
+ isns_dd_member_t *mp;
+
+ dd = isns_dd_by_id(dd_id);
+ if (dd == NULL)
+ return;
+
+ for (mp = dd->dd_members; mp; mp = mp->ddm_next) {
+ isns_object_t *obj = mp->ddm_object.obj;
+
+ if (active_only
+ && obj->ie_state != ISNS_OBJECT_STATE_MATURE)
+ continue;
+
+ isns_object_list_append(list, obj);
+ }
+}
+
+/*
+ * Helper function to remove a member referencing the given object
+ */
+static int
+isns_dd_remove_member(isns_dd_t *dd, isns_object_t *obj)
+{
+ isns_dd_member_t *mp, **pos;
+
+ pos = &dd->dd_members;
+ while ((mp = *pos) != NULL) {
+ if (mp->ddm_object.obj == obj) {
+ *pos = mp->ddm_next;
+ isns_dd_member_free(mp);
+ return 1;
+ } else {
+ pos = &mp->ddm_next;
+ }
+ }
+
+ return 0;
+}
+
+static void
+isns_dd_insert(isns_dd_t *dd)
+{
+ if (dd->dd_inserted)
+ return;
+
+ if (dd->dd_id == 0) {
+ uint32_t id = isns_dd_next_id;
+ unsigned int i;
+
+ for (i = 0; i < isns_dd_list.ddl_count; ++i) {
+ isns_dd_t *cur = isns_dd_list.ddl_data[i];
+
+ if (cur->dd_id > id)
+ break;
+ if (cur->dd_id == id)
+ ++id;
+ }
+ isns_debug_state("Allocated new DD_ID %d\n", id);
+ dd->dd_id = id;
+ isns_dd_next_id = id + 1;
+ }
+
+ /*
+ * When creating a new DD, if the DD_Symbolic_Name is
+ * not included in the Operating Attributes, or if it
+ * is included with a zero-length TLV, then the iSNS
+ * server SHALL provide a unique DD_Symbolic_Name value
+ * for the created DD. The assigned DD_Symbolic_Name
+ * value SHALL be returned in the DDRegRsp message.
+ */
+ if (dd->dd_name == NULL) {
+ char namebuf[64];
+
+ snprintf(namebuf, sizeof(namebuf), "isns.dd%u", dd->dd_id);
+ isns_assign_string(&dd->dd_name, namebuf);
+ }
+
+ isns_dd_list_insert(&isns_dd_list, dd);
+ dd->dd_inserted = 1;
+
+#ifdef DD_DEBUG
+ /* Safety first - make sure domains are sorted by DD_ID */
+ {
+ unsigned int i, prev_id = 0;
+
+ for (i = 0; i < isns_dd_list.ddl_count; ++i) {
+ isns_dd_t *cur = isns_dd_list.ddl_data[i];
+
+ isns_assert(cur->dd_id > prev_id);
+ prev_id = cur->dd_id;
+ }
+ }
+#endif
+}
+
+/*
+ * Resize the DD list
+ */
+#define LIST_SIZE(n) (((n) + 15) & ~15)
+void
+isns_dd_list_resize(isns_dd_list_t *list, unsigned int last_index)
+{
+ unsigned int new_size, cur_size;
+ isns_dd_t **new_data;
+
+ cur_size = LIST_SIZE(list->ddl_count);
+ new_size = LIST_SIZE(last_index + 1);
+ if (new_size < list->ddl_count)
+ return;
+
+ /* We don't use realloc here because we need
+ * to zero the new pointers anyway. */
+ new_data = isns_calloc(new_size, sizeof(void *));
+ isns_assert(new_data);
+
+ memcpy(new_data, list->ddl_data,
+ list->ddl_count * sizeof(void *));
+ isns_free(list->ddl_data);
+
+ list->ddl_data = new_data;
+ list->ddl_count = last_index + 1;
+}
+
+/*
+ * Find the insert position for a given DD ID.
+ * returns true iff the DD was found in the list.
+ */
+static int
+__isns_dd_list_find_pos(isns_dd_list_t *list, unsigned int id,
+ unsigned int *where)
+{
+ unsigned int hi, lo, md;
+
+ lo = 0;
+ hi = list->ddl_count;
+
+ /* binary search */
+ while (lo < hi) {
+ isns_dd_t *cur;
+
+ md = (lo + hi) / 2;
+ cur = list->ddl_data[md];
+
+ if (id == cur->dd_id) {
+ *where = md;
+ return 1;
+ }
+
+ if (id < cur->dd_id) {
+ hi = md;
+ } else {
+ lo = md + 1;
+ }
+ }
+
+ *where = hi;
+ return 0;
+}
+
+/*
+ * In-order insert
+ */
+static void
+isns_dd_list_insert(isns_dd_list_t *list, isns_dd_t *dd)
+{
+ unsigned int pos;
+
+ if (__isns_dd_list_find_pos(list, dd->dd_id, &pos)) {
+ isns_error("Internal error in %s: DD already listed\n",
+ __FUNCTION__);
+ return;
+ }
+
+ isns_dd_list_resize(list, list->ddl_count);
+ /* Shift the tail of the list to make room for new entry. */
+ memmove(list->ddl_data + pos + 1,
+ list->ddl_data + pos,
+ (list->ddl_count - pos - 1) * sizeof(void *));
+ list->ddl_data[pos] = dd;
+}
+
+/*
+ * Remove DD from list
+ */
+void
+isns_dd_list_remove(isns_dd_list_t *list, isns_dd_t *dd)
+{
+ unsigned int pos;
+
+ if (!__isns_dd_list_find_pos(list, dd->dd_id, &pos))
+ return;
+
+ /* Shift the tail of the list */
+ memmove(list->ddl_data + pos,
+ list->ddl_data + pos + 1,
+ (list->ddl_count - pos - 1) * sizeof(void *));
+ list->ddl_count -= 1;
+}
+
+isns_dd_t *
+isns_dd_by_id(uint32_t id)
+{
+ unsigned int i;
+
+ for (i = 0; i < isns_dd_list.ddl_count; ++i) {
+ isns_dd_t *dd = isns_dd_list.ddl_data[i];
+
+ if (dd && dd->dd_id == id)
+ return dd;
+ }
+
+ return NULL;
+}
+
+static isns_dd_t *
+isns_dd_by_name(const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < isns_dd_list.ddl_count; ++i) {
+ isns_dd_t *dd = isns_dd_list.ddl_data[i];
+
+ if (dd && !strcmp(dd->dd_name, name))
+ return dd;
+ }
+
+ return NULL;
+}
+
+/*
+ * Validate the operating attributes, which is surprisingly
+ * tedious for DDs. It appears as if the whole DD/DDset
+ * stuff has been slapped onto iSNS as an afterthought.
+ *
+ * DDReg has some funky rules about how eg iSCSI nodes
+ * can be identified by either name or index, and how they
+ * relate to each other. Unfortunately, the RFC is very vague
+ * in describing how to treat DDReg message that mix these
+ * two types of identification, except by saying they
+ * need to be consistent.
+ */
+static int
+isns_dd_parse_attrs(isns_dd_t *dd, isns_db_t *db,
+ const isns_attr_list_t *attrs,
+ const isns_dd_t *orig_dd,
+ int is_registration)
+{
+ isns_dd_member_t **tail;
+ const isns_dd_t *conflict;
+ unsigned int i;
+ int rv = ISNS_SUCCESS;
+
+ if (orig_dd) {
+ dd->dd_id = orig_dd->dd_id;
+ dd->dd_features = orig_dd->dd_features;
+ isns_assign_string(&dd->dd_name, orig_dd->dd_name);
+ }
+
+ isns_assert(dd->dd_members == NULL);
+ tail = &dd->dd_members;
+
+ for (i = 0; i < attrs->ial_count; ++i) {
+ isns_object_t *obj = NULL;
+ isns_attr_t *attr, *next = NULL;
+ const char *name;
+ uint32_t id;
+
+ attr = attrs->ial_data[i];
+
+ if (!isns_object_attr_valid(&isns_dd_template, attr->ia_tag_id))
+ return ISNS_INVALID_REGISTRATION;
+
+ switch (attr->ia_tag_id) {
+ case ISNS_TAG_DD_ID:
+ /* Ignore this attribute in DDDereg messages */
+ if (!is_registration)
+ continue;
+
+ /*
+ * 5.6.5.9.
+ * A DDReg message with no Message Key SHALL result
+ * in the attempted creation of a new Discovery Domain
+ * (DD). If the DD_ID attribute (with non-zero length)
+ * is included among the Operating Attributes in the
+ * DDReg message, then the new Discovery Domain SHALL be
+ * assigned the value contained in that DD_ID attribute.
+ *
+ * If the DD_ID is included in both the Message
+ * Key and Operating Attributes, then the DD_ID
+ * value in the Message Key MUST be the same as
+ * the DD_ID value in the Operating Attributes.
+ *
+ * Implementer's note: It's not clear why the standard
+ * makes an exception for the DD_ID, while all other
+ * index attributes are read-only.
+ */
+ if (ISNS_ATTR_IS_NIL(attr))
+ break;
+
+ id = attr->ia_value.iv_uint32;
+ if (dd->dd_id != 0) {
+ if (dd->dd_id != id)
+ goto invalid;
+ } else if ((conflict = isns_dd_by_id(id)) != NULL) {
+ isns_debug_state("DDReg: requested ID %d "
+ "clashes with existing DD (%s)\n",
+ id, conflict->dd_name);
+ goto invalid;
+ }
+ dd->dd_id = id;
+ break;
+
+ case ISNS_TAG_DD_SYMBOLIC_NAME:
+ /* Ignore this attribute in DDDereg messages */
+ if (!is_registration)
+ continue;
+
+ /*
+ * If the DD_Symbolic_Name is an operating
+ * attribute and its value is unique (i.e., it
+ * does not match the registered DD_Symbolic_Name
+ * for another DD), then the value SHALL be stored
+ * in the iSNS database as the DD_Symbolic_Name
+ * for the specified Discovery Domain. If the
+ * value for the DD_Symbolic_Name is not unique,
+ * then the iSNS server SHALL reject the attempted
+ * DD registration with a status code of 3
+ * (Invalid Registration).
+ */
+ if (ISNS_ATTR_IS_NIL(attr))
+ break;
+
+ name = attr->ia_value.iv_string;
+ if (dd->dd_name && strcmp(name, dd->dd_name)) {
+ isns_debug_state("DDReg: symbolic name conflict: "
+ "id=%d name=%s requested=%s\n",
+ dd->dd_id, dd->dd_name, name);
+ goto invalid;
+ }
+ if (dd->dd_name)
+ break;
+
+ if ((conflict = isns_dd_by_name(name)) != NULL) {
+ isns_debug_state("DDReg: requested symbolic name (%s) "
+ "clashes with existing DD (id=%d)\n",
+ name, conflict->dd_id);
+ goto invalid;
+ }
+ isns_assign_string(&dd->dd_name, name);
+ break;
+
+ case ISNS_TAG_DD_FEATURES:
+ /* Ignore this attribute in DDDereg messages */
+ if (!is_registration)
+ continue;
+
+ /*
+ * When creating a new DD, if the DD_Features
+ * attribute is not included in the Operating
+ * Attributes, then the iSNS server SHALL assign
+ * the default value. The default value for
+ * DD_Features is 0.
+ */
+ if (ISNS_ATTR_IS_UINT32(attr))
+ dd->dd_features = attr->ia_value.iv_uint32;
+ break;
+
+ case ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR:
+ /* portal address must be followed by port */
+ if (i + 1 >= attrs->ial_count)
+ goto invalid;
+
+ next = attrs->ial_data[i + 1];
+ if (next->ia_tag_id != ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT)
+ goto invalid;
+ i += 1;
+ /* fallthru to normal case */
+
+ case ISNS_TAG_DD_MEMBER_PORTAL_INDEX:
+ case ISNS_TAG_DD_MEMBER_ISCSI_INDEX:
+ case ISNS_TAG_DD_MEMBER_ISCSI_NAME:
+ case ISNS_TAG_DD_MEMBER_FC_PORT_NAME:
+ if (ISNS_ATTR_IS_NIL(attr))
+ goto invalid;
+
+ obj = isns_dd_get_member_object(db,
+ attr, next,
+ is_registration);
+ /* For a DD deregistration, it's okay if the
+ * object does not exist. */
+ if (obj == NULL && is_registration)
+ goto invalid;
+ break;
+
+ invalid:
+ rv = ISNS_INVALID_REGISTRATION;
+ continue;
+
+ }
+
+ if (obj) {
+ if (is_registration
+ && isns_object_test_membership(obj, dd->dd_id)) {
+ /* Duplicates are ignored */
+ isns_debug_state("Ignoring duplicate DD registration "
+ "for %s %u\n",
+ obj->ie_template->iot_name,
+ obj->ie_index);
+ } else {
+ /* This just adds the member to the temporary DD object,
+ * without changing any state in the database. */
+ isns_dd_member_t *new;
+
+ new = isns_dd_create_member(obj);
+ if (new) {
+ *tail = new;
+ tail = &new->ddm_next;
+ }
+ }
+ isns_object_release(obj);
+ }
+ }
+
+ return rv;
+}
+
+/*
+ * Helper function: extract live nodes from the DD member list
+ */
+static inline void
+isns_dd_get_member_nodes(isns_dd_member_t *members, isns_object_list_t *result)
+{
+ isns_dd_member_t *mp;
+
+ /* Extract iSCSI nodes from both list. */
+ for (mp = members; mp; mp = mp->ddm_next) {
+ isns_object_t *obj = mp->ddm_object.obj;
+
+ if (ISNS_IS_ISCSI_NODE(obj)
+ && obj->ie_state == ISNS_OBJECT_STATE_MATURE)
+ isns_object_list_append(result, obj);
+ }
+}
+
+void
+isns_dd_notify(const isns_dd_t *dd, isns_dd_member_t *unchanged,
+ isns_dd_member_t *changed, int removed)
+{
+ isns_object_list_t dd_objects = ISNS_OBJECT_LIST_INIT;
+ isns_object_list_t changed_objects = ISNS_OBJECT_LIST_INIT;
+ unsigned int i, j, event;
+
+ /* Extract iSCSI nodes from both list. */
+ isns_dd_get_member_nodes(unchanged, &dd_objects);
+ isns_dd_get_member_nodes(changed, &changed_objects);
+
+ /* Send a management SCN multicast to all
+ * control nodes that care. */
+ event = removed? ISNS_SCN_DD_MEMBER_REMOVED_MASK : ISNS_SCN_DD_MEMBER_ADDED_MASK;
+ for (i = 0; i < changed_objects.iol_count; ++i) {
+ isns_object_t *obj = changed_objects.iol_data[i];
+
+ isns_object_event(obj,
+ event | ISNS_SCN_MANAGEMENT_REGISTRATION_MASK,
+ dd->dd_object);
+ }
+
+#ifdef notagoodidea
+ /* Not sure - it may be good to send OBJECT ADDED/REMOVED instead
+ * of the DD membership messages. However, right now the SCN code
+ * will nuke all SCN registrations for a node when it sees a
+ * REMOVE event for it.
+ */
+ event = removed? ISNS_SCN_OBJECT_REMOVED_MASK : ISNS_SCN_OBJECT_ADDED_MASK;
+#endif
+
+ /* If we added an iscsi node, loop over all members
+ * and send unicast events to each iscsi node,
+ * informing them that a new member has been added/removed.
+ */
+ for (j = 0; j < changed_objects.iol_count; ++j) {
+ isns_object_t *changed = changed_objects.iol_data[j];
+
+ for (i = 0; i < dd_objects.iol_count; ++i) {
+ isns_object_t *obj = dd_objects.iol_data[i];
+
+ /* For member removal, do not send notifications
+ * if the two nodes are still visible to each
+ * other through a different discovery domain */
+ if (removed && isns_object_test_visibility(obj, changed))
+ continue;
+
+ /* Inform the old node that the new node was
+ * added/removed. */
+ isns_unicast_event(obj, changed, event, NULL);
+
+ /* Inform the new node that the old node became
+ * (in)accessible to it. */
+ isns_unicast_event(changed, obj, event, NULL);
+ }
+
+ /* Finally, inform each changed node of the other
+ * DD members that became (in)accessible to it. */
+ for (i = 0; i < changed_objects.iol_count; ++i) {
+ isns_object_t *obj = changed_objects.iol_data[i];
+
+ if (obj == changed)
+ continue;
+
+ if (removed && isns_object_test_visibility(obj, changed))
+ continue;
+
+ isns_unicast_event(changed, obj, event, NULL);
+ }
+ }
+}
+
+void
+isns_dd_add_members(isns_dd_t *dd, isns_db_t *db, isns_dd_t *new_dd)
+{
+ isns_dd_member_t *mp, **tail;
+
+ for (mp = new_dd->dd_members; mp; mp = mp->ddm_next) {
+ const char *node_name;
+ isns_object_t *obj = mp->ddm_object.obj;
+
+ /*
+ * If the Operating Attributes contain a DD
+ * Member iSCSI Name value for a Storage Node
+ * that is currently not registered in the iSNS
+ * database, then the iSNS server MUST allocate an
+ * unused iSCSI Node Index for that Storage Node.
+ * The assigned iSCSI Node Index SHALL be returned
+ * in the DDRegRsp message as the DD Member iSCSI
+ * Node Index. The allocated iSCSI Node Index
+ * value SHALL be assigned to the Storage Node
+ * if and when it registers in the iSNS database.
+ * [And likewise for portals]
+ */
+ if (obj->ie_index == 0)
+ isns_db_insert_limbo(db, obj);
+ mp->ddm_index = obj->ie_index;
+
+ /* Record the fact that the object is a member of
+ * this DD */
+ isns_object_mark_membership(obj, dd->dd_id);
+
+ switch (mp->ddm_type) {
+ case ISNS_DD_MEMBER_ISCSI_NODE:
+ if (isns_object_get_string(obj, ISNS_TAG_ISCSI_NAME, &node_name))
+ isns_assign_string(&mp->ddm_iscsi_node.name, node_name);
+
+ break;
+
+ case ISNS_DD_MEMBER_IFCP_NODE:
+ if (isns_object_get_string(obj, ISNS_TAG_FC_PORT_NAME_WWPN, &node_name))
+ isns_assign_string(&mp->ddm_ifcp_node.name, node_name);
+
+ break;
+
+ case ISNS_DD_MEMBER_PORTAL:
+ isns_portal_from_object(&mp->ddm_portal.info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ obj);
+ break;
+ }
+ }
+
+ /* Find the tail of the DD member list */
+ tail = &dd->dd_members;
+ while ((mp = *tail) != NULL)
+ tail = &mp->ddm_next;
+
+ /* Append the new list of members */
+ *tail = new_dd->dd_members;
+ new_dd->dd_members = NULL;
+}
+
+/*
+ * Remove members from a DD
+ */
+int
+isns_dd_remove_members(isns_dd_t *dd, isns_db_t *db, isns_dd_t *temp_dd)
+{
+ isns_dd_member_t *mp;
+
+ for (mp = temp_dd->dd_members; mp; mp = mp->ddm_next) {
+ isns_object_t *obj = mp->ddm_object.obj;
+
+ /* Clear the membership bit. If the object wasn't in this
+ * DD to begin with, bail out right away. */
+ if (!isns_object_clear_membership(obj, dd->dd_id)) {
+ isns_debug_state("DD dereg: object %d is not in this DD\n",
+ obj->ie_index);
+ continue;
+ }
+
+ if (!isns_dd_remove_member(dd, obj))
+ isns_error("%s: DD member not found in internal list\n",
+ __FUNCTION__);
+ }
+
+ return ISNS_SUCCESS;
+}
+
+void
+isns_dd_store(isns_db_t *db, const isns_dd_t *dd, int rewrite)
+{
+ isns_object_t *obj = dd->dd_object;
+ isns_dd_member_t *member;
+
+ if (rewrite)
+ isns_object_prune_attrs(obj);
+
+ isns_object_set_uint32(obj, ISNS_TAG_DD_ID, dd->dd_id);
+ isns_object_set_string(obj, ISNS_TAG_DD_SYMBOLIC_NAME, dd->dd_name);
+ isns_object_set_uint32(obj, ISNS_TAG_DD_FEATURES, dd->dd_features);
+
+ for (member = dd->dd_members; member; member = member->ddm_next) {
+ struct isns_dd_iscsi_node *node;
+ struct isns_dd_portal *portal;
+
+ if (!member->ddm_added && !rewrite)
+ continue;
+
+ switch (member->ddm_type) {
+ case ISNS_DD_MEMBER_ISCSI_NODE:
+ node = &member->ddm_iscsi_node;
+
+ isns_object_set_uint32(obj,
+ ISNS_TAG_DD_MEMBER_ISCSI_INDEX,
+ node->index);
+ if (node->name)
+ isns_object_set_string(obj,
+ ISNS_TAG_DD_MEMBER_ISCSI_NAME,
+ node->name);
+ break;
+
+ case ISNS_DD_MEMBER_PORTAL:
+ portal = &member->ddm_portal;
+
+ isns_object_set_uint32(obj,
+ ISNS_TAG_DD_MEMBER_PORTAL_INDEX,
+ portal->index);
+ if (portal->info.addr.sin6_family != AF_UNSPEC) {
+ isns_portal_to_object(&portal->info,
+ ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR,
+ ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT,
+ obj);
+ }
+ break;
+ }
+
+ member->ddm_added = 0;
+ }
+}
+
+/*
+ * Destroy a DD
+ * The caller should call isns_dd_release to free the DD object.
+ */
+void
+isns_dd_destroy(isns_db_t *db, isns_dd_t *dd)
+{
+ isns_db_remove(db, dd->dd_object);
+ isns_dd_list_remove(&isns_dd_list, dd);
+ dd->dd_inserted = 0;
+}
+
+int
+isns_dd_load_all(isns_db_t *db)
+{
+ isns_object_list_t list = ISNS_OBJECT_LIST_INIT;
+ unsigned int i;
+ int rc;
+
+ if (isns_dd_list_initialized)
+ return ISNS_SUCCESS;
+
+ rc = isns_db_gang_lookup(db, &isns_dd_template, NULL, &list);
+ if (rc != ISNS_SUCCESS)
+ return rc;
+
+ for (i = 0; i < list.iol_count; ++i) {
+ isns_object_t *obj = list.iol_data[i];
+ isns_dd_t *dd = NULL, *temp_dd = NULL;
+ isns_dd_member_t *mp;
+
+ temp_dd = isns_dd_alloc();
+
+ rc = isns_dd_parse_attrs(temp_dd, db, &obj->ie_attrs, NULL, 1);
+ if (rc) {
+ if (temp_dd->dd_id == 0) {
+ isns_error("Problem converting DD object (index 0x%x). No DD_ID\n",
+ obj->ie_index);
+ goto next;
+ }
+ isns_error("Problem converting DD %u. Proceeding anyway.\n",
+ temp_dd->dd_id);
+ } else {
+ isns_debug_state("Loaded DD %d from database\n", temp_dd->dd_id);
+ }
+
+ dd = isns_dd_clone(temp_dd);
+
+ dd->dd_object = isns_object_get(obj);
+
+ isns_dd_insert(dd);
+ isns_dd_add_members(dd, db, temp_dd);
+
+ /* Clear the ddm_added flag for all members, to
+ * prevent all information from being duplicated
+ * to the DB on the next DD modification. */
+ for (mp = dd->dd_members; mp; mp = mp->ddm_next)
+ mp->ddm_added = 0;
+
+next:
+ isns_dd_release(temp_dd);
+ }
+
+ isns_object_list_destroy(&list);
+ isns_dd_list_initialized = 1;
+ return ISNS_SUCCESS;
+}
+
+isns_object_t *
+isns_dd_get_member_object(isns_db_t *db, const isns_attr_t *key1,
+ const isns_attr_t *key2,
+ int create)
+{
+ isns_attr_list_t query = ISNS_ATTR_LIST_INIT;
+ isns_object_template_t *tmpl = NULL;
+ isns_object_t *obj;
+ isns_portal_info_t portal_info;
+ const char *key_string = NULL;
+ uint32_t key_index = 0;
+
+ switch (key1->ia_tag_id) {
+ case ISNS_TAG_DD_MEMBER_ISCSI_INDEX:
+ key_index = key1->ia_value.iv_uint32;
+ isns_attr_list_append_uint32(&query,
+ ISNS_TAG_ISCSI_NODE_INDEX,
+ key_index);
+ tmpl = &isns_iscsi_node_template;
+ break;
+
+ case ISNS_TAG_DD_MEMBER_ISCSI_NAME:
+ key_string = key1->ia_value.iv_string;
+ isns_attr_list_append_string(&query,
+ ISNS_TAG_ISCSI_NAME,
+ key_string);
+ tmpl = &isns_iscsi_node_template;
+ break;
+
+ case ISNS_TAG_DD_MEMBER_FC_PORT_NAME:
+ key_string = key1->ia_value.iv_string;
+ isns_attr_list_append_string(&query,
+ ISNS_TAG_FC_PORT_NAME_WWPN,
+ key_string);
+ tmpl = &isns_fc_port_template;
+ break;
+
+ case ISNS_TAG_DD_MEMBER_PORTAL_INDEX:
+ key_index = key1->ia_value.iv_uint32;
+ isns_attr_list_append_uint32(&query,
+ ISNS_TAG_PORTAL_INDEX,
+ key_index);
+ tmpl = &isns_portal_template;
+ break;
+
+ case ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR:
+ if (!isns_portal_from_attr_pair(&portal_info, key1, key2)
+ || !isns_portal_to_attr_list(&portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ &query))
+ return NULL;
+
+ key_string = isns_portal_string(&portal_info);
+ tmpl = &isns_portal_template;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ obj = isns_db_lookup(db, tmpl, &query);
+ if (!obj && create) {
+ if (!key_string) {
+ isns_debug_state("Attempt to register %s DD member "
+ "with unknown index %u\n",
+ tmpl->iot_name, key_index);
+ goto out;
+ }
+
+ obj = isns_create_object(tmpl, &query, NULL);
+ if (obj != NULL)
+ isns_debug_state("Created limbo object for "
+ "%s DD member %s\n",
+ tmpl->iot_name, key_string);
+ }
+
+out:
+ isns_attr_list_destroy(&query);
+ return obj;
+
+}
diff --git a/utils/open-isns/deregister.c b/utils/open-isns/deregister.c
new file mode 100644
index 0000000..3a7b7a6
--- /dev/null
+++ b/utils/open-isns/deregister.c
@@ -0,0 +1,271 @@
+/*
+ * Handle iSNS Device Deregistration
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "attrs.h"
+#include "objects.h"
+#include "message.h"
+#include "security.h"
+#include "util.h"
+#include "db.h"
+
+extern isns_source_t * isns_server_source;
+
+
+/*
+ * Create a registration, and set the source name
+ */
+static isns_simple_t *
+__isns_create_deregistration(isns_source_t *source, const isns_attr_list_t *attrs)
+{
+ isns_simple_t *simp;
+
+ simp = isns_simple_create(ISNS_DEVICE_DEREGISTER, source, NULL);
+ if (simp && attrs)
+ isns_attr_list_copy(&simp->is_operating_attrs, attrs);
+ return simp;
+}
+
+isns_simple_t *
+isns_create_deregistration(isns_client_t *clnt, const isns_attr_list_t *attrs)
+{
+ return __isns_create_deregistration(clnt->ic_source, attrs);
+}
+
+/*
+ * Get the next object identified by the operating attrs.
+ */
+static int
+isns_deregistration_get_next_object(isns_db_t *db,
+ struct isns_attr_list_scanner *st,
+ isns_object_list_t *result)
+{
+ isns_object_t *current;
+ int status;
+
+ status = isns_attr_list_scanner_next(st);
+ if (status)
+ return status;
+
+ /*
+ * 5.6.5.4.
+ * Valid Operating Attributes for DevDereg
+ * ---------------------------------------
+ * Entity Identifier
+ * Portal IP-Address & Portal TCP/UDP Port
+ * Portal Index
+ * iSCSI Name
+ * iSCSI Index
+ * FC Port Name WWPN
+ * FC Node Name WWNN
+ *
+ * In other words, deregistration is restricted to Entity,
+ * portal, and node
+ */
+ if (st->tmpl != &isns_entity_template
+ && st->tmpl != &isns_iscsi_node_template
+ && st->tmpl != &isns_portal_template)
+ return ISNS_INVALID_DEREGISTRATION;
+
+ /* Only key attrs allowed */
+ if (st->attrs.ial_count) {
+ /* MS Initiators send the Entity protocol along
+ * with the Entity Identifier. */
+ isns_debug_protocol("Client included invalid operating attrs "
+ "with %s:\n", st->tmpl->iot_name);
+ isns_attr_list_print(&st->attrs, isns_debug_protocol);
+ /* return ISNS_INVALID_DEREGISTRATION; */
+ }
+
+ /*
+ * 5.6.5.4
+ * Attempted deregistration of non-existing entries SHALL not
+ * be considered an isns_error.
+ */
+ current = isns_db_lookup(db, st->tmpl, &st->keys);
+ if (current != NULL) {
+ isns_object_list_append(result, current);
+ isns_object_release(current);
+ }
+
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Extract the list of objects to be deregistered from
+ * the list of operating attributes.
+ */
+static int
+isns_deregistration_get_objects(isns_simple_t *reg, isns_db_t *db,
+ isns_object_list_t *result)
+{
+ struct isns_attr_list_scanner state;
+ int status = ISNS_SUCCESS;
+
+ isns_attr_list_scanner_init(&state, NULL, &reg->is_operating_attrs);
+ state.index_acceptable = 1;
+ state.source = reg->is_source;
+
+ while (state.pos < state.orig_attrs.ial_count) {
+ status = isns_deregistration_get_next_object(db,
+ &state, result);
+
+ if (status == 0)
+ continue;
+
+ /* Translate error codes */
+ if (status == ISNS_NO_SUCH_ENTRY)
+ status = ISNS_SUCCESS;
+ else
+ if (status == ISNS_INVALID_REGISTRATION)
+ status = ISNS_INVALID_DEREGISTRATION;
+ break;
+ }
+
+ isns_attr_list_scanner_destroy(&state);
+ return status;
+}
+
+/*
+ * Process a deregistration
+ *
+ * Normally, you would expect that a deregistration removes the
+ * object from the database, and that's the end of the story.
+ * Unfortunately, someone added Discovery Domains to the protocol,
+ * requiring _some_ information to survive as long as an object
+ * is referenced by a discovery domain. Specifically, we need to
+ * retain the relationship between key attributes (eg iscsi node
+ * name) and the object index.
+ *
+ * Thus, deregistration consists of the following steps
+ * - the object is removed from the database's global scope,
+ * so that it's no longer visible to DB lookups.
+ *
+ * - the object is detached from its containing Network
+ * Entity.
+ *
+ * - all attributes except the key attr(s) and the index
+ * attribute are removed.
+ */
+int
+isns_process_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
+{
+ isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
+ isns_simple_t *reply = NULL;
+ isns_db_t *db = srv->is_db;
+ int status, dereg_status;
+ unsigned int i;
+
+ /* Get the objects to deregister */
+ status = isns_deregistration_get_objects(call, db, &objects);
+ if (status != ISNS_SUCCESS)
+ goto done;
+
+ /*
+ * 5.6.5.4
+ *
+ * For messages that change the contents of the iSNS database,
+ * the iSNS server MUST verify that the Source Attribute
+ * identifies either a Control Node or a Storage Node that is
+ * a part of the Network Entity containing the added, deleted,
+ * or modified objects.
+ */
+ /*
+ * Implementation note: this can be implemented either by
+ * explicitly checking the object's owner in isns_db_remove
+ * (which is what we do right now), or by matching only
+ * those objects that have the right owner anyway.
+ *
+ * The latter sounds like a better choice if the client
+ * uses NIL attributes, because it limits the scope of
+ * the operation; but then the RFC doesn't say whether
+ * this kind of deregistration would be valid at all.
+ */
+
+ /* Success: create a new simple message, and
+ * send it in our reply. */
+ reply = __isns_create_deregistration(srv->is_source, NULL);
+ if (reply == NULL) {
+ status = ISNS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ dereg_status = ISNS_SUCCESS;
+ for (i = 0; i < objects.iol_count; ++i) {
+ isns_object_t *obj = objects.iol_data[i];
+
+ /* Policy: check that the client is permitted
+ * to deregister this object */
+ if (!isns_policy_validate_object_access(call->is_policy,
+ call->is_source, obj,
+ call->is_function))
+ status = ISNS_SOURCE_UNAUTHORIZED;
+
+ if (status == ISNS_SUCCESS)
+ status = isns_db_remove(db, obj);
+ if (status != ISNS_SUCCESS) {
+ /*
+ * 5.7.5.4
+ *
+ * In the event of an error, this response message
+ * contains the appropriate status code as well
+ * as a list of objects from the original DevDereg
+ * message that were not successfully deregistered
+ * from the iSNS database. This list of objects
+ * is contained in the Operating Attributes
+ * of the DevDeregRsp message. Note that an
+ * attempted deregistration of a non-existent
+ * object does not constitute an isns_error, and
+ * non-existent entries SHALL not be returned
+ * in the DevDeregRsp message.
+ */
+ /*
+ * Implementation: right now this doesn't work
+ * at all, because isns_msg_set_error will
+ * discard the entire message except for the
+ * status word.
+ */
+ isns_debug_message("Failed to deregister object: %s (0x%04x)\n",
+ isns_strerror(status), status);
+
+ isns_object_extract_all(obj, &reply->is_operating_attrs);
+ dereg_status = status;
+ continue;
+ }
+
+ /*
+ * 5.7.5.4
+ * If all Nodes and Portals associated with a Network
+ * Entity are deregistered, then the Network Entity
+ * SHALL also be removed.
+ * [...]
+ * If both the Portal and iSCSI Storage Node objects
+ * associated with a Portal Group object are removed,
+ * then that Portal Group object SHALL also be removed.
+ * The Portal Group object SHALL remain registered
+ * as long as either of its associated Portal or
+ * iSCSI Storage Node objects remain registered. If a
+ * deleted Storage Node or Portal object is subsequently
+ * re-registered, then a relationship between the re-
+ * registered object and an existing Portal or Storage
+ * Node object registration, indicated by the PG object,
+ * SHALL be restored.
+ */
+ /* isns_db_remove takes care of removing dead entities,
+ * and dead portal groups.
+ */
+ }
+
+ if (status == ISNS_SUCCESS)
+ status = dereg_status;
+
+done:
+ isns_object_list_destroy(&objects);
+ *result = reply;
+ return status;
+}
diff --git a/utils/open-isns/doc/isns_config.5 b/utils/open-isns/doc/isns_config.5
new file mode 100644
index 0000000..5fbd26e
--- /dev/null
+++ b/utils/open-isns/doc/isns_config.5
@@ -0,0 +1,387 @@
+.TH ISNS_CONFIG 8 "11 May 2007"
+.SH NAME
+isns_config - iSNS configuration file
+.SH SYNOPSIS
+.B /etc/isns/isnsadm.conf
+.br
+.B /etc/isns/isnsd.conf
+.br
+.B /etc/isns/isnsdd.conf
+
+.SH DESCRIPTION
+All Open-iSNS utilities read their configuration
+from a file in
+.BR /etc/isns .
+There is a separate configuration file for each application,
+.BR isnsd ", " isnsadm ", and " isnsdd .
+The syntax and the set of supported options is identical,
+even though some options are specific to e.g. the server.
+Unless indicated, options are applicable to all utilities.
+.PP
+An Open-iSNS configuration file contains keyword-argument pairs,
+one per line. All keywords are case insensitive.
+.PP
+A
+.B #
+character introduces a comment, which extends until the
+end of the line. Empty lines are ignored.
+.PP
+There are no line continuations, and you cannot use quotes
+around arguments.
+.PP
+Some options specify timeout values, which are given in
+units of seconds by default. You can specify an explicit
+unit, however, such as
+.BR d " (days),
+.BR h " (hours),
+.BR m " (minutes), or
+.BR s " (seconds).
+.\" ------------------------------------------------------------------
+.SS Generic Options
+.TP
+.BR HostName
+By default, Open-iSNS applications will retrieve the machine's
+hostname using the
+.BR gethostname (3)
+system call, and use a DNS lookup to look up the canonical name.
+Using the
+.BR HostName
+option, you can overried this. This option is rarely needed.
+.TP
+.BR SourceName
+This option is mandatory for all Open-iSNS applications.
+This should be a name which identifies the client uniquely.
+There are two readings of RFC 4171; one requires that this
+is an iSCSI qualified name such as
+.BR iqn.2001-04.com.example.host ,
+whereas other language in the RFC suggests that this is
+pretty much a free-format string that just has to be
+unique (using e.g. the client's fully qualified domain name).
+.IP
+When using DSA authentication, Open-iSNS currently requires the source
+name to match the key identifier (SPI) of the client's public
+key.
+.IP
+If left empty, the source name is derived from the client's hostname.
+.TP
+.BR ServerAddress " (client):
+This options specifies the host name or address of
+the iSNS server to talk to. It can optionally be followed
+by a colon, and a port number.
+.IP
+Instead of a hostname, IPv4 or IPv6 addresses can be used.
+In order to avoid ambiguities, literal
+IPv6 addresses must be surrounded by square brackets,
+as in
+.BR [2001:4e5f::1] .
+.IP
+When specifying a port number, you can use either the
+numeric port, or a string name to be looked up in
+.BR /etc/services .
+When the port is omitted, it defaults to 3205, the IANA
+assigned port number of iSNS.
+.IP
+If the special string
+.B SLP:
+is used, the client will try to locate the iSNS server
+through SLP.
+.TP
+.BR SLPRegister " (server):
+If set to 1, the iSNS daemon will register itself will
+the SLP service. This allows clients to contact the
+server without having to configure its address
+statically.
+.TP
+.BR PIDFile " (server):
+This specifies the name of the server's PID file, which is
+.B /var/run/isnsd.pid
+by default.
+.\" ------------------------------------------------------------------
+.SS Database Related Options
+These options apply to the iSNS server only, and control operation
+of the iSNS database.
+.TP
+.BR Database
+This option is used to specify how the database is stored.
+Setting this to an absolute path name will make
+.B isnsd
+keep its database in the specified directory.
+.IP
+If you leave this empty,
+.B isnsd
+will keep its database in memory.
+This is also the default setting.
+.TP
+.BR DefaultDiscoveryDomain
+iSNS scopes visibility of other nodes using so-called
+Discovery Domains. A storage node A will only "see"
+storage node B, if both are members of the same
+discovery domain.
+.IP
+So if a storage node is registered which is not part of
+any discovery domain, it will not see any other nodes.
+.IP
+By setting
+.BR DefaultDiscoveryDomain=1 ,
+you can tell isnsd to create a virtual "default discovery domain", which
+holds all nodes that are not part of any administratively configured
+discovery domain.
+.IP
+By default, there is no default discovery domain.
+.TP
+.BR RegistrationPeriod
+The iSNS server can purge registered entities after a certain period
+of inactivity. This is called the registration period. Clients who
+register objects are supposed to refresh their registration within
+this period.
+.IP
+The default value is 1 hour. Setting it to 0 disables expiry
+of entities from the database.
+.TP
+.BR ESIRetries
+Open-iSNS is able to monitor the reachability of storage nodes
+and their portals by using a protocol feature called ESI
+(Entity status inquiry). Clients request ESI monitoring by
+registering an ESI port along with each portal. The server
+will send ESI messages to these portals at regular intervals.
+If the portal fails to reply several times in a row, it is
+considered dead, and will be removed from the database.
+.IP
+.B ESIRetries
+specifies the maximum number of attempts the server will make
+at contacting the portal before pronouncing it dead. If set
+to 0, the server will disable ESI and reject any registrations
+that specify an ESI port with an error code of "ESI not
+supported".
+.IP
+The default value is 3.
+.TP
+.BR ESIMinInterval
+This timeout value specifies the minimum ESI interval.
+If a client requests an ESI interval less than this value,
+it is silently rounded up.
+.IP
+The default value is 60 seconds.
+.TP
+.BR ESIMaxInterval
+This timeout value specifies the maximum ESI interval.
+If a client requests an ESI interval greater than this value,
+it is silently rounded down.
+.IP
+The default value is 10 minutes.
+.IP
+The maximum ESI interval must not exceed half the value
+of the registration period.
+.TP
+.B SCNRetries
+iSNS clients can register to receive State Change Notification
+(SCN) messages to learn about changes in the iSNS database.
+This value specifies how often the server will try to retransmit
+an SCN message until giving up.
+.IP
+The default value is 3.
+.TP
+.B SCNCallout
+This is the path name of a helper program that
+.B isnsdd
+will invoke whenever it processes a state change notification from the
+server. The helper program will be invoked with an argument indicating
+the type of event, being one of
+.BR add ", " update ", or " remove .
+This is followed by a list of attributes in
+.IB name = value
+notation, using the names and conventions described in
+.BR isnsadm (8).
+.\" ------------------------------------------------------------------
+.SS Security Related Options
+The iSNS standard defines an authentication method based on
+the DSA algorithm. Participants in a message exchange authenticate
+messages by adding an "authentication block" containing a time stamp,
+a string identifying the key used, and a digital signature of the
+message. The same method is also used by SLP, the Service Location
+Protocol.
+.PP
+The string contained in the authentication block is referred to
+as the
+.IR "Security Policy Index" (SPI).
+This string can be used by the server to look up the client's public
+key by whatever mechanism; so the string could be used as the name of
+a public key file in a directory, or to retrieve an X509 certificate
+from LDAP.
+.PP
+From the perspective of Open-iSNS client applications, there are
+only two keys: the client's own (private) key, used to sign the
+messages it sends to the server, and the server's public key,
+used to verify the signatures of incoming server messages.
+.PP
+The iSNS server needs, in addition to its own private key, access to all
+public keys of clients that will communicate to it. The latter are kept
+in what is called a key store. Key stores and their operation will
+be discussed in section
+.B Key Stores and Policy
+below.
+.PP
+The following configuration options control authentication:
+.TP
+.BR Security
+This enables or disables DSA authentication.
+When set to 1, the client will sign all messages, and expect all server
+messages to be signed.
+.IP
+When enabling security in the server, incoming messages are checked
+for the presence of an auth block. If none is present, or if the server
+cannot find a public key corresponding to the SPI, the message is treated
+as originating from an anonymous source. If the SPI is known but the
+signature is incorrect, the message is dropped silently.
+.IP
+Messages from an anonymous source will be assigned a very restrictive
+policy that allows database queries only.
+.IP
+Setting this option to 0 will turn off authentication.
+.IP
+The default value is -1, which tells iSNS to use authentication
+if the required keys are installed, and use unauthenticated iSNS
+otherwise.
+.TP
+.BR AuthName
+This is the string that will be used as the SPI in all outgoing
+messages that have an auth block. It defaults to the host name
+(please refer to option
+.BR HostName ).
+.TP
+.BR AuthKeyFile
+This is the path name of a file containing a PEM encoded DSA key.
+This key is used to sign outgoing messages.
+The default is
+.BR /etc/isns/auth_key .
+.TP
+.BR ServerKeyFile
+This option is used by client applications only, and specifies
+the path name of a file containing a PEM encoded DSA key.
+This key is used to authenticate the server's replies.
+The default is
+.BR /etc/isns/server_key.pub .
+.TP
+.BR KeyStore
+This server-side option specifies the key store to use,
+described in the next section.
+.PP
+The following two options control how iSNS will verify the
+time stamp contained in the authentication block, which
+is supposed to prevent replay attacks.
+.TP
+.B Auth.ReplayWindow
+In order to compensate for clock drift between two hosts exchanging
+iSNS messages, Open-iSNS will apply a little fuzz when comparing
+the time stamp contained in the message
+to the local system time. If the difference between
+time stamp and local system time is less than the number of seconds
+given by this option, the message is acceptable. Otherwise, it is
+rejected.
+.IP
+The default value is
+.BR 5m .
+.TP
+.B Auth.TimestampJitter
+When verifying incoming messages, Open-iSNS checks that the time
+stamps sent by the peer are increasing monotonically. In order to
+compensate for the reordering of messages by the network (eg when
+using UDP as transport), a certain time stamp jitter is accepted.
+If the time stamp of an incoming messages is no earlier than
+.B TimestampJitter
+seconds before the last time stamp received, then the message is acceptable.
+Otherwise, it is rejected.
+.IP
+The default value is
+.BR 1s .
+.\" ------------------------------------------------------------------
+.SS Key Stores and Policy
+The current implementation supports two types of key stores.
+.PP
+The simple key store uses a flat directory to store public keys, each
+key in a file of its own. The file is expected to hold the client's
+PEM-encoded public key, and it must use the client's SPI as the name.
+This type of key store is not really recommended, as it does not
+store any policy information.
+.PP
+A simple key store can be configured by setting the
+.B KeyStore
+option to the path name of the directory.
+.PP
+The recommended approach is to use the database as key store. This
+uses vendor-specific policy objects to tie SPI string, public key,
+entity name, source name and other bits of policy together, and
+store them in a persistent way.
+.PP
+The database key store is configured by setting the
+.B KeyStore
+option to the reserved value
+.BR DB: ,
+which is also the default.
+.PP
+Currently, Open-iSNS policy objects have the following attributes,
+besides the SPI:
+.TP
+Source:
+This is the source node name the client must use. It defaults to
+the SPI string.
+.TP
+Functions:
+This is a bitmap detailing which functions the client is permitted
+to invoke. The bit names correspond to the shorthand names used in
+RFC 4711, such as
+.BR DevAttrReg ,
+.BR DevAttrQry ,
+etc. The default is to allow registration, query and deregistration,
+as well as SCNRegister.
+.TP
+Entity name:
+This is the entity name assigned to the client. If set, a registration
+by the client is not permitted to use a different entity name. If
+the client sends a registration without Entity identifier, the
+server will assign the entity name given in the policy.
+The default is to not restrict the entity name.
+.TP
+Object access:
+This is a bitfield describing access permissions for each object type.
+For each object type, you can grant Read and/or Write permissions.
+Read access applies to the Query and GetNext calls; all other operations
+require write permission.
+The default grants read and write access to objects of type Entity, Storage
+Node, Portal and Portal Group; and read access to Discovery Domains.
+.TP
+Node types:
+This bitfield describes which types of storage nodes a client is
+allowed to register; the valid bit names are
+.BR target ", " initiator " and " control .
+The default is to restrict nodes to register initiators only.
+.\" ------------------------------------------------------------------
+.SS Network Related Options
+.TP
+.BR Network.MaxSockets
+This is the number of incoming connections accepted, and defaults to
+1024. This usually applies to server side only, but is relevant if you
+create a passive TCP socket for ESI or SCN.
+.TP
+.BR Network.ConnectTimeout
+This is a timeout value, which specifies the time to wait for a TCP
+connection to be established. It defaults to
+.BR 60s .
+.TP
+.BR Network.ReconnectTimeout
+When a connection attempt failed, we wait for a short time before we
+try connecting again. This is intended to take the pressure off
+overloaded servers. The default value is
+.BR 10s .
+.TP
+.BR Network.CallTimeout
+Total amount of time to wait before timing out a call to the iSNS server.
+The default value is
+.BR 60s .
+.\" ------------------------------------------------------------------
+.SH SEE ALSO
+RFC 4171,
+.BR isnsd (8),
+.BR isnsadm (8).
+.SH AUTHORS
+Olaf Kirch <olaf.kirch@oracle.com>
diff --git a/utils/open-isns/doc/isnsadm.8 b/utils/open-isns/doc/isnsadm.8
new file mode 100644
index 0000000..c3e2b83
--- /dev/null
+++ b/utils/open-isns/doc/isnsadm.8
@@ -0,0 +1,672 @@
+'\" t
+.TH ISNSADM 8 "11 May 2007"
+.SH NAME
+isnsadm \- iSNS client utility
+.SH SYNOPSIS
+.B isnsadm
+.RI [ options... ]
+.RI --register " object...
+.PP
+.B isnsadm
+.RB [ ... ]
+.RI --query " attr" [= value ]
+.PP
+.B isnsadm
+.RB [ ... ]
+.RI --deregister " attr=value
+.PP
+.B isnsadm
+.RB [ ... ]
+.RI --list " type attr=value
+.PP
+.B isnsadm
+.RB [ ... ]
+.RI --dd-register " attr=value
+.PP
+.B isnsadm
+.RB [ ... ]
+.RI --enroll " client-name attr=value
+.PP
+.B isnsadm
+.RB [ ... ]
+.RI --edit-policy " attr=value
+
+.SH DESCRIPTION
+.B Isnsadm
+is a command line utility for interacting with an iSNS
+server. It operates in one of several modes, which are
+mutually exclusive.
+Currently,
+.B isnsadm
+supports registration, query, and deregistration.
+.SH OPTIONS
+By default,
+.B isnsadm
+will take most of its settings from the configuration
+file
+.BR /etc/isns/isnsadm.conf ,
+with the exception of the following options:
+.TP
+.BI \--config " filename\fR, " \-c " filename
+This option overrides the default configuration file.
+.TP
+.BI \--debug " facility\fR, " \-d " facility
+enables debugging. Valid facilities are
+.PP
+.TS
+tab(,),box,center;
+lb|lr.
+socket,network send/receive
+auth,authentication and security related information
+message,iSNS protocol layer
+state,database state
+scn,SCN (state change notification) messages
+esi,ESI (entity status inquiry) messages
+all,all of the above
+.TE
+.PP
+.TP
+.BI \--local
+makes
+.B isnsadm
+use a Local (aka Unix) socket when talking to the iSNS
+server. This can be used by the administrator to perform
+management tasks, such as enrolling new clients, editing
+access control and so on. Local mode is only available
+to the super user.
+.TP
+.BI \--control
+makes
+.B isnsadm
+assume the identity of a control node. Control nodes are
+special in that they have more rights in accessing and
+modifying the database than normal storage nodes have.
+.PP
+When using this option,
+.B isnsadm
+will use the source name and DSA key specified by the
+.BR Control.SourceName " and " Control.AuthKeyFile
+configuration options, respectively.
+.PP
+.TP
+.BI \--key " attr" = value
+This option is recognized in registration mode only, and
+lets you specify an object key. For a more detailed explanation,
+refer to section
+.BR "Registration mode" .
+.TP
+.BI \--keyfile= filename
+When creating a policy for a new iSNS client,
+.B isnsadm
+is able to generate a DSA key for the client. The public
+part of the key is stored in a policy object in the iSNS
+server's database, whereas the private portion is stored in the
+file specified by the
+.B keyfile
+option.
+.B
+.TP
+.BI \--help
+This will print a help message and exit.
+.\"---------------------------
+.SS Built-in help
+.B Isnsadm
+has built-in help functions. When invoked with
+.BR \--help ,
+it will print a general help message showing all supported
+command modes, and exit. Specific help on an individual
+command mode is available by invoking that mode with a
+single argument of
+.BR help ,
+like this:
+.PP
+.B isnsadm --register help
+.PP
+This will print a help message describing how to use this
+command mode, followed by a list of attributes this command supports
+and a help text describing the attribute.
+.\"---------------------------
+.SS Supported attributes
+Most command modes take a list of attributes as arguments on the
+command line. The naming and syntax of these attributes as
+the same for all commands modes, however certain modes support
+only a limited set of attributes.
+.PP
+Attributes are usually given as
+.IB name = value
+pairs. Where empty (or NIL) attributes are supported, the
+attribute name by itself can be given.
+.PP
+The syntax of attribute
+.I value
+depends on the attribute type. For strings and numeric values,
+no special conventions apply, but bitfields have a special syntax
+described below.
+.PP
+The attribute name is usually preceded by the object
+type it applies to (such as
+.BR entity ),
+followed by a hyphen and the name itself. However, where the
+context clearly determines a specific object type, the prefix
+can be omitted. For instance, when editing a policy object
+using
+.BR \--edit-policy ,
+it is acceptable to use
+.B node-type
+as shorthand for
+.BR policy-node-type .
+.PP
+Likewise, in a query command, it is not permitted to mix attributes
+from different object types. Thus, the first attribute of a
+query string establishes a type context, so that the following
+two invocations are equivalent:
+.PP
+.B isnsadm --query pg-name=iqn.com.foo pg-addr=10.1.1.1 pg-port=860/tcp
+.br
+.B isnsadm --query pg-name=iqn.com.foo addr=10.1.1.1 port=860/tcp
+.PP
+.B Isnsadm
+currently supports the following attributes:
+.PP
+.TS
+tab(,),box,center;
+li|lilili
+lt|lbrlb.
+Context,Attribute,iSNS tag,Aliases
+_
+Network Entity,entity-id,1,eid
+\^,entity-prot,2
+\^,entity-index,7
+iSCSI Storage Node,iscsi-name,32
+\^,iscsi-node-type,33
+\^,iscsi-alias,34
+\^,iscsi-idx,36
+\^,iscsi-authmethod,42
+Portal,portal-addr,16
+\^,portal-port,17
+\^,portal-name,18
+\^,portal-esi-port,20
+\^,portal-esi-interval,21
+\^,portal-idx,22
+\^,portal-scn-port,23
+Portal Group,portal-group-index,52
+\^,pg-name,48
+\^,pg-addr,49
+\^,pg-port,50
+\^,pg-tag,51,pgt
+\^,pg-idx,52
+Discovery Domain,dd-id,2065
+\^,dd-name,2066
+\^,dd-member-iscsi-idx,2067
+\^,dd-member-name,2068
+\^,dd-member-fc-name,2069,
+\^,dd-member-portal-idx,2070,
+\^,dd-member-addr,2071,
+\^,dd-member-port,2072,
+\^,dd-features,2078,
+Policy Object,policy-name,-,spi
+\^,policy-key,-
+\^,policy-entity,-
+\^,policy-node-type,-
+\^,policy-object-type,-
+\^,policy-functions,-
+.TE
+.PP
+.\"---------------------------
+.SS Portal attributes
+Portal information is conveyed by two separate attributes
+in iSNS; an address attribute holding the IP address, and
+a TCP/UDP port attribute holding the port number and an indication
+of the protocol to be used (TCP or UDP).
+.PP
+When parsing a TCP/UDP port, Open-iSNS will expect a port number,
+optionally followed by a slash and the protocol. Port names
+such as "iscsi-target" are not supported.
+.PP
+As a convenience,
+.B isnsadm
+supports a notation representing a portal as one pseudo-attribute.
+Separating address and port by a colon. Thus, the following two
+are equivalent, with the latter being the shorthand representation
+of the former:
+.PP
+.BI addr= <address> " port=" <port> [/ protocol ] \fR.
+.BI portal= <adress> : port [/ protocol ]
+.PP
+This notation can be used in any context where an
+.BR addr / port
+attribute pair can appear, and may be prefixed by a type name,
+as in
+.BR pg-portal=... .
+.PP
+When using literal IPv6 addresses, the address has to be surrounded
+by square brackets, otherwise the embedded colons would create
+ambiguity:
+.BR portal=[2001:5c0:0:2::24]:860/tcp
+.PP
+.\"---------------------------
+.SS Bitfield attributes
+Some iSNS attributes are words representing a bit field.
+.B Isnsadm
+displays and parses these attributes in human-readable form
+rather than using the numerical value. The names of the bit
+values are displayed by built-in help facilities. When specifying
+a bitfield attribute on the command line, you can combine them
+using the plus (\fB+\fP) or comma (\fB,\fR) character, like this:
+.PP
+.B node-type=control+initiator
+.PP
+.\"---------------------------
+.SS Registration mode
+Registration mode is selected by using the
+.B --register
+option, followed by a list of one or more objects
+to register with the iSNS server.
+By default, this will create a network entity for the
+client (if none exists), and place the new objects inside
+it. Usually, you register all objects for
+a network entity in one operation, rather than each
+one separately.
+.PP
+Each object is specified as a type, optionally followed
+by a comma-separated list of attributes, such as
+this:
+.PP
+.B target=iqn.2005-01.org.open-iscsi.foo:disk1,alias=disk1
+.PP
+The following object types are currently supported:
+.TP
+.BI entity= name
+Tells the server to group all objects in the specified
+Network Entity container object.
+Normally, the iSNS server will automatically assign an
+entity name that is in line with its policies, and there is
+no need to specify it explicitly.
+.TP
+.BI initiator[= name ]
+This will register an iSCSI storage node of type initiator.
+By default, the name is set to the iSNS source name.
+.IP
+This can be followed by any number of iSCSI storage node
+attributes.
+.TP
+.BI target[= name ]
+This will register an iSCSI storage node of type target.
+By default, the name is set to the iSNS source name.
+.IP
+This object accepts the same set of attributes as
+.BR initiator .
+.TP
+.BI control[= name ]
+This will register an iSCSI storage node of type control.
+By default, the name is set to the iSNS source name.
+Only management nodes should be registered as control
+nodes, as this gives a node complete control over the
+iSNS database.
+.IP
+This object accepts the same set of attributes as
+.BR initiator .
+.TP
+.BI portal=[ address:port/proto ]
+This will register a portal using the given address,
+port and protocol triple. If the triple is omitted,
+.B isnsadm
+will use the client host's IP address. If the portal
+is preceded by an initiator registration (on the command
+line), the port defaults to 860/tcp; if it is preceded by
+a target registration, the port defaults to 3260/tcp.
+For multi-homed hosts, the choice of address is
+implementation dependant.
+.IP
+This can be followed by any number of portal attributes.
+.TP
+.B pg
+This will register a portal group joining the preceding
+portal and node. Portal groups can be used to describe
+the preferred portals for a given node; please refer
+to RFC 4711 for details.
+.IP
+This can be followed by any number of portal group attributes.
+The attribute list must specify a portal group tag (PGT)
+via the
+.BR pgt
+attribute.
+.PP
+There are two additional command line options of interest,
+which are used exclusively with Registration mode. One is
+.BR \--replace .
+Normally, registration mode will
+.I add
+new objects to the network entity associated with the client
+host. If you specify
+.B \--replace
+on the command line, the server will wipe the network
+entity completely, and remove all portals and storage
+nodes it contained. Then it will create a new network
+entity, and place the portals and storage nodes provided
+by the caller inside.
+.PP
+In addition, it is possible to replace just parts of a
+network entity. This is achieved by using the command line
+option
+.B \--key
+to specify the object that should be replaced.
+.PP
+For instance, assume a network entity
+contains the portal
+.BR 10.1.1.1:860 ,
+and the client's network address changed to
+.BR 10.2.7.7 .
+Then the following command will atomically update the
+database, replacing just the portal without touching the
+registered storage nodes:
+.PP
+.B " isnsadm --replace --key portal=10.1.1.1:860 portal=10.2.7.7:860
+.PP
+The
+.B \--key
+option recognizes only a subset of the usual attributes:
+.RS
+.TS
+tab(,),box;
+li|li
+lb|lb.
+Object type,Syntax
+_
+Entity,eid=\fIidentifier
+Portal,portal=\fIaddress\fP:\fPport
+iSCSI Node,iscsi-name=\fIname
+.TE
+.RE
+.PP
+To get a list of supported attributes, invoke
+.BR "isnsadm --register help" .
+.\"---------------------------
+.SS Query mode
+Query mode is selected by using the
+.B --query
+option. A query consists of a list of
+.BR attr = \fI value
+pairs. All attributes must belong to the same object type,
+i.e. queries that mix a Network Entity attribute with e.g.
+a Portal attribute will be rejected.
+.PP
+It is also possible to specify an attribute name without
+value (i.e. just
+.BR attr ),
+which will
+will match any object that has such an attribute, regardless
+of its value. This is useful when you want to query for all
+objects of a given type.
+.PP
+To obtain a list of supported attributes, invoke
+.BR "isnsadm --query help" .
+.\"---------------------------
+.SS List Mode
+In this mode,
+.B isnsadm
+will display all objects of a given type, optionally
+restricted to those matching certain attribute values.
+.PP
+The arguments to list mode are a
+.IR "type name" ,
+optionally followed by one or more
+.IB attr = value
+pairs. Only attributes pertaining to the given
+type are permitted; for instance, if you specify a
+type name of
+.BR portals ,
+only portal attributes are permitted.
+.PP
+Possible type names are:
+.BR entities ,
+.BR nodes ,
+.BR portals ,
+.BR dds ,
+.BR ddsets ,
+.BR portal-groups ", and "
+.BR policies .
+.PP
+Additional information is available via
+.BR "isnsadm --list help" .
+.\"---------------------------
+.SS Deregistration mode
+In this mode, you can deregister objects previously registered.
+Only the node which registered an entity in the first place is
+permitted to remove it, or any of its child objects. (Control
+nodes are not bound by this restriction).
+.PP
+In deregistration mode, the argument list consists of a list of
+.IB attr = value
+pairs. Deregistration supports the same set of attributes as
+query mode.
+.\"---------------------------
+.SS Discovery Domain Registration
+This mode, allows to register a discovery domain or to add
+new members to an existing discovery domain. Again, attributes
+are specified as a list of
+.IB attr = value
+pairs. Only discovery domain attributes are recognized.
+.PP
+Note, in order to add members to an existing domain, you must
+specify the domain's numeric ID. The domain's symbolic name
+is not a valid handle when referring to a discovery domain.
+.\"---------------------------
+.SS Client Enrollment
+This mode only works when the server recognizes the client
+as having control node capabilities, which is possible in
+two ways:
+.TP
+Invoke
+.B isnsadm \--local
+as super user on the host
+.B isnsd
+is running on. The
+.B \--local
+options tells it to communicate with the server through
+the local control socket.
+.TP
+Invoke
+.BR "isnsadm \--control" ,
+which tells it to assume the identity of a control node.
+When given this option,
+.B isnsadm
+will use the source name and DSA key specified by the
+.BR Control.SourceName " and " Control.AuthKeyFile
+configuration options, respectively.
+The server must be configured to grant this identity
+control node status.
+.PP
+To enroll a client, use the
+.B \--enroll
+option, followed by the (source) name of the client to enroll.
+This string will be used as the name of the security policy
+the client will use to identify itself.
+.PP
+This is followed by a list of attribute/value pairs, where the
+following set of attributes is supported:
+.PP
+.TS
+tab(,),box,center;
+li|lilili
+lb|lrlb.
+Attribute,Description,Aliases
+_
+name,Policy Name,spi
+key,Client's DSA public key
+entity,Assigned Entity Identifier
+node-type,Permitted node type(s)
+node-name,Permitted node name(s)
+functions,Bitmap of permitted functions
+object-type,Object access mask
+.TE
+.PP
+The
+.B key
+attribute is used to specify the DSA
+public key that the server should use to authenticate
+messages from this client. You can either provide a
+file name; in which case
+.B isnsadm
+will try to read the PEM encoded public key from that file.
+If no
+.B key
+attribute is given, or when using
+.BR key=gen ", " isnsadm
+will generate a DSA key. The private portion of the newly
+generated key will be stored in the file specified by
+.BI --keyfile= filename \fR.
+.PP
+The
+.B object-type
+attribute is used to specify which object types the client
+is permitted to access. This is a comma separated list of
+.IB type : perm
+pairs, where
+.I type
+can be any of
+.BR entity ", " iscsi-node ", " portal ", " portal-group ", " dd ", " ddset ", and " policy .
+The permissions can be either
+.BR rw ", or " r .
+.PP
+The
+.B functions
+attribute can be used to restrict which functions the client is
+permitted to invoke. This is a bitfield, using the standard function
+names from RFC 4171, such as
+.BR DevAttrReg ", " DevAttrQry ", etc."
+.PP
+For a description of the open-isns security model
+and policies, please refer to the
+.BR isns_config (5)
+manual page.
+.PP
+.BR "Important note" :
+In order to generate a DSA key, you have to have a set of DSA
+parameters installed. By default,
+.B isnsadm
+expects to find them in
+.BR /etc/isns/dsa.params .
+These parameters are created by calling
+.B isnsd \--init
+once on the server machine. Alternatively, you can use
+the following command:
+.PP
+.ti +8
+openssl dsaparam 1024 -out /etc/isns/dsa.params
+.ti -8
+.PP
+where 1024 is the chosen DSA key size, in bits.
+.SH EXAMPLES
+If you want to use Open-iSNS in authenticated mode,
+you first need to initialize the server's DSA key and
+DSA parameters. This can be done conveniently by using
+.PP
+.B isnsd --init
+.PP
+This will create the server's private and public key,
+and place them in
+.B /etc/isns/auth_key
+and
+.BR auth_key.pub ,
+respectively.
+.PP
+The following command will create a policy object for a
+node named
+.B isns.control ,
+and grant it control privileges:
+.PP
+.B isnsadm --local --keyfile=control.key
+.B --enroll isns.control \(rs
+.br
+.B " node-type=ALL functions=ALL object-type=ALL
+.PP
+In the process of entrolling the client, this will generate
+a DSA key pair, and place the private key portion in the
+file
+.BR control.key .
+This file must be installed as
+.BR /etc/isns/control.key
+on the host you wish to use as an iSNS management station.
+.PP
+Next, you need to create a storage node object for the
+management station:
+.PP
+.B isnsadm --local --register control
+.PP
+On the management station, you can then enroll additional
+hosts:
+.PP
+.B isnsadm --control --keyfile=somehost.key
+.B --enroll iqn.2005-01.org.open-iscsi.somehost \(rs
+.br
+.B " node-type=target+initiator
+.PP
+Again, this will generate a DSA key pair and store the private
+key portion in auth_key. Note the use of the
+.B \--control
+option that tells
+.B isnsadm
+to use the identity of the control node instead of the default
+key and source name.
+.PP
+You then need to copy
+.B somehost.key
+to the client host and install it as
+.BR /etc/isns/auth_key .
+Likewise, the server's public key (which resides in
+.BR /etc/isns/auth_key.pub
+on the server) needs to be copied to the client machine,
+and placed in
+.BR /etc/isns/server_key.pub .
+.PP
+By default, when a client registers a storage node (be
+it initiator or target) with iSNS, the client will not be
+able to see any other storage nodes. In order for targets
+to be visible to a given initiator, you need to create
+so-called Discovery Domains (or DDs for short).
+.PP
+Currently, domain membership operations require administrator
+privilege. Future extensions may allow iSNS clients to
+add themselves to one or more DDs upon registration.
+.PP
+To create a discovery domain, and add nodes to it, you can
+use
+.PP
+.B isnsadm --control --dd-register dd-name=mydomain \(rs
+.br
+.B " member-name=iqn.org.bozo.client iqn.org.bozo.jbod ...
+.PP
+In order to add members to an existing DD, you have to
+specify the numeric domain ID - using the DD name is not
+sufficient, unfortunately (this is a requirement of the
+RFC, not an implementation issue):
+.PP
+.B isnsadm --control --dd-register dd-id=42 \(rs
+.br
+.B " member-name=iqn.com.foo member-name=iqn.com.bar
+.PP
+The DD ID can be obtained by doing a query for the DD name:
+.PP
+.B isnsadm --control --query dd-name=mydomain
+.PP
+In management mode, you can also register and deregister
+nodes and portals manually, in case you want to fix up
+an inconsisteny in the database. For instance, this will
+register a node and portal on a host named client.bozo.org:
+.PP
+.B isnsadm --control --register entity=client.bozo.org \(rs
+.br
+.B " initiator=iqn.org.bozo.client portal=191.168.7.1:860
+.PP
+Note that this registration explicitly specifies the network
+entity in which to place the new objects. If you omit this,
+the new objects will be placed in an entity named
+.BR CONTROL ,
+which is decidedly not what you want.
+.SH SEE ALSO
+RFC 4171,
+.BR isnsd (8),
+.BR isns_config (5).
+.SH AUTHORS
+Olaf Kirch <olaf.kirch@oracle.com>
diff --git a/utils/open-isns/doc/isnsd.8 b/utils/open-isns/doc/isnsd.8
new file mode 100644
index 0000000..84b3913
--- /dev/null
+++ b/utils/open-isns/doc/isnsd.8
@@ -0,0 +1,93 @@
+.TH ISNSD 8 "11 May 2007"
+.SH NAME
+isnsd \- iSNS server daemon
+.SH SYNOPSIS
+.B isnsd
+.RB [ "\-f" ]
+.RB [ "\-4" ]
+.RB [ "\-6" ]
+.RB [ "\-c \fIfilename" ]
+.RB [ "\-d \fIdebug-facility" ]
+.RB [ \--dump-db ]
+.RB [ \--init ]
+
+.SH DESCRIPTION
+.B Isnsd
+implements the iSNS protocol as defined in RFC 4171.
+iSNS is a discovery protocol for iSCSI and iFCP.
+.SH OPTIONS
+By default,
+.B isnsd
+will take most of its settings from the configuration
+file
+.BR /etc/isns/isnsd.conf ,
+with the exception of the following options:
+.TP
+.BI \--config " filename\fR, " \-c " filename
+This option overrides the default configuration file.
+.TP
+.BR \--foreground , \-f
+By default,
+.B isnsd
+will put itself into the background. By specifying this option, you can
+tell it to run in the foreground. Any error messages or debug output
+will be printed to the console rather than being sent to syslog.
+.TP
+.BI \-4
+tells
+.B isnsd
+to create an IPv4 socket only. Normally, it defaults
+to IPv6 (which will accept both IPv4 and IPv6 connections).
+.TP
+.BI \-6
+tells
+.B isnsd
+explicitly
+to create an IPv6 socket only. Since it defaults
+to IPv6 anyway, this is really a no-op.
+.TP
+.BI \--debug " facility\fR, " \-d " facility
+enables debugging. Valid facilities are
+.PP
+.TS
+tab(,),box,center;
+lb|lr.
+socket,network send/receive
+auth,authentication and security related information
+message,iSNS protocol layer
+state,database state
+scn,SCN (state change notification) messages
+esi,ESI (entity status inquiry) messages
+all,all of the above
+.TE
+.PP
+.TP
+.B \--dump-db
+This is a helper function that will read the database from the
+file system, and display it in human readable form. When using
+this option,
+.B isnsd
+will not open any sockets, and terminate immediately after display
+the database.
+.IP
+This option is intended to be used by the administrator when suspecting
+that the database contains bad/inconsistent information.
+.TP
+.B \--init
+This option will create the server's authentication key, and
+the required DSA parameters. The private key is stored in the
+file specified by the
+.B AuthKey
+option (usually
+.BR /etc/isns/auth_key ).
+The public portion of the key is written to same directory,
+with the suffix
+.B .pub
+appended to the key file name.
+.SH SEE ALSO
+RFC 4171,
+.BR isnsadm (8),
+.BR isnsdd (8),
+.BR isns_config (5).
+.SH AUTHORS
+Olaf Kirch <olaf.kirch@oracle.com>
diff --git a/utils/open-isns/doc/isnsdd.8 b/utils/open-isns/doc/isnsdd.8
new file mode 100644
index 0000000..6088e28
--- /dev/null
+++ b/utils/open-isns/doc/isnsdd.8
@@ -0,0 +1,75 @@
+.TH ISNSDD 8 "11 May 2007"
+.SH NAME
+isnsdd \- iSNS discovery daemon
+.SH SYNOPSIS
+.B isnsdd
+.RB [ "\-f" ]
+.RB [ "\-c \fIfilename" ]
+.RB [ "\-d \fIdebug-facility" ]
+
+.SH DESCRIPTION
+.B Isnsdd
+is a client side daemon for iSNS. It registers storage
+nodes and portals with the iSNS service, and refreshes
+these registrations in a timely manner.
+.PP
+The daemon also registers itself to receive SCN notifications,
+and processes these. It can be configured to invoke an
+external helper application for each status notification
+received. The path name of the helper application can be
+specified via the
+.B SCNCallout
+option in the configuration file.
+.SH OPTIONS
+By default,
+.B isnsd
+will take most of its settings from the configuration
+file
+.BR /etc/isns/isnsdd.conf ,
+with the addition of the following command line options:
+.TP
+.BI \--config " filename\fR, " \-c " filename
+This option overrides the default configuration file.
+.TP
+.BR \--foreground , \-f
+By default,
+.B isnsd
+will put itself into the background. By specifying this option, you can
+tell it to run in the foreground. Any error messages or debug output
+will be printed to the console rather than being sent to syslog.
+.TP
+.BI \--role " role
+This tells the discovery daemon in which capacity is should register itself
+with the iSNS server.
+.I Role
+can be either
+.BR initiator ", or " control .
+The default is to register as an initiator.
+.IP
+Registering target nodes needs to use a different mechanism, as
+the iSCSI target server needs to inform the discovery daemon
+about each exported target separately. This is not implemented
+yet.
+.TP
+.BI \--debug " facility\fR, " \-d " facility
+enables debugging. Valid facilities are
+.PP
+.TS
+tab(,),box,center;
+lb|lr.
+socket,network send/receive
+auth,authentication and security related information
+message,iSNS protocol layer
+state,database state
+scn,SCN (state change notification) messages
+esi,ESI (entity status inquiry) messages
+all,all of the above
+.TE
+.PP
+.SH SEE ALSO
+RFC 4171,
+.BR isnsd (8),
+.BR isnsadm (8),
+.BR isns_config (5).
+.SH AUTHORS
+Olaf Kirch <olaf.kirch@oracle.com>
diff --git a/utils/open-isns/doc/rfc2608.txt b/utils/open-isns/doc/rfc2608.txt
new file mode 100644
index 0000000..fa7de62
--- /dev/null
+++ b/utils/open-isns/doc/rfc2608.txt
@@ -0,0 +1,3027 @@
+
+
+
+
+
+
+Network Working Group E. Guttman
+Request for Comments: 2608 C. Perkins
+Updates: 2165 Sun Microsystems
+Category: Standards Track J. Veizades
+ @Home Network
+ M. Day
+ Vinca Corporation
+ June 1999
+
+
+ Service Location Protocol, Version 2
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+Abstract
+
+ The Service Location Protocol provides a scalable framework for the
+ discovery and selection of network services. Using this protocol,
+ computers using the Internet need little or no static configuration
+ of network services for network based applications. This is
+ especially important as computers become more portable, and users
+ less tolerant or able to fulfill the demands of network system
+ administration.
+
+Table of Contents
+
+ 1. Introduction 3
+ 1.1. Applicability Statement . . . . . . . . . . . . . . . 3
+ 2. Terminology 4
+ 2.1. Notation Conventions . . . . . . . . . . . . . . . . . 4
+ 3. Protocol Overview 5
+ 4. URLs used with Service Location 8
+ 4.1. Service: URLs . . . . . . . . . . . . . . . . . . . . 9
+ 4.2. Naming Authorities . . . . . . . . . . . . . . . . . 10
+ 4.3. URL Entries . . . . . . . . . . . . . . . . . . . . . 10
+ 5. Service Attributes 10
+ 6. Required Features 12
+ 6.1. Use of Ports, UDP, and Multicast . . . . . . . . . . 13
+
+
+
+Guttman, et al. Standards Track [Page 1]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ 6.2. Use of TCP . . . . . . . . . . . . . . . . . . . . . 14
+ 6.3. Retransmission of SLP messages . . . . . . . . . . . 15
+ 6.4. Strings in SLP messages . . . . . . . . . . . . . . . 16
+ 6.4.1. Scope Lists in SLP . . . . . . . . . . . . . . 16
+ 7. Errors 17
+ 8. Required SLP Messages 17
+ 8.1. Service Request . . . . . . . . . . . . . . . . . . . 19
+ 8.2. Service Reply . . . . . . . . . . . . . . . . . . . . 21
+ 8.3. Service Registration . . . . . . . . . . . . . . . . . 22
+ 8.4. Service Acknowledgment . . . . . . . . . . . . . . . . 23
+ 8.5. Directory Agent Advertisement. . . . . . . . . . . . . 24
+ 8.6. Service Agent Advertisement. . . . . . . . . . . . . . 25
+ 9. Optional Features 26
+ 9.1. Service Location Protocol Extensions . . . . . . . . . 27
+ 9.2. Authentication Blocks . . . . . . . . . . . . . . . . 28
+ 9.2.1. SLP Message Authentication Rules . . . . . . . 29
+ 9.2.2. DSA with SHA-1 in Authentication Blocks . . . 30
+ 9.3. Incremental Service Registration . . . . . . . . . . 30
+ 9.4. Tag Lists . . . . . . . . . . . . . . . . . . . . . . 31
+ 10. Optional SLP Messages 32
+ 10.1. Service Type Request . . . . . . . . . . . . . . . . 32
+ 10.2. Service Type Reply . . . . . . . . . . . . . . . . . 32
+ 10.3. Attribute Request . . . . . . . . . . . . . . . . . . 33
+ 10.4. Attribute Reply . . . . . . . . . . . . . . . . . . . 34
+ 10.5. Attribute Request/Reply Examples . . . . . . . . . . . 34
+ 10.6. Service Deregistration . . . . . . . . . . . . . . . 36
+ 11. Scopes 37
+ 11.1. Scope Rules . . . . . . . . . . . . . . . . . . . . . 37
+ 11.2. Administrative and User Selectable Scopes. . . . . . . 38
+ 12. Directory Agents 38
+ 12.1. Directory Agent Rules . . . . . . . . . . . . . . . . 39
+ 12.2. Directory Agent Discovery . . . . . . . . . . . . . . 39
+ 12.2.1. Active DA Discovery . . . . . . . . . . . . . 40
+ 12.2.2. Passive DA Advertising . . . . . . . . . . . . 40
+ 12.3. Reliable Unicast to DAs and SAs. . . . . . . . . . . . 41
+ 12.4. DA Scope Configuration . . . . . . . . . . . . . . . 41
+ 12.5. DAs and Authentication Blocks. . . . . . . . . . . . . 41
+ 13. Protocol Timing Defaults 42
+ 14. Optional Configuration 43
+ 15. IANA Considerations 44
+ 16. Internationalization Considerations 45
+ 17. Security Considerations 46
+ A. Appendix: Changes to the Service Location Protocol from
+ v1 to v2 48
+ B. Appendix: Service Discovery by Type: Minimal SLPv2 Features 48
+ C. Appendix: DAAdverts with arbitrary URLs 49
+ D. Appendix: SLP Protocol Extensions 50
+ D.1. Required Attribute Missing Option . . . . . . . . . . 50
+
+
+
+Guttman, et al. Standards Track [Page 2]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ E. Acknowledgments 50
+ F. References 51
+ G. Authors' Addresses 53
+ H. Full Copyright Statement 54
+
+1. Introduction
+
+ The Service Location Protocol (SLP) provides a flexible and scalable
+ framework for providing hosts with access to information about the
+ existence, location, and configuration of networked services.
+ Traditionally, users have had to find services by knowing the name of
+ a network host (a human readable text string) which is an alias for a
+ network address. SLP eliminates the need for a user to know the name
+ of a network host supporting a service. Rather, the user supplies
+ the desired type of service and a set of attributes which describe
+ the service. Based on that description, the Service Location
+ Protocol resolves the network address of the service for the user.
+
+ SLP provides a dynamic configuration mechanism for applications in
+ local area networks. Applications are modeled as clients that need
+ to find servers attached to any of the available networks within an
+ enterprise. For cases where there are many different clients and/or
+ services available, the protocol is adapted to make use of nearby
+ Directory Agents that offer a centralized repository for advertised
+ services.
+
+ This document updates SLPv1 [RFC 2165], correcting protocol errors,
+ adding some enhancements and removing some requirements. This
+ specification has two parts. The first describes the required
+ features of the protocol. The second describes the extended features
+ of the protocol which are optional, and allow greater scalability.
+
+1.1. Applicability Statement
+
+ SLP is intended to function within networks under cooperative
+ administrative control. Such networks permit a policy to be
+ implemented regarding security, multicast routing and organization of
+ services and clients into groups which are not be feasible on the
+ scale of the Internet as a whole.
+
+ SLP has been designed to serve enterprise networks with shared
+ services, and it may not necessarily scale for wide-area service
+ discovery throughout the global Internet, or in networks where there
+ are hundreds of thousands of clients or tens of thousands of
+ services.
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 3]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+2. Terminology
+
+ User Agent (UA)
+ A process working on the user's behalf to establish
+ contact with some service. The UA retrieves service
+ information from the Service Agents or Directory Agents.
+
+ Service Agent (SA) A process working on the behalf of one or more
+ services to advertise the services.
+
+ Directory Agent (DA) A process which collects service
+ advertisements. There can only be one DA present per
+ given host.
+
+ Service Type Each type of service has a unique Service Type
+ string.
+
+ Naming Authority The agency or group which catalogues given
+ Service Types and Attributes. The default Naming
+ Authority is IANA.
+
+ Scope A set of services, typically making up a logical
+ administrative group.
+
+ URL A Universal Resource Locator [8].
+
+2.1. Notation Conventions
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [9].
+
+ Syntax Syntax for string based protocols follow the
+ conventions defined for ABNF [11].
+
+ Strings All strings are encoded using the UTF-8 [23]
+ transformation of the Unicode [6] character set and
+ are NOT null terminated when transmitted. Strings
+ are preceded by a two byte length field.
+
+ <string-list> A comma delimited list of strings with the
+ following syntax:
+
+ string-list = string / string `,' string-list
+
+ In format diagrams, any field ending with a \ indicates a variable
+ length field, given by a prior length field in the protocol.
+
+
+
+
+Guttman, et al. Standards Track [Page 4]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+3. Protocol Overview
+
+ The Service Location Protocol supports a framework by which client
+ applications are modeled as 'User Agents' and services are advertised
+ by 'Service Agents.' A third entity, called a 'Directory Agent'
+ provides scalability to the protocol.
+
+ The User Agent issues a 'Service Request' (SrvRqst) on behalf of the
+ client application, specifying the characteristics of the service
+ which the client requires. The User Agent will receive a Service
+ Reply (SrvRply) specifying the location of all services in the
+ network which satisfy the request.
+
+ The Service Location Protocol framework allows the User Agent to
+ directly issue requests to Service Agents. In this case the request
+ is multicast. Service Agents receiving a request for a service which
+ they advertise unicast a reply containing the service's location.
+
+ +------------+ ----Multicast SrvRqst----> +---------------+
+ | User Agent | | Service Agent |
+ +------------+ <----Unicast SrvRply------ +---------------+
+
+ In larger networks, one or more Directory Agents are used. The
+ Directory Agent functions as a cache. Service Agents send register
+ messages (SrvReg) containing all the services they advertise to
+ Directory Agents and receive acknowledgements in reply (SrvAck).
+ These advertisements must be refreshed with the Directory Agent or
+ they expire. User Agents unicast requests to Directory Agents
+ instead of Service Agents if any Directory Agents are known.
+
+ +-------+ -Unicast SrvRqst-> +-----------+ <-Unicast SrvReg- +--------+
+ | User | | Directory | |Service |
+ | Agent | | Agent | | Agent |
+ +-------+ <-Unicast SrvRply- +-----------+ -Unicast SrvAck-> +--------+
+
+ User and Service Agents discover Directory Agents two ways. First,
+ they issue a multicast Service Request for the 'Directory Agent'
+ service when they start up. Second, the Directory Agent sends an
+ unsolicited advertisement infrequently, which the User and Service
+ Agents listen for. In either case the Agents receive a DA
+ Advertisement (DAAdvert).
+
+ +---------------+ --Multicast SrvRqst-> +-----------+
+ | User or | <--Unicast DAAdvert-- | Directory |
+ | Service Agent | | Agent |
+ +---------------+ <-Multicast DAAdvert- +-----------+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 5]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ Services are grouped together using 'scopes'. These are strings
+ which identify services which are administratively identified. A
+ scope could indicate a location, administrative grouping, proximity
+ in a network topology or some other category. Service Agents and
+ Directory Agents are always assigned a scope string.
+
+ A User Agent is normally assigned a scope string (in which case the
+ User Agent will only be able to discover that particular grouping of
+ services). This allows a network administrator to 'provision'
+ services to users. Alternatively, the User Agent may be configured
+ with no scope at all. In that case, it will discover all available
+ scopes and allow the client application to issue requests for any
+ service available on the network.
+
+ +---------+ Multicast +-----------+ Unicast +-----------+
+ | Service | <--SrvRqst-- | User | --SrvRqst-> | Directory |
+ | Agent | | Agent | | Agent |
+ | Scope=X | Unicast | Scope=X,Y | Unicast | Scope=Y |
+ +---------+ --SrvRply--> +-----------+ <-SrvRply-- +-----------+
+
+ In the above illustration, the User Agent is configured with scopes X
+ and Y. If a service is sought in scope X, the request is multicast.
+ If it is sought in scope Y, the request is unicast to the DA.
+ Finally, if the request is to be made in both scopes, the request
+ must be both unicast and multicast.
+
+ Service Agents and User Agents may verify digital signatures provided
+ with DAAdverts. User Agents and Directory Agents may verify service
+ information registered by Service Agents. The keying material to use
+ to verify digital signatures is identified using a SLP Security
+ Parameter Index, or SLP SPI.
+
+ Every host configured to generate a digital signature includes the
+ SLP SPI used to verify it in the Authentication Block it transmits.
+ Every host which can verify a digital signature must be configured
+ with keying material and other parameters corresponding with the SLP
+ SPI such that it can perform verifying calculations.
+
+ SAs MUST accept multicast service requests and unicast service
+ requests. SAs MAY accept other requests (Attribute and Service Type
+ Requests). SAs MUST listen for multicast DA Advertisements.
+
+ The features described up to this point are required to implement. A
+ minimum implementation consists of a User Agent, Service Agent or
+ both.
+
+ There are several optional features in the protocol. Note that DAs
+ MUST support all these message types, but DA support is itself
+
+
+
+Guttman, et al. Standards Track [Page 6]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ optional to deploy on networks using SLP. UAs and SAs MAY support
+ these message types. These operations are primarily for interactive
+ use (browsing or selectively updating service registrations.) UAs
+ and SAs either support them or not depending on the requirements and
+ constraints of the environment where they will be used.
+
+ Service Type Request A request for all types of service on the
+ network. This allows generic service browsers
+ to be built.
+
+ Service Type Reply A reply to a Service Type Request.
+
+ Attribute Request A request for attributes of a given type of
+ service or attributes of a given service.
+
+ Attribute Reply A reply to an Attribute Request.
+
+ Service Deregister A request to deregister a service or some
+ attributes of a service.
+
+ Service Update A subsequent SrvRqst to an advertisement.
+ This allows individual dynamic attributes to
+ be updated.
+
+ SA Advertisement In the absence of Directory Agents, a User
+ agent may request Service Agents in order
+ to discover their scope configuration. The
+ User Agent may use these scopes in requests.
+
+ In the absence of Multicast support, Broadcast MAY be used. The
+ location of DAs may be staticly configured, discovered using SLP as
+ described above, or configured using DHCP. If a message is too large,
+ it may be unicast using TCP.
+
+ A SLPv2 implementation SHOULD support SLPv1 [22]. This support
+ includes:
+
+ 1. SLPv2 DAs are deployed, phasing out SLPv1 DAs.
+
+ 2. Unscoped SLPv1 requests are considered to be of DEFAULT scope.
+ SLPv1 UAs MUST be reconfigured to have a scope if possible.
+
+ 3. There is no way for an SLPv2 DA to behave as an unscoped SLPv1
+ DA. SLPv1 SAs MUST be reconfigured to have a scope if possible.
+
+ 4. SLPv2 DAs answer SLPv1 requests with SLPv1 replies and SLPv2
+ requests with SLPv2 replies.
+
+
+
+
+Guttman, et al. Standards Track [Page 7]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ 5. SLPv2 DAs use registrations from SLPv1 and SLPv2 in the same
+ way. That is, incoming requests from agents using either version
+ of the protocol will be matched against this common set of
+ registered services.
+
+ 6. SLPv2 registrations which use Language Tags which are greater
+ than 2 characters long will be inaccessible to SLPv1 UAs.
+
+ 7. SLPv2 DAs MUST return only service type strings in SrvTypeRply
+ messages which conform to SLPv1 service type string syntax, ie.
+ they MUST NOT return Service Type strings for abstract service
+ types.
+
+ 8. SLPv1 SrvRqsts and AttrRqsts by Service Type do not match Service
+ URLs with abstract service types. They only match Service URLs
+ with concrete service types.
+
+ SLPv1 UAs will not receive replies from SLPv2 SAs and SLPv2 UAs will
+ not receive replies from SLPv1 SAs. In order to interoperate UAs and
+ SAs of different versions require a SLPv2 DA to be present on the
+ network which supports both protocols.
+
+ The use of abstract service types in SLPv2 presents a backward
+ compatibility issue for SLPv1. It is possible that a SLPv1 UA will
+ request a service type which is actually an abstract service type.
+ Based on the rules above, the SLPv1 UA will never receive an abstract
+ Service URL reply. For example, the service type 'service:x' in a
+ SLPv1 AttrRqst will not return the attributes of 'service:x:y://orb'.
+ If the request was made with SLPv2, it would return the attributes of
+ this service.
+
+4. URLs used with Service Location
+
+ A Service URL indicates the location of a service. This URL may be
+ of the service: scheme [13] (reviewed in section 4.1), or any other
+ URL scheme conforming to the URI standard [8], except that URLs
+ without address specifications SHOULD NOT be advertised by SLP. The
+ service type for an 'generic' URL is its scheme name. For example,
+ the service type string for "http://www.srvloc.org" would be "http".
+
+ Reserved characters in URLs follow the rules in RFC 2396 [8].
+
+
+
+
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 8]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+4.1. Service: URLs
+
+ Service URL syntax and semantics are defined in [13]. Any network
+ service may be encoded in a Service URL.
+
+ This section provides an introduction to Service URLs and an example
+ showing a simple application of them, representing standard network
+ services.
+
+ A Service URL may be of the form:
+
+ "service:"<srvtype>"://"<addrspec>
+
+ The Service Type of this service: URL is defined to be the string up
+ to (but not including) the final `:' before <addrspec>, the address
+ specification.
+
+ <addrspec> is a hostname (which should be used if possible) or dotted
+ decimal notation for a hostname, followed by an optional `:' and
+ port number.
+
+ A service: scheme URL may be formed with any standard protocol name
+ by concatenating "service:" and the reserved port [1] name. For
+ example, "service:tftp://myhost" would indicate a tftp service. A
+ tftp service on a nonstandard port could be
+ "service:tftp://bad.glad.org:8080".
+
+ Service Types SHOULD be defined by a "Service Template" [13], which
+ provides expected attributes, values and protocol behavior. An
+ abstract service type (also described in [13]) has the form
+
+ "service:<abstract-type>:<concrete-type>".
+
+ The service type string "service:<abstract-type>" matches all
+ services of that abstract type. If the concrete type is included
+ also, only these services match the request. For example: a SrvRqst
+ or AttrRqst which specifies "service:printer" as the Service Type
+ will match the URL service:printer:lpr://hostname and
+ service:printer:http://hostname. If the requests specified
+ "service:printer:http" they would match only the latter URL.
+
+ An optional substring MAY follow the last `.' character in the
+ <srvtype> (or <abstract-type> in the case of an abstract service type
+ URL). This substring is the Naming Authority, as described in Section
+ 9.6. Service types with different Naming Authorities are quite
+ distinct. In other words, service:x.one and service:x.two are
+ different service types, as are service:abstract.one:y and
+ service:abstract.two:y.
+
+
+
+Guttman, et al. Standards Track [Page 9]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+4.2. Naming Authorities
+
+ A Naming Authority MAY optionally be included as part of the Service
+ Type string. The Naming Authority of a service defines the meaning
+ of the Service Types and attributes registered with and provided by
+ Service Location. The Naming Authority itself is typically a string
+ which uniquely identifies an organization. IANA is the implied
+ Naming Authority when no string is appended. "IANA" itself MUST NOT
+ be included explicitly.
+
+ Naming Authorities may define Service Types which are experimental,
+ proprietary or for private use. Using a Naming Authority, one may
+ either simply ignore attributes upon registration or create a local-
+ use only set of attributes for one's site. The procedure to use is
+ to create a 'unique' Naming Authority string and then specify the
+ Standard Attribute Definitions as described above. This Naming
+ Authority will accompany registration and queries, as described in
+ Sections 8.1 and 8.3. Service Types SHOULD be registered with IANA
+ to allow for Internet-wide interoperability.
+
+4.3. URL Entries
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Reserved | Lifetime | URL Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |URL len, contd.| URL (variable length) \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |# of URL auths | Auth. blocks (if any) \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ SLP stores URLs in protocol elements called URL Entries, which
+ associate a length, a lifetime, and possibly authentication
+ information along with the URL. URL Entries, defined as shown above,
+ are used in Service Replies and Service Registrations.
+
+5. Service Attributes
+
+ A service advertisement is often accompanied by Service Attributes.
+ These attributes are used by UAs in Service Requests to select
+ appropriate services.
+
+ The allowable attributes which may be used are typically specified by
+ a Service Template [13] for a particular service type. Services
+ which are advertised according to a standard template MUST register
+ all service attributes which the standard template requires. URLs
+ with schemes other than "service:" MAY be registered with attributes.
+
+
+
+Guttman, et al. Standards Track [Page 10]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ Non-standard attribute names SHOULD begin with "x-", because no
+ standard attribute name will ever have those initial characters.
+
+ An attribute list is a string encoding of the attributes of a
+ service. The following ABNF [11] grammar defines attribute lists:
+
+ attr-list = attribute / attribute `,' attr-list
+ attribute = `(' attr-tag `=' attr-val-list `)' / attr-tag
+ attr-val-list = attr-val / attr-val `,' attr-val-list
+ attr-tag = 1*safe-tag
+ attr-val = intval / strval / boolval / opaque
+ intval = [-]1*DIGIT
+ strval = 1*safe-val
+ boolval = "true" / "false"
+ opaque = "\FF" 1*escape-val
+ safe-val = ; Any character except reserved.
+ safe-tag = ; Any character except reserved, star and bad-tag.
+ reserved = `(' / `)' / `,' / `\' / `!' / `<' / `=' / `>' / `~' / CTL
+ escape-val = `\' HEXDIG HEXDIG
+ bad-tag = CR / LF / HTAB / `_'
+ star = `*'
+
+ The <attr-list>, if present, MUST be scanned prior to evaluation for
+ all occurrences of the escape character `\'. Reserved characters
+ MUST be escaped (other characters MUST NOT be escaped). All escaped
+ characters must be restored to their value before attempting string
+ matching. For Opaque values, escaped characters are not converted -
+ they are interpreted as bytes.
+
+ Boolean Strings which have the form "true" or "false" can
+ only take one value and may only be compared with
+ '='. Booleans are case insensitive when compared.
+
+ Integer Strings which take the form [-] 1*<digit> and fall
+ in the range "-2147483648" to "2147483647" are
+ considered to be Integers. These are compared using
+ integer comparison.
+
+ String All other Strings are matched using strict lexical
+ ordering (see Section 6.4).
+
+ Opaque Opaque values are sequences of bytes. These are
+ distinguished from Strings since they begin with
+ the sequence "\FF". This, unescaped, is an illegal
+ UTF-8 encoding, indicating that what follows is a
+ sequence of bytes expressed in escape notation which
+ constitute the binary value. For example, a '0' byte
+ is encoded "\FF\00".
+
+
+
+Guttman, et al. Standards Track [Page 11]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ A string which contains escaped values other than from the reserved
+ set of characters is illegal. If such a string is included in an
+ <attr-list>, <tag-list> or search filter, the SA or DA which receives
+ it MUST return a PARSE_ERROR to the message.
+
+ A keyword has only an <attr-tag>, and no values. Attributes can have
+ one or multiple values. All values are expressed as strings.
+
+ When values have been advertised by a SA or are registered in a DA,
+ they can take on implicit typing rules for matching incoming
+ requests.
+
+ Stored values must be consistent, i.e., x=4,true,sue,\ff\00\00 is
+ disallowed. A DA or SA receiving such an <attr-list> MUST return an
+ INVALID_REGISTRATION error.
+
+6. Required Features
+
+ This section defines the minimal implementation requirements for SAs
+ and UAs as well as their interaction with DAs. A DA is not required
+ for SLP to function, but if it is present, the UA and SA MUST
+ interact with it as defined below.
+
+ A minimal implementation may consist of either a UA or SA or both.
+ The only required features of a UA are that it can issue SrvRqsts
+ according to the rules below and interpret DAAdverts, SAAdverts and
+ SrvRply messages. The UA MUST issue requests to DAs as they are
+ discovered. An SA MUST reply to appropriate SrvRqsts with SrvRply or
+ SAAdvert messages. The SA MUST also register with DAs as they are
+ discovered.
+
+ UAs perform discovery by issuing Service Request messages. SrvRqst
+ messages are issued, using UDP, following these prioritized rules:
+
+ 1. A UA issues a request to a DA which it has been configured with
+ by DHCP.
+
+ 2. A UA issues requests to DAs which it has been statically
+ configured with.
+
+ 3. UA uses multicast/convergence SrvRqsts to discover DAs, then uses
+ that set of DAs. A UA that does not know of any DAs SHOULD retry
+ DA discovery, increasing the waiting interval between subsequent
+ attempts exponentially (doubling the wait interval each time.)
+ The recommended minimum waiting interval is CONFIG_DA_FIND
+ seconds.
+
+
+
+
+
+Guttman, et al. Standards Track [Page 12]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ 4. A UA with no knowledge of DAs sends requests using multicast
+ convergence to SAs. SAs unicast replies to UAs according to the
+ multicast convergence algorithm.
+
+ UAs and SAs are configured with a list of scopes to use according to
+ these prioritized rules:
+
+ 1. With DHCP.
+
+ 2. With static configuration. The static configuration may be
+ explicitly set to NO SCOPE for UAs, if the User Selectable Scope
+ model is used. See section 11.2.
+
+ 3. In the absence of configuration, the agent's scope is "DEFAULT".
+
+ A UA MUST issue requests with one or more of the scopes it has been
+ configured to use.
+
+ A UA which has been statically configured with NO SCOPE LIST will use
+ DA or SA discovery to determine its scope list dynamically. In this
+ case it uses an empty scope list to discover DAs and possibly SAs.
+ Then it uses the scope list it obtains from DAAdverts and possibly
+ SAAdverts in subsequent requests.
+
+ The SA MUST register all its services with any DA it discovers, if
+ the DA advertises any of the scopes it has been configured with. A
+ SA obtains information about DAs as a UA does. In addition, the SA
+ MUST listen for multicast unsolicited DAAdverts. The SA registers by
+ sending SrvReg messages to DAs, which reply with SrvReg messages to
+ indicate success. SAs register in ALL the scopes they were
+ configured to use.
+
+6.1. Use of Ports, UDP, and Multicast
+
+ DAs MUST accept unicast requests and multicast directory agent
+ discovery service requests (for the service type "service:directory-
+ agent").
+
+ SAs MUST accept multicast requests and unicast requests both. The SA
+ can distinguish between them by whether the REQUEST MCAST flag is set
+ in the SLP Message header.
+
+ The Service Location Protocol uses multicast for discovering DAs and
+ for issuing requests to SAs by default.
+
+ The reserved listening port for SLP is 427. This is the destination
+ port for all SLP messages. SLP messages MAY be transmitted on an
+ ephemeral port. Replies and acknowledgements are sent to the port
+
+
+
+Guttman, et al. Standards Track [Page 13]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ from which the request was issued. The default maximum transmission
+ unit for UDP messages is 1400 bytes excluding UDP and other headers.
+
+ If a SLP message does not fit into a UDP datagram it MUST be
+ truncated to fit, and the OVERFLOW flag is set in the reply message.
+ A UA which receives a truncated message MAY open a TCP connection
+ (see section 6.2) with the DA or SA and retransmit the request, using
+ the same XID. It MAY also attempt to make use of the truncated reply
+ or reformulate a more restrictive request which will result in a
+ smaller reply.
+
+ SLP Requests messages are multicast to The Administratively Scoped
+ SLP Multicast [17] address, which is 239.255.255.253. The default
+ TTL to use for multicast is 255.
+
+ In isolated networks, broadcasts will work in place of multicast. To
+ that end, SAs SHOULD and DAs MUST listen for broadcast Service
+ Location messages at port 427. This allows UAs which do not support
+ multicast the use of Service Location on isolated networks.
+
+ Setting multicast TTL to less than 255 (the default) limits the range
+ of SLP discovery in a network, and localizes service information in
+ the network.
+
+6.2. Use of TCP
+
+ A SrvReg or SrvDeReg may be too large to fit into a datagram. To
+ send such large SLP messages, a TCP (unicast) connection MUST be
+ established.
+
+ To avoid the need to implement TCP, one MUST insure that:
+
+ - UAs never issue requests larger than the Path MTU. SAs can omit
+ TCP support only if they never have to receive unicast requests
+ longer than the path MTU.
+
+ - UAs can accept replies with the 'OVERFLOW' flag set, and make use
+ of the first result included, or reformulate the request.
+
+ - Ensure that a SA can send a SrvRply, SrvReg, or SrvDeReg in
+ a single datagram. This means limiting the size of URLs,
+ the number of attributes and the number of authenticators
+ transmitted.
+
+ DAs MUST be able to respond to UDP and TCP requests, as well as
+ multicast DA Discovery SrvRqsts. SAs MUST be able to respond to TCP
+ unless the SA will NEVER receive a request or send a reply which will
+ exceed a datagram in size (e.g., some embedded systems).
+
+
+
+Guttman, et al. Standards Track [Page 14]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ A TCP connection MAY be used for a single SLP transaction, or for
+ multiple transactions. Since there are length fields in the message
+ headers, SLP Agents can send multiple requests along a connection and
+ read the return stream for acknowledgments and replies.
+
+ The initiating agent SHOULD close the TCP connection. The DA SHOULD
+ wait at least CONFIG_CLOSE_CONN seconds before closing an idle
+ connection. DAs and SAs SHOULD close an idle TCP connection after
+ CONFIG_CLOSE_CONN seconds to ensure robust operation, even when the
+ initiating agent neglects to close it. See Section 13 for timing
+ rules.
+
+6.3. Retransmission of SLP messages
+
+ Requests which fail to elicit a response are retransmitted. The
+ initial retransmission occurs after a CONFIG_RETRY wait period.
+ Retransmissions MUST be made with exponentially increasing wait
+ intervals (doubling the wait each time). This applies to unicast as
+ well as multicast SLP requests.
+
+ Unicast requests to a DA or SA should be retransmitted until either a
+ response (which might be an error) has been obtained, or for
+ CONFIG_RETRY_MAX seconds.
+
+ Multicast requests SHOULD be reissued over CONFIG_MC_MAX seconds
+ until a result has been obtained. UAs need only wait till they
+ obtain the first reply which matches their request. That is,
+ retransmission is not required if the requesting agent is prepared to
+ use the 'first reply' instead of 'as many replies as possible within
+ a bounded time interval.'
+
+ When SLP SrvRqst, SrvTypeRqst, and AttrRqst messages are multicast,
+ they contain a <PRList> of previous responders. Initially the
+ <PRList> is empty. When these requests are unicast, the <PRList> is
+ always empty.
+
+ Any DA or SA which sees its address in the <PRList> MUST NOT respond
+ to the request.
+
+ The message SHOULD be retransmitted until the <PRList> causes no
+ further responses to be elicited or the previous responder list and
+ the request will not fit into a single datagram or until
+ CONFIG_MC_MAX seconds elapse.
+
+ UAs which retransmit a request use the same XID. This allows a DA or
+ SA to cache its reply to the original request and then send it again,
+ should a duplicate request arrive. This cached information should
+ only be held very briefly. XIDs SHOULD be randomly chosen to avoid
+
+
+
+Guttman, et al. Standards Track [Page 15]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ duplicate XIDs in requests if UAs restart frequently.
+
+6.4. Strings in SLP messages
+
+ The escape character is a backslash (UTF-8 0x5c) followed by the two
+ hexadecimal digits of the escaped character. Only reserved
+ characters are escaped. For example, a comma (UTF-8 0x29) is escaped
+ as `\29', and a backslash `\' is escaped as `\5c'. String lists used
+ in SLP define the comma to be the delimiter between list elements, so
+ commas in data strings must be escaped in this manner. Backslashes
+ are the escape character so they also must always be escaped when
+ included in a string literally.
+
+ String comparison for order and equality in SLP MUST be case
+ insensitive inside the 0x00-0x7F subrange of UTF-8 (which corresponds
+ to ASCII character encoding). Case insensitivity SHOULD be supported
+ throughout the entire UTF-8 encoded Unicode [6] character set.
+
+ The case insensitivity rule applies to all string matching in SLPv2,
+ including Scope strings, SLP SPI strings, service types, attribute
+ tags and values in query handling, language tags, previous responder
+ lists. Comparisons of URL strings, however, is case sensitive.
+
+ White space (SPACE, CR, LF, TAB) internal to a string value is folded
+ to a single SPACE character for the sake of string comparisons.
+ White space preceding or following a string value is ignored for the
+ purposes of string comparison. For example, " Some String "
+ matches "SOME STRING".
+
+ String comparisons (using comparison operators such as `<=' or `>=')
+ are done using lexical ordering in UTF-8 encoded characters, not
+ using any language specific rules.
+
+ The reserved character `*' may precede, follow or be internal to a
+ string value in order to indicate substring matching. The query
+ including this character matches any character sequence which
+ conforms to the letters which are not wildcarded.
+
+6.4.1. Scope Lists in SLP
+
+ Scope Lists in SLPv2 have the following grammar:
+
+ scope-list = scope-val / scope-val `,' scope-list
+ scope-val = 1*safe
+ safe = ; Any character except reserved.
+ reserved = `(' / `)' / `,' / `\' / `!' / `<' / `=' / `>' / `~' / CTL
+ / `;' / `*' / `+'
+ escape-val = `\' HEXDIG HEXDIG
+
+
+
+Guttman, et al. Standards Track [Page 16]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ Scopes which include any reserved characters must replace the escaped
+ character with the escaped-val format.
+
+7. Errors
+
+ If the Error Code in a SLP reply message is nonzero, the rest of the
+ message MAY be truncated. No data is necessarily transmitted or
+ should be expected after the header and the error code, except
+ possibly for some optional extensions to clarify the error, for
+ example as in section D.1.
+
+ Errors are only returned for unicast requests. Multicast requests
+ are silently discarded if they result in an error.
+
+ LANGUAGE_NOT_SUPPORTED = 1: There is data for the service type in
+ the scope in the AttrRqst or SrvRqst, but not in the requested
+ language.
+ PARSE_ERROR = 2: The message fails to obey SLP syntax.
+ INVALID_REGISTRATION = 3: The SrvReg has problems -- e.g., a zero
+ lifetime or an omitted Language Tag.
+ SCOPE_NOT_SUPPORTED = 4: The SLP message did not include a scope in
+ its <scope-list> supported by the SA or DA.
+ AUTHENTICATION_UNKNOWN = 5: The DA or SA receives a request for an
+ unsupported SLP SPI.
+ AUTHENTICATION_ABSENT = 6: The DA expected URL and ATTR
+ authentication in the SrvReg and did not receive it.
+ AUTHENTICATION_FAILED = 7: The DA detected an authentication error in
+ an Authentication block.
+ VER_NOT_SUPPORTED = 9: Unsupported version number in message header.
+ INTERNAL_ERROR = 10: The DA (or SA) is too sick to respond.
+ DA_BUSY_NOW = 11: UA or SA SHOULD retry, using exponential back off.
+ OPTION_NOT_UNDERSTOOD = 12: The DA (or SA) received an unknown option
+ from the mandatory range (see section 9.1).
+ INVALID_UPDATE = 13: The DA received a SrvReg without FRESH set, for
+ an unregistered service or with inconsistent Service Types.
+ MSG_NOT_SUPPORTED = 14: The SA received an AttrRqst or SrvTypeRqst
+ and does not support it.
+ REFRESH_REJECTED = 15: The SA sent a SrvReg or partial SrvDereg to a
+ DA more frequently than the DA's min-refresh-interval.
+
+8. Required SLP Messages
+
+ All length fields in SLP messages are in network byte order. Where '
+ tuples' are defined, these are sequences of bytes, in the precise
+ order listed, in network byte order.
+
+ SLP messages all begin with the following header:
+
+
+
+
+Guttman, et al. Standards Track [Page 17]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Version | Function-ID | Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length, contd.|O|F|R| reserved |Next Ext Offset|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Next Extension Offset, contd.| XID |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Language Tag Length | Language Tag \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Message Type Abbreviation Function-ID
+
+ Service Request SrvRqst 1
+ Service Reply SrvRply 2
+ Service Registration SrvReg 3
+ Service Deregister SrvDeReg 4
+ Service Acknowledge SrvAck 5
+ Attribute Request AttrRqst 6
+ Attribute Reply AttrRply 7
+ DA Advertisement DAAdvert 8
+ Service Type Request SrvTypeRqst 9
+ Service Type Reply SrvTypeRply 10
+ SA Advertisement SAAdvert 11
+
+ SAs and UAs MUST support SrvRqst, SrvRply and DAAdvert. SAs MUST
+ also support SrvReg, SAAdvert and SrvAck. For UAs and SAs, support
+ for other messages are OPTIONAL.
+
+ - Length is the length of the entire SLP message, header included.
+ - The flags are: OVERFLOW (0x80) is set when a message's length
+ exceeds what can fit into a datagram. FRESH (0x40) is set on
+ every new SrvReg. REQUEST MCAST (0x20) is set when multicasting
+ or broadcasting requests. Reserved bits MUST be 0.
+ - Next Extension Offset is set to 0 unless extensions are used.
+ The first extension begins at 'offset' bytes, from the message's
+ beginning. It is placed after the SLP message data. See
+ Section 9.1 for how to interpret unrecognized SLP Extensions.
+ - XID is set to a unique value for each unique request. If the
+ request is retransmitted, the same XID is used. Replies set
+ the XID to the same value as the xid in the request. Only
+ unsolicited DAAdverts are sent with an XID of 0.
+ - Lang Tag Length is the length in bytes of the Language Tag field.
+ - Language Tag conforms to [7]. The Language Tag in a reply MUST
+ be the same as the Language Tag in the request. This field must
+ be encoded 1*8ALPHA *("-" 1*8ALPHA).
+
+
+
+
+Guttman, et al. Standards Track [Page 18]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ If an option is specified, and not included in the message, the
+ receiver MUST respond with a PARSE_ERROR.
+
+8.1. Service Request
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = SrvRqst = 1) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of <PRList> | <PRList> String \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of <service-type> | <service-type> String \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of <scope-list> | <scope-list> String \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of predicate string | Service Request <predicate> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of <SLP SPI> string | <SLP SPI> String \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ In order for a Service to match a SrvRqst, it must belong to at least
+ one requested scope, support the requested service type, and match
+ the predicate. If the predicate is present, the language of the
+ request (ignoring the dialect part of the Language Tag) must match
+ the advertised service.
+
+ <PRList> is the Previous Responder List. This <string-list> contains
+ dotted decimal notation IP (v4) addresses, and is iteratively
+ multicast to obtain all possible results (see Section 6.3). UAs
+ SHOULD implement this discovery algorithm. SAs MUST use this to
+ discover all available DAs in their scope, if they are not already
+ configured with DA addresses by some other means.
+
+ A SA silently drops all requests which include the SA's address in
+ the <PRList>. An SA which has multiple network interfaces MUST check
+ if any of the entries in the <PRList> equal any of its interfaces.
+ An entry in the PRList which does not conform to an IPv4 dotted
+ decimal address is ignored: The rest of the <PRList> is processed
+ normally and an error is not returned.
+
+ Once a <PRList> plus the request exceeds the path MTU, multicast
+ convergence stops. This algorithm is not intended to find all
+ instances; it finds 'enough' to provide useful results.
+
+ The <scope-list> is a <string-list> of configured scope names. SAs
+ and DAs which have been configured with any of the scopes in this
+ list will respond. DAs and SAs MUST reply to unicast requests with a
+
+
+
+Guttman, et al. Standards Track [Page 19]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ SCOPE_NOT_SUPPORTED error if the <scope-list> is omitted or fails to
+ include a scope they support (see Section 11). The only exceptions
+ to this are described in Section 11.2.
+
+ The <service-type> string is discussed in Section 4. Normally, a
+ SrvRqst elicits a SrvRply. There are two exceptions: If the
+ <service-type> is set to "service:directory-agent", DAs respond to
+ the SrvRqst with a DAAdvert (see Section 8.5.) If set to
+ "service:service-agent", SAs respond with a SAAdvert (see Section
+ 8.6.) If this field is omitted, a PARSE_ERROR is returned - as this
+ field is REQUIRED.
+
+ The <predicate> is a LDAPv3 search filter [14]. This field is
+ OPTIONAL. Services may be discovered simply by type and scope.
+ Otherwise, services are discovered which satisfy the <predicate>. If
+ present, it is compared to each registered service. If the attribute
+ in the filter has been registered with multiple values, the filter is
+ compared to each value and the results are ORed together, i.e.,
+ "(x=3)" matches a registration of (x=1,2,3); "(!(Y=0))" matches
+ (y=0,1) since Y can be nonzero. Note the matching is case
+ insensitive. Keywords (i.e., attributes without values) are matched
+ with a "presence" filter, as in "(keyword=*)".
+
+ An incoming request term MUST have the same type as the attribute in
+ a registration in order to match. Thus, "(x=33)" will not match '
+ x=true', etc. while "(y=foo)" will match 'y=FOO'.
+ "(|(x=33)(y=foo))" will be satisfied, even though "(x=33)" cannot be
+ satisfied, because of the `|' (boolean disjunction).
+
+ Wildcard matching MUST be done with the '=' filter. In any other
+ case, a PARSE_ERROR is returned. Request terms which include
+ wildcards are interpreted to be Strings. That is, (x=34*) would
+ match 'x=34foo', but not 'x=3432' since the first value is a String
+ while the second value is an Integer; Strings don't match Integers.
+
+ Examples of Predicates follow. <t> indicates the service type of the
+ SrvRqst, <s> gives the <scope-list> and <p> is the predicate string.
+
+ <t>=service:http <s>=DEFAULT <p>= (empty string)
+ This is a minimal request string. It matches all http
+ services advertised with the default scope.
+
+ <t>=service:pop3 <s>=SALES,DEFAULT <p>=(user=wump)
+ This is a request for all pop3 services available in
+ the SALES or DEFAULT scope which serve mail to the user
+ `wump'.
+
+
+
+
+
+Guttman, et al. Standards Track [Page 20]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ <t>=service:backup <s>=BLDG 32 <p>=(&(q<=3)(speed>=1000))
+ This returns the backup service which has a queue length
+ less than 3 and a speed greater than 1000. It will
+ return this only for services registered with the BLDG 32
+ scope.
+
+ <t>=service:directory-agent <s>=DEFAULT <p>=
+ This returns DAAdverts for all DAs in the DEFAULT scope.
+
+ DAs are discovered by sending a SrvRqst with the service type set to
+ "service:directory-agent". If a predicate is included in the
+ SrvRqst, the DA SHOULD respond only if the predicate can be satisfied
+ with the DA's attributes. The <scope-list> MUST contain all scopes
+ configured for the UA or SA which is discovering DAs.
+
+ The <SLP SPI> string indicates a SLP SPI that the requester has been
+ configured with. If this string is omitted, the responder does not
+ include any Authentication Blocks in its reply. If it is included,
+ the responder MUST return a reply which has an associated
+ authentication block with the SLP SPI in the SrvRqst. If no replies
+ may be returned because the SLP SPI is not supported, the responder
+ returns an AUTHENTICATION_UNKNOWN error.
+
+8.2. Service Reply
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = SrvRply = 2) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Error Code | URL Entry count |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | <URL Entry 1> ... <URL Entry N> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The service reply contains zero or more URL entries (see Section
+ 4.3). A service reply with zero URL entries MUST be returned in
+ response to a unicast Service Request, if no matching URLs are
+ present. A service reply with zero URL entries MUST NOT be sent in
+ response to a multicast or broadcast service request (instead, if
+ there was no match found or an error processing the request, the
+ service reply should not be generated at all).
+
+ If the reply overflows, the UA MAY simply use the first URL Entry in
+ the list. A URL obtained by SLP may not be cached longer than
+ Lifetime seconds, unless there is a URL Authenticator block present.
+
+
+
+
+
+Guttman, et al. Standards Track [Page 21]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ In that case, the cache lifetime is indicated by the Timestamp in the
+ URL Authenticator (see Section 9.2).
+
+ An authentication block is returned in the URL Entries, including the
+ SLP SPI in the SrvRqst. If no SLP SPI was included in the request,
+ no Authentication Blocks are returned in the reply. URL
+ Authentication Blocks are defined in Section 9.2.1.
+
+ If a SrvRply is sent by UDP, a URL Entry MUST NOT be included unless
+ it fits entirely without truncation.
+
+8.3. Service Registration
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = SrvReg = 3) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | <URL-Entry> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of service type string | <service-type> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of <scope-list> | <scope-list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of attr-list string | <attr-list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |# of AttrAuths |(if present) Attribute Authentication Blocks...\
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The <entry> is a URL Entry (see section 4.3). The Lifetime defines
+ how long a DA can cache the registration. SAs SHOULD reregister
+ before this lifetime expires (but SHOULD NOT more often than once per
+ second). The Lifetime MAY be set to any value between 0 and 0xffff
+ (maximum, around 18 hours). Long-lived registrations remain stale
+ longer if the service fails and the SA does not deregister the
+ service.
+
+ The <service-type> defines the service type of the URL to be
+ registered, regardless of the scheme of the URL. The <scope-list>
+ MUST contain the names of all scopes configured for the SA, which the
+ DA it is registering with supports. The default value for the
+ <scope-list> is "DEFAULT" (see Section 11).
+
+ The SA MUST register consistently with all DAs. If a SA is
+ configured with scopes X and Y and there are three DAs, whose scopes
+ are "X", "Y" and "X,Y" respectively, the SA will register the with
+ all three DAs in their respective scopes. All future updates and
+ deregistrations of the service must be sent to the same set of DAs in
+
+
+
+Guttman, et al. Standards Track [Page 22]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ the same scopes the service was initially registered in.
+
+ The <attr-list>, if present, specifies the attributes and values to
+ be associated with the URL by the DA (see Section 5).
+
+ A SA configured with the ability to sign service registrations MUST
+ sign each of the URLs and Attribute Lists using each of the keys it
+ is configured to use, and the DA it is registering with accepts.
+ (The SA MUST acquire DAAdverts for all DAs it will register with to
+ obtain the DA's SLP SPI list and attributes, as described in Section
+ 8.5). The SA supplies a SLP SPI in each authentication block
+ indicating the SLP SPI configuration required to verify the digital
+ signature. The format of the digital signatures used is defined in
+ section 9.2.1.
+
+ Subsequent registrations of previously registered services MUST
+ contain the same list of SLP SPIs as previous ones or else DAs will
+ reject them, replying with an AUTHENTICATION_ABSENT error.
+
+ A registration with the FRESH flag set will replace *entirely* any
+ previous registration for the same URL in the same language. If the
+ FRESH flag is not set, the registration is an "incremental"
+ registration (see Section 9.3).
+
+8.4. Service Acknowledgment
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = SrvAck = 5) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Error Code |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ A DA returns a SrvAck to an SA after a SrvReg. It carries only a two
+ byte Error Code (see Section 7).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 23]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+8.5. Directory Agent Advertisement
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = DAAdvert = 8) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Error Code | DA Stateless Boot Timestamp |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |DA Stateless Boot Time,, contd.| Length of URL |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ \ URL \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length of <scope-list> | <scope-list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length of <attr-list> | <attr-list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length of <SLP SPI List> | <SLP SPI List> String \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | # Auth Blocks | Authentication block (if any) \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The Error Code is set to 0 when the DAAdvert is multicast. If the
+ DAAdvert is being returned due to a unicast SrvRqst (ie. a request
+ without the REQUEST MCAST flag set) the DA returns the same errors a
+ SrvRply would.
+
+ The <scope-list> of the SrvRqst must either be omitted or include a
+ scope which the DA supports. The DA Stateless Boot Timestamp
+ indicates the state of the DA (see section 12.1).
+
+ The DA MAY include a list of its attributes in the DAAdvert. This
+ list SHOULD be kept short, as the DAAdvert must fit into a datagram
+ in order to be multicast.
+
+ A potential scaling problem occurs in SLPv2 if SAs choose too low a
+ Lifetime. In this case, an onerous amount of reregistration occurs
+ as more services are deployed. SLPv2 allows DAs to control SAs
+ frequency of registration. A DA MAY reissue a DAAdvert with a new
+ set of attributes at any time, to change the reregistration behavior
+ of SAs. These apply only to subsequent registrations; existing
+ service registrations with the DA retain their registered lifetimes.
+
+ If the DAAdvert includes the attribute "min-refresh-interval" it MUST
+ be set to a single Integer value indicating a number of seconds. If
+ this attribute is present SAs MUST NOT refresh any particular service
+ advertisement more frequently than this value. If SrvReg with the
+ FRESH FLAG not set or SrvDereg with a non-empty tag list updating a
+
+
+
+Guttman, et al. Standards Track [Page 24]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ particular service are received more often than the value for the
+ DA's advertised "min-refresh-interval" attribute the DA SHOULD reject
+ the message and return a REFRESH_REJECTED error in the SrvAck.
+
+ The URL is "service:directory-agent://"<addr> of the DA, where <addr>
+ is the dotted decimal numeric address of the DA. The <scope-list> of
+ the DA MUST NOT be NULL.
+
+ The SLP SPI List is the list of SPIs that the DA is capable of
+ verifying. SAs MUST NOT register services with authentication blocks
+ for those SLP SPIs which are not on the list. DAs will reject
+ service registrations which they cannot verify, returning an
+ AUTHENTICATION_UNKNOWN error.
+
+ The format of DAAdvert signatures is defined in Section 9.2.1.
+
+ The SLP SPI which is used to verify the DAAdvert is included in the
+ Authentication Block. When DAAdverts are multicast, they may have to
+ transmit multiple DAAdvert Authentication Blocks. If the DA is
+ configured to be able to generate signatures for more than one SPI,
+ the DA MUST include one Authentication Block for each SPI. If all
+ these Authentication Blocks do not fit in a single datagram (to
+ multicast or broadcast) the DA MUST send separate DAAdverts so that
+ Authentication Blocks for all the SPIs the DA is capable of
+ generating are sent.
+
+ If the DAAdvert is being sent in response to a SrvRqst, the DAAdvert
+ contains only the authentication block with the SLP SPI in the
+ SrvRqst, if the DA is configured to be able to produce digital
+ signatures using that SLP SPI. If the SrvRqst is unicast to the DA
+ (the REQUEST MCAST flag in the header is not set) and an unsupported
+ SLP SPI is included, the DA replies with a DAAdvert with the Error
+ Code set to an AUTHENTICATION_UNKNOWN error.
+
+ UAs SHOULD be configured with SLP SPIs that will allow them to verify
+ DA Advertisements. If the UA is configured with SLP SPIs and
+ receives a DAAdvert which fails to be verified using one of them, the
+ UA MUST discard it.
+
+8.6. Service Agent Advertisement
+
+ User Agents MUST NOT solicit SA Advertisements if they have been
+ configured to use a particular DA, if they have been configured with
+ a <scope-list> or if DAs have been discovered. UAs solicit SA
+ Advertisements only when they are explicitly configured to use User
+ Selectable scopes (see Section 11.2) in order to discover the scopes
+ that SAs support. This allows UAs without scope configuration to
+ make use of either DAs or SAs without any functional difference
+
+
+
+Guttman, et al. Standards Track [Page 25]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ except performance.
+
+ A SA MAY be configured with attributes, and SHOULD support the
+ attribute 'service-type' whose value is all the service types of
+ services represented by the SA. SAs MUST NOT respond if the SrvRqst
+ predicate is not satisfied. For example, only SAs offering 'nfs'
+ services SHOULD respond with a SAAdvert to a SrvRqst for service type
+ "service:service-agent" which includes a predicate "(service-
+ type=nfs)".
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = SAAdvert = 11) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length of URL | URL \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length of <scope-list> | <scope-list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length of <attr-list> | <attr-list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | # auth blocks | authentication block (if any) \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The SA responds only to multicast SA discovery requests which either
+ include no <scope-list> or a scope which they are configured to use.
+
+ The SAAdvert MAY include a list of attributes the SA supports. This
+ attribute list SHOULD be kept short so that the SAAdvert will not
+ exceed the path MTU in size.
+
+ The URL is "service:service-agent://"<addr> of the SA, where <addr>
+ is the dotted decimal numeric address of the SA. The <scope-list> of
+ the SA MUST NOT be null.
+
+ The SAAdvert contains one SAAdvert Authentication block for each SLP
+ SPI the SA can produce Authentication Blocks for. If the UA can
+ verify the Authentication Block of the SAAdvert, and the SAAdvert
+ fails to be verified, the UA MUST discard it.
+
+9. Optional Features
+
+ The features described in this section are not mandatory. Some are
+ useful for interactive use of SLP (where a user rather than a program
+ will select services, using a browsing interface for example) and for
+ scalability of SLP to larger networks.
+
+
+
+
+
+Guttman, et al. Standards Track [Page 26]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+9.1. Service Location Protocol Extensions
+
+ The format of a Service Location Extension is:
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Extension ID | Next Extension Offset |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Offset, contd.| Extension Data \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Extension IDs are assigned in the following way:
+
+ 0x0000-0x3FFF Standardized. Optional to implement. Ignore if
+ unrecognized.
+ 0x4000-0x7FFF Standardized. Mandatory to implement. A UA or SA
+ which receives this option in a reply and does not understand
+ it MUST silently discard the reply. A DA or SA which receives
+ this option in a request and does not understand it MUST return
+ an OPTION_NOT_UNDERSTOOD error.
+ 0x8000-0x8FFF For private use (not standardized). Optional to
+ implement. Ignore if unrecognized.
+ 0x9000-0xFFFF Reserved.
+
+ The three byte offset to next extension indicates the position of the
+ next extension as offset from the beginning of the SLP message.
+
+ The offset value is 0 if there are no extensions following the
+ current extension.
+
+ If the offset is 0, the length of the current Extension Data is
+ determined by subtracting total length of the SLP message as given in
+ the SLP message header minus the offset of the current extension.
+
+ Extensions defined in this document are in Section D. See section 15
+ for procedures that are required when specifying new SLP extensions.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 27]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+9.2. Authentication Blocks
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Block Structure Descriptor | Authentication Block Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Timestamp |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SLP SPI String Length | SLP SPI String \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Structured Authentication Block ... \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Authentication blocks are returned with certain SLP messages to
+ verify that the contents have not been modified, and have been
+ transmitted by an authorized agent. The authentication data
+ (contained in the Structured Authentication Block) is typically case
+ sensitive. Even though SLP registration data (e.g., attribute
+ values) are typically are not case sensitive, the case of the
+ registration data has to be preserved by the registering DA so that
+ UAs will be able to verify the data used for calculating digital
+ signature data.
+
+ The Block Structure Descriptor (BSD) identifies the format of the
+ Authenticator which follows. BSDs 0x0000-0x7FFF will be maintained
+ by IANA. BSDs 0x8000-0x8FFF are for private use.
+
+ The Authentication Block Length is the length of the entire block,
+ starting with the BSD.
+
+ The Timestamp is the time that the authenticator expires (to prevent
+ replay attacks.) The Timestamp is a 32-bit unsigned fixed-point
+ number of seconds relative to 0h on 1 January 1970. SAs use this
+ value to indicate when the validity of the digital signature expires.
+ This Timestamp will wrap back to 0 in the year 2106. Once the value
+ of the Timestamp wraps, the time at which the Timestamp is relative
+ to resets. For example, after 06h28 and 16 seconds 5 February 2106,
+ all Timestamp values will be relative to that epoch date.
+
+ The SLP Security Parameters Index (SPI) string identifies the key
+ length, algorithm parameters and keying material to be used by agents
+ to verify the signature data in the Structured Authentication Block.
+ The SLP SPI string has the same grammar as the <scope-val> defined in
+ Section 6.4.1.
+
+ Reserved characters in SLP SPI strings must be escaped using the same
+ convention as used throughout SLPv2.
+
+
+
+Guttman, et al. Standards Track [Page 28]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ SLP SPIs deployed in a site MUST be unique. An SLP SPI used for
+ BSD=0x0002 must not be the same as used for some other BSD.
+
+ All SLP agents MUST implement DSA [20] (BSD=0x0002). SAs MUST
+ register services with DSA authentication blocks, and they MAY
+ register them with other authentication blocks using other
+ algorithms. SAs MUST use DSA authentication blocks in SrvDeReg
+ messages and DAs MUST use DSA authentication blocks in unsolicited
+ DAAdverts.
+
+9.2.1. SLP Message Authentication Rules
+
+ The sections below define how to calculate the value to apply to the
+ algorithm identified by the BSD value. The components listed are
+ used as if they were a contiguous single byte aligned buffer in the
+ order given.
+
+ URL
+ 16-bit Length of SLP SPI String, SLP SPI String.
+ 16-bit Length of URL, URL,
+ 32-bit Timestamp.
+
+ Attribute List
+ 16-bit Length of SLP SPI String, SLP SPI String,
+ 16-bit length of <attr-list>, <attr-list>,
+ 32-bit Timestamp.
+
+ DAAdvert
+ 16-bit Length of SLP SPI String, SLP SPI String,
+ 32-bit DA Stateless Boot Timestamp,
+ 16-bit Length of URL, URL,
+ 16-bit Length of <attr-list>, <attr-list>,
+ 16-bit Length of DA's <scope-list>, DA's <scope-list>,
+ 16-bit Length of DA's <SLP SPI List>, DA's <SLP SPI List>,
+ 32-bit Timestamp.
+
+ The first SLP SPI is the SLP SPI in the Authentication
+ Block. This SLP SPI indicates the keying material and other
+ parameters to use to verify the DAAdvert. The SLP SPI List is
+ the list of SLP SPIs the DA itself supports, and is able to
+ verify.
+
+ SAAdvert
+ 16-bit Length of SLP SPI String, SLP SPI String,
+ 16-bit Length of URL, URL,
+ 16-bit Length of <attr-list>, <attr-list>,
+ 16-bit Length of <scope-list>, <scope-list>,
+ 32-bit Timestamp.
+
+
+
+Guttman, et al. Standards Track [Page 29]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+9.2.2 DSA with SHA-1 in Authentication Blocks
+
+ BSD=0x0002 is defined to be DSA with SHA-1. The signature
+ calculation is defined by [20]. The signature format conforms to
+ that in the X.509 v3 certificate:
+
+ 1. The signature algorithm identifier (an OID)
+ 2. The signature value (an octet string)
+ 3. The certificate path.
+
+ All data is represented in ASN.1 encoding:
+
+ id-dsa-with-sha1 ID ::= {
+ iso(1) member-body(2) us(840) x9-57 (10040)
+ x9cm(4) 3 }
+
+ i.e., the ASN.1 encoding of 1.2.840.10040.4.3 followed immediately
+ by:
+
+ Dss-Sig-Value ::= SEQUENCE {
+ r INTEGER,
+ s INTEGER }
+
+ i.e., the binary ASN.1 encoding of r and s computed using DSA and
+ SHA-1. This is followed by a certificate path, as defined by X.509
+ [10], [2], [3], [4], [5].
+
+ Authentication Blocks for BSD=0x0002 have the following format. In
+ the future, BSDs may be assigned which have different formats.
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ASN.1 encoded DSA signature \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+9.3. Incremental Service Registration
+
+ Incremental registrations update attribute values for a previously
+ registered service. Incremental service registrations are useful
+ when only a single attribute has changed, for instance. In an
+ incremental registration, the FRESH flag in the SrvReg header is NOT
+ set.
+
+ The new registration's attributes replace the previous
+ registration's, but do not affect attributes which were included
+ previously and are not present in the update.
+
+
+
+
+Guttman, et al. Standards Track [Page 30]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ For example, suppose service:x://a.org has been registered with
+ attributes A=1, B=2, C=3. If an incremental registration comes for
+ service:x://a.org with attributes C=30, D=40, then the attributes for
+ the service after the update are A=1, B=2, C=30, D=40.
+
+ Incremental registrations MUST NOT be performed for services
+ registered with Authentication Blocks. These must be registered with
+ ALL attributes, with the FRESH flag in the SrvReg header set. DAs
+ which receive such registration messages return an
+ AUTHENTICATION_FAILED error.
+
+ If the FRESH flag is not set and the DA does not have a prior
+ registration for the service, the incremental registration fails with
+ error code INVALID_UPDATE.
+
+ The SA MUST use the same <scope-list> in an update message as was
+ used in the prior registration. If this is not done, the DA returns
+ a SCOPE_NOT_SUPPORTED error. In order to change the scope of a
+ service advertisement it MUST be deregistered first and reregistered
+ with a new <scope-list>.
+
+ The SA MUST use the same <service-type> in an update message as was
+ used in a prior registration of the same URL. If this is not done,
+ the DA returns an INVALID_UPDATE error.
+
+9.4. Tag Lists
+
+ Tag lists are used in SrvDeReg and AttrReq messages. The syntax of a
+ <tag-list> item is:
+
+ tag-filter = simple-tag / substring
+ simple-tag = 1*filt-char
+ substring = [initial] any [final]
+ initial = 1*filt-char
+ any = `*' *(filt-char `*')
+ final = 1*filt-char
+ filt-char = Any character excluding <reserved> and <bad-tag> (see
+ grammar in Section 5).
+
+ Wild card characters in a <tag-list> item match arbitrary sequences
+ of characters. For instance "*bob*" matches "some bob I know",
+ "bigbob", "bobby" and "bob".
+
+
+
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 31]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+10. Optional SLP Messages
+
+ The additional requests provide features for user interaction and for
+ efficient updating of service advertisements with dynamic attributes.
+
+10.1. Service Type Request
+
+ The Service Type Request (SrvTypeRqst) allows a UA to discover all
+ types of service on a network. This is useful for general purpose
+ service browsers.
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = SrvTypeRqst = 9) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of PRList | <PRList> String \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of Naming Authority | <Naming Authority String> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of <scope-list> | <scope-list> String \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The <PRList> list and <scope-list> are interpreted as in Section 8.1.
+
+ The Naming Authority string, if present in the request, will limit
+ the reply to Service Type strings with the specified Naming
+ Authority. If the Naming Authority string is absent, the IANA
+ registered service types will be returned. If the length of the
+ Naming Authority is set to 0xFFFF, the Naming Authority string is
+ omitted and ALL Service Types are returned, regardless of Naming
+ Authority.
+
+10.2. Service Type Reply
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = SrvTypeRply = 10) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Error Code | length of <srvType-list> |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | <srvtype--list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The service-type Strings (as described in Section 4.1) are provided
+ in <srvtype-list>, which is a <string-list>.
+
+
+
+
+Guttman, et al. Standards Track [Page 32]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ If a service type has a Naming Authority other than IANA it MUST be
+ returned following the service type string and a `.' character.
+ Service types with the IANA Naming Authority do not include a Naming
+ Authority string.
+
+10.3. Attribute Request
+
+ The Attribute Request (AttrRqst) allows a UA to discover attributes
+ of a given service (by supplying its URL) or for an entire service
+ type. The latter feature allows the UA to construct a query for an
+ available service by selecting desired features. The UA may request
+ that all attributes are returned, or only a subset of them.
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = AttrRqst = 6) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of PRList | <PRList> String \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of URL | URL \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of <scope-list> | <scope-list> string \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of <tag-list> string | <tag-list> string \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | length of <SLP SPI> string | <SLP SPI> string \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The <PRList>, <scope-list> and <SLP SPI> string are interpreted as in
+ Section 8.1.
+
+ The URL field can take two forms. It can simply be a Service Type
+ (see Section 4.1), such as "http" or "service:tftp". In this case,
+ all attributes and the full range of values for each attribute of all
+ services of the given Service Type is returned.
+
+ The URL field may alternatively be a full URL, such as
+ "service:printer:lpr://igore.wco.ftp.com:515/draft" or
+ "nfs://max.net/znoo". In this, only the registered attributes for
+ the specified URL are returned.
+
+ The <tag-list> field is a <string-list> of attribute tags, as defined
+ in Section 9.4 which indicates the attributes to return in the
+ AttrRply. If <tag-list> is omitted, all attributes are returned.
+ <tag-list> MUST be omitted and a full URL MUST be included when
+ attributes when a SLP SPI List string is included, otherwise the DA
+ will reply with an AUTHENTICATION_FAILED error.
+
+
+
+Guttman, et al. Standards Track [Page 33]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+10.4. Attribute Reply
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = AttrRply = 7) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Error Code | length of <attr-list> |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | <attr-list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |# of AttrAuths | Attribute Authentication Block (if present) \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The format of the <attr-list> and the Authentication Block is as
+ specified for SrvReg (see Section 9.2.1).
+
+ Attribute replies SHOULD be returned with the original case of the
+ string registration intact, as they are likely to be human readable.
+ In the case where the AttrRqst was by service type, all attributes
+ defined for the service type, and all their values are returned.
+
+ Although white space is folded for string matching, attribute tags
+ and values MUST be returned with their original white space
+ preserved.
+
+ Only one copy of each attribute tag or String value should be
+ returned, arbitrarily choosing one version (with respect to upper and
+ lower case and white space internal to the strings): Duplicate
+ attributes and values SHOULD be removed. An arbitrary version of the
+ string value and tag name is chosen for the merge. For example:
+ "(A=a a,b)" merged with "(a=A A,B)" may yield "(a=a a,B)".
+
+10.5. Attribute Request/Reply Examples
+
+ Suppose that printer services have been registered as follows:
+
+ Registered Service:
+ URL = service:printer:lpr://igore.wco.ftp.com/draft
+ scope-list = Development
+ Lang. Tag = en
+ Attributes = (Name=Igore),(Description=For developers only),
+ (Protocol=LPR),(location-description=12th floor),
+ (Operator=James Dornan \3cdornan@monster\3e),
+ (media-size=na-letter),(resolution=res-600),x-OK
+
+ URL = service:printer:lpr://igore.wco.ftp.com/draft
+ scope-list = Development
+
+
+
+Guttman, et al. Standards Track [Page 34]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ Lang. Tag = de
+ Attributes = (Name=Igore),(Description=Nur fuer Entwickler),
+ (Protocol=LPR),(location-description=13te Etage),
+ (Operator=James Dornan \3cdornan@monster\3e),
+ (media-size=na-letter),(resolution=res-600),x-OK
+
+ URL = service:printer:http://not.wco.ftp.com/cgi-bin/pub-prn
+ scope-list = Development
+ Lang. Tag = en
+ Attributes = (Name=Not),(Description=Experimental IPP printer),
+ (Protocol=http),(location-description=QA bench),
+ (media-size=na-letter),(resolution=other),x-BUSY
+
+ Notice the first printer, "Igore" is registered in both English and
+ German. The `<' and `>' characters in the Operator attribute value
+ which are part of the Email address had to be escaped, as they are
+ reserved characters for values.
+
+ Attribute tags are not translated, though attribute values may be,
+ see [13].
+
+ The attribute Request:
+
+ URL = service:printer:lpr://igore.wco.ftp.com/draft
+ scope-list = Development
+ Lang. Tag = de
+ tag-list = resolution,loc*
+
+ receives the Attribute Reply:
+
+ (location-description=13te Etage),(resolution=res-600)
+
+ The attribute Request:
+
+ URL = service:printer
+ scope-list = Development
+ Lang. Tag = en
+ tag-list = x-*,resolution,protocol
+
+ receives an Attribute Reply containing:
+
+ (protocols=http,LPR),(resolution=res-600,other),x-OK,x-BUSY
+
+ The first request is by service instance and returns the requested
+ values, in German. The second request is by abstract service type
+ (see Section 4) and returns values from both "Igore" and "Not".
+
+
+
+
+
+Guttman, et al. Standards Track [Page 35]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ An attribute Authentication Block is returned if an authentication
+ block with the SLP SPI in the AttrRqst can be returned. Note that
+ the <attr-list> returned from a DA with an Authentication Block MUST
+ be identical to the <attr-list> registered by a SA, in order for the
+ authentication verification calculations to be possible.
+
+ A SA or DA only returns an Attribute Authentication Block if the
+ AttrRqst included a full URL in the request and no tag list.
+
+ If an SLP SPI is specified in a unicast request (the REQUEST MCAST
+ flag in the header is not set) and the SA or DA cannot return an
+ Authentication Block with that SLP SPI, an AUTHENTICATION_UNKNOWN
+ error is returned. The # of Attr Auths field is set to 0 if there no
+ Authentication Block is included, or 1 if an Authentication Block
+ follows.
+
+10.6. Service Deregistration
+
+ A DA deletes a service registration when its Lifetime expires.
+ Services SHOULD be deregistered when they are no longer available,
+ rather than leaving the registrations to time out.
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Location header (function = SrvDeReg = 4) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length of <scope-list> | <scope-list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | URL Entry \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length of <tag-list> | <tag-list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The <scope-list> is a <string-list> (see section 2.1).
+
+ The SA MUST retry if there is no response from the DA, see Section
+ 12.3. The DA acknowledges a SrvDeReg with a SrvAck. Once the SA
+ receives an acknowledgment indicating success, the service and/or
+ attributes are no longer advertised by the DA. The DA deregisters the
+ service or service attributes from every scope specified in the
+ SrvDeReg which it was previously registered in.
+
+ The SA MUST deregister all services with the same scope list used to
+ register the service with a DA. If this is not done in the SrvDeReg
+ message, the DA returns a SCOPE_NOT_SUPPORTED error. The Lifetime
+ field in the URL Entry is ignored for the purposes of the SrvDeReg.
+
+
+
+
+Guttman, et al. Standards Track [Page 36]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ The <tag-list> is a <string-list> of attribute tags to deregister as
+ defined in Section 9.4. If no <tag-list> is present, the SrvDeReg
+ deregisters the service in all languages it has been registered in.
+ If the <tag-list> is present, the SrvDeReg deregisters the attributes
+ whose tags are listed in the tag spec. Services registered with
+ Authentication Blocks MUST NOT include a <tag-list> in a SrvDeReg
+ message: A DA will respond with an AUTHENTICATION_FAILED error in
+ this case.
+
+ If the service to be deregistered was registered with an
+ authentication block or blocks, a URL authentication block for each
+ of the SLP SPIs registered must be included in the SrvDeReg.
+ Otherwise, the DA returns an AUTHENTICATION_ABSENT error. If the
+ message fails to be verified by the DA, an AUTHENTICATION_FAILED
+ error is returned by the DA.
+
+11. Scopes
+
+ Scopes are sets of services. The primary use of Scopes is to provide
+ the ability to create administrative groupings of services. A set of
+ services may be assigned a scope by network administrators. A client
+ seeking services is configured to use one or more scopes. The user
+ will only discover those services which have been configured for him
+ or her to use. By configuring UAs and SAs with scopes,
+ administrators may provision services. Scopes strings are case
+ insensitive. The default SCOPE string is "DEFAULT".
+
+ Scopes are the primary means an administrator has to scale SLP
+ deployments to larger networks. When DAs with NON-DEFAULT scopes are
+ present on the network, further gains can be had by configuring UAs
+ and SAs to have a predefined non-default scope. These agents can
+ then perform DA discovery and make requests using their scope. This
+ will limit the number of replies.
+
+11.1. Scope Rules
+
+ SLP messages which fail to contain a scope that the receiving Agent
+ is configured to use are dropped (if the request was multicast) or a
+ SCOPE_NOT_SUPPORTED error is returned (if the request was unicast).
+ Every SrvRqst (except for DA and SA discovery requests), SrvReg,
+ AttrRqst, SrvTypeRqst, DAAdvert, and SAAdvert message MUST include a
+ <scope-list>.
+
+ A UA MUST unicast its SLP messages to a DA which supports the desired
+ scope, in preference to multicasting a request to SAs. A UA MAY
+ multicast the request if no DA is available in the scope it is
+ configured to use.
+
+
+
+
+Guttman, et al. Standards Track [Page 37]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+11.2. Administrative and User Selectable Scopes
+
+ All requests and services are scoped. The two exceptions are
+ SrvRqsts for "service:directory-agent" and "service:service-agent".
+ These MAY have a zero-length <scope-list> when used to enable the
+ user to make scope selections. In this case UAs obtain their scope
+ list from DAAdverts (or if DAs are not available, from SAAdverts.)
+
+ Otherwise, if SAs and UAs are to use any scope other than the default
+ (i.e., "DEFAULT"), the UAs and SAs are configured with lists of
+ scopes to use by system administrators, perhaps automatically by way
+ of DHCP option 78 or 79 [21]. Such administrative scoping allows
+ services to be provisioned, so that users will only see services they
+ are intended to see.
+
+ User configurable scopes allow a user to discover any service, but
+ require them to do their own selection of scope. This is similar to
+ the way AppleTalk [12] and SMB [19] networking allow user selection
+ of AppleTalk Zone or workgroups.
+
+ Note that the two configuration choices are not compatible. One
+ model allows administrators control over service provision. The
+ other delegates this to users (who may not be prepared to do any
+ configuration of their system).
+
+12. Directory Agents
+
+ DAs cache service location and attribute information. They exist to
+ enhance the performance and scalability of SLP. Multiple DAs provide
+ further scalability and robustness of operation, since they can each
+ store service information for the same SAs, in case one of the DAs
+ fails.
+
+ A DA provides a centralized store for service information. This is
+ useful in a network with several subnets or with many SLP Agents.
+ The DA address can be dynamically configured with UAs and SAs using
+ DHCP, or by using static configuration.
+
+ SAs configured to use DAs with DHCP or static configuration MUST
+ unicast a SrvRqst to the DA, when the SA is initialized. The SrvRqst
+ omits the scope list and sets the service type of the request to
+ "service:directory-agent". The DA will return a DAAdvert with its
+ attributes, SLP SPI list, and other parameters which are essential
+ for proper SA to DA communication.
+
+ Passive detection of DAs by SAs enables services to be advertised
+ consistently among DAs of the same scope. Advertisements expire if
+ not renewed, leaving only transient stale registrations in DAs, even
+
+
+
+Guttman, et al. Standards Track [Page 38]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ in the case of a failure of a SA.
+
+ A single DA can support many UAs. UAs send the same requests to DAs
+ that they would send to SAs and expect the same results. DAs reduce
+ the load on SAs, making simpler implementations of SAs possible.
+
+ UAs MUST be prepared for the possibility that the service information
+ they obtain from DAs is stale.
+
+12.1. Directory Agent Rules
+
+ When DAs are present, each SA MUST register its services with DAs
+ that support one or more of its scope(s).
+
+ UAs MUST unicast requests directly to a DA (when scoping rules
+ allow), hence avoiding using the multicast convergence algorithm, to
+ obtain service information. This decreases network utilization and
+ increases the speed at which UAs can obtain service information.
+
+ DAs MUST flush service advertisements once their lifetime expires or
+ their URL Authentication Block "Timestamp" of expiration is past.
+
+ DAAdverts MUST include DA Stateless Boot Timestamp, in the same
+ format as the Authentication Block (see Section 9.2). The Timestamp
+ in the Authentication Block indicates the time at which all previous
+ registrations were lost (i.e., the last stateless reboot). The
+ Timestamp is set to 0 in a DAAdvert to notify UAs and SAs that the DA
+ is going down. DAs MUST NOT use equal or lesser Boot Timestamps to
+ previous ones, if they go down and restart without service
+ registration state. This would mislead SAs to not reregister with
+ the DA.
+
+ DAs which receive a multicast SrvRqst for the service type
+ "service:directory-agent" MUST silently discard it if the <scope-
+ list> is (a) not omitted and (b) does not include a scope they are
+ configured to use. Otherwise the DA MUST respond with a DAAdvert.
+
+ DAs MUST respond to AttrRqst and SrvTypeRqst messages (these are
+ OPTIONAL only for SAs, not DAs.)
+
+12.2. Directory Agent Discovery
+
+ UAs can discover DAs using static configuration, DHCP options 78 and
+ 79, or by multicasting (or broadcasting) Service Requests using the
+ convergence algorithm in Section 6.3.
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 39]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ See Section 6 regarding unsolicited DAAdverts. Section 12.2.2
+ describes how SAs may reduce the number of times they must reregister
+ with DAs in response to unsolicited DAAdverts.
+
+ DAs MUST send unsolicited DAAdverts once per CONFIG_DA_BEAT. An
+ unsolicited DAAdvert has an XID of 0. SAs MUST listen for DAAdverts,
+ passively, as described in Section 8.5. UAs MAY do this. If they do
+ not listen for unsolicited DAAdverts, however, they will not discover
+ DAs as they become available. UAs SHOULD, in this case, do periodic
+ active DA discovery, see Section 6.
+
+ A URL with the scheme "service:directory-agent" indicates the DA's
+ location as defined in Section 8.5. For example:
+ "service:directory-agent://foobawooba.org".
+
+ The following sections suggest timing algorithms which enhance the
+ scalability of SLP.
+
+12.2.1. Active DA Discovery
+
+ After a UA or SA restarts, its initial DA discovery request SHOULD be
+ delayed for some random time uniformly distributed from 0 to
+ CONFIG_START_WAIT seconds.
+
+ The UA or SA sends the DA Discovery request using a SrvRqst, as
+ described in Section 8.1. DA Discovery requests MUST include a
+ Previous Responder List. SrvRqsts for Active DA Discovery SHOULD NOT
+ be sent more than once per CONFIG_DA_FIND seconds.
+
+ After discovering a new DA, a SA MUST wait a random time between 0
+ and CONFIG_REG_ACTIVE seconds before registering their services.
+
+12.2.2. Passive DA Advertising
+
+ A DA MUST multicast (or broadcast) an unsolicited DAAdvert every
+ CONFIG_DA_BEAT seconds. CONFIG_DA_BEAT SHOULD be specified to
+ prevent DAAdverts from using more than 1% of the available bandwidth.
+
+ All UAs and SAs which receive the unsolicited DAAdvert SHOULD examine
+ its DA stateless Boot Timestamp. If it is set to 0, the DA is going
+ down and no further messages should be sent to it.
+
+ If a SA detects a DA it has never encountered (with a nonzero
+ timestamp,) the SA must register with it. SAs MUST examine the
+ DAAdvert's timestamp to determine if the DA has had a stateless
+ reboot since the SA last registered with it. If so it registers with
+ the DA. SAs MUST wait a random interval between 0 and
+ CONFIG_REG_PASSIVE before beginning DA registration.
+
+
+
+Guttman, et al. Standards Track [Page 40]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+12.3. Reliable Unicast to DAs and SAs
+
+ If a DA or SA fails to respond to a unicast UDP message in
+ CONFIG_RETRY seconds, the message should be retried. The wait
+ interval for each subsequent retransmission MUST exponentially
+ increase, doubling each time. If a DA or SA fails to respond after
+ CONFIG_RETRY_MAX seconds, the sender should consider the receiver to
+ have gone down. The UA should use a different DA. If no such DA
+ responds, DA discovery should be used to find a new DA. If no DA is
+ available, multicast requests to SAs are used.
+
+12.4. DA Scope Configuration
+
+ By default, DAs are configured with the "DEFAULT" scope.
+ Administrators may add other configured scopes, in order to support
+ UAs and SAs in non default scopes. The default configuration MUST
+ NOT be removed from the DA unless:
+
+ - There are other DAs which support the "DEFAULT" scope, or
+
+ - All UAs and SAs have been configured with non-default scopes.
+
+ Non-default scopes can be phased-in as the SLP deployment grows.
+ Default scopes should be phased out only when the non-default scopes
+ are universally configured.
+
+ If a DA and SA are coresident on a host (quite possibly implemented
+ by the same process), configuration of the host is considerably
+ simplified if the SA supports only scopes also supported by the DA.
+ That is, the SA SHOULD NOT advertise services in any scopes which are
+ not supported by the coresident DA. This means that incoming requests
+ can be answered by a single data store; the SA and DA registrations
+ do not need to be kept separately.
+
+12.5. DAs and Authentication Blocks
+
+ DAs are not configured to sign service registrations or attribute
+ lists. They simply cache services registered by Service Agents. DAs
+ MUST NOT accept registrations including authentication blocks for SLP
+ SPIs which it is not configured with, see Section 8.5.
+
+ A DA protects registrations which are made with authentication blocks
+ using SLP SPIs it is configured to use. If a service S is
+ registered, a subsequent registration (which will replace the
+ adertisement) or a deregistration (which will remove it) MUST include
+ an Authentication Block with the corresponding SLP SPI, see Section
+ 8.3 and Section 10.6.
+
+
+
+
+Guttman, et al. Standards Track [Page 41]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ Example:
+
+ A DA is configured to be able to verify Authentication Blocks with
+ SLP SPIs "X,Y", that is X and Y.
+
+ An SA registers a service with an Authentication Block with SPI "Z".
+ The DA stores the registration, but discards the Authentication
+ Block. If a UA requests a service with an SLP SPI string "Z", the DA
+ will respond with an AUTHENTICATION_UNKNOWN error.
+
+ An SA registers a service S with Authentication Blocks including SLP
+ SPIs "X" and "Y". If a UA requests a service with an SLP SPI string
+ "X" the DA will be able to return S (if the service type, language,
+ scope and predicate of the SrvRqst match S) The DA will also return
+ the Authentication Block with SLP SPI set to "X". If the DA receives
+ a subsequent SrvDeReg for S (which will remove the advertisement) or
+ a subsequent SrvReg for S (which will replace it), the message must
+ include two URL Authentication Blocks, one each for SPIs "X" and "Y".
+ If either of these were absent, the DA would return an
+ AUTHENTICATION_ABSENT error.
+
+13. Protocol Timing Defaults
+
+Interval name Section Default Value Meaning
+------------------- ------- ------------- ------------------------
+CONFIG_MC_MAX 6.3 15 seconds Max time to wait for a
+ complete multicast query
+ response (all values.)
+CONFIG_START_WAIT 12.2.1 3 seconds Wait to perform DA
+ discovery on reboot.
+CONFIG_RETRY 12.3 2 seconds Wait interval before
+ initial retransmission
+ of multicast or unicast
+ requests.
+CONFIG_RETRY_MAX 12.3 15 seconds Give up on unicast
+ request retransmission.
+CONFIG_DA_BEAT 12.2.2 3 hours DA Heartbeat, so that SAs
+ passively detect new DAs.
+CONFIG_DA_FIND 12.3 900 seconds Minimum interval to wait
+ before repeating Active
+ DA discovery.
+CONFIG_REG_PASSIVE 12.2 1-3 seconds Wait to register services
+ on passive DA discovery.
+CONFIG_REG_ACTIVE 8.3 1-3 seconds Wait to register services
+ on active DA discovery.
+CONFIG_CLOSE_CONN 6.2 5 minutes DAs and SAs close idle
+ connections.
+
+
+
+
+Guttman, et al. Standards Track [Page 42]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+14. Optional Configuration
+
+ Broadcast Only
+ Any SLP agent SHOULD be configurable to use broadcast
+ only. See Sections 6.1 and 12.2.
+
+ Predefined DA
+ A UA or SA SHOULD be configurable to use a predefined DA.
+
+ No DA Discovery
+ The UA or SA SHOULD be configurable to ONLY use
+ predefined and DHCP-configured DAs and perform no active
+ or passive DA discovery.
+
+ Multicast TTL
+ The default multicast TTL is 255. Agents SHOULD be
+ configurable to use other values. A lower value will
+ focus the multicast convergence algorithm on smaller
+ subnetworks, decreasing the number of responses and
+ increases the performance of service location. This
+ may result in UAs obtaining different results for the
+ identical requests depending on where they are connected
+ to the network.
+
+ Timing Values
+ Time values other than the default MAY be configurable.
+ See Section 13.
+
+ Scopes
+ A UA MAY be configurable to support User Selectable
+ scopes by omitting all predefined scopes. See
+ Section 11.2. A UA or SA MUST be configurable to use
+ specific scopes by default. Additionally, a UA or SA
+ MUST be configurable to use specific scopes for requests
+ for and registrations of specific service types. The
+ scope or scopes of a DA MUST be configurable. The
+ default value for a DA is to have the scope "DEFAULT" if
+ not otherwise configured.
+
+ DHCP Configuration
+ DHCP options 78 and 79 may be used to configure SLP. If
+ DA locations are configured using DHCP, these SHOULD
+ be used in preference to DAs discovered actively or
+ passively. One or more of the scopes configured using
+ DHCP MUST be used in requests. The entire configured
+ <scope-list> MUST be used in registration and DA
+ configuration messages.
+
+
+
+
+Guttman, et al. Standards Track [Page 43]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ Service Template
+ UAs and SAs MAY be configured by using Service Templates.
+ Besides simplifying the specification of attribute
+ values, this also allows them to enforce the inclusion
+ of 'required' attributes in SrvRqst, SrvReg and SrvDeReg
+ messages. DAs MAY be configured with templates to
+ allow them to WARN UAs and SAs in these cases. See
+ Section 10.4.
+
+ SLP SPI for service discovery
+ Agents SHOULD be configurable to support SLP SPIs using
+ the following parameters: BSD=2 (DSA with SHA-1) and
+ a public key identified by the SLP SPI String. In
+ the future, when a Public Key Infrastructure exists,
+ SLP Agents may be able to obtain public keys and
+ cryptographic parameters corresponding to the names used
+ in SLP SPI Strings.
+
+ Note that if the SLP SPI string chosen is identical
+ to a scope string, it is effectively the same as a
+ Protected Scope in SLPv1. Namely, every SA advertising
+ in that scope would be configured with the same Private
+ Key. Every DA and UA of that scope would be configured
+ with the appropriate Public Key to verify signatures
+ produced by those SAs. This is a convenient way to
+ configure SLP deployments in the absence of a Public Key
+ Infrastructure. Currently, it would be too difficult to
+ manage the keying of UAs and DAs if each SA had its own
+ key.
+
+ SLP SPI for Directory Agent discovery
+ Agents SHOULD be configurable to support SLP SPIs as
+ above, to be used when discovering DAs. This SPI SHOULD
+ be sent in SrvRqsts to discover DAs and be used to verify
+ multicast DAAdvert messages.
+
+ SA and DA Private Key
+ SAs and DAs which can generate digital signatures require
+ a Private Key and a corresponding SLP SPI indentifier
+ to include in the Authentication Block. The SLP SPI
+ identifies the Public Key to use to verify the digital
+ signature in the Authentication Block.
+
+15. IANA Considerations
+
+ SLP includes four sets of identifiers which may be registered with
+ IANA. The policies for these registrations (See [18]) are noted in
+ each case.
+
+
+
+Guttman, et al. Standards Track [Page 44]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ The Block Structure Descriptor (BSD) identifies the format of the
+ Authenticator which follows. BSDs 0x8000-0x8FFF are for Private Use.
+
+ Further Block Structured Descriptor (BSD) values, from the range
+ 0x0003-0x7FFF may be standardized in the future by submitting a
+ document which describes:
+
+ - The data format of the Structured Authenticator block.
+
+ - Which cryptographic algorithm to use (including a reference
+ to a technical specification of the algorithm.)
+
+ - The format of any keying material required for
+ preconfiguring UAs, DAs and SAs. Also include any
+ considerations regarding key distribution.
+
+ - Security considerations to alert others to the strengths and
+ weaknesses of the approach.
+
+ The IANA will assign Cryptographic BSD numbers on the basis of IETF
+ Consenus.
+
+ New function-IDs, in the range 12-255, may be standardized by the
+ method of IETF Consensus.
+
+ New SLP Extensions with types in the range 2-65535 may be registered
+ following review by a Designated Expert.
+
+ New error numbers in the range 15-65535 are assigned on the basis of
+ a Standards Action.
+
+ Protocol elements used with Service Location Protocol may also
+ require IANA registration actions. SLP is used in conjunction with
+ "service:" URLs and Service Templates [13]. These are standardized
+ by review of a Designated Expert and a mailing list (See [13].)
+
+16. Internationalization Considerations
+
+ SLP messages support the use of multiple languages by providing a
+ Language Tag field in the common message header (see Section 8).
+
+ Services MAY be registered in multiple languages. This provides
+ attributes so that users with different language skills may select
+ services interactively.
+
+ Attribute tags are not translated. Attribute values may be
+ translated unless the Service Template [13] defines the attribute
+ values to be 'literal'.
+
+
+
+Guttman, et al. Standards Track [Page 45]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ A service which is registered in multiple languages may be queried in
+ multiple languages. The language of the SrvRqst or AttrRqst is used
+ to satisfy the request. If the requested language is not supported,
+ a LANGUAGE_NOT_SUPPORTED error is returned. SrvRply and AttrRply
+ messages are always in the same language of the request.
+
+ A DA or SA MAY be configured with translations of Service Templates
+ [13] for the same service type. This will allow the DA or SA to
+ translate a request (say in Italian) to the language of the service
+ advertisement (say in English) and then translate the reply back to
+ Italian. Similarly, a UA MAY use templates to translate outgoing
+ requests and incoming replies.
+
+ The dialect field in the Language Tag MAY be used: Requests which
+ can be fulfilled by matching a language and dialect will be preferred
+ to those which match only the language portion. Otherwise, dialects
+ have no effect on matching requests.
+
+17. Security Considerations
+
+ SLP provides for authentication of service URLs and service
+ attributes. This provides UAs and DAs with knowledge of the
+ integrity of service URLs and attributes included in SLP messages.
+ The only systems which can generate digital signatures are those
+ which have been configured by administrators in advance. Agents
+ which verify signed data may assume it is 'trustworthy' inasmuch as
+ administrators have ensured the cryptographic keying of SAs and DAs
+ reflects 'trustworthiness.'
+
+ Service Location does not provide confidentiality. Because the
+ objective of this protocol is to advertise services to a community of
+ users, confidentiality might not generally be needed when this
+ protocol is used in non-sensitive environments. Specialized schemes
+ might be able to provide confidentiality, if needed in the future.
+ Sites requiring confidentiality should implement the IP Encapsulating
+ Security Payload (ESP) [3] to provide confidentiality for Service
+ Location messages.
+
+ If Agents are not configured to generate Authentication Blocks and
+ Agents are not configured to verify them, an adversary might easily
+ use this protocol to advertise services on servers controlled by the
+ adversary and thereby gain access to users' private information.
+ Further, an adversary using this protocol will find it much easier to
+ engage in selective denial of service attacks. Sites that are in
+ potentially hostile environments (e.g., are directly connected to the
+ Internet) should consider the advantages of distributing keys
+ associated with SLP SPIs prior to deploying the sensitive directory
+ agents or service agents.
+
+
+
+Guttman, et al. Standards Track [Page 46]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ SLP is useful as a bootstrap protocol. It may be used in
+ environments in which no preconfiguration is possible. In such
+ situations, a certain amount of "blind faith" is required: Without
+ any prior configuration it is impossible to use any of the security
+ mechanisms described above. SLP will make use of the mechanisms
+ provided by the Security Area of the IETF for key distribution as
+ they become available. At this point it would only be possible to
+ gain the benefits associated with the use of Authentication Blocks if
+ cryptographic information and SLP SPIs can be preconfigured with the
+ end systems before they use SLP.
+
+ SLPv2 enables a number of security policies with the mechanisms it
+ includes. A SLPv2 UA could, for instance, reject any SLP message
+ which did not carry an authentication block which it could verify.
+ This is not the only policy which is possible to implement.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 47]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+A. Appendix: Changes to the Service Location Protocol from v1 to v2
+
+ SLP version 2 (SLPv2) corrects race conditions present in SLPv1 [22].
+ In addition, authentication has been reworked to provide more
+ flexibility and protection (especially for DA Advertisements). SLPv2
+ also changes the formats and definition of many flags and values and
+ reduces the number of 'required features.' SLPv2 clarifies and
+ changes the use of 'Scopes', eliminating support for 'unscoped
+ directory agents' and 'unscoped requests'. SLPv2 uses LDAPv3
+ compatible string encodings of attributes and search filters. Other
+ changes (such as Language and Character set handling) adopt practices
+ recommended by the Internet Engineering Steering Group.
+
+ Effort has been made to make SLPv2 operate the same whether DAs are
+ present or not. For this reason, a new message (the SAAdvert) has
+ been added. This allows UAs to discover scope information in the
+ absence of administrative configuration and DAs. This was not
+ possible in SLPv1.
+
+ SLPv2 is incompatible in some respects with SLPv1. If a DA which
+ supports both SLPv1 and SLPv2 with the same scope is present,
+ services advertised by SAs using either version of the protocol will
+ be available to both SLPv1 and SLPv2 UAs. SLPv1 DAs SHOULD be phased
+ out and replace with SLPv2 DAs which support both versions of the
+ protocol.
+
+ SLPv1 allows services to be advertised and requested without a scope.
+ Further, DAs can be configured without a scope. This is incompatible
+ with SLPv2 and presents scalability problems. To facilitate this
+ forward migration, SLPv1 agents MUST use scopes for all registrations
+ and requests. SLPv1 DAs MUST be configured with a scope list. This
+ constitutes a revision of RFC 2165 [22].
+
+B. Appendix: Service Discovery by Type: Minimal SLPv2 Features
+
+ Service Agents may advertise services without attributes. This will
+ enable only discovery of services by type. Service types discovered
+ this way will have a Service Template [13] defined which specifies
+ explicitly that no attributes are associated with the service
+ advertisement. Service types associated with Service Templates which
+ specify attributes MUST NOT be advertised by SAs which do not support
+ attributes.
+
+ While discovery of service by service type is a subset of the
+ features possible using SLPv2 this form of discovery is consistent
+ with the current generation of products that allow simple browsing of
+ all services in a 'zone' or 'workgroup' by type. In some cases,
+ attribute discovery, security and feature negotiation is handled by
+
+
+
+Guttman, et al. Standards Track [Page 48]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ application layer protocols - all that is required is the basic
+ discovery of services that support a certain service.
+
+ UAs requesting only service of that service type would only need to
+ support service type and scope fields of the Service Request. UAs
+ would still perform DA discovery and unicast SLPv2 SrvRqst messages
+ to DAs in their scope once they were discovered instead of
+ multicasting them.
+
+ SAs would also perform DA discovery and use a SLPv2 SrvReg to
+ register all their advertised services with SLPv2 DAs in their scope.
+ These advertisements would needless to say contain no attribute
+ string.
+
+ These minimal SAs could ignore the Language Tag in requests since
+ SrvRqst messages would contain no attributes, hence no strings would
+ be internationalized. Further, any non-null predicate string would
+ fail to match a service advertisement with no attributes, so these
+ SAs would not have to parse and interpret search filters. Overflow
+ will never occur in SrvRqst, SrvRply or SrvReg messages so TCP
+ message handling would not have to be implemented. Finally, all
+ AttrRqst messages could be dropped by the SA, since no attributes are
+ supported.
+
+C. Appendix: DAAdverts with arbitrary URLs
+
+ Using Active DA Discovery, a SrvRqst with its service type field set
+ to "service:directory-agent". DAs will respond with a DAAdvert
+ containing a URL with the "service:directory-agent:" scheme. This is
+ the same DAAdvert that such a DA would multicast in unsolicited DA
+ advertisements.
+
+ A UA or SA which receives an unsolicited DAAdvert MUST examine the
+ URL to determine if it has a recognized scheme. If the UA or SA does
+ not recognize the DAAdvert's URL scheme, the DAAdvert is silently
+ discarded. This document specifies only how to use URLs with the
+ "service:directory-agent:" scheme.
+
+ This provides the possibility for forward compatibility with future
+ versions of SLP and enables other services to advertise their ability
+ to serve as a clearinghouse for service location information.
+
+ For example, if LDAPv3 [15] is used for service registration and
+ discovery by a set of end systems, they could interpret a LDAP URL
+ [16] to passively discover the LDAP server to use for this purpose.
+ This document does not specify how this is done: SLPv2 agents
+ without further support would simply discard this DAAdvert.
+
+
+
+
+Guttman, et al. Standards Track [Page 49]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+D. Appendix: SLP Protocol Extensions
+
+D.1. Required Attribute Missing Option
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Extension Type = 0x0001 | Extension Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Template IDVer Length | Template IDVer String \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |Required Attr <tag-list> Length| Required Attr <tag-list> \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Required attributes and the format of the IDVer string are defined by
+ [13].
+
+ If a SA or DA receives a SrvRqst or a SrvReg which fails to include a
+ Required Attribute for the requested Service Type (according to the
+ Service Template), it MAY return the Required Attribute Extension in
+ addition to the reply corresponding to the message. The sender
+ SHOULD reissue the message with a search filter including the
+ attributes listed in the returned Required Attribute Extension.
+ Similarly, the Required Attribute Extension may be returned in
+ response to a SrvDereg message that contains a required attribute
+ tag.
+
+ The Template IDVer String is the name and version number string of
+ the Service Template which defines the given attribute as required.
+ It SHOULD be included, but can be omitted if a given SA or DA has
+ been individually configured to have 'required attributes.'
+
+ The Required Attribute <tag-list> MUST NOT include wild cards.
+
+E. Acknowledgments
+
+ This document incorporates ideas from work on several discovery
+ protocols, including RDP by Perkins and Harjono, and PDS by Michael
+ Day. We are grateful for contributions by Ye Gu and Peter Ford.
+ John Veizades was instrumental in the standardization of the Service
+ Location Protocol. Implementors at Novell, Axis Communications and
+ Sun Microsystems have contributed significantly to make this a much
+ clearer and more consistent document.
+
+
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 50]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+F. References
+
+ [1] Port numbers, July 1997.
+ ftp://ftp.isi.edu/in-notes/iana/assignments/port-numbers.
+
+ [2] ISO/IEC JTC1/SC 21. Certificate Extensions. Draft Amendment
+ DAM 4 to ISO/IEC 9594-2, December 1996.
+
+ [3] ISO/IEC JTC1/SC 21. Certificate Extensions. Draft Amendment
+ DAM 2 to ISO/IEC 9594-6, December 1996.
+
+ [4] ISO/IEC JTC1/SC 21. Certificate Extensions. Draft Amendment
+ DAM 1 to ISO/IEC 9594-7, December 1996.
+
+ [5] ISO/IEC JTC1/SC 21. Certificate Extensions. Draft Amendment
+ DAM 1 to ISO/IEC 9594-8, December 1996.
+
+ [6] Unicode Technical Report #8. The Unicode Standard, version 2.1.
+ Technical report, The Unicode Consortium, 1998.
+
+ [7] Alvestrand, H., "Tags for the Identification of Languages",
+ RFC 1766, March 1995.
+
+ [8] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform
+ Resource Identifiers (URI): Generic Syntax", RFC 2396,
+ August 1998.
+
+ [9] Bradner, S., "Key Words for Use in RFCs to Indicate Requirement
+ Levels", BCP 14, RFC 2119, March 1997.
+
+ [10] CCITT. The Directory Authentication Framework. Recommendation
+ X.509, 1988.
+
+ [11] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 2234, November 1997.
+
+ [12] S. Gursharan, R. Andrews, and A. Oppenheimer. Inside AppleTalk.
+ Addison-Wesley, 1990.
+
+ [13] Guttman, E., Perkins, C. and J. Kempf, "Service Templates and
+ service: Schemes", RFC 2609, June 1999.
+
+ [14] Howes, T., "The String Representation of LDAP Search Filters",
+ RFC 2254, December 1997.
+
+ [15] Wahl, M., Howes, T. and S. Kille, "Lightweight Directory
+ Access Protocol (v3)", RFC 2251, December 1997.
+
+
+
+
+Guttman, et al. Standards Track [Page 51]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+ [16] Howes, T. and M. Smith, "The LDAP URL Format", RFC 2255,
+ December 1997.
+
+ [17] Meyer, D., "Administratively Scoped IP Multicast", RFC 2365,
+ July 1998.
+
+ [18] Narten, T. and H. Alvestrand, "Guidelines for Writing
+ an IANA Considerations Section in RFCs, BCP 26, RFC 2434,
+ October 1998.
+
+ [19] Microsoft Networks. SMB File Sharing Protocol Extensions 3.0,
+ Document Version 1.09, November 1989.
+
+ [20] National Institute of Standards and Technology. Digital
+ signature standard. Technical Report NIST FIPS PUB 186, U.S.
+ Department of Commerce, May 1994.
+
+ [21] Perkins, C. and E. Guttman, "DHCP Options for Service Location
+ Protocol", RFC 2610, June 1999.
+
+ [22] Veizades, J., Guttman, E., Perkins, C. and S. Kaplan, "Service
+ Location Protocol", RFC 2165, July 1997.
+
+ [23] Yergeau, F., "UTF-8, a transformation format of ISO 10646",
+ RFC 2279, January 1998.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 52]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+G. Authors' Addresses
+
+ Erik Guttman
+ Sun Microsystems
+ Bahnstr. 2
+ 74915 Waibstadt
+ Germany
+
+ Phone: +49 7263 911 701
+ EMail: Erik.Guttman@sun.com
+
+
+ Charles Perkins
+ Sun Microsystems
+ 901 San Antonio Road
+ Palo Alto, CA 94040
+ USA
+
+ Phone: +1 650 786 6464
+ EMail: cperkins@sun.com
+
+
+ John Veizades
+ @Home Network
+ 425 Broadway
+ Redwood City, CA 94043
+ USA
+
+ Phone: +1 650 569 5243
+ EMail: veizades@home.net
+
+
+ Michael Day
+ Vinca Corporation.
+ 1201 North 800 East
+ Orem, Utah 84097 USA
+
+ Phone: +1 801 376-5083
+ EMail: mday@vinca.com
+
+
+
+
+
+
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 53]
+
+RFC 2608 Service Location Protocol, Version 2 June 1999
+
+
+H. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE."
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Guttman, et al. Standards Track [Page 54]
+
diff --git a/utils/open-isns/doc/rfc3279.txt b/utils/open-isns/doc/rfc3279.txt
new file mode 100644
index 0000000..04e7b07
--- /dev/null
+++ b/utils/open-isns/doc/rfc3279.txt
@@ -0,0 +1,1515 @@
+
+
+
+
+
+
+Network Working Group W. Polk
+Request for Comments: 3279 NIST
+Obsoletes: 2528 R. Housley
+Category: Standards Track RSA Laboratories
+ L. Bassham
+ NIST
+ April 2002
+
+ Algorithms and Identifiers for the
+ Internet X.509 Public Key Infrastructure
+ Certificate and Certificate Revocation List (CRL) Profile
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2002). All Rights Reserved.
+
+Abstract
+
+ This document specifies algorithm identifiers and ASN.1 encoding
+ formats for digital signatures and subject public keys used in the
+ Internet X.509 Public Key Infrastructure (PKI). Digital signatures
+ are used to sign certificates and certificate revocation list (CRLs).
+ Certificates include the public key of the named subject.
+
+Table of Contents
+
+ 1 Introduction . . . . . . . . . . . . . . . . . . . . . . 2
+ 2 Algorithm Support . . . . . . . . . . . . . . . . . . . . 3
+ 2.1 One-Way Hash Functions . . . . . . . . . . . . . . . . 3
+ 2.1.1 MD2 One-Way Hash Functions . . . . . . . . . . . . . 3
+ 2.1.2 MD5 One-Way Hash Functions . . . . . . . . . . . . . 4
+ 2.1.3 SHA-1 One-Way Hash Functions . . . . . . . . . . . . 4
+ 2.2 Signature Algorithms . . . . . . . . . . . . . . . . . 4
+ 2.2.1 RSA Signature Algorithm . . . . . . . . . . . . . . . 5
+ 2.2.2 DSA Signature Algorithm . . . . . . . . . . . . . . . 6
+ 2.2.3 Elliptic Curve Digital Signature Algorithm . . . . . 7
+ 2.3 Subject Public Key Algorithms . . . . . . . . . . . . . 7
+ 2.3.1 RSA Keys . . . . . . . . . . . . . . . . . . . . . . 8
+ 2.3.2 DSA Signature Keys . . . . . . . . . . . . . . . . . 9
+ 2.3.3 Diffie-Hellman Key Exchange Keys . . . . . . . . . . 10
+
+
+
+Polk, et al. Standards Track [Page 1]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ 2.3.4 KEA Public Keys . . . . . . . . . . . . . . . . . . . 11
+ 2.3.5 ECDSA and ECDH Public Keys . . . . . . . . . . . . . 13
+ 3 ASN.1 Module . . . . . . . . . . . . . . . . . . . . . . 18
+ 4 References . . . . . . . . . . . . . . . . . . . . . . . 24
+ 5 Security Considerations . . . . . . . . . . . . . . . . . 25
+ 6 Intellectual Property Rights . . . . . . . . . . . . . . 26
+ 7 Author Addresses . . . . . . . . . . . . . . . . . . . . 26
+ 8 Full Copyright Statement . . . . . . . . . . . . . . . . 27
+
+1 Introduction
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC 2119].
+
+ This document specifies algorithm identifiers and ASN.1 [X.660]
+ encoding formats for digital signatures and subject public keys used
+ in the Internet X.509 Public Key Infrastructure (PKI). This
+ specification supplements [RFC 3280], "Internet X.509 Public Key
+ Infrastructure: Certificate and Certificate Revocation List (CRL)
+ Profile." Implementations of this specification MUST also conform to
+ RFC 3280.
+
+ This specification defines the contents of the signatureAlgorithm,
+ signatureValue, signature, and subjectPublicKeyInfo fields within
+ Internet X.509 certificates and CRLs.
+
+ This document identifies one-way hash functions for use in the
+ generation of digital signatures. These algorithms are used in
+ conjunction with digital signature algorithms.
+
+ This specification describes the encoding of digital signatures
+ generated with the following cryptographic algorithms:
+
+ * Rivest-Shamir-Adelman (RSA);
+ * Digital Signature Algorithm (DSA); and
+ * Elliptic Curve Digital Signature Algorithm (ECDSA).
+
+ This document specifies the contents of the subjectPublicKeyInfo
+ field in Internet X.509 certificates. For each algorithm, the
+ appropriate alternatives for the the keyUsage extension are provided.
+ This specification describes encoding formats for public keys used
+ with the following cryptographic algorithms:
+
+ * Rivest-Shamir-Adelman (RSA);
+ * Digital Signature Algorithm (DSA);
+ * Diffie-Hellman (DH);
+ * Key Encryption Algorithm (KEA);
+
+
+
+Polk, et al. Standards Track [Page 2]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ * Elliptic Curve Digital Signature Algorithm (ECDSA); and
+ * Elliptic Curve Diffie-Hellman (ECDH).
+
+2 Algorithm Support
+
+ This section describes cryptographic algorithms which may be used
+ with the Internet X.509 certificate and CRL profile [RFC 3280]. This
+ section describes one-way hash functions and digital signature
+ algorithms which may be used to sign certificates and CRLs, and
+ identifies object identifiers (OIDs) for public keys contained in a
+ certificate.
+
+ Conforming CAs and applications MUST, at a minimum, support digital
+ signatures and public keys for one of the specified algorithms. When
+ using any of the algorithms identified in this specification,
+ conforming CAs and applications MUST support them as described.
+
+2.1 One-way Hash Functions
+
+ This section identifies one-way hash functions for use in the
+ Internet X.509 PKI. One-way hash functions are also called message
+ digest algorithms. SHA-1 is the preferred one-way hash function for
+ the Internet X.509 PKI. However, PEM uses MD2 for certificates [RFC
+ 1422] [RFC 1423] and MD5 is used in other legacy applications. For
+ these reasons, MD2 and MD5 are included in this profile. The data
+ that is hashed for certificate and CRL signing is fully described in
+ [RFC 3280].
+
+2.1.1 MD2 One-way Hash Function
+
+ MD2 was developed by Ron Rivest for RSA Security. RSA Security has
+ recently placed the MD2 algorithm in the public domain. Previously,
+ RSA Data Security had granted license for use of MD2 for non-
+ commercial Internet Privacy-Enhanced Mail (PEM). MD2 may continue to
+ be used with PEM certificates, but SHA-1 is preferred. MD2 produces
+ a 128-bit "hash" of the input. MD2 is fully described in [RFC 1319].
+
+ At the Selected Areas in Cryptography '95 conference in May 1995,
+ Rogier and Chauvaud presented an attack on MD2 that can nearly find
+ collisions [RC95]. Collisions occur when one can find two different
+ messages that generate the same message digest. A checksum operation
+ in MD2 is the only remaining obstacle to the success of the attack.
+ For this reason, the use of MD2 for new applications is discouraged.
+ It is still reasonable to use MD2 to verify existing signatures, as
+ the ability to find collisions in MD2 does not enable an attacker to
+ find new messages having a previously computed hash value.
+
+
+
+
+
+Polk, et al. Standards Track [Page 3]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+2.1.2 MD5 One-way Hash Function
+
+ MD5 was developed by Ron Rivest for RSA Security. RSA Security has
+ placed the MD5 algorithm in the public domain. MD5 produces a 128-
+ bit "hash" of the input. MD5 is fully described in [RFC 1321].
+
+ Den Boer and Bosselaers [DB94] have found pseudo-collisions for MD5,
+ but there are no other known cryptanalytic results. The use of MD5
+ for new applications is discouraged. It is still reasonable to use
+ MD5 to verify existing signatures.
+
+2.1.3 SHA-1 One-way Hash Function
+
+ SHA-1 was developed by the U.S. Government. SHA-1 produces a 160-bit
+ "hash" of the input. SHA-1 is fully described in [FIPS 180-1]. RFC
+ 3174 [RFC 3174] also describes SHA-1, and it provides an
+ implementation of the algorithm.
+
+2.2 Signature Algorithms
+
+ Certificates and CRLs conforming to [RFC 3280] may be signed with any
+ public key signature algorithm. The certificate or CRL indicates the
+ algorithm through an algorithm identifier which appears in the
+ signatureAlgorithm field within the Certificate or CertificateList.
+ This algorithm identifier is an OID and has optionally associated
+ parameters. This section identifies algorithm identifiers and
+ parameters that MUST be used in the signatureAlgorithm field in a
+ Certificate or CertificateList.
+
+ Signature algorithms are always used in conjunction with a one-way
+ hash function.
+
+ This section identifies OIDS for RSA, DSA, and ECDSA. The contents
+ of the parameters component for each algorithm vary; details are
+ provided for each algorithm.
+
+ The data to be signed (e.g., the one-way hash function output value)
+ is formatted for the signature algorithm to be used. Then, a private
+ key operation (e.g., RSA encryption) is performed to generate the
+ signature value. This signature value is then ASN.1 encoded as a BIT
+ STRING and included in the Certificate or CertificateList in the
+ signature field.
+
+
+
+
+
+
+
+
+
+Polk, et al. Standards Track [Page 4]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+2.2.1 RSA Signature Algorithm
+
+ The RSA algorithm is named for its inventors: Rivest, Shamir, and
+ Adleman. This profile includes three signature algorithms based on
+ the RSA asymmetric encryption algorithm. The signature algorithms
+ combine RSA with either the MD2, MD5, or the SHA-1 one-way hash
+ functions.
+
+ The signature algorithm with SHA-1 and the RSA encryption algorithm
+ is implemented using the padding and encoding conventions described
+ in PKCS #1 [RFC 2313]. The message digest is computed using the
+ SHA-1 hash algorithm.
+
+ The RSA signature algorithm, as specified in PKCS #1 [RFC 2313]
+ includes a data encoding step. In this step, the message digest and
+ the OID for the one-way hash function used to compute the digest are
+ combined. When performing the data encoding step, the md2, md5, and
+ id-sha1 OIDs MUST be used to specify the MD2, MD5, and SHA-1 one-way
+ hash functions, respectively:
+
+ md2 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) US(840) rsadsi(113549)
+ digestAlgorithm(2) 2 }
+
+ md5 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) US(840) rsadsi(113549)
+ digestAlgorithm(2) 5 }
+
+ id-sha1 OBJECT IDENTIFIER ::= {
+ iso(1) identified-organization(3) oiw(14) secsig(3)
+ algorithms(2) 26 }
+
+ The signature algorithm with MD2 and the RSA encryption algorithm is
+ defined in PKCS #1 [RFC 2313]. As defined in PKCS #1 [RFC 2313], the
+ ASN.1 OID used to identify this signature algorithm is:
+
+ md2WithRSAEncryption OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
+ pkcs-1(1) 2 }
+
+ The signature algorithm with MD5 and the RSA encryption algorithm is
+ defined in PKCS #1 [RFC 2313]. As defined in PKCS #1 [RFC 2313], the
+ ASN.1 OID used to identify this signature algorithm is:
+
+ md5WithRSAEncryption OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
+ pkcs-1(1) 4 }
+
+
+
+
+Polk, et al. Standards Track [Page 5]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ The ASN.1 object identifier used to identify this signature algorithm
+ is:
+
+ sha-1WithRSAEncryption OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
+ pkcs-1(1) 5 }
+
+ When any of these three OIDs appears within the ASN.1 type
+ AlgorithmIdentifier, the parameters component of that type SHALL be
+ the ASN.1 type NULL.
+
+ The RSA signature generation process and the encoding of the result
+ is described in detail in PKCS #1 [RFC 2313].
+
+2.2.2 DSA Signature Algorithm
+
+ The Digital Signature Algorithm (DSA) is defined in the Digital
+ Signature Standard (DSS). DSA was developed by the U.S. Government,
+ and DSA is used in conjunction with the SHA-1 one-way hash function.
+ DSA is fully described in [FIPS 186]. The ASN.1 OID used to identify
+ this signature algorithm is:
+
+ id-dsa-with-sha1 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) x9-57 (10040)
+ x9cm(4) 3 }
+
+ When the id-dsa-with-sha1 algorithm identifier appears as the
+ algorithm field in an AlgorithmIdentifier, the encoding SHALL omit
+ the parameters field. That is, the AlgorithmIdentifier SHALL be a
+ SEQUENCE of one component: the OBJECT IDENTIFIER id-dsa-with-sha1.
+
+ The DSA parameters in the subjectPublicKeyInfo field of the
+ certificate of the issuer SHALL apply to the verification of the
+ signature.
+
+ When signing, the DSA algorithm generates two values. These values
+ are commonly referred to as r and s. To easily transfer these two
+ values as one signature, they SHALL be ASN.1 encoded using the
+ following ASN.1 structure:
+
+ Dss-Sig-Value ::= SEQUENCE {
+ r INTEGER,
+ s INTEGER }
+
+
+
+
+
+
+
+
+Polk, et al. Standards Track [Page 6]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+2.2.3 ECDSA Signature Algorithm
+
+ The Elliptic Curve Digital Signature Algorithm (ECDSA) is defined in
+ [X9.62]. The ASN.1 object identifiers used to identify ECDSA are
+ defined in the following arc:
+
+ ansi-X9-62 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) 10045 }
+
+ id-ecSigType OBJECT IDENTIFIER ::= {
+ ansi-X9-62 signatures(4) }
+
+ ECDSA is used in conjunction with the SHA-1 one-way hash function.
+ The ASN.1 object identifier used to identify ECDSA with SHA-1 is:
+
+ ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
+ id-ecSigType 1 }
+
+ When the ecdsa-with-SHA1 algorithm identifier appears as the
+ algorithm field in an AlgorithmIdentifier, the encoding MUST omit the
+ parameters field. That is, the AlgorithmIdentifier SHALL be a
+ SEQUENCE of one component: the OBJECT IDENTIFIER ecdsa-with-SHA1.
+
+ The elliptic curve parameters in the subjectPublicKeyInfo field of
+ the certificate of the issuer SHALL apply to the verification of the
+ signature.
+
+ When signing, the ECDSA algorithm generates two values. These values
+ are commonly referred to as r and s. To easily transfer these two
+ values as one signature, they MUST be ASN.1 encoded using the
+ following ASN.1 structure:
+
+ Ecdsa-Sig-Value ::= SEQUENCE {
+ r INTEGER,
+ s INTEGER }
+
+2.3 Subject Public Key Algorithms
+
+ Certificates conforming to [RFC 3280] may convey a public key for any
+ public key algorithm. The certificate indicates the algorithm
+ through an algorithm identifier. This algorithm identifier is an OID
+ and optionally associated parameters.
+
+ This section identifies preferred OIDs and parameters for the RSA,
+ DSA, Diffie-Hellman, KEA, ECDSA, and ECDH algorithms. Conforming CAs
+ MUST use the identified OIDs when issuing certificates containing
+
+
+
+
+
+Polk, et al. Standards Track [Page 7]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ public keys for these algorithms. Conforming applications supporting
+ any of these algorithms MUST, at a minimum, recognize the OID
+ identified in this section.
+
+2.3.1 RSA Keys
+
+ The OID rsaEncryption identifies RSA public keys.
+
+ pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
+ rsadsi(113549) pkcs(1) 1 }
+
+ rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1}
+
+ The rsaEncryption OID is intended to be used in the algorithm field
+ of a value of type AlgorithmIdentifier. The parameters field MUST
+ have ASN.1 type NULL for this algorithm identifier.
+
+ The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey:
+
+ RSAPublicKey ::= SEQUENCE {
+ modulus INTEGER, -- n
+ publicExponent INTEGER } -- e
+
+ where modulus is the modulus n, and publicExponent is the public
+ exponent e. The DER encoded RSAPublicKey is the value of the BIT
+ STRING subjectPublicKey.
+
+ This OID is used in public key certificates for both RSA signature
+ keys and RSA encryption keys. The intended application for the key
+ MAY be indicated in the key usage field (see [RFC 3280]). The use of
+ a single key for both signature and encryption purposes is not
+ recommended, but is not forbidden.
+
+ If the keyUsage extension is present in an end entity certificate
+ which conveys an RSA public key, any combination of the following
+ values MAY be present:
+
+ digitalSignature;
+ nonRepudiation;
+ keyEncipherment; and
+ dataEncipherment.
+
+ If the keyUsage extension is present in a CA or CRL issuer
+ certificate which conveys an RSA public key, any combination of the
+ following values MAY be present:
+
+ digitalSignature;
+ nonRepudiation;
+
+
+
+Polk, et al. Standards Track [Page 8]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ keyEncipherment;
+ dataEncipherment;
+ keyCertSign; and
+ cRLSign.
+
+ However, this specification RECOMMENDS that if keyCertSign or cRLSign
+ is present, both keyEncipherment and dataEncipherment SHOULD NOT be
+ present.
+
+2.3.2 DSA Signature Keys
+
+ The Digital Signature Algorithm (DSA) is defined in the Digital
+ Signature Standard (DSS) [FIPS 186]. The DSA OID supported by this
+ profile is:
+
+ id-dsa OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 }
+
+ The id-dsa algorithm syntax includes optional domain parameters.
+ These parameters are commonly referred to as p, q, and g. When
+ omitted, the parameters component MUST be omitted entirely. That is,
+ the AlgorithmIdentifier MUST be a SEQUENCE of one component: the
+ OBJECT IDENTIFIER id-dsa.
+
+ If the DSA domain parameters are present in the subjectPublicKeyInfo
+ AlgorithmIdentifier, the parameters are included using the following
+ ASN.1 structure:
+
+ Dss-Parms ::= SEQUENCE {
+ p INTEGER,
+ q INTEGER,
+ g INTEGER }
+
+ The AlgorithmIdentifier within subjectPublicKeyInfo is the only place
+ within a certificate where the parameters may be used. If the DSA
+ algorithm parameters are omitted from the subjectPublicKeyInfo
+ AlgorithmIdentifier and the CA signed the subject certificate using
+ DSA, then the certificate issuer's DSA parameters apply to the
+ subject's DSA key. If the DSA domain parameters are omitted from the
+ SubjectPublicKeyInfo AlgorithmIdentifier and the CA signed the
+ subject certificate using a signature algorithm other than DSA, then
+ the subject's DSA domain parameters are distributed by other means.
+ If the subjectPublicKeyInfo AlgorithmIdentifier field omits the
+ parameters component, the CA signed the subject with a signature
+ algorithm other than DSA, and the subject's DSA parameters are not
+ available through other means, then clients MUST reject the
+ certificate.
+
+
+
+
+Polk, et al. Standards Track [Page 9]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ The DSA public key MUST be ASN.1 DER encoded as an INTEGER; this
+ encoding shall be used as the contents (i.e., the value) of the
+ subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo
+ data element.
+
+ DSAPublicKey ::= INTEGER -- public key, Y
+
+ If the keyUsage extension is present in an end entity certificate
+ which conveys a DSA public key, any combination of the following
+ values MAY be present:
+
+ digitalSignature;
+ nonRepudiation;
+
+ If the keyUsage extension is present in a CA or CRL issuer
+ certificate which conveys a DSA public key, any combination of the
+ following values MAY be present:
+
+ digitalSignature;
+ nonRepudiation;
+ keyCertSign; and
+ cRLSign.
+
+2.3.3 Diffie-Hellman Key Exchange Keys
+
+ The Diffie-Hellman OID supported by this profile is defined in
+ [X9.42].
+
+ dhpublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+ us(840) ansi-x942(10046) number-type(2) 1 }
+
+ The dhpublicnumber OID is intended to be used in the algorithm field
+ of a value of type AlgorithmIdentifier. The parameters field of that
+ type, which has the algorithm-specific syntax ANY DEFINED BY
+ algorithm, have the ASN.1 type DomainParameters for this algorithm.
+
+ DomainParameters ::= SEQUENCE {
+ p INTEGER, -- odd prime, p=jq +1
+ g INTEGER, -- generator, g
+ q INTEGER, -- factor of p-1
+ j INTEGER OPTIONAL, -- subgroup factor
+ validationParms ValidationParms OPTIONAL }
+
+ ValidationParms ::= SEQUENCE {
+ seed BIT STRING,
+ pgenCounter INTEGER }
+
+
+
+
+
+Polk, et al. Standards Track [Page 10]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ The fields of type DomainParameters have the following meanings:
+
+ p identifies the prime p defining the Galois field;
+
+ g specifies the generator of the multiplicative subgroup of order
+ g;
+
+ q specifies the prime factor of p-1;
+
+ j optionally specifies the value that satisfies the equation
+ p=jq+1 to support the optional verification of group parameters;
+
+ seed optionally specifies the bit string parameter used as the
+ seed for the domain parameter generation process; and
+
+ pgenCounter optionally specifies the integer value output as part
+ of the of the domain parameter prime generation process.
+
+ If either of the domain parameter generation components (pgenCounter
+ or seed) is provided, the other MUST be present as well.
+
+ The Diffie-Hellman public key MUST be ASN.1 encoded as an INTEGER;
+ this encoding shall be used as the contents (i.e., the value) of the
+ subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo
+ data element.
+
+ DHPublicKey ::= INTEGER -- public key, y = g^x mod p
+
+ If the keyUsage extension is present in a certificate which conveys a
+ DH public key, the following values may be present:
+
+ keyAgreement;
+ encipherOnly; and
+ decipherOnly.
+
+ If present, the keyUsage extension MUST assert keyAgreement and MAY
+ assert either encipherOnly and decipherOnly. The keyUsage extension
+ MUST NOT assert both encipherOnly and decipherOnly.
+
+2.3.4 KEA Public Keys
+
+ This section identifies the preferred OID and parameters for the
+ inclusion of a KEA public key in a certificate. The Key Exchange
+ Algorithm (KEA) is a key agreement algorithm. Two parties may
+ generate a "pairwise key" if and only if they share the same KEA
+ parameters. The KEA parameters are not included in a certificate;
+ instead a domain identifier is supplied in the parameters field.
+
+
+
+
+Polk, et al. Standards Track [Page 11]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ When the SubjectPublicKeyInfo field contains a KEA key, the algorithm
+ identifier and parameters SHALL be as defined in [SDN.701r]:
+
+ id-keyExchangeAlgorithm OBJECT IDENTIFIER ::=
+ { 2 16 840 1 101 2 1 1 22 }
+
+ KEA-Parms-Id ::= OCTET STRING
+
+ CAs MUST populate the parameters field of the AlgorithmIdentifier
+ within the SubjectPublicKeyInfo field of each certificate containing
+ a KEA public key with an 80-bit parameter identifier (OCTET STRING),
+ also known as the domain identifier. The domain identifier is
+ computed in three steps:
+
+ (1) the KEA domain parameters (p, q, and g) are DER encoded using
+ the Dss-Parms structure;
+
+ (2) a 160-bit SHA-1 hash is generated from the parameters; and
+
+ (3) the 160-bit hash is reduced to 80-bits by performing an
+ "exclusive or" of the 80 high order bits with the 80 low order
+ bits.
+
+ The resulting value is encoded such that the most significant byte of
+ the 80-bit value is the first octet in the octet string. The Dss-
+ Parms is provided above in Section 2.3.2.
+
+ A KEA public key, y, is conveyed in the subjectPublicKey BIT STRING
+ such that the most significant bit (MSB) of y becomes the MSB of the
+ BIT STRING value field and the least significant bit (LSB) of y
+ becomes the LSB of the BIT STRING value field. This results in the
+ following encoding:
+
+ BIT STRING tag;
+ BIT STRING length;
+ 0 (indicating that there are zero unused bits in the final octet
+ of y); and
+ BIT STRING value field including y.
+
+ The key usage extension may optionally appear in a KEA certificate.
+ If a KEA certificate includes the keyUsage extension, only the
+ following values may be asserted:
+
+ keyAgreement;
+ encipherOnly; and
+ decipherOnly.
+
+
+
+
+
+Polk, et al. Standards Track [Page 12]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ If present, the keyUsage extension MUST assert keyAgreement and MAY
+ assert either encipherOnly and decipherOnly. The keyUsage extension
+ MUST NOT assert both encipherOnly and decipherOnly.
+
+2.3.5 ECDSA and ECDH Keys
+
+ This section identifies the preferred OID and parameter encoding for
+ the inclusion of an ECDSA or ECDH public key in a certificate. The
+ Elliptic Curve Digital Signature Algorithm (ECDSA) is defined in
+ [X9.62]. ECDSA is the elliptic curve mathematical analog of the
+ Digital Signature Algorithm [FIPS 186]. The Elliptic Curve Diffie
+ Hellman (ECDH) algorithm is a key agreement algorithm defined in
+ [X9.63].
+
+ ECDH is the elliptic curve mathematical analog of the Diffie-Hellman
+ key agreement algorithm as specified in [X9.42]. The ECDSA and ECDH
+ specifications use the same OIDs and parameter encodings. The ASN.1
+ object identifiers used to identify these public keys are defined in
+ the following arc:
+
+ ansi-X9-62 OBJECT IDENTIFIER ::=
+ { iso(1) member-body(2) us(840) 10045 }
+
+ When certificates contain an ECDSA or ECDH public key, the
+ id-ecPublicKey algorithm identifier MUST be used. The id-ecPublicKey
+ algorithm identifier is defined as follows:
+
+ id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 }
+
+ id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 }
+
+ This OID is used in public key certificates for both ECDSA signature
+ keys and ECDH encryption keys. The intended application for the key
+ may be indicated in the key usage field (see [RFC 3280]). The use of
+ a single key for both signature and encryption purposes is not
+ recommended, but is not forbidden.
+
+ ECDSA and ECDH require use of certain parameters with the public key.
+ The parameters may be inherited from the issuer, implicitly included
+ through reference to a "named curve," or explicitly included in the
+ certificate.
+
+ EcpkParameters ::= CHOICE {
+ ecParameters ECParameters,
+ namedCurve OBJECT IDENTIFIER,
+ implicitlyCA NULL }
+
+
+
+
+
+Polk, et al. Standards Track [Page 13]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ When the parameters are inherited, the parameters field SHALL contain
+ implictlyCA, which is the ASN.1 value NULL. When parameters are
+ specified by reference, the parameters field SHALL contain the
+ named-Curve choice, which is an object identifier. When the
+ parameters are explicitly included, they SHALL be encoded in the
+ ASN.1 structure ECParameters:
+
+ ECParameters ::= SEQUENCE {
+ version ECPVer, -- version is always 1
+ fieldID FieldID, -- identifies the finite field over
+ -- which the curve is defined
+ curve Curve, -- coefficients a and b of the
+ -- elliptic curve
+ base ECPoint, -- specifies the base point P
+ -- on the elliptic curve
+ order INTEGER, -- the order n of the base point
+ cofactor INTEGER OPTIONAL -- The integer h = #E(Fq)/n
+ }
+
+ ECPVer ::= INTEGER {ecpVer1(1)}
+
+ Curve ::= SEQUENCE {
+ a FieldElement,
+ b FieldElement,
+ seed BIT STRING OPTIONAL }
+
+ FieldElement ::= OCTET STRING
+
+ ECPoint ::= OCTET STRING
+
+ The value of FieldElement SHALL be the octet string representation of
+ a field element following the conversion routine in [X9.62], Section
+ 4.3.3. The value of ECPoint SHALL be the octet string representation
+ of an elliptic curve point following the conversion routine in
+ [X9.62], Section 4.3.6. Note that this octet string may represent an
+ elliptic curve point in compressed or uncompressed form.
+
+ Implementations that support elliptic curve according to this
+ specification MUST support the uncompressed form and MAY support the
+ compressed form.
+
+ The components of type ECParameters have the following meanings:
+
+ version specifies the version number of the elliptic curve
+ parameters. It MUST have the value 1 (ecpVer1).
+
+
+
+
+
+
+Polk, et al. Standards Track [Page 14]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ fieldID identifies the finite field over which the elliptic curve
+ is defined. Finite fields are represented by values of the
+ parameterized type FieldID, constrained to the values of the
+ objects defined in the information object set FieldTypes.
+ Additional detail regarding fieldID is provided below.
+
+ curve specifies the coefficients a and b of the elliptic curve E.
+ Each coefficient is represented as a value of type FieldElement,
+ an OCTET STRING. seed is an optional parameter used to derive the
+ coefficients of a randomly generated elliptic curve.
+
+ base specifies the base point P on the elliptic curve. The base
+ point is represented as a value of type ECPoint, an OCTET STRING.
+
+ order specifies the order n of the base point.
+
+ cofactor is the integer h = #E(Fq)/n. This parameter is specified
+ as OPTIONAL. However, the cofactor MUST be included in ECDH
+ public key parameters. The cofactor is not required to support
+ ECDSA, except in parameter validation. The cofactor MAY be
+ included to support parameter validation for ECDSA keys.
+ Parameter validation is not required by this specification.
+
+ The AlgorithmIdentifier within SubjectPublicKeyInfo is the only place
+ within a certificate where the parameters may be used. If the
+ elliptic curve parameters are specified as implicitlyCA in the
+ SubjectPublicKeyInfo AlgorithmIdentifier and the CA signed the
+ subject certificate using ECDSA, then the certificate issuer's ECDSA
+ parameters apply to the subject's ECDSA key. If the elliptic curve
+ parameters are specified as implicitlyCA in the SubjectPublicKeyInfo
+ AlgorithmIdentifier and the CA signed the certificate using a
+ signature algorithm other than ECDSA, then clients MUST not make use
+ of the elliptic curve public key.
+
+ FieldID ::= SEQUENCE {
+ fieldType OBJECT IDENTIFIER,
+ parameters ANY DEFINED BY fieldType }
+
+ FieldID is a SEQUENCE of two components, fieldType and parameters.
+ The fieldType contains an object identifier value that uniquely
+ identifies the type contained in the parameters.
+
+ The object identifier id-fieldType specifies an arc containing the
+ object identifiers of each field type. It has the following value:
+
+ id-fieldType OBJECT IDENTIFIER ::= { ansi-X9-62 fieldType(1) }
+
+
+
+
+
+Polk, et al. Standards Track [Page 15]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ The object identifiers prime-field and characteristic-two-field name
+ the two kinds of fields defined in this Standard. They have the
+ following values:
+
+ prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 }
+
+ Prime-p ::= INTEGER -- Field size p (p in bits)
+
+ characteristic-two-field OBJECT IDENTIFIER ::= { id-fieldType 2 }
+
+ Characteristic-two ::= SEQUENCE {
+ m INTEGER, -- Field size 2^m
+ basis OBJECT IDENTIFIER,
+ parameters ANY DEFINED BY basis }
+
+ The object identifier id-characteristic-two-basis specifies an arc
+ containing the object identifiers for each type of basis for the
+ characteristic-two finite fields. It has the following value:
+
+ id-characteristic-two-basis OBJECT IDENTIFIER ::= {
+ characteristic-two-field basisType(1) }
+
+ The object identifiers gnBasis, tpBasis and ppBasis name the three
+ kinds of basis for characteristic-two finite fields defined by
+ [X9.62]. They have the following values:
+
+ gnBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 1 }
+
+ -- for gnBasis, the value of the parameters field is NULL
+
+ tpBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 2 }
+
+ -- type of parameters field for tpBasis is Trinomial
+
+ Trinomial ::= INTEGER
+
+ ppBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 3 }
+
+ -- type of parameters field for ppBasis is Pentanomial
+
+ Pentanomial ::= SEQUENCE {
+ k1 INTEGER,
+ k2 INTEGER,
+ k3 INTEGER }
+
+
+
+
+
+
+
+Polk, et al. Standards Track [Page 16]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ The elliptic curve public key (an ECPoint which is an OCTET STRING)
+ is mapped to a subjectPublicKey (a BIT STRING) as follows: the most
+ significant bit of the OCTET STRING becomes the most significant bit
+ of the BIT STRING, and the least significant bit of the OCTET STRING
+ becomes the least significant bit of the BIT STRING. Note that this
+ octet string may represent an elliptic curve point in compressed or
+ uncompressed form. Implementations that support elliptic curve
+ according to this specification MUST support the uncompressed form
+ and MAY support the compressed form.
+
+ If the keyUsage extension is present in a CA or CRL issuer
+ certificate which conveys an elliptic curve public key, any
+ combination of the following values MAY be present:
+
+ digitalSignature;
+ nonRepudiation; and
+ keyAgreement.
+
+ If the keyAgreement value is present, either of the following values
+ MAY be present:
+
+ encipherOnly; and
+ decipherOnly.
+
+ The keyUsage extension MUST NOT assert both encipherOnly and
+ decipherOnly.
+
+ If the keyUsage extension is present in a CA certificate which
+ conveys an elliptic curve public key, any combination of the
+ following values MAY be present:
+
+ digitalSignature;
+ nonRepudiation;
+ keyAgreement;
+ keyCertSign; and
+ cRLSign.
+
+ As above, if the keyUsage extension asserts keyAgreement then it MAY
+ assert either encipherOnly and decipherOnly. However, this
+ specification RECOMMENDS that if keyCertSign or cRLSign is present,
+ keyAgreement, encipherOnly, and decipherOnly SHOULD NOT be present.
+
+
+
+
+
+
+
+
+
+
+Polk, et al. Standards Track [Page 17]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+3 ASN.1 Module
+
+ PKIX1Algorithms88 { iso(1) identified-organization(3) dod(6)
+ internet(1) security(5) mechanisms(5) pkix(7) id-mod(0)
+ id-mod-pkix1-algorithms(17) }
+
+ DEFINITIONS EXPLICIT TAGS ::= BEGIN
+
+ -- EXPORTS All;
+
+ -- IMPORTS NONE;
+
+ --
+ -- One-way Hash Functions
+ --
+
+ md2 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) rsadsi(113549)
+ digestAlgorithm(2) 2 }
+
+ md5 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) rsadsi(113549)
+ digestAlgorithm(2) 5 }
+
+ id-sha1 OBJECT IDENTIFIER ::= {
+ iso(1) identified-organization(3) oiw(14) secsig(3)
+ algorithms(2) 26 }
+
+ --
+ -- DSA Keys and Signatures
+ --
+
+ -- OID for DSA public key
+
+ id-dsa OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 1 }
+
+ -- encoding for DSA public key
+
+ DSAPublicKey ::= INTEGER -- public key, y
+
+ Dss-Parms ::= SEQUENCE {
+ p INTEGER,
+ q INTEGER,
+ g INTEGER }
+
+
+
+
+
+
+Polk, et al. Standards Track [Page 18]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ -- OID for DSA signature generated with SHA-1 hash
+
+ id-dsa-with-sha1 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) x9-57 (10040) x9algorithm(4) 3 }
+
+ -- encoding for DSA signature generated with SHA-1 hash
+
+ Dss-Sig-Value ::= SEQUENCE {
+ r INTEGER,
+ s INTEGER }
+
+ --
+ -- RSA Keys and Signatures
+ --
+
+ -- arc for RSA public key and RSA signature OIDs
+
+ pkcs-1 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
+
+ -- OID for RSA public keys
+
+ rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 }
+
+ -- OID for RSA signature generated with MD2 hash
+
+ md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 }
+
+ -- OID for RSA signature generated with MD5 hash
+
+ md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 }
+
+ -- OID for RSA signature generated with SHA-1 hash
+
+ sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
+
+ -- encoding for RSA public key
+
+ RSAPublicKey ::= SEQUENCE {
+ modulus INTEGER, -- n
+ publicExponent INTEGER } -- e
+
+
+
+
+
+
+
+
+
+
+Polk, et al. Standards Track [Page 19]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ --
+ -- Diffie-Hellman Keys
+ --
+
+ dhpublicnumber OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) ansi-x942(10046)
+ number-type(2) 1 }
+
+ -- encoding for DSA public key
+
+ DHPublicKey ::= INTEGER -- public key, y = g^x mod p
+
+ DomainParameters ::= SEQUENCE {
+ p INTEGER, -- odd prime, p=jq +1
+ g INTEGER, -- generator, g
+ q INTEGER, -- factor of p-1
+ j INTEGER OPTIONAL, -- subgroup factor, j>= 2
+ validationParms ValidationParms OPTIONAL }
+
+ ValidationParms ::= SEQUENCE {
+ seed BIT STRING,
+ pgenCounter INTEGER }
+
+ --
+ -- KEA Keys
+ --
+
+ id-keyExchangeAlgorithm OBJECT IDENTIFIER ::=
+ { 2 16 840 1 101 2 1 1 22 }
+
+ KEA-Parms-Id ::= OCTET STRING
+
+ --
+ -- Elliptic Curve Keys, Signatures, and Curves
+ --
+
+ ansi-X9-62 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) 10045 }
+
+ FieldID ::= SEQUENCE { -- Finite field
+ fieldType OBJECT IDENTIFIER,
+ parameters ANY DEFINED BY fieldType }
+
+ -- Arc for ECDSA signature OIDS
+
+ id-ecSigType OBJECT IDENTIFIER ::= { ansi-X9-62 signatures(4) }
+
+
+
+
+
+Polk, et al. Standards Track [Page 20]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ -- OID for ECDSA signatures with SHA-1
+
+ ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { id-ecSigType 1 }
+
+ -- OID for an elliptic curve signature
+ -- format for the value of an ECDSA signature value
+
+ ECDSA-Sig-Value ::= SEQUENCE {
+ r INTEGER,
+ s INTEGER }
+
+ -- recognized field type OIDs are defined in the following arc
+
+ id-fieldType OBJECT IDENTIFIER ::= { ansi-X9-62 fieldType(1) }
+
+ -- where fieldType is prime-field, the parameters are of type Prime-p
+
+ prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 }
+
+ Prime-p ::= INTEGER -- Finite field F(p), where p is an odd prime
+
+ -- where fieldType is characteristic-two-field, the parameters are
+ -- of type Characteristic-two
+
+ characteristic-two-field OBJECT IDENTIFIER ::= { id-fieldType 2 }
+
+ Characteristic-two ::= SEQUENCE {
+ m INTEGER, -- Field size 2^m
+ basis OBJECT IDENTIFIER,
+ parameters ANY DEFINED BY basis }
+
+ -- recognized basis type OIDs are defined in the following arc
+
+ id-characteristic-two-basis OBJECT IDENTIFIER ::= {
+ characteristic-two-field basisType(3) }
+
+ -- gnbasis is identified by OID gnBasis and indicates
+ -- parameters are NULL
+
+ gnBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 1 }
+
+ -- parameters for this basis are NULL
+
+ -- trinomial basis is identified by OID tpBasis and indicates
+ -- parameters of type Pentanomial
+
+ tpBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 2 }
+
+
+
+
+Polk, et al. Standards Track [Page 21]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ -- Trinomial basis representation of F2^m
+ -- Integer k for reduction polynomial xm + xk + 1
+
+ Trinomial ::= INTEGER
+
+ -- for pentanomial basis is identified by OID ppBasis and indicates
+ -- parameters of type Pentanomial
+
+ ppBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 3 }
+
+ -- Pentanomial basis representation of F2^m
+ -- reduction polynomial integers k1, k2, k3
+ -- f(x) = x**m + x**k3 + x**k2 + x**k1 + 1
+
+ Pentanomial ::= SEQUENCE {
+ k1 INTEGER,
+ k2 INTEGER,
+ k3 INTEGER }
+
+ -- The object identifiers gnBasis, tpBasis and ppBasis name
+ -- three kinds of basis for characteristic-two finite fields
+
+ FieldElement ::= OCTET STRING -- Finite field element
+
+ ECPoint ::= OCTET STRING -- Elliptic curve point
+
+ -- Elliptic Curve parameters may be specified explicitly,
+ -- specified implicitly through a "named curve", or
+ -- inherited from the CA
+
+ EcpkParameters ::= CHOICE {
+ ecParameters ECParameters,
+ namedCurve OBJECT IDENTIFIER,
+ implicitlyCA NULL }
+
+ ECParameters ::= SEQUENCE { -- Elliptic curve parameters
+ version ECPVer,
+ fieldID FieldID,
+ curve Curve,
+ base ECPoint, -- Base point G
+ order INTEGER, -- Order n of the base point
+ cofactor INTEGER OPTIONAL } -- The integer h = #E(Fq)/n
+
+ ECPVer ::= INTEGER {ecpVer1(1)}
+
+
+
+
+
+
+
+Polk, et al. Standards Track [Page 22]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ Curve ::= SEQUENCE {
+ a FieldElement, -- Elliptic curve coefficient a
+ b FieldElement, -- Elliptic curve coefficient b
+ seed BIT STRING OPTIONAL }
+
+ id-publicKeyType OBJECT IDENTIFIER ::= { ansi-X9-62 keyType(2) }
+
+ id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 }
+
+ -- Named Elliptic Curves in ANSI X9.62.
+
+ ellipticCurve OBJECT IDENTIFIER ::= { ansi-X9-62 curves(3) }
+
+ c-TwoCurve OBJECT IDENTIFIER ::= {
+ ellipticCurve characteristicTwo(0) }
+
+ c2pnb163v1 OBJECT IDENTIFIER ::= { c-TwoCurve 1 }
+ c2pnb163v2 OBJECT IDENTIFIER ::= { c-TwoCurve 2 }
+ c2pnb163v3 OBJECT IDENTIFIER ::= { c-TwoCurve 3 }
+ c2pnb176w1 OBJECT IDENTIFIER ::= { c-TwoCurve 4 }
+ c2tnb191v1 OBJECT IDENTIFIER ::= { c-TwoCurve 5 }
+ c2tnb191v2 OBJECT IDENTIFIER ::= { c-TwoCurve 6 }
+ c2tnb191v3 OBJECT IDENTIFIER ::= { c-TwoCurve 7 }
+ c2onb191v4 OBJECT IDENTIFIER ::= { c-TwoCurve 8 }
+ c2onb191v5 OBJECT IDENTIFIER ::= { c-TwoCurve 9 }
+ c2pnb208w1 OBJECT IDENTIFIER ::= { c-TwoCurve 10 }
+ c2tnb239v1 OBJECT IDENTIFIER ::= { c-TwoCurve 11 }
+ c2tnb239v2 OBJECT IDENTIFIER ::= { c-TwoCurve 12 }
+ c2tnb239v3 OBJECT IDENTIFIER ::= { c-TwoCurve 13 }
+ c2onb239v4 OBJECT IDENTIFIER ::= { c-TwoCurve 14 }
+ c2onb239v5 OBJECT IDENTIFIER ::= { c-TwoCurve 15 }
+ c2pnb272w1 OBJECT IDENTIFIER ::= { c-TwoCurve 16 }
+ c2pnb304w1 OBJECT IDENTIFIER ::= { c-TwoCurve 17 }
+ c2tnb359v1 OBJECT IDENTIFIER ::= { c-TwoCurve 18 }
+ c2pnb368w1 OBJECT IDENTIFIER ::= { c-TwoCurve 19 }
+ c2tnb431r1 OBJECT IDENTIFIER ::= { c-TwoCurve 20 }
+
+ primeCurve OBJECT IDENTIFIER ::= { ellipticCurve prime(1) }
+
+ prime192v1 OBJECT IDENTIFIER ::= { primeCurve 1 }
+ prime192v2 OBJECT IDENTIFIER ::= { primeCurve 2 }
+ prime192v3 OBJECT IDENTIFIER ::= { primeCurve 3 }
+ prime239v1 OBJECT IDENTIFIER ::= { primeCurve 4 }
+ prime239v2 OBJECT IDENTIFIER ::= { primeCurve 5 }
+ prime239v3 OBJECT IDENTIFIER ::= { primeCurve 6 }
+ prime256v1 OBJECT IDENTIFIER ::= { primeCurve 7 }
+
+ END
+
+
+
+Polk, et al. Standards Track [Page 23]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+4 References
+
+ [FIPS 180-1] Federal Information Processing Standards Publication
+ (FIPS PUB) 180-1, Secure Hash Standard, 17 April 1995.
+ [Supersedes FIPS PUB 180 dated 11 May 1993.]
+
+ [FIPS 186-2] Federal Information Processing Standards Publication
+ (FIPS PUB) 186, Digital Signature Standard, 27 January
+ 2000. [Supersedes FIPS PUB 186-1 dated 15 December
+ 1998.]
+
+ [P1363] IEEE P1363, "Standard Specifications for Public-Key
+ Cryptography", 2001.
+
+ [RC95] Rogier, N. and Chauvaud, P., "The compression function
+ of MD2 is not collision free," Presented at Selected
+ Areas in Cryptography '95, May 1995.
+
+ [RFC 1034] Mockapetris, P., "Domain Names - Concepts and
+ Facilities", STD 13, RFC 1034, November 1987.
+
+ [RFC 1319] Kaliski, B., "The MD2 Message-Digest Algorithm", RFC
+ 1319, April 1992.
+
+ [RFC 1321] Rivest, R., "The MD5 Message-Digest Algorithm", RFC
+ 1321, April 1992.
+
+ [RFC 1422] Kent, S., "Privacy Enhancement for Internet Electronic
+ Mail: Part II: Certificate-Based Key Management", RFC
+ 1422, February 1993.
+
+ [RFC 1423] Balenson, D., "Privacy Enhancement for Internet
+ Electronic Mail: Part III: Algorithms, Modes, and
+ Identifiers", RFC 1423, February 1993.
+
+ [RFC 2119] Bradner, S., "Key Words for Use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC 2313] Kaliski, B., "PKCS #1: RSA Encryption Version 1.5",
+ RFC 2313, March 1998.
+
+ [RFC 2459] Housley, R., Ford, W., Polk, W. and D. Solo "Internet
+ X.509 Public Key Infrastructure: Certificate and CRL
+ Profile", RFC 2459, January, 1999.
+
+ [RFC 3174] Eastlake, D. and P. Jones, "US Secure Hash Algorithm 1
+ (SHA1)", RFC 3174, September 2001.
+
+
+
+
+Polk, et al. Standards Track [Page 24]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ [RFC 3280] Housley, R., Polk, W., Ford, W. and D. Solo, "Internet
+ X.509 Public Key Infrastructure Certificate and
+ Certificate Revocation List (CRL) Profile", RFC 3280,
+ April 2002.
+
+ [SDN.701r] SDN.701, "Message Security Protocol 4.0", Revision A
+ 1997-02-06.
+
+ [X.208] CCITT Recommendation X.208: Specification of Abstract
+ Syntax Notation One (ASN.1), 1988.
+
+ [X.660] ITU-T Recommendation X.660 Information Technology -
+ ASN.1 encoding rules: Specification of Basic Encoding
+ Rules (BER), Canonical Encoding Rules (CER) and
+ Distinguished Encoding Rules (DER), 1997.
+
+ [X9.42] ANSI X9.42-2000, "Public Key Cryptography for The
+ Financial Services Industry: Agreement of Symmetric
+ Keys Using Discrete Logarithm Cryptography", December,
+ 1999.
+
+ [X9.62] X9.62-1998, "Public Key Cryptography For The Financial
+ Services Industry: The Elliptic Curve Digital
+ Signature Algorithm (ECDSA)", January 7, 1999.
+
+ [X9.63] ANSI X9.63-2001, "Public Key Cryptography For The
+ Financial Services Industry: Key Agreement and Key
+ Transport Using Elliptic Curve Cryptography", Work in
+ Progress.
+
+5 Security Considerations
+
+ This specification does not constrain the size of public keys or
+ their parameters for use in the Internet PKI. However, the key size
+ selected impacts the strength achieved when implementing
+ cryptographic services. Selection of appropriate key sizes is
+ critical to implementing appropriate security.
+
+ This specification does not identify particular elliptic curves for
+ use in the Internet PKI. However, the particular curve selected
+ impact the strength of the digital signatures. Some curves are
+ cryptographically stronger than others!
+
+ In general, use of "well-known" curves, such as the "named curves"
+ from ANSI X9.62, is a sound strategy. For additional information,
+ refer to X9.62 Appendix H.1.3, "Key Length Considerations" and
+ Appendix A.1, "Avoiding Cryptographically Weak Keys".
+
+
+
+
+Polk, et al. Standards Track [Page 25]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+ This specification supplements RFC 3280. The security considerations
+ section of that document applies to this specification as well.
+
+6 Intellectual Property Rights
+
+ The IETF has been notified of intellectual property rights claimed in
+ regard to some or all of the specification contained in this
+ document. For more information consult the online list of claimed
+ rights.
+
+ The IETF takes no position regarding the validity or scope of any
+ intellectual property or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; neither does it represent that it
+ has made any effort to identify any such rights. Information on the
+ IETF's procedures with respect to rights in standards-track and
+ standards- related documentation can be found in BCP-11. Copies of
+ claims of rights made available for publication and any assurances of
+ licenses to be made available, or the result of an attempt made to
+ obtain a general license or permission for the use of such
+ proprietary rights by implementors or users of this specification can
+ be obtained from the IETF Secretariat.
+
+7 Author Addresses:
+
+ Tim Polk
+ NIST
+ 100 Bureau Drive, Stop 8930
+ Gaithersburg, MD 20899-8930
+ USA
+ EMail: tim.polk@nist.gov
+
+ Russell Housley
+ RSA Laboratories
+ 918 Spring Knoll Drive
+ Herndon, VA 20170
+ USA
+ EMail: rhousley@rsasecurity.com
+
+ Larry Bassham
+ NIST
+ 100 Bureau Drive, Stop 8930
+ Gaithersburg, MD 20899-8930
+ USA
+ EMail: lbassham@nist.gov
+
+
+
+
+
+Polk, et al. Standards Track [Page 26]
+
+RFC 3279 Algorithms and Identifiers April 2002
+
+
+8. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2002). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Polk, et al. Standards Track [Page 27]
+
diff --git a/utils/open-isns/doc/rfc3720.txt b/utils/open-isns/doc/rfc3720.txt
new file mode 100644
index 0000000..2041f93
--- /dev/null
+++ b/utils/open-isns/doc/rfc3720.txt
@@ -0,0 +1,14395 @@
+
+
+
+
+
+
+Network Working Group J. Satran
+Request for Comments: 3720 K. Meth
+Category: Standards Track IBM
+ C. Sapuntzakis
+ Cisco Systems
+ M. Chadalapaka
+ Hewlett-Packard Co.
+ E. Zeidner
+ IBM
+ April 2004
+
+
+ Internet Small Computer Systems Interface (iSCSI)
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+Abstract
+
+ This document describes a transport protocol for Internet Small
+ Computer Systems Interface (iSCSI) that works on top of TCP. The
+ iSCSI protocol aims to be fully compliant with the standardized SCSI
+ architecture model.
+
+ SCSI is a popular family of protocols that enable systems to
+ communicate with I/O devices, especially storage devices. SCSI
+ protocols are request/response application protocols with a common
+ standardized architecture model and basic command set, as well as
+ standardized command sets for different device classes (disks, tapes,
+ media-changers etc.).
+
+ As system interconnects move from the classical bus structure to a
+ network structure, SCSI has to be mapped to network transport
+ protocols. IP networks now meet the performance requirements of fast
+ system interconnects and as such are good candidates to "carry" SCSI.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 1]
+
+RFC 3720 iSCSI April 2004
+
+
+Table of Contents
+
+ 1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . 9
+ 2. Definitions and Acronyms. . . . . . . . . . . . . . . . . . . 10
+ 2.1. Definitions. . . . . . . . . . . . . . . . . . . . . . 10
+ 2.2. Acronyms . . . . . . . . . . . . . . . . . . . . . . . 14
+ 2.3. Conventions. . . . . . . . . . . . . . . . . . . . . . 16
+ 2.3.1. Word Rule. . . . . . . . . . . . . . . . . . 16
+ 2.3.2. Half-Word Rule . . . . . . . . . . . . . . . 17
+ 2.3.3. Byte Rule. . . . . . . . . . . . . . . . . . 17
+ 3. Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . 17
+ 3.1. SCSI Concepts. . . . . . . . . . . . . . . . . . . . . 17
+ 3.2. iSCSI Concepts and Functional Overview . . . . . . . . 18
+ 3.2.1. Layers and Sessions. . . . . . . . . . . . . 19
+ 3.2.2. Ordering and iSCSI Numbering . . . . . . . . 19
+ 3.2.2.1. Command Numbering and
+ Acknowledging . . . . . . . . . . 20
+ 3.2.2.2. Response/Status Numbering and
+ Acknowledging . . . . . . . . . . 23
+ 3.2.2.3. Data Sequencing . . . . . . . . 24
+ 3.2.3. iSCSI Login. . . . . . . . . . . . . . . . . 24
+ 3.2.4. iSCSI Full Feature Phase . . . . . . . . . . 25
+ 3.2.4.1. Command Connection Allegiance . . 26
+ 3.2.4.2. Data Transfer Overview. . . . . . 27
+ 3.2.4.3. Tags and Integrity Checks . . . . 28
+ 3.2.4.4. Task Management . . . . . . . . . 28
+ 3.2.5. iSCSI Connection Termination . . . . . . . . 29
+ 3.2.6. iSCSI Names. . . . . . . . . . . . . . . . . 29
+ 3.2.6.1. iSCSI Name Properties . . . . . . 30
+ 3.2.6.2. iSCSI Name Encoding . . . . . . . 31
+ 3.2.6.3. iSCSI Name Structure. . . . . . . 32
+ 3.2.6.3.1. Type "iqn." (iSCSI
+ Qualified Name) . . . 32
+ 3.2.6.3.2. Type "eui." (IEEE
+ EUI-64 format). . . . 34
+ 3.2.7. Persistent State . . . . . . . . . . . . . . 34
+ 3.2.8. Message Synchronization and Steering . . . . 35
+ 3.2.8.1. Sync/Steering and iSCSI PDU
+ Length . . . . . . . . . . . . . 36
+ 3.3. iSCSI Session Types. . . . . . . . . . . . . . . . . . 36
+ 3.4. SCSI to iSCSI Concepts Mapping Model . . . . . . . . . 37
+ 3.4.1. iSCSI Architecture Model . . . . . . . . . . 37
+ 3.4.2. SCSI Architecture Model. . . . . . . . . . . 39
+ 3.4.3. Consequences of the Model. . . . . . . . . . 41
+ 3.4.3.1. I_T Nexus State . . . . . . . . . 42
+ 3.5. Request/Response Summary . . . . . . . . . . . . . . . 42
+ 3.5.1. Request/Response Types Carrying SCSI Payload 43
+ 3.5.1.1. SCSI-Command . . . . . . . . . . 43
+
+
+
+Satran, et al. Standards Track [Page 2]
+
+RFC 3720 iSCSI April 2004
+
+
+ 3.5.1.2. SCSI-Response . . . . . . . . . 43
+ 3.5.1.3. Task Management Function Request. 44
+ 3.5.1.4. Task Management Function Response 44
+ 3.5.1.5. SCSI Data-Out and SCSI Data-In. . 44
+ 3.5.1.6. Ready To Transfer (R2T) . . . . . 45
+ 3.5.2. Requests/Responses carrying SCSI and iSCSI
+ Payload. . . . . . . . . . . . . . . . . . . 46
+ 3.5.2.1. Asynchronous Message. . . . . . . 46
+ 3.5.3. Requests/Responses Carrying iSCSI Only
+ Payload. . . . . . . . . . . . . . . . . . . 46
+ 3.5.3.1. Text Request and Text Response. . 46
+ 3.5.3.2. Login Request and Login Response. 47
+ 3.5.3.3. Logout Request and Response . . . 47
+ 3.5.3.4. SNACK Request . . . . . . . . . . 48
+ 3.5.3.5. Reject. . . . . . . . . . . . . . 48
+ 3.5.3.6. NOP-Out Request and NOP-In
+ Response . . . . . . . . . . . . 48
+ 4. SCSI Mode Parameters for iSCSI. . . . . . . . . . . . . . . . 48
+ 5. Login and Full Feature Phase Negotiation. . . . . . . . . . . 48
+ 5.1. Text Format. . . . . . . . . . . . . . . . . . . . . . 50
+ 5.2. Text Mode Negotiation. . . . . . . . . . . . . . . . . 53
+ 5.2.1. List negotiations. . . . . . . . . . . . . . 56
+ 5.2.2. Simple-value Negotiations. . . . . . . . . . 56
+ 5.3. Login Phase. . . . . . . . . . . . . . . . . . . . . . 57
+ 5.3.1. Login Phase Start. . . . . . . . . . . . . . 60
+ 5.3.2. iSCSI Security Negotiation . . . . . . . . . 62
+ 5.3.3. Operational Parameter Negotiation During
+ the Login Phase. . . . . . . . . . . . . . . 63
+ 5.3.4. Connection Reinstatement . . . . . . . . . . 64
+ 5.3.5. Session Reinstatement, Closure, and Timeout. 64
+ 5 5.3.5.1. Loss of Nexus
+ Notification. . . . . 65
+ 5.3.6. Session Continuation and Failure . . . . . . 65
+ 5.4. Operational Parameter Negotiation Outside the Login
+ Phase. . . . . . . . . . . . . . . . . . . . . . . . . 66
+ 6. iSCSI Error Handling and Recovery . . . . . . . . . . . . . . 67
+ 6.1. Overview . . . . . . . . . . . . . . . . . . . . . . . 67
+ 6.1.1. Background . . . . . . . . . . . . . . . . . 67
+ 6.1.2. Goals. . . . . . . . . . . . . . . . . . . . 67
+ 6.1.3. Protocol Features and State Expectations . . 68
+ 6.1.4. Recovery Classes . . . . . . . . . . . . . . 69
+ 6.1.4.1. Recovery Within-command . . . . . 69
+ 6.1.4.2. Recovery Within-connection. . . . 70
+ 6.1.4.3. Connection Recovery . . . . . . . 71
+ 6.1.4.4. Session Recovery. . . . . . . . . 72
+ 6.1.5. Error Recovery Hierarchy . . . . . . . . . . . 72
+ 6.2. Retry and Reassign in Recovery . . . . . . . . . . . . 74
+ 6.2.1. Usage of Retry . . . . . . . . . . . . . . . 74
+
+
+
+Satran, et al. Standards Track [Page 3]
+
+RFC 3720 iSCSI April 2004
+
+
+ 6.2.2. Allegiance Reassignment. . . . . . . . . . . 75
+ 6.3. Usage Of Reject PDU in Recovery. . . . . . . . . . . . 76
+ 6.4. Connection Timeout Management. . . . . . . . . . . . . 76
+ 6.4.1. Timeouts on Transport Exception Events . . . 77
+ 6.4.2. Timeouts on Planned Decommissioning. . . . . 77
+ 6.5. Implicit Termination of Tasks. . . . . . . . . . . . . 77
+ 6.6. Format Errors. . . . . . . . . . . . . . . . . . . . . 78
+ 6.7. Digest Errors. . . . . . . . . . . . . . . . . . . . . 78
+ 6.8. Sequence Errors. . . . . . . . . . . . . . . . . . . . 80
+ 6.9. SCSI Timeouts. . . . . . . . . . . . . . . . . . . . . 81
+ 6.10. Negotiation Failures . . . . . . . . . . . . . . . . . 81
+ 6.11. Protocol Errors. . . . . . . . . . . . . . . . . . . . 82
+ 6.12. Connection Failures. . . . . . . . . . . . . . . . . . 82
+ 6.13. Session Errors . . . . . . . . . . . . . . . . . . . . 83
+ 7. State Transitions . . . . . . . . . . . . . . . . . . . . . . 84
+ 7.1. Standard Connection State Diagrams . . . . . . . . . . 84
+ 7.1.1. State Descriptions for Initiators and
+ Targets. . . . . . . . . . . . . . . . . . . 84
+ 7.1.2. State Transition Descriptions for Initiators
+ and Targets. . . . . . . . . . . . . . . . . 85
+ 7.1.3. Standard Connection State Diagram for an
+ Initiator. . . . . . . . . . . . . . . . . . 88
+ 7.1.4. Standard Connection State Diagram for a
+ Target . . . . . . . . . . . . . . . . . . . 90
+ 7.2. Connection Cleanup State Diagram for Initiators and
+ Targets. . . . . . . . . . . . . . . . . . . . . . . . 92
+ 7.2.1. State Descriptions for Initiators and
+ Targets. . . . . . . . . . . . . . . . . . . 94
+ 7.2.2. State Transition Descriptions for Initiators
+ and Targets. . . . . . . . . . . . . . . . . 94
+ 7.3. Session State Diagrams . . . . . . . . . . . . . . . . 95
+ 7.3.1. Session State Diagram for an Initiator . . . 95
+ 7.3.2. Session State Diagram for a Target . . . . . 96
+ 7.3.3. State Descriptions for Initiators and
+ Targets. . . . . . . . . . . . . . . . . . . 97
+ 7.3.4. State Transition Descriptions for Initiators
+ and Targets. . . . . . . . . . . . . . . . . 98
+ 8. Security Considerations . . . . . . . . . . . . . . . . . . . 99
+ 8.1. iSCSI Security Mechanisms. . . . . . . . . . . . . . . 100
+ 8.2. In-band Initiator-Target Authentication. . . . . . . . 100
+ 8.2.1. CHAP Considerations. . . . . . . . . . . . . 101
+ 8.2.2. SRP Considerations . . . . . . . . . . . . . 103
+ 8.3. IPsec. . . . . . . . . . . . . . . . . . . . . . . . . 104
+ 8.3.1. Data Integrity and Authentication. . . . . . 104
+ 8.3.2. Confidentiality. . . . . . . . . . . . . . . 105
+ 8.3.3. Policy, Security Associations, and
+ Cryptographic Key Management . . . . . . . . 105
+ 9. Notes to Implementers . . . . . . . . . . . . . . . . . . . . 106
+
+
+
+Satran, et al. Standards Track [Page 4]
+
+RFC 3720 iSCSI April 2004
+
+
+ 9.1. Multiple Network Adapters. . . . . . . . . . . . . . . 106
+ 9.1.1. Conservative Reuse of ISIDs. . . . . . . . . 107
+ 9.1.2. iSCSI Name, ISID, and TPGT Use . . . . . . . 107
+ 9.2. Autosense and Auto Contingent Allegiance (ACA) . . . . 109
+ 9.3. iSCSI Timeouts . . . . . . . . . . . . . . . . . . . . 109
+ 9.4. Command Retry and Cleaning Old Command Instances . . . 110
+ 9.5. Synch and Steering Layer and Performance . . . . . . . 110
+ 9.6. Considerations for State-dependent Devices and
+ Long-lasting SCSI Operations . . . . . . . . . . . . . 111
+ 9.6.1. Determining the Proper ErrorRecoveryLevel. . 112
+ 10. iSCSI PDU Formats . . . . . . . . . . . . . . . . . . . . . . 112
+ 10.1. iSCSI PDU Length and Padding . . . . . . . . . . . . . 113
+ 10.2. PDU Template, Header, and Opcodes. . . . . . . . . . . 113
+ 10.2.1. Basic Header Segment (BHS) . . . . . . . . . 114
+ 10.2.1.1. I . . . . . . . . . . . . . . . . 115
+ 10.2.1.2. Opcode. . . . . . . . . . . . . . 115
+ 10.2.1.3. Final (F) bit . . . . . . . . . . 116
+ 10.2.1.4. Opcode-specific Fields. . . . . . 116
+ 10.2.1.5. TotalAHSLength. . . . . . . . . . 116
+ 10.2.1.6. DataSegmentLength . . . . . . . . 116
+ 10.2.1.7. LUN . . . . . . . . . . . . . . . 116
+ 10.2.1.8. Initiator Task Tag. . . . . . . . 117
+ 10.2.2. Additional Header Segment (AHS) . . . . . . . 117
+ 10.2.2.1. AHSType . . . . . . . . . . . . . 117
+ 10.2.2.2. AHSLength . . . . . . . . . . . . 117
+ 10.2.2.3. Extended CDB AHS. . . . . . . . . 118
+ 10.2.2.4. Bidirectional Expected Read-Data
+ Length AHS. . . . . . . . . . . . 118
+ 10.2.3. Header Digest and Data Digest. . . . . . . . 118
+ 10.2.4. Data Segment . . . . . . . . . . . . . . . . 119
+ 10.3. SCSI Command . . . . . . . . . . . . . . . . . . . . . 119
+ 10.3.1. Flags and Task Attributes (byte 1) . . . . . 120
+ 10.3.2. CmdSN - Command Sequence Number. . . . . . . 120
+ 10.3.3. ExpStatSN. . . . . . . . . . . . . . . . . . 120
+ 10.3.4. Expected Data Transfer Length. . . . . . . . 121
+ 10.3.5. CDB - SCSI Command Descriptor Block. . . . . 121
+ 10.3.6. Data Segment - Command Data. . . . . . . . . 121
+ 10.4. SCSI Response. . . . . . . . . . . . . . . . . . . . . 122
+ 10.4.1. Flags (byte 1) . . . . . . . . . . . . . . . 123
+ 10.4.2. Status . . . . . . . . . . . . . . . . . . . 123
+ 10.4.3. Response . . . . . . . . . . . . . . . . . . 124
+ 10.4.4. SNACK Tag. . . . . . . . . . . . . . . . . . 125
+ 10.4.5. Residual Count . . . . . . . . . . . . . . . 125
+ 10.4.6. Bidirectional Read Residual Count. . . . . . 125
+ 10.4.7. Data Segment - Sense and Response Data
+ Segment. . . . . . . . . . . . . . . . . . . 125
+ 10.4.7.1. SenseLength . . . . . . . . . . . 126
+ 10.4.7.2. Sense Data. . . . . . . . . . . . 126
+
+
+
+Satran, et al. Standards Track [Page 5]
+
+RFC 3720 iSCSI April 2004
+
+
+ 10.4.8. ExpDataSN. . . . . . . . . . . . . . . . . . 127
+ 10.4.9. StatSN - Status Sequence Number. . . . . . . 127
+ 10.4.10. ExpCmdSN - Next Expected CmdSN from this
+ Initiator. . . . . . . . . . . . . . . . . . 128
+ 10.4.11. MaxCmdSN - Maximum CmdSN from this Initiator 128
+ 10.5. Task Management Function Request . . . . . . . . . . . 129
+ 10.5.1. Function . . . . . . . . . . . . . . . . . . 129
+ 10.5.2. TotalAHSLength and DataSegmentLength . . . . 132
+ 10.5.3. LUN. . . . . . . . . . . . . . . . . . . . . 132
+ 10.5.4. Referenced Task Tag. . . . . . . . . . . . . 132
+ 10.5.5. RefCmdSN . . . . . . . . . . . . . . . . . . 132
+ 10.5.6. ExpDataSN. . . . . . . . . . . . . . . . . . 133
+ 10.6. Task Management Function Response. . . . . . . . . . . 134
+ 10.6.1. Response . . . . . . . . . . . . . . . . . . 134
+ 10.6.2. Task Management Actions on Task Sets . . . . 136
+ 10.6.3. TotalAHSLength and DataSegmentLength . . . . 137
+ 10.7. SCSI Data-Out & SCSI Data-In . . . . . . . . . . . . . 137
+ 10.7.1. F (Final) Bit. . . . . . . . . . . . . . . . 139
+ 10.7.2. A (Acknowledge) Bit. . . . . . . . . . . . . 139
+ 10.7.3. Flags (byte 1) . . . . . . . . . . . . . . . 140
+ 10.7.4. Target Transfer Tag and LUN. . . . . . . . . 140
+ 10.7.5. DataSN . . . . . . . . . . . . . . . . . . . 141
+ 10.7.6. Buffer Offset. . . . . . . . . . . . . . . . 141
+ 10.7.7. DataSegmentLength. . . . . . . . . . . . . . 141
+ 10.8. Ready To Transfer (R2T). . . . . . . . . . . . . . . . 142
+ 10.8.1. TotalAHSLength and DataSegmentLength . . . . 143
+ 10.8.2. R2TSN. . . . . . . . . . . . . . . . . . . . 143
+ 10.8.3. StatSN . . . . . . . . . . . . . . . . . . . 144
+ 10.8.4. Desired Data Transfer Length and Buffer
+ Offset . . . . . . . . . . . . . . . . . . . 144
+ 10.8.5. Target Transfer Tag. . . . . . . . . . . . . 144
+ 10.9. Asynchronous Message . . . . . . . . . . . . . . . . . 145
+ 10.9.1. AsyncEvent . . . . . . . . . . . . . . . . . 146
+ 10.9.2. AsyncVCode . . . . . . . . . . . . . . . . . 147
+ 10.9.3. LUN. . . . . . . . . . . . . . . . . . . . . 147
+ 10.9.4. Sense Data and iSCSI Event Data. . . . . . . 148
+ 10.9.4.1. SenseLength . . . . . . . . . . . 148
+ 10.10. Text Request . . . . . . . . . . . . . . . . . . . . . 149
+ 10.10.1. F (Final) Bit. . . . . . . . . . . . . . . . 150
+ 10.10.2. C (Continue) Bit . . . . . . . . . . . . . . 150
+ 10.10.3. Initiator Task Tag . . . . . . . . . . . . . 150
+ 10.10.4. Target Transfer Tag. . . . . . . . . . . . . 150
+ 10.10.5. Text . . . . . . . . . . . . . . . . . . . . 151
+ 10.11. Text Response. . . . . . . . . . . . . . . . . . . . . 152
+ 10.11.1. F (Final) Bit. . . . . . . . . . . . . . . . 152
+ 10.11.2. C (Continue) Bit . . . . . . . . . . . . . . 153
+ 10.11.3. Initiator Task Tag . . . . . . . . . . . . . 153
+ 10.11.4. Target Transfer Tag. . . . . . . . . . . . . 153
+
+
+
+Satran, et al. Standards Track [Page 6]
+
+RFC 3720 iSCSI April 2004
+
+
+ 10.11.5. StatSN . . . . . . . . . . . . . . . . . . . 154
+ 10.11.6. Text Response Data . . . . . . . . . . . . . 154
+ 10.12. Login Request. . . . . . . . . . . . . . . . . . . . . 154
+ 10.12.1. T (Transit) Bit. . . . . . . . . . . . . . . 155
+ 10.12.2. C (Continue) Bit . . . . . . . . . . . . . . 155
+ 10.12.3. CSG and NSG. . . . . . . . . . . . . . . . . 156
+ 10.12.4. Version. . . . . . . . . . . . . . . . . . . 156
+ 10.12.4.1. Version-max. . . . . . . . . . . 156
+ 10.12.4.2. Version-min. . . . . . . . . . . 156
+ 10.12.5. ISID . . . . . . . . . . . . . . . . . . . . 157
+ 10.12.6. TSIH . . . . . . . . . . . . . . . . . . . . 158
+ 10.12.7. Connection ID - CID. . . . . . . . . . . . . 158
+ 10.12.8. CmdSN. . . . . . . . . . . . . . . . . . . . 159
+ 10.12.9. ExpStatSN. . . . . . . . . . . . . . . . . . 159
+ 10.12.10. Login Parameters . . . . . . . . . . . . . . 159
+ 10.13. Login Response . . . . . . . . . . . . . . . . . . . . 160
+ 10.13.1. Version-max. . . . . . . . . . . . . . . . . 160
+ 10.13.2. Version-active . . . . . . . . . . . . . . . 161
+ 10.13.3. TSIH . . . . . . . . . . . . . . . . . . . . 161
+ 10.13.4. StatSN . . . . . . . . . . . . . . . . . . . 161
+ 10.13.5. Status-Class and Status-Detail . . . . . . . 161
+ 10.13.6. T (Transit) Bit. . . . . . . . . . . . . . . 164
+ 10.13.7. C (Continue) Bit . . . . . . . . . . . . . . 164
+ 10.13.8. Login Parameters . . . . . . . . . . . . . . 164
+ 10.14. Logout Request . . . . . . . . . . . . . . . . . . . . 165
+ 10.14.1. Reason Code. . . . . . . . . . . . . . . . . 167
+ 10.14.2. TotalAHSLength and DataSegmentLength . . . . 168
+ 10.14.3. CID. . . . . . . . . . . . . . . . . . . . . 168
+ 10.14.4. ExpStatSN. . . . . . . . . . . . . . . . . . 168
+ 10.14.5. Implicit termination of tasks. . . . . . . . 168
+ 10.15. Logout Response. . . . . . . . . . . . . . . . . . . . 169
+ 10.15.1. Response . . . . . . . . . . . . . . . . . . 170
+ 10.15.2. TotalAHSLength and DataSegmentLength . . . . 170
+ 10.15.3. Time2Wait. . . . . . . . . . . . . . . . . . 170
+ 10.15.4. Time2Retain. . . . . . . . . . . . . . . . . 170
+ 10.16. SNACK Request. . . . . . . . . . . . . . . . . . . . . 171
+ 10.16.1. Type . . . . . . . . . . . . . . . . . . . . 172
+ 10.16.2. Data Acknowledgement . . . . . . . . . . . . 173
+ 10.16.3. Resegmentation . . . . . . . . . . . . . . . 173
+ 10.16.4. Initiator Task Tag . . . . . . . . . . . . . 174
+ 10.16.5. Target Transfer Tag or SNACK Tag . . . . . . 174
+ 10.16.6. BegRun . . . . . . . . . . . . . . . . . . . 174
+ 10.16.7. RunLength. . . . . . . . . . . . . . . . . . 174
+ 10.17. Reject . . . . . . . . . . . . . . . . . . . . . . . . 175
+ 10.17.1. Reason . . . . . . . . . . . . . . . . . . . 176
+ 10.17.2. DataSN/R2TSN . . . . . . . . . . . . . . . . 177
+ 10.17.3. StatSN, ExpCmdSN and MaxCmdSN. . . . . . . . 177
+ 10.17.4. Complete Header of Bad PDU . . . . . . . . . 177
+
+
+
+Satran, et al. Standards Track [Page 7]
+
+RFC 3720 iSCSI April 2004
+
+
+ 10.18. NOP-Out. . . . . . . . . . . . . . . . . . . . . . . . 178
+ 10.18.1. Initiator Task Tag . . . . . . . . . . . . . 179
+ 10.18.2. Target Transfer Tag. . . . . . . . . . . . . 179
+ 10.18.3. Ping Data. . . . . . . . . . . . . . . . . . 179
+ 10.19. NOP-In . . . . . . . . . . . . . . . . . . . . . . . . 180
+ 10.19.1. Target Transfer Tag. . . . . . . . . . . . . 181
+ 10.19.2. StatSN . . . . . . . . . . . . . . . . . . . 181
+ 10.19.3. LUN. . . . . . . . . . . . . . . . . . . . . 181
+ 11. iSCSI Security Text Keys and Authentication Methods . . . . . 181
+ 11.1. AuthMethod . . . . . . . . . . . . . . . . . . . . . . 182
+ 11.1.1. Kerberos . . . . . . . . . . . . . . . . . . 184
+ 11.1.2. Simple Public-Key Mechanism (SPKM) . . . . . 184
+ 11.1.3. Secure Remote Password (SRP) . . . . . . . . 185
+ 11.1.4. Challenge Handshake Authentication Protocol
+ (CHAP) . . . . . . . . . . . . . . . . . . . 186
+ 12. Login/Text Operational Text Keys. . . . . . . . . . . . . . . 187
+ 12.1. HeaderDigest and DataDigest. . . . . . . . . . . . . . 188
+ 12.2. MaxConnections . . . . . . . . . . . . . . . . . . . . 190
+ 12.3. SendTargets. . . . . . . . . . . . . . . . . . . . . . 191
+ 12.4. TargetName . . . . . . . . . . . . . . . . . . . . . . 191
+ 12.5. InitiatorName. . . . . . . . . . . . . . . . . . . . . 192
+ 12.6. TargetAlias. . . . . . . . . . . . . . . . . . . . . . 192
+ 12.7. InitiatorAlias . . . . . . . . . . . . . . . . . . . . 193
+ 12.8. TargetAddress. . . . . . . . . . . . . . . . . . . . . 193
+ 12.9. TargetPortalGroupTag . . . . . . . . . . . . . . . . . 194
+ 12.10. InitialR2T . . . . . . . . . . . . . . . . . . . . . . 194
+ 12.11. ImmediateData. . . . . . . . . . . . . . . . . . . . . 195
+ 12.12. MaxRecvDataSegmentLength . . . . . . . . . . . . . . . 196
+ 12.13. MaxBurstLength . . . . . . . . . . . . . . . . . . . . 196
+ 12.14. FirstBurstLength . . . . . . . . . . . . . . . . . . . 197
+ 12.15. DefaultTime2Wait . . . . . . . . . . . . . . . . . . . 197
+ 12.16. DefaultTime2Retain . . . . . . . . . . . . . . . . . . 198
+ 12.17. MaxOutstandingR2T. . . . . . . . . . . . . . . . . . . 198
+ 12.18. DataPDUInOrder . . . . . . . . . . . . . . . . . . . . 198
+ 12.19. DataSequenceInOrder. . . . . . . . . . . . . . . . . . 199
+ 12.20. ErrorRecoveryLevel . . . . . . . . . . . . . . . . . . 199
+ 12.21. SessionType. . . . . . . . . . . . . . . . . . . . . . 200
+ 12.22. The Private or Public Extension Key Format . . . . . . 200
+ 13. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 201
+ 13.1. Naming Requirements. . . . . . . . . . . . . . . . . . 203
+ 13.2. Mechanism Specification Requirements . . . . . . . . . 203
+ 13.3. Publication Requirements . . . . . . . . . . . . . . . 203
+ 13.4. Security Requirements. . . . . . . . . . . . . . . . . 203
+ 13.5. Registration Procedure . . . . . . . . . . . . . . . . 204
+ 13.5.1. Present the iSCSI extension item to the
+ Community. . . . . . . . . . . . . . . . . . 204
+ 13.5.2. iSCSI extension item review and IESG
+ approval . . . . . . . . . . . . . . . . . . 204
+
+
+
+Satran, et al. Standards Track [Page 8]
+
+RFC 3720 iSCSI April 2004
+
+
+ 13.5.3. IANA Registration. . . . . . . . . . . . . . 204
+ 13.5.4. Standard iSCSI extension item-label format . 204
+ 13.6. IANA Procedures for Registering iSCSI extension items. 205
+ References. . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
+ Appendix A. Sync and Steering with Fixed Interval Markers . . . . 209
+ A.1. Markers At Fixed Intervals . . . . . . . . . . . . . . 209
+ A.2. Initial Marker-less Interval . . . . . . . . . . . . . 210
+ A.3. Negotiation. . . . . . . . . . . . . . . . . . . . . . 210
+ A.3.1. OFMarker, IFMarker . . . . . . . . . . . . . 210
+ A.3.2. OFMarkInt, IFMarkInt . . . . . . . . . . . . 211
+ Appendix B. Examples . . . . . . . . . . . . . . . . . . . . . . 212
+ B.1. Read Operation Example . . . . . . . . . . . . . . . . 212
+ B.2. Write Operation Example. . . . . . . . . . . . . . . . 213
+ B.3. R2TSN/DataSN Use Examples. . . . . . . . . . . . . . . 214
+ B.4. CRC Examples . . . . . . . . . . . . . . . . . . . . . 217
+ Appendix C. Login Phase Examples . . . . . . . . . . . . . . . . 219
+ Appendix D. SendTargets Operation. . . . . . . . . . . . . . . . 229
+ Appendix E. Algorithmic Presentation of Error Recovery Classes . 233
+ E.1. General Data Structure and Procedure Description . . . 233
+ E.2. Within-command Error Recovery Algorithms . . . . . . . 234
+ E.2.1. Procedure Descriptions . . . . . . . . . . . 234
+ E.2.2. Initiator Algorithms . . . . . . . . . . . . 235
+ E.2.3. Target Algorithms. . . . . . . . . . . . . . 237
+ E.3. Within-connection Recovery Algorithms. . . . . . . . . 240
+ E.3.1. Procedure Descriptions . . . . . . . . . . . 240
+ E.3.2. Initiator Algorithms . . . . . . . . . . . . 241
+ E.3.3. Target Algorithms. . . . . . . . . . . . . . 243
+ E.4. Connection Recovery Algorithms . . . . . . . . . . . . 243
+ E.4.1. Procedure Descriptions . . . . . . . . . . . 243
+ E.4.2. Initiator Algorithms . . . . . . . . . . . . 244
+ E.4.3. Target Algorithms. . . . . . . . . . . . . . 246
+ Appendix F. Clearing Effects of Various Events on Targets. . . . 249
+ F.1. Clearing Effects on iSCSI Objects. . . . . . . . . . . 249
+ F.2. Clearing Effects on SCSI Objects . . . . . . . . . . . 253
+ Acknowledgements. . . . . . . . . . . . . . . . . . . . . . . . . 254
+ Authors' Addresses. . . . . . . . . . . . . . . . . . . . . . . . 256
+ Full Copyright Statement. . . . . . . . . . . . . . . . . . . . . 257
+
+1. Introduction
+
+ The Small Computer Systems Interface (SCSI) is a popular family of
+ protocols for communicating with I/O devices, especially storage
+ devices. SCSI is a client-server architecture. Clients of a SCSI
+ interface are called "initiators". Initiators issue SCSI "commands"
+ to request services from components, logical units of a server known
+ as a "target". A "SCSI transport" maps the client-server SCSI
+ protocol to a specific interconnect. An Initiator is one endpoint of
+ a SCSI transport and a target is the other endpoint.
+
+
+
+Satran, et al. Standards Track [Page 9]
+
+RFC 3720 iSCSI April 2004
+
+
+ The SCSI protocol has been mapped over various transports, including
+ Parallel SCSI, IPI, IEEE-1394 (firewire) and Fibre Channel. These
+ transports are I/O specific and have limited distance capabilities.
+
+ The iSCSI protocol defined in this document describes a means of
+ transporting SCSI packets over TCP/IP (see [RFC791], [RFC793],
+ [RFC1035], [RFC1122]), providing for an interoperable solution which
+ can take advantage of existing Internet infrastructure, Internet
+ management facilities, and address distance limitations.
+
+2. Definitions and Acronyms
+
+2.1. Definitions
+
+ - Alias: An alias string can also be associated with an iSCSI Node.
+ The alias allows an organization to associate a user-friendly
+ string with the iSCSI Name. However, the alias string is not a
+ substitute for the iSCSI Name.
+
+ - CID (Connection ID): Connections within a session are identified by
+ a connection ID. It is a unique ID for this connection within the
+ session for the initiator. It is generated by the initiator and
+ presented to the target during login requests and during logouts
+ that close connections.
+
+ - Connection: A connection is a TCP connection. Communication
+ between the initiator and target occurs over one or more TCP
+ connections. The TCP connections carry control messages, SCSI
+ commands, parameters, and data within iSCSI Protocol Data Units
+ (iSCSI PDUs).
+
+ - iSCSI Device: A SCSI Device using an iSCSI service delivery
+ subsystem. Service Delivery Subsystem is defined by [SAM2] as a
+ transport mechanism for SCSI commands and responses.
+
+ - iSCSI Initiator Name: The iSCSI Initiator Name specifies the
+ worldwide unique name of the initiator.
+
+ - iSCSI Initiator Node: The "initiator". The word "initiator" has
+ been appropriately qualified as either a port or a device in the
+ rest of the document when the context is ambiguous. All
+ unqualified usages of "initiator" refer to an initiator port (or
+ device) depending on the context.
+
+ - iSCSI Layer: This layer builds/receives iSCSI PDUs and
+ relays/receives them to/from one or more TCP connections that form
+ an initiator-target "session".
+
+
+
+
+Satran, et al. Standards Track [Page 10]
+
+RFC 3720 iSCSI April 2004
+
+
+ - iSCSI Name: The name of an iSCSI initiator or iSCSI target.
+
+ - iSCSI Node: The iSCSI Node represents a single iSCSI initiator or
+ iSCSI target. There are one or more iSCSI Nodes within a Network
+ Entity. The iSCSI Node is accessible via one or more Network
+ Portals. An iSCSI Node is identified by its iSCSI Name. The
+ separation of the iSCSI Name from the addresses used by and for the
+ iSCSI Node allows multiple iSCSI Nodes to use the same address, and
+ the same iSCSI Node to use multiple addresses.
+
+ - iSCSI Target Name: The iSCSI Target Name specifies the worldwide
+ unique name of the target.
+
+ - iSCSI Target Node: The "target".
+
+ - iSCSI Task: An iSCSI task is an iSCSI request for which a response
+ is expected.
+
+ - iSCSI Transfer Direction: The iSCSI transfer direction is defined
+ with regard to the initiator. Outbound or outgoing transfers are
+ transfers from the initiator to the target, while inbound or
+ incoming transfers are from the target to the initiator.
+
+ - ISID: The initiator part of the Session Identifier. It is
+ explicitly specified by the initiator during Login.
+
+ - I_T nexus: According to [SAM2], the I_T nexus is a relationship
+ between a SCSI Initiator Port and a SCSI Target Port. For iSCSI,
+ this relationship is a session, defined as a relationship between
+ an iSCSI Initiator's end of the session (SCSI Initiator Port) and
+ the iSCSI Target's Portal Group. The I_T nexus can be identified
+ by the conjunction of the SCSI port names; that is, the I_T nexus
+ identifier is the tuple (iSCSI Initiator Name + ',i,'+ ISID, iSCSI
+ Target Name + ',t,'+ Portal Group Tag).
+
+ - Network Entity: The Network Entity represents a device or gateway
+ that is accessible from the IP network. A Network Entity must have
+ one or more Network Portals, each of which can be used to gain
+ access to the IP network by some iSCSI Nodes contained in that
+ Network Entity.
+
+ - Network Portal: The Network Portal is a component of a Network
+ Entity that has a TCP/IP network address and that may be used by an
+ iSCSI Node within that Network Entity for the connection(s) within
+ one of its iSCSI sessions. A Network Portal in an initiator is
+ identified by its IP address. A Network Portal in a target is
+ identified by its IP address and its listening TCP port.
+
+
+
+
+Satran, et al. Standards Track [Page 11]
+
+RFC 3720 iSCSI April 2004
+
+
+ - Originator: In a negotiation or exchange, the party that initiates
+ the negotiation or exchange.
+
+ - PDU (Protocol Data Unit): The initiator and target divide their
+ communications into messages. The term "iSCSI protocol data unit"
+ (iSCSI PDU) is used for these messages.
+
+ - Portal Groups: iSCSI supports multiple connections within the same
+ session; some implementations will have the ability to combine
+ connections in a session across multiple Network Portals. A Portal
+ Group defines a set of Network Portals within an iSCSI Network
+ Entity that collectively supports the capability of coordinating a
+ session with connections spanning these portals. Not all Network
+ Portals within a Portal Group need participate in every session
+ connected through that Portal Group. One or more Portal Groups may
+ provide access to an iSCSI Node. Each Network Portal, as utilized
+ by a given iSCSI Node, belongs to exactly one portal group within
+ that node.
+
+ - Portal Group Tag: This 16-bit quantity identifies a Portal Group
+ within an iSCSI Node. All Network Portals with the same portal
+ group tag in the context of a given iSCSI Node are in the same
+ Portal Group.
+
+ - Recovery R2T: An R2T generated by a target upon detecting the loss
+ of one or more Data-Out PDUs through one of the following means: a
+ digest error, a sequence error, or a sequence reception timeout. A
+ recovery R2T carries the next unused R2TSN, but requests all or
+ part of the data burst that an earlier R2T (with a lower R2TSN) had
+ already requested.
+
+ - Responder: In a negotiation or exchange, the party that responds to
+ the originator of the negotiation or exchange.
+
+ - SCSI Device: This is the SAM2 term for an entity that contains one
+ or more SCSI ports that are connected to a service delivery
+ subsystem and supports a SCSI application protocol. For example, a
+ SCSI Initiator Device contains one or more SCSI Initiator Ports and
+ zero or more application clients. A Target Device contains one or
+ more SCSI Target Ports and one or more device servers and
+ associated logical units. For iSCSI, the SCSI Device is the
+ component within an iSCSI Node that provides the SCSI
+ functionality. As such, there can be at most, one SCSI Device
+ within a given iSCSI Node. Access to the SCSI Device can only be
+ achieved in an iSCSI normal operational session. The SCSI Device
+ Name is defined to be the iSCSI Name of the node.
+
+
+
+
+
+Satran, et al. Standards Track [Page 12]
+
+RFC 3720 iSCSI April 2004
+
+
+ - SCSI Layer: This builds/receives SCSI CDBs (Command Descriptor
+ Blocks) and relays/receives them with the remaining command execute
+ [SAM2] parameters to/from the iSCSI Layer.
+
+ - Session: The group of TCP connections that link an initiator with a
+ target form a session (loosely equivalent to a SCSI I-T nexus).
+ TCP connections can be added and removed from a session. Across
+ all connections within a session, an initiator sees one and the
+ same target.
+
+ - SCSI Initiator Port: This maps to the endpoint of an iSCSI normal
+ operational session. An iSCSI normal operational session is
+ negotiated through the login process between an iSCSI initiator
+ node and an iSCSI target node. At successful completion of this
+ process, a SCSI Initiator Port is created within the SCSI Initiator
+ Device. The SCSI Initiator Port Name and SCSI Initiator Port
+ Identifier are both defined to be the iSCSI Initiator Name together
+ with (a) a label that identifies it as an initiator port
+ name/identifier and (b) the ISID portion of the session identifier.
+
+ - SCSI Port: This is the SAM2 term for an entity in a SCSI Device
+ that provides the SCSI functionality to interface with a service
+ delivery subsystem. For iSCSI, the definition of the SCSI
+ Initiator Port and the SCSI Target Port are different.
+
+ - SCSI Port Name: A name made up as UTF-8 [RFC2279] characters and
+ includes the iSCSI Name + 'i' or 't' + ISID or Portal Group Tag.
+
+
+ - SCSI Target Port: This maps to an iSCSI Target Portal Group.
+
+ - SCSI Target Port Name and SCSI Target Port Identifier: These are
+ both defined to be the iSCSI Target Name together with (a) a label
+ that identifies it as a target port name/identifier and (b) the
+ portal group tag.
+
+ - SSID (Session ID): A session between an iSCSI initiator and an
+ iSCSI target is defined by a session ID that is a tuple composed of
+ an initiator part (ISID) and a target part (Target Portal Group
+ Tag). The ISID is explicitly specified by the initiator at session
+ establishment. The Target Portal Group Tag is implied by the
+ initiator through the selection of the TCP endpoint at connection
+ establishment. The TargetPortalGroupTag key must also be returned
+ by the target as a confirmation during connection establishment
+ when TargetName is given.
+
+ - Target Portal Group Tag: A numerical identifier (16-bit) for an
+ iSCSI Target Portal Group.
+
+
+
+Satran, et al. Standards Track [Page 13]
+
+RFC 3720 iSCSI April 2004
+
+
+ - TSIH (Target Session Identifying Handle): A target assigned tag for
+ a session with a specific named initiator. The target generates it
+ during session establishment. Its internal format and content are
+ not defined by this protocol, except for the value 0 that is
+ reserved and used by the initiator to indicate a new session. It
+ is given to the target during additional connection establishment
+ for the same session.
+
+2.2. Acronyms
+
+ Acronym Definition
+ ------------------------------------------------------------
+ 3DES Triple Data Encryption Standard
+ ACA Auto Contingent Allegiance
+ AEN Asynchronous Event Notification
+ AES Advanced Encryption Standard
+ AH Additional Header (not the IPsec AH!)
+ AHS Additional Header Segment
+ API Application Programming Interface
+ ASC Additional Sense Code
+ ASCII American Standard Code for Information Interchange
+ ASCQ Additional Sense Code Qualifier
+ BHS Basic Header Segment
+ CBC Cipher Block Chaining
+ CD Compact Disk
+ CDB Command Descriptor Block
+ CHAP Challenge Handshake Authentication Protocol
+ CID Connection ID
+ CO Connection Only
+ CRC Cyclic Redundancy Check
+ CRL Certificate Revocation List
+ CSG Current Stage
+ CSM Connection State Machine
+ DES Data Encryption Standard
+ DNS Domain Name Server
+ DOI Domain of Interpretation
+ DVD Digital Versatile Disk
+ ESP Encapsulating Security Payload
+ EUI Extended Unique Identifier
+ FFP Full Feature Phase
+ FFPO Full Feature Phase Only
+ FIM Fixed Interval Marker
+ Gbps Gigabits per Second
+ HBA Host Bus Adapter
+ HMAC Hashed Message Authentication Code
+ I_T Initiator_Target
+ I_T_L Initiator_Target_LUN
+ IANA Internet Assigned Numbers Authority
+
+
+
+Satran, et al. Standards Track [Page 14]
+
+RFC 3720 iSCSI April 2004
+
+
+ ID Identifier
+ IDN Internationalized Domain Name
+ IEEE Institute of Electrical & Electronics Engineers
+ IETF Internet Engineering Task Force
+ IKE Internet Key Exchange
+ I/O Input - Output
+ IO Initialize Only
+ IP Internet Protocol
+ IPsec Internet Protocol Security
+ IPv4 Internet Protocol Version 4
+ IPv6 Internet Protocol Version 6
+ IQN iSCSI Qualified Name
+ ISID Initiator Session ID
+ ITN iSCSI Target Name
+ ITT Initiator Task Tag
+ KRB5 Kerberos V5
+ LFL Lower Functional Layer
+ LTDS Logical-Text-Data-Segment
+ LO Leading Only
+ LU Logical Unit
+ LUN Logical Unit Number
+ MAC Message Authentication Codes
+ NA Not Applicable
+ NIC Network Interface Card
+ NOP No Operation
+ NSG Next Stage
+ OS Operating System
+ PDU Protocol Data Unit
+ PKI Public Key Infrastructure
+ R2T Ready To Transfer
+ R2TSN Ready To Transfer Sequence Number
+ RDMA Remote Direct Memory Access
+ RFC Request For Comments
+ SAM SCSI Architecture Model
+ SAM2 SCSI Architecture Model - 2
+ SAN Storage Area Network
+ SCSI Small Computer Systems Interface
+ SN Sequence Number
+ SNACK Selective Negative Acknowledgment - also
+ Sequence Number Acknowledgement for data
+ SPKM Simple Public-Key Mechanism
+ SRP Secure Remote Password
+ SSID Session ID
+ SW Session Wide
+ TCB Task Control Block
+ TCP Transmission Control Protocol
+ TPGT Target Portal Group Tag
+ TSIH Target Session Identifying Handle
+
+
+
+Satran, et al. Standards Track [Page 15]
+
+RFC 3720 iSCSI April 2004
+
+
+ TTT Target Transfer Tag
+ UFL Upper Functional Layer
+ ULP Upper Level Protocol
+ URN Uniform Resource Names [RFC2396]
+ UTF Universal Transformation Format
+ WG Working Group
+
+2.3. Conventions
+
+ In examples, "I->" and "T->" show iSCSI PDUs sent by the initiator
+ and target respectively.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+ iSCSI messages - PDUs - are represented by diagrams as in the
+ following example:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0| Basic Header Segment (BHS) |
+ +---------------+---------------+---------------+---------------+
+ ----------
+ +| |
+ +---------------+---------------+---------------+---------------+
+
+ The diagrams include byte and bit numbering.
+
+ The following representation and ordering rules are observed in this
+ document:
+
+ - Word Rule
+ - Half-word Rule
+ - Byte Rule
+
+2.3.1. Word Rule
+
+ A word holds four consecutive bytes. Whenever a word has numeric
+ content, it is considered an unsigned number in base 2 positional
+ representation with the lowest numbered byte (e.g., byte 0) bit 0
+ representing 2**31 and bit 1 representing 2**30 through lowest
+ numbered byte + 3 (e.g., byte 3) bit 7 representing 2**0.
+
+ Decimal and hexadecimal representation of word values map this
+ representation to decimal or hexadecimal positional notation.
+
+
+
+Satran, et al. Standards Track [Page 16]
+
+RFC 3720 iSCSI April 2004
+
+
+2.3.2. Half-Word Rule
+
+ A half-word holds two consecutive bytes. Whenever a half-word has
+ numeric content it is considered an unsigned number in base 2
+ positional representation with the lowest numbered byte (e.g., byte
+ 0), bit 0 representing 2**15 and bit 1 representing 2**14 through
+ lowest numbered byte + 1 (e.g., byte 1), bit 7 representing 2**0.
+
+ Decimal and hexadecimal representation of half-word values map this
+ representation to decimal or hexadecimal positional notation.
+
+2.3.3. Byte Rule
+
+ For every PDU, bytes are sent and received in increasing numbered
+ order (network order).
+
+ Whenever a byte has numerical content, it is considered an unsigned
+ number in base 2 positional representation with bit 0 representing
+ 2**7 and bit 1 representing 2**6 through bit 7 representing 2**0.
+
+3. Overview
+
+3.1. SCSI Concepts
+
+ The SCSI Architecture Model-2 [SAM2] describes in detail the
+ architecture of the SCSI family of I/O protocols. This section
+ provides a brief background of the SCSI architecture and is intended
+ to familiarize readers with its terminology.
+
+ At the highest level, SCSI is a family of interfaces for requesting
+ services from I/O devices, including hard drives, tape drives, CD and
+ DVD drives, printers, and scanners. In SCSI terminology, an
+ individual I/O device is called a "logical unit" (LU).
+
+ SCSI is a client-server architecture. Clients of a SCSI interface
+ are called "initiators". Initiators issue SCSI "commands" to request
+ services from components, logical units, of a server known as a
+ "target". The "device server" on the logical unit accepts SCSI
+ commands and processes them.
+
+ A "SCSI transport" maps the client-server SCSI protocol to a specific
+ interconnect. Initiators are one endpoint of a SCSI transport. The
+ "target" is the other endpoint. A target can contain multiple
+ Logical Units (LUs). Each Logical Unit has an address within a
+ target called a Logical Unit Number (LUN).
+
+ A SCSI task is a SCSI command or possibly a linked set of SCSI
+ commands. Some LUs support multiple pending (queued) tasks, but the
+
+
+
+Satran, et al. Standards Track [Page 17]
+
+RFC 3720 iSCSI April 2004
+
+
+ queue of tasks is managed by the logical unit. The target uses an
+ initiator provided "task tag" to distinguish between tasks. Only one
+ command in a task can be outstanding at any given time.
+
+ Each SCSI command results in an optional data phase and a required
+ response phase. In the data phase, information can travel from the
+ initiator to target (e.g., WRITE), target to initiator (e.g., READ),
+ or in both directions. In the response phase, the target returns the
+ final status of the operation, including any errors.
+
+ Command Descriptor Blocks (CDB) are the data structures used to
+ contain the command parameters that an initiator sends to a target.
+ The CDB content and structure is defined by [SAM2] and device-type
+ specific SCSI standards.
+
+3.2. iSCSI Concepts and Functional Overview
+
+ The iSCSI protocol is a mapping of the SCSI remote procedure
+ invocation model (see [SAM2]) over the TCP protocol. SCSI commands
+ are carried by iSCSI requests and SCSI responses and status are
+ carried by iSCSI responses. iSCSI also uses the request response
+ mechanism for iSCSI protocol mechanisms.
+
+ For the remainder of this document, the terms "initiator" and
+ "target" refer to "iSCSI initiator node" and "iSCSI target node",
+ respectively (see Section 3.4.1 iSCSI Architecture Model) unless
+ otherwise qualified.
+
+ In keeping with similar protocols, the initiator and target divide
+ their communications into messages. This document uses the term
+ "iSCSI protocol data unit" (iSCSI PDU) for these messages.
+
+ For performance reasons, iSCSI allows a "phase-collapse". A command
+ and its associated data may be shipped together from initiator to
+ target, and data and responses may be shipped together from targets.
+
+ The iSCSI transfer direction is defined with respect to the
+ initiator. Outbound or outgoing transfers are transfers from an
+ initiator to a target, while inbound or incoming transfers are from a
+ target to an initiator.
+
+ An iSCSI task is an iSCSI request for which a response is expected.
+
+ In this document "iSCSI request", "iSCSI command", request, or
+ (unqualified) command have the same meaning. Also, unless otherwise
+ specified, status, response, or numbered response have the same
+ meaning.
+
+
+
+
+Satran, et al. Standards Track [Page 18]
+
+RFC 3720 iSCSI April 2004
+
+
+3.2.1. Layers and Sessions
+
+ The following conceptual layering model is used to specify initiator
+ and target actions and the way in which they relate to transmitted
+ and received Protocol Data Units:
+
+ a) the SCSI layer builds/receives SCSI CDBs (Command Descriptor
+ Blocks) and passes/receives them with the remaining command
+ execute parameters ([SAM2]) to/from
+
+ b) the iSCSI layer that builds/receives iSCSI PDUs and
+ relays/receives them to/from one or more TCP connections; the
+ group of connections form an initiator-target "session".
+
+ Communication between the initiator and target occurs over one or
+ more TCP connections. The TCP connections carry control messages,
+ SCSI commands, parameters, and data within iSCSI Protocol Data Units
+ (iSCSI PDUs). The group of TCP connections that link an initiator
+ with a target form a session (loosely equivalent to a SCSI I_T nexus,
+ see Section 3.4.2 SCSI Architecture Model). A session is defined by
+ a session ID that is composed of an initiator part and a target part.
+ TCP connections can be added and removed from a session. Each
+ connection within a session is identified by a connection ID (CID).
+
+ Across all connections within a session, an initiator sees one
+ "target image". All target identifying elements, such as LUN, are
+ the same. A target also sees one "initiator image" across all
+ connections within a session. Initiator identifying elements, such
+ as the Initiator Task Tag, are global across the session regardless
+ of the connection on which they are sent or received.
+
+ iSCSI targets and initiators MUST support at least one TCP connection
+ and MAY support several connections in a session. For error recovery
+ purposes, targets and initiators that support a single active
+ connection in a session SHOULD support two connections during
+ recovery.
+
+3.2.2. Ordering and iSCSI Numbering
+
+ iSCSI uses Command and Status numbering schemes and a Data sequencing
+ scheme.
+
+ Command numbering is session-wide and is used for ordered command
+ delivery over multiple connections. It can also be used as a
+ mechanism for command flow control over a session.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 19]
+
+RFC 3720 iSCSI April 2004
+
+
+ Status numbering is per connection and is used to enable missing
+ status detection and recovery in the presence of transient or
+ permanent communication errors.
+
+ Data sequencing is per command or part of a command (R2T triggered
+ sequence) and is used to detect missing data and/or R2T PDUs due to
+ header digest errors.
+
+ Typically, fields in the iSCSI PDUs communicate the Sequence Numbers
+ between the initiator and target. During periods when traffic on a
+ connection is unidirectional, iSCSI NOP-Out/In PDUs may be utilized
+ to synchronize the command and status ordering counters of the target
+ and initiator.
+
+ The iSCSI session abstraction is equivalent to the SCSI I_T nexus,
+ and the iSCSI session provides an ordered command delivery from the
+ SCSI initiator to the SCSI target. For detailed design
+ considerations that led to the iSCSI session model as it is defined
+ here and how it relates the SCSI command ordering features defined in
+ SCSI specifications to the iSCSI concepts see [CORD].
+
+3.2.2.1. Command Numbering and Acknowledging
+
+ iSCSI performs ordered command delivery within a session. All
+ commands (initiator-to-target PDUs) in transit from the initiator to
+ the target are numbered.
+
+ iSCSI considers a task to be instantiated on the target in response
+ to every request issued by the initiator. A set of task management
+ operations including abort and reassign (see Section 10.5 Task
+ Management Function Request) may be performed on any iSCSI task.
+
+ Some iSCSI tasks are SCSI tasks, and many SCSI activities are related
+ to a SCSI task ([SAM2]). In all cases, the task is identified by the
+ Initiator Task Tag for the life of the task.
+
+ The command number is carried by the iSCSI PDU as CmdSN
+ (Command Sequence Number). The numbering is session-wide. Outgoing
+ iSCSI PDUs carry this number. The iSCSI initiator allocates CmdSNs
+ with a 32-bit unsigned counter (modulo 2**32). Comparisons and
+ arithmetic on CmdSN use Serial Number Arithmetic as defined in
+ [RFC1982] where SERIAL_BITS = 32.
+
+ Commands meant for immediate delivery are marked with an immediate
+ delivery flag; they MUST also carry the current CmdSN. CmdSN does
+ not advance after a command marked for immediate delivery is sent.
+
+
+
+
+
+Satran, et al. Standards Track [Page 20]
+
+RFC 3720 iSCSI April 2004
+
+
+ Command numbering starts with the first login request on the first
+ connection of a session (the leading login on the leading connection)
+ and command numbers are incremented by 1 for every non-immediate
+ command issued afterwards.
+
+ If immediate delivery is used with task management commands, these
+ commands may reach the target before the tasks on which they are
+ supposed to act. However their CmdSN serves as a marker of their
+ position in the stream of commands. The initiator and target must
+ ensure that the task management commands act as specified by [SAM2].
+ For example, both commands and responses appear as if delivered in
+ order. Whenever CmdSN for an outgoing PDU is not specified by an
+ explicit rule, CmdSN will carry the current value of the local CmdSN
+ variable (see later in this section).
+
+ The means by which an implementation decides to mark a PDU for
+ immediate delivery or by which iSCSI decides by itself to mark a PDU
+ for immediate delivery are beyond the scope of this document.
+
+ The number of commands used for immediate delivery is not limited and
+ their delivery for execution is not acknowledged through the
+ numbering scheme. Immediate commands MAY be rejected by the iSCSI
+ target layer due to a lack of resources. An iSCSI target MUST be
+ able to handle at least one immediate task management command and one
+ immediate non-task-management iSCSI command per connection at any
+ time.
+
+ In this document, delivery for execution means delivery to the SCSI
+ execution engine or an iSCSI protocol specific execution engine
+ (e.g., for text requests with public or private extension keys
+ involving an execution component). With the exception of the
+ commands marked for immediate delivery, the iSCSI target layer MUST
+ deliver the commands for execution in the order specified by CmdSN.
+ Commands marked for immediate delivery may be delivered by the iSCSI
+ target layer for execution as soon as detected. iSCSI may avoid
+ delivering some commands to the SCSI target layer if required by a
+ prior SCSI or iSCSI action (e.g., CLEAR TASK SET Task Management
+ request received before all the commands on which it was supposed to
+ act).
+
+ On any connection, the iSCSI initiator MUST send the commands in
+ increasing order of CmdSN, except for commands that are retransmitted
+ due to digest error recovery and connection recovery.
+
+ For the numbering mechanism, the initiator and target maintain the
+ following three variables for each session:
+
+
+
+
+
+Satran, et al. Standards Track [Page 21]
+
+RFC 3720 iSCSI April 2004
+
+
+ - CmdSN - the current command Sequence Number, advanced by 1 on
+ each command shipped except for commands marked for immediate
+ delivery. CmdSN always contains the number to be assigned to
+ the next Command PDU.
+ - ExpCmdSN - the next expected command by the target. The target
+ acknowledges all commands up to, but not including, this
+ number. The initiator treats all commands with CmdSN less than
+ ExpCmdSN as acknowledged. The target iSCSI layer sets the
+ ExpCmdSN to the largest non-immediate CmdSN that it can deliver
+ for execution plus 1 (no holes in the CmdSN sequence).
+ - MaxCmdSN - the maximum number to be shipped. The queuing
+ capacity of the receiving iSCSI layer is MaxCmdSN - ExpCmdSN +
+ 1.
+
+ The initiator's ExpCmdSN and MaxCmdSN are derived from
+ target-to-initiator PDU fields. Comparisons and arithmetic on
+ ExpCmdSN and MaxCmdSN MUST use Serial Number Arithmetic as defined in
+ [RFC1982] where SERIAL_BITS = 32.
+
+ The target MUST NOT transmit a MaxCmdSN that is less than
+ ExpCmdSN-1. For non-immediate commands, the CmdSN field can take any
+ value from ExpCmdSN to MaxCmdSN inclusive. The target MUST silently
+ ignore any non-immediate command outside of this range or non-
+ immediate duplicates within the range. The CmdSN carried by
+ immediate commands may lie outside the ExpCmdSN to MaxCmdSN range.
+ For example, if the initiator has previously sent a non-immediate
+ command carrying the CmdSN equal to MaxCmdSN, the target window is
+ closed. For group task management commands issued as immediate
+ commands, CmdSN indicates the scope of the group action (e.g., on
+ ABORT TASK SET indicates which commands are aborted).
+
+ MaxCmdSN and ExpCmdSN fields are processed by the initiator as
+ follows:
+
+ - If the PDU MaxCmdSN is less than the PDU ExpCmdSN-1 (in Serial
+ Arithmetic Sense), they are both ignored.
+ - If the PDU MaxCmdSN is greater than the local MaxCmdSN (in
+ Serial Arithmetic Sense), it updates the local MaxCmdSN;
+ otherwise, it is ignored.
+ - If the PDU ExpCmdSN is greater than the local ExpCmdSN (in
+ Serial Arithmetic Sense), it updates the local ExpCmdSN;
+ otherwise, it is ignored.
+
+ This sequence is required because updates may arrive out of order
+ (e.g., the updates are sent on different TCP connections).
+
+ iSCSI initiators and targets MUST support the command numbering
+ scheme.
+
+
+
+Satran, et al. Standards Track [Page 22]
+
+RFC 3720 iSCSI April 2004
+
+
+ A numbered iSCSI request will not change its allocated CmdSN,
+ regardless of the number of times and circumstances in which it is
+ reissued (see Section 6.2.1 Usage of Retry). At the target, CmdSN is
+ only relevant when the command has not created any state related to
+ its execution (execution state); afterwards, CmdSN becomes
+ irrelevant. Testing for the execution state (represented by
+ identifying the Initiator Task Tag) MUST precede any other action at
+ the target. If no execution state is found, it is followed by
+ ordering and delivery. If an execution state is found, it is
+ followed by delivery.
+
+ If an initiator issues a command retry for a command with CmdSN R on
+ a connection when the session CmdSN value is Q, it MUST NOT advance
+ the CmdSN past R + 2**31 -1 unless the connection is no longer
+ operational (i.e., it has returned to the FREE state, see Section
+ 7.1.3 Standard Connection State Diagram for an Initiator), the
+ connection has been reinstated (see Section 5.3.4 Connection
+ Reinstatement), or a non-immediate command with CmdSN equal or
+ greater than Q was issued subsequent to the command retry on the same
+ connection and the reception of that command is acknowledged by the
+ target (see Section 9.4 Command Retry and Cleaning Old Command
+ Instances).
+
+ A target MUST NOT issue a command response or Data-In PDU with status
+ before acknowledging the command. However, the acknowledgement can
+ be included in the response or Data-In PDU.
+
+3.2.2.2. Response/Status Numbering and Acknowledging
+
+ Responses in transit from the target to the initiator are numbered.
+ The StatSN (Status Sequence Number) is used for this purpose. StatSN
+ is a counter maintained per connection. ExpStatSN is used by the
+ initiator to acknowledge status. The status sequence number space is
+ 32-bit unsigned-integers and the arithmetic operations are the
+ regular mod(2**32) arithmetic.
+
+ Status numbering starts with the Login response to the first Login
+ request of the connection. The Login response includes an initial
+ value for status numbering (any initial value is valid).
+
+ To enable command recovery, the target MAY maintain enough state
+ information for data and status recovery after a connection failure.
+ A target doing so can safely discard all of the state information
+ maintained for recovery of a command after the delivery of the status
+ for the command (numbered StatSN) is acknowledged through ExpStatSN.
+
+ A large absolute difference between StatSN and ExpStatSN may indicate
+ a failed connection. Initiators MUST undertake recovery actions if
+
+
+
+Satran, et al. Standards Track [Page 23]
+
+RFC 3720 iSCSI April 2004
+
+
+ the difference is greater than an implementation defined constant
+ that MUST NOT exceed 2**31-1.
+
+ Initiators and Targets MUST support the response-numbering scheme.
+
+3.2.2.3. Data Sequencing
+
+ Data and R2T PDUs transferred as part of some command execution MUST
+ be sequenced. The DataSN field is used for data sequencing. For
+ input (read) data PDUs, DataSN starts with 0 for the first data PDU
+ of an input command and advances by 1 for each subsequent data PDU.
+ For output data PDUs, DataSN starts with 0 for the first data PDU of
+ a sequence (the initial unsolicited sequence or any data PDU sequence
+ issued to satisfy an R2T) and advances by 1 for each subsequent data
+ PDU. R2Ts are also sequenced per command. For example, the first
+ R2T has an R2TSN of 0 and advances by 1 for each subsequent R2T. For
+ bidirectional commands, the target uses the DataSN/R2TSN to sequence
+ Data-In and R2T PDUs in one continuous sequence (undifferentiated).
+ Unlike command and status, data PDUs and R2Ts are not acknowledged by
+ a field in regular outgoing PDUs. Data-In PDUs can be acknowledged
+ on demand by a special form of the SNACK PDU. Data and R2T PDUs are
+ implicitly acknowledged by status for the command. The DataSN/R2TSN
+ field enables the initiator to detect missing data or R2T PDUs.
+
+ For any read or bidirectional command, a target MUST issue less than
+ 2**32 combined R2T and Data-In PDUs. Any output data sequence MUST
+ contain less than 2**32 Data-Out PDUs.
+
+3.2.3. iSCSI Login
+
+ The purpose of the iSCSI login is to enable a TCP connection for
+ iSCSI use, authentication of the parties, negotiation of the
+ session's parameters and marking of the connection as belonging to an
+ iSCSI session.
+
+ A session is used to identify to a target all the connections with a
+ given initiator that belong to the same I_T nexus. (For more details
+ on how a session relates to an I_T nexus, see Section 3.4.2 SCSI
+ Architecture Model).
+
+ The targets listen on a well-known TCP port or other TCP port for
+ incoming connections. The initiator begins the login process by
+ connecting to one of these TCP ports.
+
+ As part of the login process, the initiator and target SHOULD
+ authenticate each other and MAY set a security association protocol
+ for the session. This can occur in many different ways and is
+ subject to negotiation.
+
+
+
+Satran, et al. Standards Track [Page 24]
+
+RFC 3720 iSCSI April 2004
+
+
+ To protect the TCP connection, an IPsec security association MAY be
+ established before the Login request. For information on using IPsec
+ security for iSCSI see Chapter 8 and [RFC3723].
+
+ The iSCSI Login Phase is carried through Login requests and
+ responses. Once suitable authentication has occurred and operational
+ parameters have been set, the session transitions to the Full Feature
+ Phase and the initiator may start to send SCSI commands. The
+ security policy for whether, and by what means, a target chooses to
+ authorize an initiator is beyond the scope of this document. For a
+ more detailed description of the Login Phase, see Chapter 5.
+
+ The login PDU includes the ISID part of the session ID (SSID). The
+ target portal group that services the login is implied by the
+ selection of the connection endpoint. For a new session, the TSIH is
+ zero. As part of the response, the target generates a TSIH.
+
+ During session establishment, the target identifies the SCSI
+ initiator port (the "I" in the "I_T nexus") through the value pair
+ (InitiatorName, ISID). We describe InitiatorName later in this
+ section. Any persistent state (e.g., persistent reservations) on the
+ target that is associated with a SCSI initiator port is identified
+ based on this value pair. Any state associated with the SCSI target
+ port (the "T" in the "I_T nexus") is identified externally by the
+ TargetName and portal group tag (see Section 3.4.1 iSCSI Architecture
+ Model). ISID is subject to reuse restrictions because it is used to
+ identify a persistent state (see Section 3.4.3 Consequences of the
+ Model).
+
+ Before the Full Feature Phase is established, only Login Request and
+ Login Response PDUs are allowed. Login requests and responses MUST
+ be used exclusively during Login. On any connection, the login phase
+ MUST immediately follow TCP connection establishment and a subsequent
+ Login Phase MUST NOT occur before tearing down a connection.
+
+ A target receiving any PDU except a Login request before the Login
+ phase is started MUST immediately terminate the connection on which
+ the PDU was received. Once the Login phase has started, if the
+ target receives any PDU except a Login request, it MUST send a Login
+ reject (with Status "invalid during login") and then disconnect. If
+ the initiator receives any PDU except a Login response, it MUST
+ immediately terminate the connection.
+
+3.2.4. iSCSI Full Feature Phase
+
+ Once the initiator is authorized to do so, the iSCSI session is in
+ the iSCSI Full Feature Phase. A session is in Full Feature Phase
+ after successfully finishing the Login Phase on the first (leading)
+
+
+
+Satran, et al. Standards Track [Page 25]
+
+RFC 3720 iSCSI April 2004
+
+
+ connection of a session. A connection is in Full Feature Phase if
+ the session is in Full Feature Phase and the connection login has
+ completed successfully. An iSCSI connection is not in Full Feature
+ Phase
+
+ a) when it does not have an established transport connection,
+
+ OR
+
+ b) when it has a valid transport connection, but a successful
+ login was not performed or the connection is currently logged
+ out.
+
+ In a normal Full Feature Phase, the initiator may send SCSI commands
+ and data to the various LUs on the target by encapsulating them in
+ iSCSI PDUs that go over the established iSCSI session.
+
+3.2.4.1. Command Connection Allegiance
+
+ For any iSCSI request issued over a TCP connection, the corresponding
+ response and/or other related PDU(s) MUST be sent over the same
+ connection. We call this "connection allegiance". If the original
+ connection fails before the command is completed, the connection
+ allegiance of the command may be explicitly reassigned to a different
+ transport connection as described in detail in Section 6.2 Retry and
+ Reassign in Recovery.
+
+ Thus, if an initiator issues a READ command, the target MUST send the
+ requested data, if any, followed by the status to the initiator over
+ the same TCP connection that was used to deliver the SCSI command.
+ If an initiator issues a WRITE command, the initiator MUST send the
+ data, if any, for that command over the same TCP connection that was
+ used to deliver the SCSI command. The target MUST return Ready To
+ Transfer (R2T), if any, and the status over the same TCP connection
+ that was used to deliver the SCSI command. Retransmission requests
+ (SNACK PDUs) and the data and status that they generate MUST also use
+ the same connection.
+
+ However, consecutive commands that are part of a SCSI linked
+ command-chain task (see [SAM2]) MAY use different connections.
+ Connection allegiance is strictly per-command and not per-task.
+ During the iSCSI Full Feature Phase, the initiator and target MAY
+ interleave unrelated SCSI commands, their SCSI Data, and responses
+ over the session.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 26]
+
+RFC 3720 iSCSI April 2004
+
+
+3.2.4.2. Data Transfer Overview
+
+ Outgoing SCSI data (initiator to target user data or command
+ parameters) is sent as either solicited data or unsolicited data.
+ Solicited data are sent in response to R2T PDUs. Unsolicited data
+ can be sent as part of an iSCSI command PDU ("immediate data") or in
+ separate iSCSI data PDUs.
+
+ Immediate data are assumed to originate at offset 0 in the initiator
+ SCSI write-buffer (outgoing data buffer). All other Data PDUs have
+ the buffer offset set explicitly in the PDU header.
+
+ An initiator may send unsolicited data up to FirstBurstLength as
+ immediate (up to the negotiated maximum PDU length), in a separate
+ PDU sequence or both. All subsequent data MUST be solicited. The
+ maximum length of an individual data PDU or the immediate-part of the
+ first unsolicited burst MAY be negotiated at login.
+
+ The maximum amount of unsolicited data that can be sent with a
+ command is negotiated at login through the FirstBurstLength key. A
+ target MAY separately enable immediate data (through the
+ ImmediateData key) without enabling the more general (separate data
+ PDUs) form of unsolicited data (through the InitialR2T key).
+
+ Unsolicited data on write are meant to reduce the effect of latency
+ on throughput (no R2T is needed to start sending data). In addition,
+ immediate data is meant to reduce the protocol overhead (both
+ bandwidth and execution time).
+
+ An iSCSI initiator MAY choose not to send unsolicited data, only
+ immediate data or FirstBurstLength bytes of unsolicited data with a
+ command. If any non-immediate unsolicited data is sent, the total
+ unsolicited data MUST be either FirstBurstLength, or all of the data
+ if the total amount is less than the FirstBurstLength.
+
+ It is considered an error for an initiator to send unsolicited data
+ PDUs to a target that operates in R2T mode (only solicited data are
+ allowed). It is also an error for an initiator to send more
+ unsolicited data, whether immediate or as separate PDUs, than
+ FirstBurstLength.
+
+ An initiator MUST honor an R2T data request for a valid outstanding
+ command (i.e., carrying a valid Initiator Task Tag) and deliver all
+ the requested data provided the command is supposed to deliver
+ outgoing data and the R2T specifies data within the command bounds.
+ The initiator action is unspecified for receiving an R2T request that
+ specifies data, all or part, outside of the bounds of the command.
+
+
+
+
+Satran, et al. Standards Track [Page 27]
+
+RFC 3720 iSCSI April 2004
+
+
+ A target SHOULD NOT silently discard data and then request
+ retransmission through R2T. Initiators SHOULD NOT keep track of the
+ data transferred to or from the target (scoreboarding). SCSI targets
+ perform residual count calculation to check how much data was
+ actually transferred to or from the device by a command. This may
+ differ from the amount the initiator sent and/or received for reasons
+ such as retransmissions and errors. Read or bidirectional commands
+ implicitly solicit the transmission of the entire amount of data
+ covered by the command. SCSI data packets are matched to their
+ corresponding SCSI commands by using tags specified in the protocol.
+
+ In addition, iSCSI initiators and targets MUST enforce some ordering
+ rules. When unsolicited data is used, the order of the unsolicited
+ data on each connection MUST match the order in which the commands on
+ that connection are sent. Command and unsolicited data PDUs may be
+ interleaved on a single connection as long as the ordering
+ requirements of each are maintained (e.g., command N+1 MAY be sent
+ before the unsolicited Data-Out PDUs for command N, but the
+ unsolicited Data-Out PDUs for command N MUST precede the unsolicited
+ Data-Out PDUs of command N+1). A target that receives data out of
+ order MAY terminate the session.
+
+3.2.4.3. Tags and Integrity Checks
+
+ Initiator tags for pending commands are unique initiator-wide for a
+ session. Target tags are not strictly specified by the protocol. It
+ is assumed that target tags are used by the target to tag (alone or
+ in combination with the LUN) the solicited data. Target tags are
+ generated by the target and "echoed" by the initiator. These
+ mechanisms are designed to accomplish efficient data delivery along
+ with a large degree of control over the data flow.
+
+ As the Initiator Task Tag is used to identify a task during its
+ execution, the iSCSI initiator and target MUST verify that all other
+ fields used in task-related PDUs have values that are consistent with
+ the values used at the task instantiation based on the Initiator Task
+ Tag (e.g., the LUN used in an R2T PDU MUST be the same as the one
+ used in the SCSI command PDU used to instantiate the task). Using
+ inconsistent field values is considered a protocol error.
+
+3.2.4.4. Task Management
+
+ SCSI task management assumes that individual tasks and task groups
+ can be aborted solely based on the task tags (for individual tasks)
+ or the timing of the task management command (for task groups), and
+ that the task management action is executed synchronously - i.e., no
+ message involving an aborted task will be seen by the SCSI initiator
+ after receiving the task management response. In iSCSI initiators
+
+
+
+Satran, et al. Standards Track [Page 28]
+
+RFC 3720 iSCSI April 2004
+
+
+ and targets interact asynchronously over several connections. iSCSI
+ specifies the protocol mechanism and implementation requirements
+ needed to present a synchronous view while using an asynchronous
+ infrastructure.
+
+3.2.5. iSCSI Connection Termination
+
+ An iSCSI connection may be terminated by use of a transport
+ connection shutdown or a transport reset. Transport reset is assumed
+ to be an exceptional event.
+
+ Graceful TCP connection shutdowns are done by sending TCP FINs. A
+ graceful transport connection shutdown SHOULD only be initiated by
+ either party when the connection is not in iSCSI Full Feature Phase.
+ A target MAY terminate a Full Feature Phase connection on internal
+ exception events, but it SHOULD announce the fact through an
+ Asynchronous Message PDU. Connection termination with outstanding
+ commands may require recovery actions.
+
+ If a connection is terminated while in Full Feature Phase, connection
+ cleanup (see section 7) is required prior to recovery. By doing
+ connection cleanup before starting recovery, the initiator and target
+ will avoid receiving stale PDUs after recovery.
+
+3.2.6. iSCSI Names
+
+ Both targets and initiators require names for the purpose of
+ identification. In addition, names enable iSCSI storage resources to
+ be managed regardless of location (address). An iSCSI node name is
+ also the SCSI device name of an iSCSI device. The iSCSI name of a
+ SCSI device is the principal object used in authentication of targets
+ to initiators and initiators to targets. This name is also used to
+ identify and manage iSCSI storage resources.
+
+ iSCSI names must be unique within the operational domain of the end
+ user. However, because the operational domain of an IP network is
+ potentially worldwide, the iSCSI name formats are architected to be
+ worldwide unique. To assist naming authorities in the construction
+ of worldwide unique names, iSCSI provides two name formats for
+ different types of naming authorities.
+
+ iSCSI names are associated with iSCSI nodes, and not iSCSI network
+ adapter cards, to ensure that the replacement of network adapter
+ cards does not require reconfiguration of all SCSI and iSCSI resource
+ allocation information.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 29]
+
+RFC 3720 iSCSI April 2004
+
+
+ Some SCSI commands require that protocol-specific identifiers be
+ communicated within SCSI CDBs. See Section 3.4.2 SCSI Architecture
+ Model for the definition of the SCSI port name/identifier for iSCSI
+ ports.
+
+ An initiator may discover the iSCSI Target Names to which it has
+ access, along with their addresses, using the SendTargets text
+ request, or other techniques discussed in [RFC3721].
+
+3.2.6.1. iSCSI Name Properties
+
+ Each iSCSI node, whether an initiator or target, MUST have an iSCSI
+ name.
+
+ Initiators and targets MUST support the receipt of iSCSI names of up
+ to the maximum length of 223 bytes.
+
+ The initiator MUST present both its iSCSI Initiator Name and the
+ iSCSI Target Name to which it wishes to connect in the first login
+ request of a new session or connection. The only exception is if a
+ discovery session (see Section 2.3 iSCSI Session Types) is to be
+ established. In this case, the iSCSI Initiator Name is still
+ required, but the iSCSI Target Name MAY be omitted.
+
+ iSCSI names have the following properties:
+
+ a) iSCSI names are globally unique. No two initiators or targets
+ can have the same name.
+ b) iSCSI names are permanent. An iSCSI initiator node or target
+ node has the same name for its lifetime.
+ c) iSCSI names do not imply a location or address. An iSCSI
+ initiator or target can move, or have multiple addresses. A
+ change of address does not imply a change of name.
+ d) iSCSI names do not rely on a central name broker; the naming
+ authority is distributed.
+ e) iSCSI names support integration with existing unique naming
+ schemes.
+ f) iSCSI names rely on existing naming authorities. iSCSI does
+ not create any new naming authority.
+
+ The encoding of an iSCSI name has the following properties:
+
+ a) iSCSI names have the same encoding method regardless of the
+ underlying protocols.
+ b) iSCSI names are relatively simple to compare. The algorithm
+ for comparing two iSCSI names for equivalence does not rely on
+ an external server.
+
+
+
+
+Satran, et al. Standards Track [Page 30]
+
+RFC 3720 iSCSI April 2004
+
+
+ c) iSCSI names are composed only of displayable characters. iSCSI
+ names allow the use of international character sets but are not
+ case sensitive. No whitespace characters are used in iSCSI
+ names.
+ d) iSCSI names may be transported using both binary and
+ ASCII-based protocols.
+
+ An iSCSI name really names a logical software entity, and is not tied
+ to a port or other hardware that can be changed. For instance, an
+ initiator name should name the iSCSI initiator node, not a particular
+ NIC or HBA. When multiple NICs are used, they should generally all
+ present the same iSCSI initiator name to the targets, because they
+ are simply paths to the same SCSI layer. In most operating systems,
+ the named entity is the operating system image.
+
+ Similarly, a target name should not be tied to hardware interfaces
+ that can be changed. A target name should identify the logical
+ target and must be the same for the target regardless of the physical
+ portion being addressed. This assists iSCSI initiators in
+ determining that the two targets it has discovered are really two
+ paths to the same target.
+
+ The iSCSI name is designed to fulfill the functional requirements for
+ Uniform Resource Names (URN) [RFC1737]. For example, it is required
+ that the name have a global scope, be independent of address or
+ location, and be persistent and globally unique. Names must be
+ extensible and scalable with the use of naming authorities. The name
+ encoding should be both human and machine readable. See [RFC1737]
+ for further requirements.
+
+3.2.6.2. iSCSI Name Encoding
+
+ An iSCSI name MUST be a UTF-8 encoding of a string of Unicode
+ characters with the following properties:
+
+ - It is in Normalization Form C (see "Unicode Normalization
+ Forms" [UNICODE]).
+ - It only contains characters allowed by the output of the iSCSI
+ stringprep template (described in [RFC3722]).
+ - The following characters are used for formatting iSCSI names:
+
+ - dash ('-'=U+002d)
+ - dot ('.'=U+002e)
+ - colon (':'=U+003a)
+
+ - The UTF-8 encoding of the name is not larger than 223 bytes.
+
+
+
+
+
+Satran, et al. Standards Track [Page 31]
+
+RFC 3720 iSCSI April 2004
+
+
+ The stringprep process is described in [RFC3454]; iSCSI's use of the
+ stringprep process is described in [RFC3722]. Stringprep is a method
+ designed by the Internationalized Domain Name (IDN) working group to
+ translate human-typed strings into a format that can be compared as
+ opaque strings. Strings MUST NOT include punctuation, spacing,
+ diacritical marks, or other characters that could get in the way of
+ readability. The stringprep process also converts strings into
+ equivalent strings of lower-case characters.
+
+ The stringprep process does not need to be implemented if the names
+ are only generated using numeric and lower-case (any character set)
+ alphabetic characters.
+
+ Once iSCSI names encoded in UTF-8 are "normalized" they may be safely
+ compared byte-for-byte.
+
+3.2.6.3. iSCSI Name Structure
+
+ An iSCSI name consists of two parts--a type designator followed by a
+ unique name string.
+
+ The iSCSI name does not define any new naming authorities. Instead,
+ it supports two existing ways of designating naming authorities: an
+ iSCSI-Qualified Name, using domain names to identify a naming
+ authority, and the EUI format, where the IEEE Registration Authority
+ assists in the formation of worldwide unique names (EUI-64 format).
+
+ The type designator strings currently defined are:
+
+ iqn. - iSCSI Qualified name
+ eui. - Remainder of the string is an IEEE EUI-64
+ identifier, in ASCII-encoded hexadecimal.
+
+ These two naming authority designators were considered sufficient at
+ the time of writing this document. The creation of additional naming
+ type designators for iSCSI may be considered by the IETF and detailed
+ in separate RFCs.
+
+3.2.6.3.1. Type "iqn." (iSCSI Qualified Name)
+
+ This iSCSI name type can be used by any organization that owns a
+ domain name. This naming format is useful when an end user or
+ service provider wishes to assign iSCSI names for targets and/or
+ initiators.
+
+ To generate names of this type, the person or organization generating
+ the name must own a registered domain name. This domain name does
+ not have to be active, and does not have to resolve to an address; it
+
+
+
+Satran, et al. Standards Track [Page 32]
+
+RFC 3720 iSCSI April 2004
+
+
+ just needs to be reserved to prevent others from generating iSCSI
+ names using the same domain name.
+
+ Since a domain name can expire, be acquired by another entity, or may
+ be used to generate iSCSI names by both owners, the domain name must
+ be additionally qualified by a date during which the naming authority
+ owned the domain name. For this reason, a date code is provided as
+ part of the "iqn." format.
+
+ The iSCSI qualified name string consists of:
+
+ - The string "iqn.", used to distinguish these names from "eui."
+ formatted names.
+ - A date code, in yyyy-mm format. This date MUST be a date
+ during which the naming authority owned the domain name used in
+ this format, and SHOULD be the first month in which the domain
+ name was owned by this naming authority at 00:01 GMT of the
+ first day of the month. This date code uses the Gregorian
+ calendar. All four digits in the year must be present. Both
+ digits of the month must be present, with January == "01" and
+ December == "12". The dash must be included.
+ - A dot "."
+ - The reversed domain name of the naming authority (person or
+ organization) creating this iSCSI name.
+ - An optional, colon (:) prefixed, string within the character
+ set and length boundaries that the owner of the domain name
+ deems appropriate. This may contain product types, serial
+ numbers, host identifiers, or software keys (e.g., it may
+ include colons to separate organization boundaries). With the
+ exception of the colon prefix, the owner of the domain name can
+ assign everything after the reversed domain name as desired.
+ It is the responsibility of the entity that is the naming
+ authority to ensure that the iSCSI names it assigns are
+ worldwide unique. For example, "Example Storage Arrays, Inc.",
+ might own the domain name "example.com".
+
+ The following are examples of iSCSI qualified names that might be
+ generated by "EXAMPLE Storage Arrays, Inc."
+
+ Naming String defined by
+ Type Date Auth "example.com" naming authority
+ +--++-----+ +---------+ +--------------------------------+
+ | || | | | | |
+
+ iqn.2001-04.com.example:storage:diskarrays-sn-a8675309
+ iqn.2001-04.com.example
+ iqn.2001-04.com.example:storage.tape1.sys1.xyz
+ iqn.2001-04.com.example:storage.disk2.sys1.xyz
+
+
+
+Satran, et al. Standards Track [Page 33]
+
+RFC 3720 iSCSI April 2004
+
+
+
+3.2.6.3.2. Type "eui." (IEEE EUI-64 format)
+
+ The IEEE Registration Authority provides a service for assigning
+ globally unique identifiers [EUI]. The EUI-64 format is used to
+ build a global identifier in other network protocols. For example,
+ Fibre Channel defines a method of encoding it into a WorldWideName.
+ For more information on registering for EUI identifiers, see [OUI].
+
+ The format is "eui." followed by an EUI-64 identifier (16
+ ASCII-encoded hexadecimal digits).
+
+ Example iSCSI name:
+
+ Type EUI-64 identifier (ASCII-encoded hexadecimal)
+ +--++--------------+
+ | || |
+ eui.02004567A425678D
+
+ The IEEE EUI-64 iSCSI name format might be used when a manufacturer
+ is already registered with the IEEE Registration Authority and uses
+ EUI-64 formatted worldwide unique names for its products.
+
+ More examples of name construction are discussed in [RFC3721].
+
+3.2.7. Persistent State
+
+ iSCSI does not require any persistent state maintenance across
+ sessions. However, in some cases, SCSI requires persistent
+ identification of the SCSI initiator port name (See Section 3.4.2
+ SCSI Architecture Model and Section 3.4.3 Consequences of the Model).
+
+ iSCSI sessions do not persist through power cycles and boot
+ operations.
+
+ All iSCSI session and connection parameters are re-initialized upon
+ session and connection creation.
+
+ Commands persist beyond connection termination if the session
+ persists and command recovery within the session is supported.
+ However, when a connection is dropped, command execution, as
+ perceived by iSCSI (i.e., involving iSCSI protocol exchanges for the
+ affected task), is suspended until a new allegiance is established by
+ the 'task reassign' task management function. (See Section 10.5 Task
+ Management Function Request.)
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 34]
+
+RFC 3720 iSCSI April 2004
+
+
+3.2.8. Message Synchronization and Steering
+
+ iSCSI presents a mapping of the SCSI protocol onto TCP. This
+ encapsulation is accomplished by sending iSCSI PDUs of varying
+ lengths. Unfortunately, TCP does not have a built-in mechanism for
+ signaling message boundaries at the TCP layer. iSCSI overcomes this
+ obstacle by placing the message length in the iSCSI message header.
+ This serves to delineate the end of the current message as well as
+ the beginning of the next message.
+
+ In situations where IP packets are delivered in order from the
+ network, iSCSI message framing is not an issue and messages are
+ processed one after the other. In the presence of IP packet
+ reordering (i.e., frames being dropped), legacy TCP implementations
+ store the "out of order" TCP segments in temporary buffers until the
+ missing TCP segments arrive, upon which the data must be copied to
+ the application buffers. In iSCSI, it is desirable to steer the SCSI
+ data within these out of order TCP segments into the pre-allocated
+ SCSI buffers rather than store them in temporary buffers. This
+ decreases the need for dedicated reassembly buffers as well as the
+ latency and bandwidth related to extra copies.
+
+ Relying solely on the "message length" information from the iSCSI
+ message header may make it impossible to find iSCSI message
+ boundaries in subsequent TCP segments due to the loss of a TCP
+ segment that contains the iSCSI message length. The missing TCP
+ segment(s) must be received before any of the following segments can
+ be steered to the correct SCSI buffers (due to the inability to
+ determine the iSCSI message boundaries). Since these segments cannot
+ be steered to the correct location, they must be saved in temporary
+ buffers that must then be copied to the SCSI buffers.
+
+ Different schemes can be used to recover synchronization. To make
+ these schemes work, iSCSI implementations have to make sure that the
+ appropriate protocol layers are provided with enough information to
+ implement a synchronization and/or data steering mechanism. One of
+ these schemes is detailed in Appendix A. - Sync and Steering with
+ Fixed Interval Markers -.
+
+ The Fixed Interval Markers (FIM) scheme works by inserting markers in
+ the payload stream at fixed intervals that contain the offset for the
+ start of the next iSCSI PDU.
+
+ Under normal circumstances (no PDU loss or data reception out of
+ order), iSCSI data steering can be accomplished by using the
+ identifying tag and the data offset fields in the iSCSI header in
+ addition to the TCP sequence number from the TCP header. The
+
+
+
+
+Satran, et al. Standards Track [Page 35]
+
+RFC 3720 iSCSI April 2004
+
+
+ identifying tag helps associate the PDU with a SCSI buffer address
+ while the data offset and TCP sequence number are used to determine
+ the offset within the buffer.
+
+ When the part of the TCP data stream containing an iSCSI PDU header
+ is delayed or lost, markers may be used to minimize the damage as
+ follows:
+
+ - Markers indicate where the next iSCSI PDU starts and enable
+ continued processing when iSCSI headers have to be dropped due to
+ data errors discovered at the iSCSI level (e.g., iSCSI header CRC
+ errors).
+
+ - Markers help minimize the amount of data that has to be kept by
+ the TCP/iSCSI layer while waiting for a late TCP packet arrival
+ or recovery, because later they might help find iSCSI PDU headers
+ and use the information contained in those to steer data to SCSI
+ buffers.
+
+3.2.8.1. Sync/Steering and iSCSI PDU Length
+
+ When a large iSCSI message is sent, the TCP segment(s) that contain
+ the iSCSI header may be lost. The remaining TCP segment(s), up to
+ the next iSCSI message, must be buffered (in temporary buffers)
+ because the iSCSI header that indicates to which SCSI buffers the
+ data are to be steered was lost. To minimize the amount of
+ buffering, it is recommended that the iSCSI PDU length be restricted
+ to a small value (perhaps a few TCP segments in length). During
+ login, each end of the iSCSI session specifies the maximum iSCSI PDU
+ length it will accept.
+
+3.3. iSCSI Session Types
+
+ iSCSI defines two types of sessions:
+
+ a) Normal operational session - an unrestricted session.
+ b) Discovery-session - a session only opened for target
+ discovery. The target MUST ONLY accept text requests with the
+ SendTargets key and a logout request with the reason "close
+ the session". All other requests MUST be rejected.
+
+ The session type is defined during login with the key=value parameter
+ in the login command.
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 36]
+
+RFC 3720 iSCSI April 2004
+
+
+3.4. SCSI to iSCSI Concepts Mapping Model
+
+ The following diagram shows an example of how multiple iSCSI Nodes
+ (targets in this case) can coexist within the same Network Entity and
+ can share Network Portals (IP addresses and TCP ports). Other more
+ complex configurations are also possible. For detailed descriptions
+ of the components of these diagrams, see Section 3.4.1 iSCSI
+ Architecture Model.
+
+ +-----------------------------------+
+ | Network Entity (iSCSI Client) |
+ | |
+ | +-------------+ |
+ | | iSCSI Node | |
+ | | (Initiator) | |
+ | +-------------+ |
+ | | | |
+ | +--------------+ +--------------+ |
+ | |Network Portal| |Network Portal| |
+ | | 10.1.30.4 | | 10.1.40.6 | |
+ +-+--------------+-+--------------+-+
+ | |
+ | IP Networks |
+ | |
+ +-+--------------+-+--------------+-+
+ | |Network Portal| |Network Portal| |
+ | | 10.1.30.21 | | 10.1.40.3 | |
+ | | TCP Port 3260| | TCP Port 3260| |
+ | +--------------+ +--------------+ |
+ | | | |
+ | ----------------- |
+ | | | |
+ | +-------------+ +--------------+ |
+ | | iSCSI Node | | iSCSI Node | |
+ | | (Target) | | (Target) | |
+ | +-------------+ +--------------+ |
+ | |
+ | Network Entity (iSCSI Server) |
+ +-----------------------------------+
+
+3.4.1. iSCSI Architecture Model
+
+ This section describes the part of the iSCSI architecture model that
+ has the most bearing on the relationship between iSCSI and the SCSI
+ Architecture Model.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 37]
+
+RFC 3720 iSCSI April 2004
+
+
+ a) Network Entity - represents a device or gateway that is
+ accessible from the IP network. A Network Entity must have
+ one or more Network Portals (see item d), each of which can be
+ used by some iSCSI Nodes (see item (b)) contained in that
+ Network Entity to gain access to the IP network.
+
+ b) iSCSI Node - represents a single iSCSI initiator or iSCSI
+ target. There are one or more iSCSI Nodes within a Network
+ Entity. The iSCSI Node is accessible via one or more Network
+ Portals (see item d). An iSCSI Node is identified by its
+ iSCSI Name (see Section 3.2.6 iSCSI Names and Chapter 12).
+ The separation of the iSCSI Name from the addresses used by
+ and for the iSCSI node allows multiple iSCSI nodes to use the
+ same addresses, and the same iSCSI node to use multiple
+ addresses.
+
+ c) An alias string may also be associated with an iSCSI Node.
+ The alias allows an organization to associate a user friendly
+ string with the iSCSI Name. However, the alias string is not
+ a substitute for the iSCSI Name.
+
+ d) Network Portal - a component of a Network Entity that has a
+ TCP/IP network address and that may be used by an iSCSI Node
+ within that Network Entity for the connection(s) within one of
+ its iSCSI sessions. In an initiator, it is identified by its
+ IP address. In a target, it is identified by its IP address
+ and its listening TCP port.
+
+ e) Portal Groups - iSCSI supports multiple connections within the
+ same session; some implementations will have the ability to
+ combine connections in a session across multiple Network
+ Portals. A Portal Group defines a set of Network Portals
+ within an iSCSI Node that collectively supports the capability
+ of coordinating a session with connections that span these
+ portals. Not all Network Portals within a Portal Group need
+ to participate in every session connected through that Portal
+ Group. One or more Portal Groups may provide access to an
+ iSCSI Node. Each Network Portal, as utilized by a given iSCSI
+ Node, belongs to exactly one portal group within that node.
+ Portal Groups are identified within an iSCSI Node by a portal
+ group tag, a simple unsigned-integer between 0 and 65535 (see
+ Section 12.3 SendTargets). All Network Portals with the same
+ portal group tag in the context of a given iSCSI Node are in
+ the same Portal Group.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 38]
+
+RFC 3720 iSCSI April 2004
+
+
+ Both iSCSI Initiators and iSCSI Targets have portal groups,
+ though only the iSCSI Target Portal Groups are used directly
+ in the iSCSI protocol (e.g., in SendTargets). For references
+ to the initiator Portal Groups, see Section 9.1.1 Conservative
+ Reuse of ISIDs.
+
+ f) Portals within a Portal Group should support similar session
+ parameters, because they may participate in a common session.
+
+ The following diagram shows an example of one such configuration on a
+ target and how a session that shares Network Portals within a Portal
+ Group may be established.
+
+ ----------------------------IP Network---------------------
+ | | |
+ +----|---------------|-----+ +----|---------+
+ | +---------+ +---------+ | | +---------+ |
+ | | Network | | Network | | | | Network | |
+ | | Portal | | Portal | | | | Portal | |
+ | +--|------+ +---------+ | | +---------+ |
+ | | | | | | |
+ | | Portal | | | | Portal |
+ | | Group 1 | | | | Group 2 |
+ +--------------------------+ +--------------+
+ | | |
+ +--------|---------------|--------------------|--------------------+
+ | | | | |
+ | +----------------------------+ +-----------------------------+ |
+ | | iSCSI Session (Target side)| | iSCSI Session (Target side) | |
+ | | | | | |
+ | | (TSIH = 56) | | (TSIH = 48) | |
+ | +----------------------------+ +-----------------------------+ |
+ | |
+ | iSCSI Target Node |
+ | (within Network Entity, not shown) |
+ +------------------------------------------------------------------+
+
+3.4.2. SCSI Architecture Model
+
+ This section describes the relationship between the SCSI Architecture
+ Model [SAM2] and the constructs of the SCSI device, SCSI port and I_T
+ nexus, and the iSCSI constructs described in Section 3.4.1 iSCSI
+ Architecture Model.
+
+ This relationship implies implementation requirements in order to
+ conform to the SAM2 model and other SCSI operational functions.
+ These requirements are detailed in Section 3.4.3 Consequences of the
+ Model.
+
+
+
+Satran, et al. Standards Track [Page 39]
+
+RFC 3720 iSCSI April 2004
+
+
+ The following list outlines mappings of SCSI architectural elements
+ to iSCSI.
+
+ a) SCSI Device - the SAM2 term for an entity that contains one or
+ more SCSI ports that are connected to a service delivery
+ subsystem and supports a SCSI application protocol. For
+ example, a SCSI Initiator Device contains one or more SCSI
+ Initiator Ports and zero or more application clients. A SCSI
+ Target Device contains one or more SCSI Target Ports and one
+ or more logical units. For iSCSI, the SCSI Device is the
+ component within an iSCSI Node that provides the SCSI
+ functionality. As such, there can be one SCSI Device, at
+ most, within an iSCSI Node. Access to the SCSI Device can
+ only be achieved in an iSCSI normal operational session (see
+ Section 3.3 iSCSI Session Types). The SCSI Device Name is
+ defined to be the iSCSI Name of the node and MUST be used in
+ the iSCSI protocol.
+
+ b) SCSI Port - the SAM2 term for an entity in a SCSI Device that
+ provides the SCSI functionality to interface with a service
+ delivery subsystem or transport. For iSCSI, the definition of
+ SCSI Initiator Port and SCSI Target Port are different.
+
+ SCSI Initiator Port: This maps to one endpoint of an iSCSI
+ normal operational session (see Section 3.3 iSCSI Session
+ Types). An iSCSI normal operational session is negotiated
+ through the login process between an iSCSI initiator node and
+ an iSCSI target node. At successful completion of this
+ process, a SCSI Initiator Port is created within the SCSI
+ Initiator Device. The SCSI Initiator Port Name and SCSI
+ Initiator Port Identifier are both defined to be the iSCSI
+ Initiator Name together with (a) a label that identifies it as
+ an initiator port name/identifier and (b) the ISID portion of
+ the session identifier.
+
+ SCSI Target Port: This maps to an iSCSI Target Portal Group.
+ The SCSI Target Port Name and the SCSI Target Port Identifier
+ are both defined to be the iSCSI Target Name together with (a)
+ a label that identifies it as a target port name/identifier
+ and (b) the portal group tag.
+
+ The SCSI Port Name MUST be used in iSCSI. When used in SCSI
+ parameter data, the SCSI port name MUST be encoded as:
+ - The iSCSI Name in UTF-8 format, followed by
+ - a comma separator (1 byte), followed by
+ - the ASCII character 'i' (for SCSI Initiator Port) or the
+ ASCII character 't' (for SCSI Target Port) (1 byte),
+ followed by
+
+
+
+Satran, et al. Standards Track [Page 40]
+
+RFC 3720 iSCSI April 2004
+
+
+ - a comma separator (1 byte), followed by
+ - a text encoding as a hex-constant (see Section 5.1 Text
+ Format) of the ISID (for SCSI initiator port) or the portal
+ group tag (for SCSI target port) including the initial 0X
+ or 0x and the terminating null (15 bytes).
+
+ The ASCII character 'i' or 't' is the label that identifies
+ this port as either a SCSI Initiator Port or a SCSI Target
+ Port.
+
+ c) I_T nexus - a relationship between a SCSI Initiator Port and a
+ SCSI Target Port, according to [SAM2]. For iSCSI, this
+ relationship is a session, defined as a relationship between
+ an iSCSI Initiator's end of the session (SCSI Initiator Port)
+ and the iSCSI Target's Portal Group. The I_T nexus can be
+ identified by the conjunction of the SCSI port names or by the
+ iSCSI session identifier SSID. iSCSI defines the I_T nexus
+ identifier to be the tuple (iSCSI Initiator Name + 'i' + ISID,
+ iSCSI Target Name + 't' + Portal Group Tag).
+
+ NOTE: The I_T nexus identifier is not equal to the session
+ identifier (SSID).
+
+3.4.3. Consequences of the Model
+
+ This section describes implementation and behavioral requirements
+ that result from the mapping of SCSI constructs to the iSCSI
+ constructs defined above. Between a given SCSI initiator port and a
+ given SCSI target port, only one I_T nexus (session) can exist. No
+ more than one nexus relationship (parallel nexus) is allowed by
+ [SAM2]. Therefore, at any given time, only one session can exist
+ between a given iSCSI initiator node and an iSCSI target node, with
+ the same session identifier (SSID).
+
+ These assumptions lead to the following conclusions and requirements:
+
+ ISID RULE: Between a given iSCSI Initiator and iSCSI Target Portal
+ Group (SCSI target port), there can only be one session with a given
+ value for ISID that identifies the SCSI initiator port. See Section
+ 10.12.5 ISID.
+
+ The structure of the ISID that contains a naming authority component
+ (see Section 10.12.5 ISID and [RFC3721]) provides a mechanism to
+ facilitate compliance with the ISID rule. (See Section 9.1.1
+ Conservative Reuse of ISIDs.)
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 41]
+
+RFC 3720 iSCSI April 2004
+
+
+ The iSCSI Initiator Node should manage the assignment of ISIDs prior
+ to session initiation. The "ISID RULE" does not preclude the use of
+ the same ISID from the same iSCSI Initiator with different Target
+ Portal Groups on the same iSCSI target or on other iSCSI targets (see
+ Section 9.1.1 Conservative Reuse of ISIDs). Allowing this would be
+ analogous to a single SCSI Initiator Port having relationships
+ (nexus) with multiple SCSI target ports on the same SCSI target
+ device or SCSI target ports on other SCSI target devices. It is also
+ possible to have multiple sessions with different ISIDs to the same
+ Target Portal Group. Each such session would be considered to be
+ with a different initiator even when the sessions originate from the
+ same initiator device. The same ISID may be used by a different
+ iSCSI initiator because it is the iSCSI Name together with the ISID
+ that identifies the SCSI Initiator Port.
+
+ NOTE: A consequence of the ISID RULE and the specification for the
+ I_T nexus identifier is that two nexus with the same identifier
+ should never exist at the same time.
+
+ TSIH RULE: The iSCSI Target selects a non-zero value for the TSIH at
+ session creation (when an initiator presents a 0 value at Login).
+ After being selected, the same TSIH value MUST be used whenever the
+ initiator or target refers to the session and a TSIH is required.
+
+3.4.3.1. I_T Nexus State
+
+ Certain nexus relationships contain an explicit state (e.g.,
+ initiator-specific mode pages) that may need to be preserved by the
+ device server [SAM2] in a logical unit through changes or failures in
+ the iSCSI layer (e.g., session failures). In order for that state to
+ be restored, the iSCSI initiator should reestablish its session
+ (re-login) to the same Target Portal Group using the previous ISID.
+ That is, it should perform session recovery as described in Chapter
+ 6. This is because the SCSI initiator port identifier and the SCSI
+ target port identifier (or relative target port) form the datum that
+ the SCSI logical unit device server uses to identify the I_T nexus.
+
+3.5. Request/Response Summary
+
+ This section lists and briefly describes all the iSCSI PDU types
+ (request and responses).
+
+ All iSCSI PDUs are built as a set of one or more header segments
+ (basic and auxiliary) and zero or one data segments. The header
+ group and the data segment may each be followed by a CRC (digest).
+
+ The basic header segment has a fixed length of 48 bytes.
+
+
+
+
+Satran, et al. Standards Track [Page 42]
+
+RFC 3720 iSCSI April 2004
+
+
+3.5.1. Request/Response Types Carrying SCSI Payload
+
+3.5.1.1. SCSI-Command
+
+ This request carries the SCSI CDB and all the other SCSI execute
+ command procedure call (see [SAM2]) IN arguments such as task
+ attributes, Expected Data Transfer Length for one or both transfer
+ directions (the latter for bidirectional commands), and Task Tag (as
+ part of the I_T_L_x nexus). The I_T_L nexus is derived by the
+ initiator and target from the LUN field in the request and the I_T
+ nexus is implicit in the session identification.
+
+ In addition, the SCSI-command PDU carries information required for
+ the proper operation of the iSCSI protocol - the command sequence
+ number (CmdSN) for the session and the expected status number
+ (ExpStatSN) for the connection.
+
+ All or part of the SCSI output (write) data associated with the SCSI
+ command may be sent as part of the SCSI-Command PDU as a data
+ segment.
+
+3.5.1.2. SCSI-Response
+
+ The SCSI-Response carries all the SCSI execute-command procedure call
+ (see [SAM2]) OUT arguments and the SCSI execute-command procedure
+ call return value.
+
+ The SCSI-Response contains the residual counts from the operation, if
+ any, an indication of whether the counts represent an overflow or an
+ underflow, and the SCSI status if the status is valid or a response
+ code (a non-zero return value for the execute-command procedure call)
+ if the status is not valid.
+
+ For a valid status that indicates that the command has been
+ processed, but resulted in an exception (e.g., a SCSI CHECK
+ CONDITION), the PDU data segment contains the associated sense data.
+ The use of Autosense ([SAM2]) is REQUIRED by iSCSI.
+
+ Some data segment content may also be associated (in the data
+ segment) with a non-zero response code.
+
+ In addition, the SCSI-Response PDU carries information required for
+ the proper operation of the iSCSI protocol:
+
+ - The number of Data-In PDUs that a target has sent (to enable
+ the initiator to check that all have arrived).
+ - StatSN - the Status Sequence Number on this connection.
+
+
+
+
+Satran, et al. Standards Track [Page 43]
+
+RFC 3720 iSCSI April 2004
+
+
+ - ExpCmdSN - the next Expected Command Sequence Number at the
+ target.
+ - MaxCmdSN - the maximum CmdSN acceptable at the target from
+ this initiator.
+
+3.5.1.3 Task Management Function Request
+
+ The Task Management function request provides an initiator with a way
+ to explicitly control the execution of one or more SCSI Tasks or
+ iSCSI functions. The PDU carries a function identifier (which task
+ management function to perform) and enough information to
+ unequivocally identify the task or task-set on which to perform the
+ action, even if the task(s) to act upon has not yet arrived or has
+ been discarded due to an error.
+
+ The referenced tag identifies an individual task if the function
+ refers to an individual task.
+
+ The I_T_L nexus identifies task sets. In iSCSI the I_T_L nexus is
+ identified by the LUN and the session identification (the session
+ identifies an I_T nexus).
+
+ For task sets, the CmdSN of the Task Management function request
+ helps identify the tasks upon which to act, namely all tasks
+ associated with a LUN and having a CmdSN preceding the Task
+ Management function request CmdSN.
+
+ For a Task Management function, the coordination between responses to
+ the tasks affected and the Task Management function response is done
+ by the target.
+
+3.5.1.4. Task Management Function Response
+
+ The Task Management function response carries an indication of
+ function completion for a Task Management function request including
+ how it was completed (response and qualifier) and additional
+ information for failure responses.
+
+ After the Task Management response indicates Task Management function
+ completion, the initiator will not receive any additional responses
+ from the affected tasks.
+
+3.5.1.5. SCSI Data-Out and SCSI Data-In
+
+ SCSI Data-Out and SCSI Data-In are the main vehicles by which SCSI
+ data payload is carried between initiator and target. Data payload
+ is associated with a specific SCSI command through the Initiator Task
+ Tag. For target convenience, outgoing solicited data also carries a
+
+
+
+Satran, et al. Standards Track [Page 44]
+
+RFC 3720 iSCSI April 2004
+
+
+ Target Transfer Tag (copied from R2T) and the LUN. Each PDU contains
+ the payload length and the data offset relative to the buffer address
+ contained in the SCSI execute command procedure call.
+
+ In each direction, the data transfer is split into "sequences". An
+ end-of-sequence is indicated by the F bit.
+
+ An outgoing sequence is either unsolicited (only the first sequence
+ can be unsolicited) or consists of all the Data-Out PDUs sent in
+ response to an R2T.
+
+ Input sequences are built to enable the direction switching for
+ bidirectional commands.
+
+ For input, the target may request positive acknowledgement of input
+ data. This is limited to sessions that support error recovery and is
+ implemented through the A bit in the SCSI Data-In PDU header.
+
+ Data-In and Data-Out PDUs also carry the DataSN to enable the
+ initiator and target to detect missing PDUs (discarded due to an
+ error).
+
+ In addition, StatSN is carried by the Data-In PDUs.
+
+ To enable a SCSI command to be processed while involving a minimum
+ number of messages, the last SCSI Data-In PDU passed for a command
+ may also contain the status if the status indicates termination with
+ no exceptions (no sense or response involved).
+
+3.5.1.6. Ready To Transfer (R2T)
+
+ R2T is the mechanism by which the SCSI target "requests" the
+ initiator for output data. R2T specifies to the initiator the offset
+ of the requested data relative to the buffer address from the execute
+ command procedure call and the length of the solicited data.
+
+ To help the SCSI target associate the resulting Data-Out with an R2T,
+ the R2T carries a Target Transfer Tag that will be copied by the
+ initiator in the solicited SCSI Data-Out PDUs. There are no protocol
+ specific requirements with regard to the value of these tags, but it
+ is assumed that together with the LUN, they will enable the target to
+ associate data with an R2T.
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 45]
+
+RFC 3720 iSCSI April 2004
+
+
+ R2T also carries information required for proper operation of the
+ iSCSI protocol, such as:
+
+ - R2TSN (to enable an initiator to detect a missing R2T)
+ - StatSN
+ - ExpCmdSN
+ - MaxCmdSN
+
+3.5.2. Requests/Responses carrying SCSI and iSCSI Payload
+
+3.5.2.1. Asynchronous Message
+
+ Asynchronous Messages are used to carry SCSI asynchronous events
+ (AEN) and iSCSI asynchronous messages.
+
+ When carrying an AEN, the event details are reported as sense data in
+ the data segment.
+
+3.5.3. Requests/Responses Carrying iSCSI Only Payload
+
+3.5.3.1. Text Request and Text Response
+
+ Text requests and responses are designed as a parameter negotiation
+ vehicle and as a vehicle for future extension.
+
+ In the data segment, Text Requests/Responses carry text information
+ using a simple "key=value" syntax.
+
+ Text Request/Responses may form extended sequences using the same
+ Initiator Task Tag. The initiator uses the F (Final) flag bit in the
+ text request header to indicate its readiness to terminate a
+ sequence. The target uses the F (Final) flag bit in the text
+ response header to indicate its consent to sequence termination.
+
+ Text Request and Responses also use the Target Transfer Tag to
+ indicate continuation of an operation or a new beginning. A target
+ that wishes to continue an operation will set the Target Transfer Tag
+ in a Text Response to a value different from the default 0xffffffff.
+ An initiator willing to continue will copy this value into the Target
+ Transfer Tag of the next Text Request. If the initiator wants to
+ restart the current target negotiation (start fresh) will set the
+ Target Transfer Tag to 0xffffffff.
+
+ Although a complete exchange is always started by the initiator,
+ specific parameter negotiations may be initiated by the initiator or
+ target.
+
+
+
+
+
+Satran, et al. Standards Track [Page 46]
+
+RFC 3720 iSCSI April 2004
+
+
+3.5.3.2. Login Request and Login Response
+
+ Login Requests and Responses are used exclusively during the Login
+ Phase of each connection to set up the session and connection
+ parameters. (The Login Phase consists of a sequence of login
+ requests and responses carrying the same Initiator Task Tag.)
+
+ A connection is identified by an arbitrarily selected connection-ID
+ (CID) that is unique within a session.
+
+ Similar to the Text Requests and Responses, Login Requests/Responses
+ carry key=value text information with a simple syntax in the data
+ segment.
+
+ The Login Phase proceeds through several stages (security
+ negotiation, operational parameter negotiation) that are selected
+ with two binary coded fields in the header -- the "current stage"
+ (CSG) and the "next stage" (NSG) with the appearance of the latter
+ being signaled by the "transit" flag (T).
+
+ The first Login Phase of a session plays a special role, called the
+ leading login, which determines some header fields (e.g., the version
+ number, the maximum number of connections, and the session
+ identification).
+
+ The CmdSN initial value is also set by the leading login.
+
+ StatSN for each connection is initiated by the connection login.
+
+ A login request may indicate an implied logout (cleanup) of the
+ connection to be logged in (a connection restart) by using the same
+ Connection ID (CID) as an existing connection, as well as the same
+ session identifying elements of the session to which the old
+ connection was associated.
+
+3.5.3.3. Logout Request and Response
+
+ Logout Requests and Responses are used for the orderly closing of
+ connections for recovery or maintenance. The logout request may be
+ issued following a target prompt (through an asynchronous message) or
+ at an initiators initiative. When issued on the connection to be
+ logged out, no other request may follow it.
+
+ The Logout Response indicates that the connection or session cleanup
+ is completed and no other responses will arrive on the connection (if
+ received on the logging out connection). In addition, the Logout
+ Response indicates how long the target will continue to hold
+ resources for recovery (e.g., command execution that continues on a
+
+
+
+Satran, et al. Standards Track [Page 47]
+
+RFC 3720 iSCSI April 2004
+
+
+ new connection) in the text key Time2Retain and how long the
+ initiator must wait before proceeding with recovery in the text key
+ Time2Wait.
+
+3.5.3.4. SNACK Request
+
+ With the SNACK Request, the initiator requests retransmission of
+ numbered-responses or data from the target. A single SNACK request
+ covers a contiguous set of missing items, called a run, of a given
+ type of items. The type is indicated in a type field in the PDU
+ header. The run is composed of an initial item (StatSN, DataSN,
+ R2TSN) and the number of missed Status, Data, or R2T PDUs. For long
+ Data-In sequences, the target may request (at predefined minimum
+ intervals) a positive acknowledgement for the data sent. A SNACK
+ request with a type field that indicates ACK and the number of
+ Data-In PDUs acknowledged conveys this positive acknowledgement.
+
+3.5.3.5. Reject
+
+ Reject enables the target to report an iSCSI error condition (e.g.,
+ protocol, unsupported option) that uses a Reason field in the PDU
+ header and includes the complete header of the bad PDU in the Reject
+ PDU data segment.
+
+3.5.3.6. NOP-Out Request and NOP-In Response
+
+ This request/response pair may be used by an initiator and target as
+ a "ping" mechanism to verify that a connection/session is still
+ active and all of its components are operational. Such a ping may be
+ triggered by the initiator or target. The triggering party indicates
+ that it wants a reply by setting a value different from the default
+ 0xffffffff in the corresponding Initiator/Target Transfer Tag.
+
+ NOP-In/NOP-Out may also be used "unidirectional" to convey to the
+ initiator/target command, status or data counter values when there is
+ no other "carrier" and there is a need to update the initiator/
+ target.
+
+4. SCSI Mode Parameters for iSCSI
+
+ There are no iSCSI specific mode pages.
+
+5. Login and Full Feature Phase Negotiation
+
+ iSCSI parameters are negotiated at session or connection
+ establishment by using Login Requests and Responses (see Section
+ 3.2.3 iSCSI Login) and during the Full Feature Phase (Section 3.2.4
+ iSCSI Full Feature Phase) by using Text Requests and Responses. In
+
+
+
+Satran, et al. Standards Track [Page 48]
+
+RFC 3720 iSCSI April 2004
+
+
+ both cases the mechanism used is an exchange of iSCSI-text-key=value
+ pairs. For brevity iSCSI-text-keys are called just keys in the rest
+ of this document.
+
+ Keys are either declarative or require negotiation and the key
+ description indicates if the key is declarative or requires
+ negotiation.
+
+ For the declarative keys, the declaring party sets a value for the
+ key. The key specification indicates if the key can be declared by
+ the initiator, target or both.
+
+ For the keys that require negotiation one of the parties (the
+ proposing party) proposes a value or set of values by including the
+ key=value in the data part of a Login or Text Request or Response
+ PDUs. The other party (the accepting party) makes a selection based
+ on the value or list of values proposed and includes the selected
+ value in a key=value in the data part of one of the following Login
+ or Text Response or Request PDUs. For most of the keys both the
+ initiator and target can be proposing parties.
+
+ The login process proceeds in two stages - the security negotiation
+ stage and the operational parameter negotiation stage. Both stages
+ are optional but at least one of them has to be present to enable the
+ setting of some mandatory parameters.
+
+ If present, the security negotiation stage precedes the operational
+ parameter negotiation stage.
+
+ Progression from stage to stage is controlled by the T (Transition)
+ bit in the Login Request/Response PDU header. Through the T bit set
+ to 1, the initiator indicates that it would like to transition. The
+ target agrees to the transition (and selects the next stage) when
+ ready. A field in the Login PDU header indicates the current stage
+ (CSG) and during transition, another field indicates the next stage
+ (NSG) proposed (initiator) and selected (target).
+
+ The text negotiation process is used to negotiate or declare
+ operational parameters. The negotiation process is controlled by the
+ F (final) bit in the PDU header. During text negotiations, the F bit
+ is used by the initiator to indicate that it is ready to finish the
+ negotiation and by the Target to acquiesce the end of negotiation.
+
+ Since some key=value pairs may not fit entirely in a single PDU, the
+ C (continuation) bit is used (both in Login and Text) to indicate
+ that "more follows".
+
+
+
+
+
+Satran, et al. Standards Track [Page 49]
+
+RFC 3720 iSCSI April 2004
+
+
+ The text negotiation uses an additional mechanism by which a target
+ may deliver larger amounts of data to an enquiring initiator. The
+ target sets a Target Task Tag to be used as a bookmark that when
+ returned by the initiator, means "go on". If reset to a "neutral
+ value", it means "forget about the rest".
+
+ This chapter details types of keys and values used, the syntax rules
+ for parameter formation, and the negotiation schemes to be used with
+ different types of parameters.
+
+5.1. Text Format
+
+ The initiator and target send a set of key=value pairs encoded in
+ UTF-8 Unicode. All the text keys and text values specified in this
+ document are to be presented and interpreted in the case in which
+ they appear in this document. They are case sensitive.
+
+ The following character symbols are used in this document for text
+ items (the hexadecimal values represent Unicode code points):
+
+ (a-z, A-Z) - letters
+ (0-9) - digits
+ " " (0x20) - space
+ "." (0x2e) - dot
+ "-" (0x2d) - minus
+ "+" (0x2b) - plus
+ "@" (0x40) - commercial at
+ "_" (0x5f) - underscore
+ "=" (0x3d) - equal
+ ":" (0x3a) - colon
+ "/" (0x2f) - solidus or slash
+ "[" (0x5b) - left bracket
+ "]" (0x5d) - right bracket
+ null (0x00) - null separator
+ "," (0x2c) - comma
+ "~" (0x7e) - tilde
+
+ Key=value pairs may span PDU boundaries. An initiator or target that
+ sends partial key=value text within a PDU indicates that more text
+ follows by setting the C bit in the Text or Login Request or Text or
+ Login Response to 1. Data segments in a series of PDUs that have the
+ C bit set to 1 and end with a PDU that have the C bit set to 0, or
+ include a single PDU that has the C bit set to 0, have to be
+ considered as forming a single logical-text-data-segment (LTDS).
+
+ Every key=value pair, including the last or only pair in a LTDS, MUST
+ be followed by one null (0x00) delimiter.
+
+
+
+
+Satran, et al. Standards Track [Page 50]
+
+RFC 3720 iSCSI April 2004
+
+
+ A key-name is whatever precedes the first "=" in the key=value pair.
+ The term key is used frequently in this document in place of
+ key-name.
+
+ A value is whatever follows the first "=" in the key=value pair up to
+ the end of the key=value pair, but not including the null delimiter.
+
+ The following definitions will be used in the rest of this document:
+
+ standard-label: A string of one or more characters that consist of
+ letters, digits, dot, minus, plus, commercial at, or underscore.
+ A standard-label MUST begin with a capital letter and must not
+ exceed 63 characters.
+
+ key-name: A standard-label.
+
+ text-value: A string of zero or more characters that consist of
+ letters, digits, dot, minus, plus, commercial at, underscore,
+ slash, left bracket, right bracket, or colon.
+
+ iSCSI-name-value: A string of one or more characters that consist
+ of minus, dot, colon, or any character allowed by the output of
+ the iSCSI string-prep template as specified in [RFC3722] (see
+ also Section 3.2.6.2 iSCSI Name Encoding).
+
+ iSCSI-local-name-value: A UTF-8 string; no null characters are
+ allowed in the string. This encoding is to be used for localized
+ (internationalized) aliases.
+
+ boolean-value: The string "Yes" or "No".
+
+ hex-constant: A hexadecimal constant encoded as a string that
+ starts with "0x" or "0X" followed by one or more digits or the
+ letters a, b, c, d, e, f, A, B, C, D, E, or F. Hex-constants are
+ used to encode numerical values or binary strings. When used to
+ encode numerical values, the excessive use of leading 0 digits is
+ discouraged. The string following 0X (or 0x) represents a base16
+ number that starts with the most significant base16 digit,
+ followed by all other digits in decreasing order of significance
+ and ending with the least-significant base16 digit. When used to
+ encode binary strings, hexadecimal constants have an implicit
+ byte-length that includes four bits for every hexadecimal digit
+ of the constant, including leading zeroes. For example, a
+ hex-constant of n hexadecimal digits has a byte-length of (the
+ integer part of) (n+1)/2.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 51]
+
+RFC 3720 iSCSI April 2004
+
+
+ decimal-constant: An unsigned decimal number with the digit 0 or a
+ string of one or more digits that start with a non-zero digit.
+ Decimal-constants are used to encode numerical values or binary
+ strings. Decimal constants can only be used to encode binary
+ strings if the string length is explicitly specified. There is
+ no implicit length for decimal strings. Decimal-constant MUST
+ NOT be used for parameter values if the values can be equal or
+ greater than 2**64 (numerical) or for binary strings that can be
+ longer than 64 bits.
+
+ base64-constant: base64 constant encoded as a string that starts
+ with "0b" or "0B" followed by 1 or more digits or letters or plus
+ or slash or equal. The encoding is done according to [RFC2045]
+ and each character, except equal, represents a base64 digit or a
+ 6-bit binary string. Base64-constants are used to encode
+ numerical-values or binary strings. When used to encode
+ numerical values, the excessive use of leading 0 digits (encoded
+ as A) is discouraged. The string following 0B (or 0b) represents
+ a base64 number that starts with the most significant base64
+ digit, followed by all other digits in decreasing order of
+ significance and ending with the least-significant base64 digit;
+ the least significant base64 digit may be optionally followed by
+ pad digits (encoded as equal) that are not considered as part of
+ the number. When used to encode binary strings, base64-constants
+ have an implicit
+ byte-length that includes six bits for every character of the
+ constant, excluding trailing equals (i.e., a base64-constant of n
+ base64 characters excluding the trailing equals has a byte-length
+ of ((the integer part of) (n*3/4)). Correctly encoded base64
+ strings cannot have n values of 1, 5 ... k*4+1.
+
+ numerical-value: An unsigned integer always less than 2**64 encoded
+ as a decimal-constant or a hex-constant. Unsigned integer
+ arithmetic applies to numerical-values.
+
+ large-numerical-value: An unsigned integer that can be larger than
+ or equal to 2**64 encoded as a hex constant, or
+ base64-constant. Unsigned integer arithmetic applies to
+ large-numeric-values.
+
+ numeric-range: Two numerical-values separated by a tilde where the
+ value to the right of tilde must not be lower than the value to
+ the left.
+
+ regular-binary-value: A binary string not longer than 64 bits
+ encoded as a decimal constant, hex constant, or base64-constant.
+ The length of the string is either specified by the key
+ definition or is the implicit byte-length of the encoded string.
+
+
+
+Satran, et al. Standards Track [Page 52]
+
+RFC 3720 iSCSI April 2004
+
+
+ large-binary-value: A binary string longer than 64 bits encoded as
+ a hex-constant or base64-constant. The length of the string is
+ either specified by the key definition or is the implicit
+ byte-length of the encoded string.
+
+ binary-value: A regular-binary-value or a large-binary-value.
+ Operations on binary values are key specific.
+
+ simple-value: Text-value, iSCSI-name-value, boolean-value,
+ numeric-value, a numeric-range, or a binary-value.
+
+ list-of-values: A sequence of text-values separated by a comma.
+
+ If not otherwise specified, the maximum length of a simple-value (not
+ its encoded representation) is 255 bytes, not including the delimiter
+ (comma or zero byte).
+
+ Any iSCSI target or initiator MUST support receiving at least 8192
+ bytes of key=value data in a negotiation sequence. When proposing or
+ accepting authentication methods that explicitly require support for
+ very long authentication items, the initiator and target MUST support
+ receiving of at least 64 kilobytes of key=value data (see Appendix
+ 11.1.2 - Simple Public-Key Mechanism (SPKM) - that require support
+ for public key certificates).
+
+5.2. Text Mode Negotiation
+
+ During login, and thereafter, some session or connection parameters
+ are either declared or negotiated through an exchange of textual
+ information.
+
+ The initiator starts the negotiation and/or declaration through a
+ Text or Login Request and indicates when it is ready for completion
+ (by setting the F bit to 1 and keeping it to 1 in a Text Request or
+ the T bit in the Login Request). As negotiation text may span PDU
+ boundaries, a Text or Login Request or Text or Login Response PDU
+ that has the C bit set to 1 MUST NOT have the F/T bit set to 1.
+
+ A target receiving a Text or Login Request with the C bit set to 1
+ MUST answer with a Text or Login Response with no data segment
+ (DataSegmentLength 0). An initiator receiving a Text or Login
+ Response with the C bit set to 1 MUST answer with a Text or Login
+ Request with no data segment (DataSegmentLength 0).
+
+ A target or initiator SHOULD NOT use a Text or Login Response or Text
+ or Login Request with no data segment (DataSegmentLength 0) unless
+ explicitly required by a general or a key-specific negotiation rule.
+
+
+
+
+Satran, et al. Standards Track [Page 53]
+
+RFC 3720 iSCSI April 2004
+
+
+ The format of a declaration is:
+
+ Declarer-> <key>=<valuex>
+
+ The general format of text negotiation is:
+
+ Proposer-> <key>=<valuex>
+ Acceptor-> <key>={<valuey>|NotUnderstood|Irrelevant|Reject}
+
+ Thus a declaration is a one-way textual exchange while a negotiation
+ is a two-way exchange.
+
+ The proposer or declarer can either be the initiator or the target,
+ and the acceptor can either be the target or initiator, respectively.
+ Targets are not limited to respond to key=value pairs as proposed by
+ the initiator. The target may propose key=value pairs of its own.
+
+ All negotiations are explicit (i.e., the result MUST only be based on
+ newly exchanged or declared values). There are no implicit
+ proposals. If a proposal is not made, then a reply cannot be
+ expected. Conservative design also requires that default values
+ should not be relied upon when use of some other value has serious
+ consequences.
+
+ The value proposed or declared can be a numerical-value, a
+ numerical-range defined by lower and upper values with both integers
+ separated by a tilde, a binary value, a text-value, an
+ iSCSI-name-value, an iSCSI-local-name-value, a boolean-value (Yes or
+ No), or a list of comma separated text-values. A range, a
+ large-numerical-value, an iSCSI-name-value and an
+ iSCSI-local-name-value MAY ONLY be used if it is explicitly allowed.
+ An accepted value can be a numerical-value, a large-numerical-value,
+ a text-value, or a boolean-value.
+
+ If a specific key is not relevant for the current negotiation, the
+ acceptor may answer with the constant "Irrelevant" for all types of
+ negotiation. However the negotiation is not considered as failed if
+ the answer is "Irrelevant". The "Irrelevant" answer is meant for
+ those cases in which several keys are presented by a proposing party
+ but the selection made by the acceptor for one of the keys makes
+ other keys irrelevant. The following example illustrates the use of
+ "Irrelevant":
+
+ I->T OFMarker=Yes,OFMarkInt=2048~8192
+ T->I OFMarker=No,OFMarkInt=Irrelevant
+
+ I->T X#vkey1=(bla,alb,None),X#vkey2=(bla,alb)
+ T->I X#vkey1=None,X#vkey2=Irrelevant
+
+
+
+Satran, et al. Standards Track [Page 54]
+
+RFC 3720 iSCSI April 2004
+
+
+
+ Any key not understood by the acceptor may be ignored by the acceptor
+ without affecting the basic function. However, the answer for a key
+ not understood MUST be key=NotUnderstood.
+
+ The constants "None", "Reject", "Irrelevant", and "NotUnderstood" are
+ reserved and MUST ONLY be used as described here. Violation of this
+ rule is a protocol error (in particular the use of "Reject",
+ "Irrelevant", and "NotUnderstood" as proposed values).
+
+ Reject or Irrelevant are legitimate negotiation options where allowed
+ but their excessive use is discouraged. A negotiation is considered
+ complete when the acceptor has sent the key value pair even if the
+ value is "Reject", "Irrelevant", or "NotUnderstood. Sending the key
+ again would be a re-negotiation and is forbidden for many keys.
+
+ If the acceptor sends "Reject" as an answer the negotiated key is
+ left at its current value (or default if no value was set). If the
+ current value is not acceptable to the proposer on the connection or
+ to the session it is sent, the proposer MAY choose to terminate the
+ connection or session.
+
+ All keys in this document, except for the X extension formats, MUST
+ be supported by iSCSI initiators and targets when used as specified
+ here. If used as specified, these keys MUST NOT be answered with
+ NotUnderstood.
+
+ Implementers may introduce new keys by prefixing them with
+ "X-", followed by their (reversed) domain name, or with new keys
+ registered with IANA prefixing them with X#. For example, the entity
+ owning the domain example.com can issue:
+
+ X-com.example.bar.foo.do_something=3
+
+ or a new registered key may be used as in:
+
+ X#SuperCalyPhraGilistic=Yes
+
+ Implementers MAY also introduce new values, but ONLY for new keys or
+ authentication methods (see Section 11 iSCSI Security Text Keys and
+ Authentication Methods), or digests (see Section 12.1 HeaderDigest
+ and DataDigest).
+
+ Whenever parameter action or acceptance is dependent on other
+ parameters, the dependency rules and parameter sequence must be
+ specified with the parameters.
+
+
+
+
+
+Satran, et al. Standards Track [Page 55]
+
+RFC 3720 iSCSI April 2004
+
+
+ In the Login Phase (see Section 5.3 Login Phase), every stage is a
+ separate negotiation. In the FullFeaturePhase, a Text Request
+ Response sequence is a negotiation. Negotiations MUST be handled as
+ atomic operations. For example, all negotiated values go into effect
+ after the negotiation concludes in agreement or are ignored if the
+ negotiation fails.
+
+ Some parameters may be subject to integrity rules (e.g., parameter-x
+ must not exceed parameter-y or parameter-u not 1 implies parameter-v
+ be Yes). Whenever required, integrity rules are specified with the
+ keys. Checking for compliance with the integrity rule must only be
+ performed after all the parameters are available (the existent and
+ the newly negotiated). An iSCSI target MUST perform integrity
+ checking before the new parameters take effect. An initiator MAY
+ perform integrity checking.
+
+ An iSCSI initiator or target MAY terminate a negotiation that does
+ not end within a reasonable time or number of exchanges.
+
+5.2.1. List negotiations
+
+ In list negotiation, the originator sends a list of values (which may
+ include "None") in its order of preference.
+
+ The responding party MUST respond with the same key and the first
+ value that it supports (and is allowed to use for the specific
+ originator) selected from the originator list.
+
+ The constant "None" MUST always be used to indicate a missing
+ function. However, "None" is only a valid selection if it is
+ explicitly proposed.
+
+ If an acceptor does not understand any particular value in a list, it
+ MUST ignore it. If an acceptor does not support, does not
+ understand, or is not allowed to use any of the proposed options with
+ a specific originator, it may use the constant "Reject" or terminate
+ the negotiation. The selection of a value not proposed MUST be
+ handled as a protocol error.
+
+5.2.2. Simple-value Negotiations
+
+ For simple-value negotiations, the accepting party MUST answer with
+ the same key. The value it selects becomes the negotiation result.
+
+ Proposing a value not admissible (e.g., not within the specified
+ bounds) MAY be answered with the constant "Reject" or the acceptor
+ MAY select an admissible value.
+
+
+
+
+Satran, et al. Standards Track [Page 56]
+
+RFC 3720 iSCSI April 2004
+
+
+ The selection by the acceptor, of a value not admissible under the
+ selection rules is considered a protocol error. The selection rules
+ are key-specific.
+
+ For a numerical range, the value selected must be an integer within
+ the proposed range or "Reject" (if the range is unacceptable).
+
+ In Boolean negotiations (i.e., those that result in keys taking the
+ values Yes or No), the accepting party MUST answer with the same key
+ and the result of the negotiation when the received value does not
+ determine that result by itself. The last value transmitted becomes
+ the negotiation result. The rules for selecting the value to answer
+ with are expressed as Boolean functions of the value received, and
+ the value that the accepting party would have selected if given a
+ choice.
+
+ Specifically, the two cases in which answers are OPTIONAL are:
+
+ - The Boolean function is "AND" and the value "No" is received.
+ The outcome of the negotiation is "No".
+ - The Boolean function is "OR" and the value "Yes" is received.
+ The outcome of the negotiation is "Yes".
+
+ Responses are REQUIRED in all other cases, and the value chosen and
+ sent by the acceptor becomes the outcome of the negotiation.
+
+5.3. Login Phase
+
+ The Login Phase establishes an iSCSI connection between an initiator
+ and a target; it also creates a new session or associates the
+ connection to an existing session. The Login Phase sets the iSCSI
+ protocol parameters, security parameters, and authenticates the
+ initiator and target to each other.
+
+ The Login Phase is only implemented via Login Request and Responses.
+ The whole Login Phase is considered as a single task and has a single
+ Initiator Task Tag (similar to the linked SCSI commands).
+
+ The default MaxRecvDataSegmentLength is used during Login.
+
+ The Login Phase sequence of requests and responses proceeds as
+ follows:
+
+ - Login initial request
+ - Login partial response (optional)
+ - More Login Requests and Responses (optional)
+ - Login Final-Response (mandatory)
+
+
+
+
+Satran, et al. Standards Track [Page 57]
+
+RFC 3720 iSCSI April 2004
+
+
+ The initial Login Request of any connection MUST include the
+ InitiatorName key=value pair. The initial Login Request of the first
+ connection of a session MAY also include the SessionType key=value
+ pair. For any connection within a session whose type is not
+ "Discovery", the first Login Request MUST also include the TargetName
+ key=value pair.
+
+ The Login Final-response accepts or rejects the Login Request.
+
+ The Login Phase MAY include a SecurityNegotiation stage and a
+ LoginOperationalNegotiation stage or both, but MUST include at least
+ one of them. The included stage MAY be empty except for the
+ mandatory names.
+
+ The Login Requests and Responses contain a field (CSG) that indicates
+ the current negotiation stage (SecurityNegotiation or
+ LoginOperationalNegotiation). If both stages are used, the
+ SecurityNegotiation MUST precede the LoginOperationalNegotiation.
+
+ Some operational parameters can be negotiated outside the login
+ through Text Requests and Responses.
+
+ Security MUST be completely negotiated within the Login Phase. The
+ use of underlying IPsec security is specified in Chapter 8 and in
+ [RFC3723]. iSCSI support for security within the protocol only
+ consists of authentication in the Login Phase.
+
+ In some environments, a target or an initiator is not interested in
+ authenticating its counterpart. It is possible to bypass
+ authentication through the Login Request and Response.
+
+ The initiator and target MAY want to negotiate iSCSI authentication
+ parameters. Once this negotiation is completed, the channel is
+ considered secure.
+
+ Most of the negotiation keys are only allowed in a specific stage.
+ The SecurityNegotiation keys appear in Chapter 11 and the
+ LoginOperationalNegotiation keys appear in Chapter 12. Only a
+ limited set of keys (marked as Any-Stage in Chapter 12) may be used
+ in any of the two stages.
+
+ Any given Login Request or Response belongs to a specific stage; this
+ determines the negotiation keys allowed with the request or response.
+ It is considered to be a protocol error to send a key that is not
+ allowed in the current stage.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 58]
+
+RFC 3720 iSCSI April 2004
+
+
+ Stage transition is performed through a command exchange (request/
+ response) that carries the T bit and the same CSG code. During this
+ exchange, the next stage is selected by the target through the "next
+ stage" code (NSG). The selected NSG MUST NOT exceed the value stated
+ by the initiator. The initiator can request a transition whenever it
+ is ready, but a target can only respond with a transition after one
+ is proposed by the initiator.
+
+ In a negotiation sequence, the T bit settings in one pair of Login
+ Request-Responses have no bearing on the T bit settings of the next
+ pair. An initiator that has a T bit set to 1 in one pair and is
+ answered with a T bit setting of 0, may issue the next request with
+ the T bit set to 0.
+
+ When a transition is requested by the initiator and acknowledged by
+ the target, both the initiator and target switch to the selected
+ stage.
+
+ Targets MUST NOT submit parameters that require an additional
+ initiator Login Request in a Login Response with the T bit set to 1.
+
+ Stage transitions during login (including entering and exit) are only
+ possible as outlined in the following table:
+
+ +-----------------------------------------------------------+
+ |From To -> | Security | Operational | FullFeature |
+ | | | | | |
+ | V | | | |
+ +-----------------------------------------------------------+
+ | (start) | yes | yes | no |
+ +-----------------------------------------------------------+
+ | Security | no | yes | yes |
+ +-----------------------------------------------------------+
+ | Operational | no | no | yes |
+ +-----------------------------------------------------------+
+
+ The Login Final-Response that accepts a Login Request can only come
+ as a response to a Login Request with the T bit set to 1, and both
+ the request and response MUST indicate FullFeaturePhase as the next
+ phase via the NSG field.
+
+ Neither the initiator nor the target should attempt to declare or
+ negotiate a parameter more than once during login except for
+ responses to specific keys that explicitly allow repeated key
+ declarations (e.g., TargetAddress). An attempt to
+ renegotiate/redeclare parameters not specifically allowed MUST be
+ detected by the initiator and target. If such an attempt is detected
+
+
+
+
+Satran, et al. Standards Track [Page 59]
+
+RFC 3720 iSCSI April 2004
+
+
+ by the target, the target MUST respond with Login reject (initiator
+ error); if detected by the initiator, the initiator MUST drop the
+ connection.
+
+5.3.1. Login Phase Start
+
+ The Login Phase starts with a Login Request from the initiator to the
+ target. The initial Login Request includes:
+
+ - Protocol version supported by the initiator.
+ - iSCSI Initiator Name and iSCSI Target Name
+ - ISID, TSIH, and connection Ids
+ - Negotiation stage that the initiator is ready to enter.
+
+ A login may create a new session or it may add a connection to an
+ existing session. Between a given iSCSI Initiator Node (selected
+ only by an InitiatorName) and a given iSCSI target defined by an
+ iSCSI TargetName and a Target Portal Group Tag, the login results are
+ defined by the following table:
+
+
+ +------------------------------------------------------------------+
+ |ISID | TSIH | CID | Target action |
+ +------------------------------------------------------------------+
+ |new | non-zero | any | fail the login |
+ | | | | ("session does not exist") |
+ +------------------------------------------------------------------+
+ |new | zero | any | instantiate a new session |
+ +------------------------------------------------------------------+
+ |existing | zero | any | do session reinstatement |
+ | | | | (see section 5.3.5) |
+ +------------------------------------------------------------------+
+ |existing | non-zero | new | add a new connection to |
+ | | existing | | the session |
+ +------------------------------------------------------------------+
+ |existing | non-zero |existing| do connection reinstatement|
+ | | existing | | (see section 5.3.4) |
+ +------------------------------------------------------------------+
+ |existing | non-zero | any | fail the login |
+ | | new | | ("session does not exist") |
+ +------------------------------------------------------------------+
+
+ Determination of "existing" or "new" are made by the target.
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 60]
+
+RFC 3720 iSCSI April 2004
+
+
+ Optionally, the Login Request may include:
+
+ - Security parameters
+ OR
+ - iSCSI operational parameters
+ AND/OR
+ - The next negotiation stage that the initiator is ready to
+ enter.
+
+ The target can answer the login in the following ways:
+
+ - Login Response with Login reject. This is an immediate rejection
+ from the target that causes the connection to terminate and the
+ session to terminate if this is the first (or only) connection of
+ a new session. The T bit and the CSG and NSG fields are
+ reserved.
+ - Login Response with Login Accept as a final response (T bit set
+ to 1 and the NSG in both request and response are set to
+ FullFeaturePhase). The response includes the protocol version
+ supported by the target and the session ID, and may include iSCSI
+ operational or security parameters (that depend on the current
+ stage).
+ - Login Response with Login Accept as a partial response (NSG not
+ set to FullFeaturePhase in both request and response) that
+ indicates the start of a negotiation sequence. The response
+ includes the protocol version supported by the target and either
+ security or iSCSI parameters (when no security mechanism is
+ chosen) supported by the target.
+
+ If the initiator decides to forego the SecurityNegotiation stage, it
+ issues the Login with the CSG set to LoginOperationalNegotiation and
+ the target may reply with a Login Response that indicates that it is
+ unwilling to accept the connection (see Section 10.13 Login Response)
+ without SecurityNegotiation and will terminate the connection with a
+ response of Authentication failure (see Section 10.13.5 Status-Class
+ and Status-Detail).
+
+ If the initiator is willing to negotiate iSCSI security, but is
+ unwilling to make the initial parameter proposal and may accept a
+ connection without iSCSI security, it issues the Login with the T bit
+ set to 1, the CSG set to SecurityNegotiation, and the NSG set to
+ LoginOperationalNegotiation. If the target is also ready to skip
+ security, the Login Response only contains the TargetPortalGroupTag
+ key (see Section 12.9 TargetPortalGroupTag), the T bit set to 1, the
+ CSG set to SecurityNegotiation, and the NSG set to
+ LoginOperationalNegotiation.
+
+
+
+
+
+Satran, et al. Standards Track [Page 61]
+
+RFC 3720 iSCSI April 2004
+
+
+ An initiator that chooses to operate without iSCSI security, with all
+ the operational parameters taking the default values, issues the
+ Login with the T bit set to 1, the CSG set to
+ LoginOperationalNegotiation, and the NSG set to FullFeaturePhase. If
+ the target is also ready to forego security and can finish its
+ LoginOperationalNegotiation, the Login Response has T bit set to 1,
+ the CSG set to LoginOperationalNegotiation, and the NSG set to
+ FullFeaturePhase in the next stage.
+
+ During the Login Phase the iSCSI target MUST return the
+ TargetPortalGroupTag key with the first Login Response PDU with which
+ it is allowed to do so (i.e., the first Login Response issued after
+ the first Login Request with the C bit set to 0) for all session
+ types when TargetName is given and the response is not a redirection.
+ The TargetPortalGroupTag key value indicates the iSCSI portal group
+ servicing the Login Request PDU. If the reconfiguration of iSCSI
+ portal groups is a concern in a given environment, the iSCSI
+ initiator should use this key to ascertain that it had indeed
+ initiated the Login Phase with the intended target portal group.
+
+5.3.2. iSCSI Security Negotiation
+
+ The security exchange sets the security mechanism and authenticates
+ the initiator user and the target to each other. The exchange
+ proceeds according to the authentication method chosen in the
+ negotiation phase and is conducted using the Login Requests' and
+ responses' key=value parameters.
+
+ An initiator directed negotiation proceeds as follows:
+
+ - The initiator sends a Login Request with an ordered list of the
+ options it supports (authentication algorithm). The options are
+ listed in the initiator's order of preference. The initiator MAY
+ also send private or public extension options.
+
+ - The target MUST reply with the first option in the list it
+ supports and is allowed to use for the specific initiator unless
+ it does not support any, in which case it MUST answer with
+ "Reject" (see Section 5.2 Text Mode Negotiation). The parameters
+ are encoded in UTF8 as key=value. For security parameters, see
+ Chapter 11.
+
+ - When the initiator considers that it is ready to conclude the
+ SecurityNegotiation stage, it sets the T bit to 1 and the NSG to
+ what it would like the next stage to be. The target will then
+ set the T bit to 1 and set the NSG to the next stage in the Login
+ Response when it finishes sending its security keys. The next
+
+
+
+
+Satran, et al. Standards Track [Page 62]
+
+RFC 3720 iSCSI April 2004
+
+
+ stage selected will be the one the target selected. If the next
+ stage is FullFeaturePhase, the target MUST respond with a Login
+ Response with the TSIH value.
+
+ If the security negotiation fails at the target, then the target MUST
+ send the appropriate Login Response PDU. If the security negotiation
+ fails at the initiator, the initiator SHOULD close the connection.
+
+ It should be noted that the negotiation might also be directed by the
+ target if the initiator does support security, but is not ready to
+ direct the negotiation (propose options).
+
+5.3.3. Operational Parameter Negotiation During the Login Phase
+
+ Operational parameter negotiation during the login MAY be done:
+
+ - Starting with the first Login Request if the initiator does not
+ propose any security/integrity option.
+
+ - Starting immediately after the security negotiation if the
+ initiator and target perform such a negotiation.
+
+ Operational parameter negotiation MAY involve several Login
+ Request-Response exchanges started and terminated by the initiator.
+ The initiator MUST indicate its intent to terminate the negotiation
+ by setting the T bit to 1; the target sets the T bit to 1 on the last
+ response.
+
+ If the target responds to a Login Request that has the T bit set to 1
+ with a Login Response that has the T bit set to 0, the initiator
+ should keep sending the Login Request (even empty) with the T bit set
+ to 1, while it still wants to switch stage, until it receives the
+ Login Response that has the T bit set to 1 or it receives a key that
+ requires it to set the T bit to 0.
+
+ Some session specific parameters can only be specified during the
+ Login Phase of the first connection of a session (i.e., begun by a
+ Login Request that contains a zero-valued TSIH) - the leading Login
+ Phase (e.g., the maximum number of connections that can be used for
+ this session).
+
+ A session is operational once it has at least one connection in
+ FullFeaturePhase. New or replacement connections can only be added
+ to a session after the session is operational.
+
+ For operational parameters, see Chapter 12.
+
+
+
+
+
+Satran, et al. Standards Track [Page 63]
+
+RFC 3720 iSCSI April 2004
+
+
+5.3.4. Connection Reinstatement
+
+ Connection reinstatement is the process of an initiator logging in
+ with an ISID-TSIH-CID combination that is possibly active from the
+ target's perspective, which causes the implicit logging out of the
+ connection corresponding to the CID, and reinstating a new Full
+ Feature Phase iSCSI connection in its place (with the same CID).
+ Thus, the TSIH in the Login PDU MUST be non-zero and the CID does not
+ change during a connection reinstatement. The Login Request performs
+ the logout function of the old connection if an explicit logout was
+ not performed earlier. In sessions with a single connection, this
+ may imply the opening of a second connection with the sole purpose of
+ cleaning up the first. Targets MUST support opening a second
+ connection even when they do not support multiple connections in Full
+ Feature Phase if ErrorRecoveryLevel is 2 and SHOULD support opening a
+ second connection if ErrorRecoveryLevel is less than 2.
+
+ If the operational ErrorRecoveryLevel is 2, connection reinstatement
+ enables future task reassignment. If the operational
+ ErrorRecoveryLevel is less than 2, connection reinstatement is the
+ replacement of the old CID without enabling task reassignment. In
+ this case, all the tasks that were active on the old CID must be
+ immediately terminated without further notice to the initiator.
+
+ The initiator connection state MUST be CLEANUP_WAIT (section 7.1.3)
+ when the initiator attempts a connection reinstatement.
+
+ In practical terms, in addition to the implicit logout of the old
+ connection, reinstatement is equivalent to a new connection login.
+
+5.3.5. Session Reinstatement, Closure, and Timeout
+
+ Session reinstatement is the process of the initiator logging in with
+ an ISID that is possibly 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).
+ Therefore, the TSIH in the Login PDU MUST be zero to signal session
+ reinstatement. Session reinstatement causes all the tasks that were
+ active on the old session to be immediately terminated by the target
+ without further notice to the initiator.
+
+ The initiator session state MUST be FAILED (Section 7.3 Session State
+ Diagrams) when the initiator attempts a session reinstatement.
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 64]
+
+RFC 3720 iSCSI April 2004
+
+
+ Session closure is an event defined to be one of the following:
+
+ - A successful "session close" logout.
+ - A successful "connection close" logout for the last Full Feature
+ Phase connection when no other connection in the session is
+ waiting for cleanup (Section 7.2 Connection Cleanup State Diagram
+ for Initiators and Targets) and no tasks in the session are
+ waiting for reassignment.
+
+ Session timeout is an event defined to occur when the last connection
+ state timeout expires and no tasks are waiting for reassignment.
+ This takes the session to the FREE state (N6 transition in the
+ session state diagram).
+
+5.3.5.1. Loss of Nexus Notification
+
+ The iSCSI layer provides the SCSI layer with the "I_T nexus loss"
+ notification when any one of the following events happens:
+
+ a) Successful completion of session reinstatement.
+ b) Session closure event.
+ c) Session timeout event.
+
+ Certain SCSI object clearing actions may result due to the
+ notification in the SCSI end nodes, as documented in Appendix F.
+ - Clearing Effects of Various Events on Targets -.
+
+5.3.6. Session Continuation and Failure
+
+ Session continuation is the process by which the state of a
+ preexisting session continues to be used by connection reinstatement
+ (Section 5.3.4 Connection Reinstatement), or by adding a connection
+ with a new CID. Either of these actions associates the new transport
+ connection with the session state.
+
+ Session failure is an event where the last Full Feature Phase
+ connection reaches the CLEANUP_WAIT state (Section 7.2 Connection
+ Cleanup State Diagram for Initiators and Targets), or completes a
+ successful recovery logout, thus causing all active tasks (that are
+ formerly allegiant to the connection) to start waiting for task
+ reassignment.
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 65]
+
+RFC 3720 iSCSI April 2004
+
+
+5.4. Operational Parameter Negotiation Outside the Login Phase
+
+ Some operational parameters MAY be negotiated outside (after) the
+ Login Phase.
+
+ Parameter negotiation in Full Feature Phase is done through Text
+ requests and responses. Operational parameter negotiation MAY
+ involve several Text request-response exchanges, which the initiator
+ always starts and terminates using the same Initiator Task Tag. The
+ initiator MUST indicate its intent to terminate the negotiation by
+ setting the F bit to 1; the target sets the F bit to 1 on the last
+ response.
+
+ If the target responds to a Text request with the F bit set to 1 and
+ with a Text response with the F bit set to 0, the initiator should
+ keep sending the Text request (even empty) with the F bit set to 1,
+ while it still wants to finish the negotiation, until it receives the
+ Text response with the F bit set to 1. Responding to a Text request
+ with the F bit set to 1 with an empty (no key=value pairs) response
+ with the F bit set to 0 is discouraged.
+
+ Targets MUST NOT submit parameters that require an additional
+ initiator Text request in a Text response with the F bit set to 1.
+
+ In a negotiation sequence, the F bit settings in one pair of Text
+ request-responses have no bearing on the F bit settings of the next
+ pair. An initiator that has the F bit set to 1 in a request and is
+ being answered with an F bit setting of 0 may issue the next request
+ with the F bit set to 0.
+
+ Whenever the target responds with the F bit set to 0, it MUST set the
+ Target Transfer Tag to a value other than the default 0xffffffff.
+
+ An initiator MAY reset an operational parameter negotiation by
+ issuing a Text request with the Target Transfer Tag set to the value
+ 0xffffffff after receiving a response with the Target Transfer Tag
+ set to a value other than 0xffffffff. A target may reset an
+ operational parameter negotiation by answering a Text request with a
+ Reject PDU.
+
+ Neither the initiator nor the target should attempt to declare or
+ negotiate a parameter more than once during any negotiation sequence
+ without an intervening operational parameter negotiation reset,
+ except for responses to specific keys that explicitly allow repeated
+ key declarations (e.g., TargetAddress). If detected by the target,
+ this MUST result in a Reject PDU with a reason of "protocol error".
+ The initiator MUST reset the negotiation as outlined above.
+
+
+
+
+Satran, et al. Standards Track [Page 66]
+
+RFC 3720 iSCSI April 2004
+
+
+ Parameters negotiated by a text exchange negotiation sequence only
+ become effective after the negotiation sequence is completed.
+
+6. iSCSI Error Handling and Recovery
+
+6.1. Overview
+
+6.1.1. Background
+
+ The following two considerations prompted the design of much of the
+ error recovery functionality in iSCSI:
+
+ i) An iSCSI PDU may fail the digest check and be dropped, despite
+ being received by the TCP layer. The iSCSI layer must
+ optionally be allowed to recover such dropped PDUs.
+ ii) A TCP connection may fail at any time during the data
+ transfer. All the active tasks must optionally be allowed to
+ continue on a different TCP connection within the same
+ session.
+
+ Implementations have considerable flexibility in deciding what degree
+ of error recovery to support, when to use it and by which mechanisms
+ to achieve the required behavior. Only the externally visible
+ actions of the error recovery mechanisms must be standardized to
+ ensure interoperability.
+
+ This chapter describes a general model for recovery in support of
+ interoperability. See Appendix E. - Algorithmic Presentation of
+ Error Recovery Classes - for further detail on how the described
+ model may be implemented. Compliant implementations do not have to
+ match the implementation details of this model as presented, but the
+ external behavior of such implementations must correspond to the
+ externally observable characteristics of the presented model.
+
+6.1.2. Goals
+
+ The major design goals of the iSCSI error recovery scheme are as
+ follows:
+
+ a) Allow iSCSI implementations to meet different requirements by
+ defining a collection of error recovery mechanisms that
+ implementations may choose from.
+ b) Ensure interoperability between any two implementations
+ supporting different sets of error recovery capabilities.
+ c) Define the error recovery mechanisms to ensure command
+ ordering even in the face of errors, for initiators that
+ demand ordering.
+
+
+
+
+Satran, et al. Standards Track [Page 67]
+
+RFC 3720 iSCSI April 2004
+
+
+ d) Do not make additions in the fast path, but allow moderate
+ complexity in the error recovery path.
+ e) Prevent both the initiator and target from attempting to
+ recover the same set of PDUs at the same time. For example,
+ there must be a clear "error recovery functionality
+ distribution" between the initiator and target.
+
+6.1.3. Protocol Features and State Expectations
+
+ The initiator mechanisms defined in connection with error recovery
+ are:
+
+ a) NOP-OUT to probe sequence numbers of the target (section
+ 10.18)
+ b) Command retry (section 6.2.1)
+ c) Recovery R2T support (section 6.7)
+ d) Requesting retransmission of status/data/R2T using the SNACK
+ facility (section 10.16)
+ e) Acknowledging the receipt of the data (section 10.16)
+ f) Reassigning the connection allegiance of a task to a different
+ TCP connection (section 6.2.2)
+ g) Terminating the entire iSCSI session to start afresh (section
+ 6.1.4.4)
+
+ The target mechanisms defined in connection with error recovery are:
+
+ a) NOP-IN to probe sequence numbers of the initiator (section
+ 10.19)
+ b) Requesting retransmission of data using the recovery R2T
+ feature (section 6.7)
+ c) SNACK support (section 10.16) d) Requesting that parts of
+ read data be acknowledged (section 10.7.2)
+ e) Allegiance reassignment support (section 6.2.2)
+ f) Terminating the entire iSCSI session to force the initiator to
+ start over (section 6.1.4.4)
+
+ For any outstanding SCSI command, it is assumed that iSCSI, in
+ conjunction with SCSI at the initiator, is able to keep enough
+ information to be able to rebuild the command PDU, and that outgoing
+ data is available (in host memory) for retransmission while the
+ command is outstanding. It is also assumed that at the target,
+ incoming data (read data) MAY be kept for recovery or it can be
+ reread from a device server.
+
+ It is further assumed that a target will keep the "status & sense"
+ for a command it has executed if it supports status retransmission.
+ A target that agrees to support data retransmission is expected to be
+ prepared to retransmit the outgoing data (i.e., Data-In) on request
+
+
+
+Satran, et al. Standards Track [Page 68]
+
+RFC 3720 iSCSI April 2004
+
+
+ until either the status for the completed command is acknowledged, or
+ the data in question has been separately acknowledged.
+
+6.1.4. Recovery Classes
+
+ iSCSI enables the following classes of recovery (in the order of
+ increasing scope of affected iSCSI tasks):
+
+ - Within a command (i.e., without requiring command restart).
+ - Within a connection (i.e., without requiring the connection to
+ be rebuilt, but perhaps requiring command restart).
+ - Connection recovery (i.e., perhaps requiring connections to be
+ rebuilt and commands to be reissued).
+ - Session recovery.
+
+ The recovery scenarios detailed in the rest of this section are
+ representative rather than exclusive. In every case, they detail the
+ lowest class recovery that MAY be attempted. The implementer is left
+ to decide under which circumstances to escalate to the next recovery
+ class and/or what recovery classes to implement. Both the iSCSI
+ target and initiator MAY escalate the error handling to an error
+ recovery class, which impacts a larger number of iSCSI tasks in any
+ of the cases identified in the following discussion.
+
+ In all classes, the implementer has the choice of deferring errors to
+ the SCSI initiator (with an appropriate response code), in which case
+ the task, if any, has to be removed from the target and all the side
+ effects, such as ACA, must be considered.
+
+ Use of within-connection and within-command recovery classes MUST NOT
+ be attempted before the connection is in Full Feature Phase.
+
+ In the detailed description of the recovery classes, the mandating
+ terms (MUST, SHOULD, MAY, etc.) indicate normative actions to be
+ executed if the recovery class is supported and used.
+
+6.1.4.1. Recovery Within-command
+
+ At the target, the following cases lend themselves to
+ within-command recovery:
+
+ - Lost data PDU - realized through one of the following:
+
+ a) Data digest error - dealt with as specified in Section 6.7
+ Digest Errors, using the option of a recovery R2T.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 69]
+
+RFC 3720 iSCSI April 2004
+
+
+ b) Sequence reception timeout (no data or
+ partial-data-and-no-F-bit) - considered an implicit sequence
+ error and dealt with as specified in Section 6.8 Sequence
+ Errors, using the option of a recovery R2T.
+ c) Header digest error, which manifests as a sequence reception
+ timeout or a sequence error - dealt with as specified in
+ Section 6.8 Sequence Errors, using the option of a recovery
+ R2T.
+
+ At the initiator, the following cases lend themselves to
+ within-command recovery:
+
+ Lost data PDU or lost R2T - realized through one of the
+ following:
+
+ a) Data digest error - dealt with as specified in Section 6.7
+ Digest Errors, using the option of a SNACK.
+ b) Sequence reception timeout (no status) or response reception
+ timeout - dealt with as specified in Section 6.8 Sequence
+ Errors, using the option of a SNACK.
+ c) Header digest error, which manifests as a sequence reception
+ timeout or a sequence error - dealt with as specified in
+ Section 6.8 Sequence Errors, using the option of a SNACK.
+
+ To avoid a race with the target, which may already have a recovery
+ R2T or a termination response on its way, an initiator SHOULD NOT
+ originate a SNACK for an R2T based on its internal timeouts (if any).
+ Recovery in this case is better left to the target.
+
+ The timeout values used by the initiator and target are outside the
+ scope of this document. Sequence reception timeout is generally a
+ large enough value to allow the data sequence transfer to be
+ complete.
+
+6.1.4.2. Recovery Within-connection
+
+ At the initiator, the following cases lend themselves to
+ within-connection recovery:
+
+ - Requests not acknowledged for a long time. Requests are
+ acknowledged explicitly through ExpCmdSN or implicitly by
+ receiving data and/or status. The initiator MAY retry
+ non-acknowledged commands as specified in Section 6.2 Retry and
+ Reassign in Recovery.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 70]
+
+RFC 3720 iSCSI April 2004
+
+
+ - Lost iSCSI numbered Response. It is recognized by either
+ identifying a data digest error on a Response PDU or a Data-In
+ PDU carrying the status, or by receiving a Response PDU with a
+ higher StatSN than expected. In the first case, digest error
+ handling is done as specified in Section 6.7 Digest Errors using
+ the option of a SNACK. In the second case, sequence error
+ handling is done as specified in Section 6.8 Sequence Errors,
+ using the option of a SNACK.
+
+ At the target, the following cases lend themselves to
+ within-connection recovery:
+
+ - Status/Response not acknowledged for a long time. The target MAY
+ issue a NOP-IN (with a valid Target Transfer Tag or otherwise)
+ that carries the next status sequence number it is going to use
+ in the StatSN field. This helps the initiator detect any missing
+ StatSN(s) and issue a SNACK for the status.
+
+ The timeout values used by the initiator and the target are outside
+ the scope of this document.
+
+6.1.4.3. Connection Recovery
+
+ At an iSCSI initiator, the following cases lend themselves to
+ connection recovery:
+
+ - TCP connection failure: The initiator MUST close the connection.
+ It then MUST either implicitly or explicitly logout the failed
+ connection with the reason code "remove the connection for
+ recovery" and reassign connection allegiance for all commands
+ still in progress associated with the failed connection on one or
+ more connections (some or all of which MAY be newly established
+ connections) using the "Task reassign" task management function
+ (see Section 10.5.1 Function). For an initiator, a command is in
+ progress as long as it has not received a response or a Data-In
+ PDU including status.
+
+ Note: The logout function is mandatory. However, a new connection
+ establishment is only mandatory if the failed connection was the
+ last or only connection in the session.
+
+ - Receiving an Asynchronous Message that indicates one or all
+ connections in a session has been dropped. The initiator MUST
+ handle it as a TCP connection failure for the connection(s)
+ referred to in the Message.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 71]
+
+RFC 3720 iSCSI April 2004
+
+
+ At an iSCSI target, the following cases lend themselves to connection
+ recovery:
+
+ - TCP connection failure. The target MUST close the connection and,
+ if more than one connection is available, the target SHOULD send
+ an Asynchronous Message that indicates it has dropped the
+ connection. Then, the target will wait for the initiator to
+ continue recovery.
+
+6.1.4.4. Session Recovery
+
+ Session recovery should be performed when all other recovery attempts
+ have failed. Very simple initiators and targets MAY perform session
+ recovery on all iSCSI errors and rely on recovery on the SCSI layer
+ and above.
+
+ Session recovery implies the closing of all TCP connections,
+ internally aborting all executing and queued tasks for the given
+ initiator at the target, terminating all outstanding SCSI commands
+ with an appropriate SCSI service response at the initiator, and
+ restarting a session on a new set of connection(s) (TCP connection
+ establishment and login on all new connections).
+
+ For possible clearing effects of session recovery on SCSI and iSCSI
+ objects, refer to Appendix F. - Clearing Effects of Various Events on
+ Targets -.
+
+6.1.5. Error Recovery Hierarchy
+
+ The error recovery classes described so far are organized into a
+ hierarchy for ease in understanding and to limit the implementation
+ complexity. With few and well defined recovery levels
+ interoperability is easier to achieve. The attributes of this
+ hierarchy are as follows:
+
+ a) Each level is a superset of the capabilities of the previous
+ level. For example, Level 1 support implies supporting all
+ capabilities of Level 0 and more.
+ b) As a corollary, supporting a higher error recovery level means
+ increased sophistication and possibly an increase in resource
+ requirements.
+ c) Supporting error recovery level "n" is advertised and
+ negotiated by each iSCSI entity by exchanging the text key
+ "ErrorRecoveryLevel=n". The lower of the two exchanged values
+ is the operational ErrorRecoveryLevel for the session.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 72]
+
+RFC 3720 iSCSI April 2004
+
+
+ The following diagram represents the error recovery hierarchy.
+
+ +
+ /
+ / 2 \ <-- Connection recovery
+ +-----+
+ / 1 \ <-- Digest failure recovery
+ +---------+
+ / 0 \ <-- Session failure recovery
+ +-------------+
+
+ The following table lists the error recovery capabilities expected
+ from the implementations that support each error recovery level.
+
+ +-------------------+--------------------------------------------+
+ |ErrorRecoveryLevel | Associated Error recovery capabilities |
+ +-------------------+--------------------------------------------+
+ | 0 | Session recovery class |
+ | | (Section 6.1.4.4 Session Recovery) |
+ +-------------------+--------------------------------------------+
+ | 1 | Digest failure recovery (See Note below.) |
+ | | plus the capabilities of ER Level 0 |
+ +-------------------+--------------------------------------------+
+ | 2 | Connection recovery class |
+ | | (Section 6.1.4.3 Connection Recovery) |
+ | | plus the capabilities of ER Level 1 |
+ +-------------------+--------------------------------------------+
+
+ Note: Digest failure recovery is comprised of two recovery classes:
+ Within-Connection recovery class (Section 6.1.4.2 Recovery Within-
+ connection) and Within-Command recovery class (Section 6.1.4.1
+ Recovery Within-command).
+
+ When a defined value of ErrorRecoveryLevel is proposed by an
+ originator in a text negotiation, the originator MUST support the
+ functionality defined for the proposed value and additionally, the
+ functionality corresponding to any defined value numerically less
+ than the proposed. When a defined value of ErrorRecoveryLevel is
+ returned by a responder in a text negotiation, the responder MUST
+ support the functionality corresponding to the ErrorRecoveryLevel it
+ is accepting.
+
+ When either party attempts to use error recovery functionality beyond
+ what is negotiated, the recovery attempts MAY fail unless an a priori
+ agreement outside the scope of this document exists between the two
+ parties to provide such support.
+
+
+
+
+
+Satran, et al. Standards Track [Page 73]
+
+RFC 3720 iSCSI April 2004
+
+
+ Implementations MUST support error recovery level "0", while the rest
+ are OPTIONAL to implement. In implementation terms, the above
+ striation means that the following incremental sophistication with
+ each level is required.
+
+ +-------------------+---------------------------------------------+
+ |Level transition | Incremental requirement |
+ +-------------------+---------------------------------------------+
+ | 0->1 | PDU retransmissions on the same connection |
+ +-------------------+---------------------------------------------+
+ | 1->2 | Retransmission across connections and |
+ | | allegiance reassignment |
+ +-------------------+---------------------------------------------+
+
+6.2. Retry and Reassign in Recovery
+
+ This section summarizes two important and somewhat related iSCSI
+ protocol features used in error recovery.
+
+6.2.1. Usage of Retry
+
+ By resending the same iSCSI command PDU ("retry") in the absence of a
+ command acknowledgement (by way of an ExpCmdSN update) or a response,
+ an initiator attempts to "plug" (what it thinks are) the
+ discontinuities in CmdSN ordering on the target end. Discarded
+ command PDUs, due to digest errors, may have created these
+ discontinuities.
+
+ Retry MUST NOT be used for reasons other than plugging command
+ sequence gaps, and in particular, cannot be used for requesting PDU
+ retransmissions from a target. Any such PDU retransmission requests
+ for a currently allegiant command in progress may be made using the
+ SNACK mechanism described in section 10.16, although the usage of
+ SNACK is OPTIONAL.
+
+ If initiators, as part of plugging command sequence gaps as described
+ above, inadvertently issue retries for allegiant commands already in
+ progress (i.e., targets did not see the discontinuities in CmdSN
+ ordering), the duplicate commands are silently ignored by targets as
+ specified in section 3.2.2.1.
+
+ When an iSCSI command is retried, the command PDU MUST carry the
+ original Initiator Task Tag and the original operational attributes
+ (e.g., flags, function names, LUN, CDB etc.) as well as the original
+ CmdSN. The command being retried MUST be sent on the same connection
+ as the original command unless the original connection was already
+ successfully logged out.
+
+
+
+
+Satran, et al. Standards Track [Page 74]
+
+RFC 3720 iSCSI April 2004
+
+
+6.2.2. Allegiance Reassignment
+
+ By issuing a "task reassign" task management request (Section 10.5.1
+ Function), the initiator signals its intent to continue an already
+ active command (but with no current connection allegiance) as part of
+ connection recovery. This means that a new connection allegiance is
+ requested for the command, which seeks to associate it to the
+ connection on which the task management request is being issued.
+ Before the allegiance reassignment is attempted for a task, an
+ implicit or explicit Logout with the reason code "remove the
+ connection for recovery" ( see section 10.14) MUST be successfully
+ completed for the previous connection to which the task was
+ allegiant.
+
+ In reassigning connection allegiance for a command, the targets
+ SHOULD continue the command from its current state. For example,
+ when reassigning read commands, the target SHOULD take advantage of
+ the ExpDataSN field provided by the Task Management function request
+ (which must be set to zero if there was no data transfer) and bring
+ the read command to completion by sending the remaining data and
+ sending (or resending) the status. ExpDataSN acknowledges all data
+ sent up to, but not including, the Data-In PDU and or R2T with DataSN
+ (or R2TSN) equal to ExpDataSN. However, targets may choose to
+ send/receive all unacknowledged data or all of the data on a
+ reassignment of connection allegiance if unable to recover or
+ maintain an accurate state. Initiators MUST not subsequently request
+ data retransmission through Data SNACK for PDUs numbered less than
+ ExpDataSN (i.e., prior to the acknowledged sequence number). For all
+ types of commands, a reassignment request implies that the task is
+ still considered in progress by the initiator and the target must
+ conclude the task appropriately if the target returns the "Function
+ Complete" response to the reassignment request. This might possibly
+ involve retransmission of data/R2T/status PDUs as necessary, but MUST
+ involve the (re)transmission of the status PDU.
+
+ It is OPTIONAL for targets to support the allegiance reassignment.
+ This capability is negotiated via the ErrorRecoveryLevel text key
+ during the login time. When a target does not support allegiance
+ reassignment, it MUST respond with a Task Management response code of
+ "Allegiance reassignment not supported". If allegiance reassignment
+ is supported by the target, but the task is still allegiant to a
+ different connection, or a successful recovery Logout of the
+ previously allegiant connection was not performed, the target MUST
+ respond with a Task Management response code of "Task still
+ allegiant".
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 75]
+
+RFC 3720 iSCSI April 2004
+
+
+ If allegiance reassignment is supported by the target, the Task
+ Management response to the reassignment request MUST be issued before
+ the reassignment becomes effective.
+
+ If a SCSI Command that involves data input is reassigned, any SNACK
+ Tag it holds for a final response from the original connection is
+ deleted and the default value of 0 MUST be used instead.
+
+6.3. Usage Of Reject PDU in Recovery
+
+ Targets MUST NOT implicitly terminate an active task by sending a
+ Reject PDU for any PDU exchanged during the life of the task. If the
+ target decides to terminate the task, a Response PDU (SCSI, Text,
+ Task, etc.) must be returned by the target to conclude the task. If
+ the task had never been active before the Reject (i.e., the Reject is
+ on the command PDU), targets should not send any further responses
+ because the command itself is being discarded.
+
+ The above rule means that the initiator can eventually expect a
+ response on receiving Rejects, if the received Reject is for a PDU
+ other than the command PDU itself. The non-command Rejects only have
+ diagnostic value in logging the errors, and they can be used for
+ retransmission decisions by the initiators.
+
+ The CmdSN of the rejected command PDU (if it is a non-immediate
+ command) MUST NOT be considered received by the target (i.e., a
+ command sequence gap must be assumed for the CmdSN), even though the
+ CmdSN of the rejected command PDU may be reliably ascertained. Upon
+ receiving the Reject, the initiator MUST plug the CmdSN gap in order
+ to continue to use the session. The gap may be plugged either by
+ transmitting a command PDU with the same CmdSN, or by aborting the
+ task (see section 6.9 on how an abort may plug a CmdSN gap).
+
+ When a data PDU is rejected and its DataSN can be ascertained, a
+ target MUST advance ExpDataSN for the current data burst if a
+ recovery R2T is being generated. The target MAY advance its
+ ExpDataSN if it does not attempt to recover the lost data PDU.
+
+6.4. Connection Timeout Management
+
+ iSCSI defines two session-global timeout values (in seconds)
+ - Time2Wait and Time2Retain - that are applicable when an iSCSI Full
+ Feature Phase connection is taken out of service either intentionally
+ or by an exception. Time2Wait is the initial "respite time" before
+ attempting an explicit/implicit Logout for the CID in question or
+ task reassignment for the affected tasks (if any). Time2Retain is
+ the maximum time after the initial respite interval that the task
+ and/or connection state(s) is/are guaranteed to be maintained on the
+
+
+
+Satran, et al. Standards Track [Page 76]
+
+RFC 3720 iSCSI April 2004
+
+
+ target to cater to a possible recovery attempt. Recovery attempts
+ for the connection and/or task(s) SHOULD NOT be made before Time2Wait
+ seconds, but MUST be completed within Time2Retain seconds after that
+ initial Time2Wait waiting period.
+
+6.4.1. Timeouts on Transport Exception Events
+
+ A transport connection shutdown or a transport reset without any
+ preceding iSCSI protocol interactions informing the end-points of the
+ fact causes a Full Feature Phase iSCSI connection to be abruptly
+ terminated. The timeout values to be used in this case are the
+ negotiated values of defaultTime2Wait (Section 12.15
+ DefaultTime2Wait) and DefaultTime2Retain (Section 12.16
+ DefaultTime2Retain) text keys for the session.
+
+6.4.2. Timeouts on Planned Decommissioning
+
+ Any planned decommissioning of a Full Feature Phase iSCSI connection
+ is preceded by either a Logout Response PDU, or an Async Message PDU.
+ The Time2Wait and Time2Retain field values (section 10.15) in a
+ Logout Response PDU, and the Parameter2 and Parameter3 fields of an
+ Async Message (AsyncEvent types "drop the connection" or "drop all
+ the connections"; section 10.9.1) specify the timeout values to be
+ used in each of these cases.
+
+ These timeout values are only applicable for the affected connection,
+ and the tasks active on that connection. These timeout values have
+ no bearing on initiator timers (if any) that are already running on
+ connections or tasks associated with that session.
+
+6.5. Implicit Termination of Tasks
+
+ A target implicitly terminates the active tasks due to iSCSI protocol
+ dynamics in the following cases:
+
+ a) When a connection is implicitly or explicitly logged out with
+ the reason code of "Close the connection" and there are active
+ tasks allegiant to that connection.
+
+ b) When a connection fails and the connection state eventually
+ times out (state transition M1 in Section 7.2.2 State
+ Transition Descriptions for Initiators and Targets) and there
+ are active tasks allegiant to that connection.
+
+ c) When a successful Logout with the reason code of "remove the
+ connection for recovery" is performed while there are active
+ tasks allegiant to that connection, and those tasks eventually
+
+
+
+
+Satran, et al. Standards Track [Page 77]
+
+RFC 3720 iSCSI April 2004
+
+
+ time out after the Time2Wait and Time2Retain periods without
+ allegiance reassignment.
+
+ d) When a connection is implicitly or explicitly logged out with
+ the reason code of "Close the session" and there are active
+ tasks in that session.
+
+ If the tasks terminated in the above cases a), b, c) and d)are SCSI
+ tasks, they must be internally terminated as if with CHECK CONDITION
+ status. This status is only meaningful for appropriately handling
+ the internal SCSI state and SCSI side effects with respect to
+ ordering because this status is never communicated back as a
+ terminating status to the initiator. However additional actions may
+ have to be taken at SCSI level depending on the SCSI context as
+ defined by the SCSI standards (e.g., queued commands and ACA, in
+ cases a), b), and c), after the tasks are terminated, the target MUST
+ report a Unit Attention condition on the next command processed on
+ any connection for each affected I_T_L nexus with the status of CHECK
+ CONDITION, and the ASC/ASCQ value of 47h/7Fh - "SOME COMMANDS CLEARED
+ BY ISCSI PROTOCOL EVENT" , etc. - see [SAM2] and [SPC3]).
+
+6.6. Format Errors
+
+ The following two explicit violations of PDU layout rules are format
+ errors:
+
+ a) Illegal contents of any PDU header field except the Opcode
+ (legal values are specified in Section 10 iSCSI PDU Formats).
+ b) Inconsistent field contents (consistent field contents are
+ specified in Section 10 iSCSI PDU Formats).
+
+ Format errors indicate a major implementation flaw in one of the
+ parties.
+
+ When a target or an initiator receives an iSCSI PDU with a format
+ error, it MUST immediately terminate all transport connections in the
+ session either with a connection close or with a connection reset and
+ escalate the format error to session recovery (see Section 6.1.4.4
+ Session Recovery).
+
+6.7. Digest Errors
+
+ The discussion of the legal choices in handling digest errors below
+ excludes session recovery as an explicit option, but either party
+ detecting a digest error may choose to escalate the error to session
+ recovery.
+
+
+
+
+
+Satran, et al. Standards Track [Page 78]
+
+RFC 3720 iSCSI April 2004
+
+
+ When a target or an initiator receives any iSCSI PDU, with a header
+ digest error, it MUST either discard the header and all data up to
+ the beginning of a later PDU or close the connection. Because the
+ digest error indicates that the length field of the header may have
+ been corrupted, the location of the beginning of a later PDU needs to
+ be reliably ascertained by other means such as the operation of a
+ sync and steering layer.
+
+ When a target receives any iSCSI PDU with a payload digest error, it
+ MUST answer with a Reject PDU with a reason code of
+ Data-Digest-Error and discard the PDU.
+
+ - If the discarded PDU is a solicited or unsolicited iSCSI data
+ PDU (for immediate data in a command PDU, non-data PDU rule
+ below applies), the target MUST do one of the following:
+ a) Request retransmission with a recovery R2T.
+ b) Terminate the task with a response PDU with a CHECK
+ CONDITION Status and an iSCSI Condition of "protocol service
+ CRC error" (Section 10.4.7.2 Sense Data). If the target
+ chooses to implement this option, it MUST wait to receive
+ all the data (signaled by a Data PDU with the final bit set
+ for all outstanding R2Ts) before sending the response PDU.
+ A task management command (such as an abort task) from the
+ initiator during this wait may also conclude the task.
+ - No further action is necessary for targets if the discarded PDU
+ is a non-data PDU. In case of immediate data being present on
+ a discarded command, the immediate data is implicitly recovered
+ when the task is retried (see section 6.2.1), followed by the
+ entire data transfer for the task.
+
+ When an initiator receives any iSCSI PDU with a payload digest error,
+ it MUST discard the PDU.
+
+ - If the discarded PDU is an iSCSI data PDU, the initiator MUST do
+ one of the following:
+
+ a) Request the desired data PDU through SNACK. In response to the
+ SNACK, the target MUST either resend the data PDU or reject the
+ SNACK with a Reject PDU with a reason code of "SNACK reject" in
+ which case:
+ i) If the status has not already been sent for the command,
+ the target MUST terminate the command with a CHECK
+ CONDITION Status and an iSCSI Condition of "SNACK rejected"
+ (Section 10.4.7.2 Sense Data).
+ ii) If the status was already sent, no further action is
+ necessary for the target. The initiator in this case MUST
+ wait for the status to be received and then discard it, so
+ as to internally signal the completion with CHECK CONDITION
+
+
+
+Satran, et al. Standards Track [Page 79]
+
+RFC 3720 iSCSI April 2004
+
+
+ Status and an iSCSI Condition of "protocol service CRC
+ error" (Section 10.4.7.2 Sense Data).
+ b) Abort the task and terminate the command with an error.
+
+ - If the discarded PDU is a response PDU, the initiator MUST do one
+ of the following:
+
+ a) Request PDU retransmission with a status SNACK.
+ b) Logout the connection for recovery and continue the tasks on a
+ different connection instance as described in Section 6.2 Retry
+ and Reassign in Recovery.
+ c) Logout to close the connection (abort all the commands
+ associated with the connection).
+
+ - No further action is necessary for initiators if the discarded PDU
+ is an unsolicited PDU (e.g., Async, Reject). Task timeouts as in
+ the initiator waiting for a command completion, or process
+ timeouts, as in the target waiting for a Logout, will ensure that
+ the correct operational behavior will result in these cases
+ despite the discarded PDU.
+
+6.8. Sequence Errors
+
+ When an initiator receives an iSCSI R2T/data PDU with an out of order
+ R2TSN/DataSN or a SCSI response PDU with an ExpDataSN that implies
+ missing data PDU(s), it means that the initiator must have detected a
+ header or payload digest error on one or more earlier R2T/data PDUs.
+ The initiator MUST address these implied digest errors as described
+ in Section 6.7 Digest Errors. When a target receives a data PDU with
+ an out of order DataSN, it means that the target must have hit a
+ header or payload digest error on at least one of the earlier data
+ PDUs. The target MUST address these implied digest errors as
+ described in Section 6.7 Digest Errors.
+
+ When an initiator receives an iSCSI status PDU with an out of order
+ StatSN that implies missing responses, it MUST address the one or
+ more missing status PDUs as described in Section 6.7 Digest Errors.
+ As a side effect of receiving the missing responses, the initiator
+ may discover missing data PDUs. If the initiator wants to recover
+ the missing data for a command, it MUST NOT acknowledge the received
+ responses that start from the StatSN of the relevant command, until
+ it has completed receiving all the data PDUs of the command.
+
+ When an initiator receives duplicate R2TSNs (due to proactive
+ retransmission of R2Ts by the target) or duplicate DataSNs (due to
+ proactive SNACKs by the initiator), it MUST discard the duplicates.
+
+
+
+
+
+Satran, et al. Standards Track [Page 80]
+
+RFC 3720 iSCSI April 2004
+
+
+6.9. SCSI Timeouts
+
+ An iSCSI initiator MAY attempt to plug a command sequence gap on the
+ target end (in the absence of an acknowledgement of the command by
+ way of ExpCmdSN) before the ULP timeout by retrying the
+ unacknowledged command, as described in Section 6.2 Retry and
+ Reassign in Recovery.
+
+ On a ULP timeout for a command (that carried a CmdSN of n), if the
+ iSCSI initiator intends to continue the session, it MUST abort the
+ command by either using an appropriate Task Management function
+ request for the specific command, or a "close the connection" Logout.
+ When using an ABORT TASK, if the ExpCmdSN is still less than (n+1),
+ the target may see the abort request while missing the original
+ command itself due to one of the following reasons:
+
+ - Original command was dropped due to digest error.
+ - Connection on which the original command was sent was
+ successfully logged out. Upon logout, the unacknowledged
+ commands issued on the connection being logged out are
+ discarded.
+
+ If the abort request is received and the original command is missing,
+ targets MUST consider the original command with that RefCmdSN to be
+ received and issue a Task Management response with the response code:
+ "Function Complete". This response concludes the task on both ends.
+ If the abort request is received and the target can determine (based
+ on the Referenced Task Tag) that the command was received and
+ executed and also that the response was sent prior to the abort, then
+ the target MUST respond with the response code of "Task Does Not
+ Exist".
+
+6.10. Negotiation Failures
+
+ Text request and response sequences, when used to set/negotiate
+ operational parameters, constitute the negotiation/parameter setting.
+ A negotiation failure is considered to be one or more of the
+ following:
+
+ - None of the choices, or the stated value, is acceptable to one
+ of the sides in the negotiation.
+ - The text request timed out and possibly terminated.
+ - The text request was answered with a Reject PDU.
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 81]
+
+RFC 3720 iSCSI April 2004
+
+
+ The following two rules should be used to address negotiation
+ failures:
+
+ - During Login, any failure in negotiation MUST be considered a
+ login process failure and the Login Phase must be terminated,
+ and with it, the connection. If the target detects the
+ failure, it must terminate the login with the appropriate Login
+ Response code.
+
+ - A failure in negotiation, while in the Full Feature Phase, will
+ terminate the entire negotiation sequence that may consist of a
+ series of text requests that use the same Initiator Task Tag.
+ The operational parameters of the session or the connection
+ MUST continue to be the values agreed upon during an earlier
+ successful negotiation (i.e., any partial results of this
+ unsuccessful negotiation MUST NOT take effect and MUST be
+ discarded).
+
+6.11. Protocol Errors
+
+ Mapping framed messages over a "stream" connection, such as TCP,
+ makes the proposed mechanisms vulnerable to simple software framing
+ errors. On the other hand, the introduction of framing mechanisms to
+ limit the effects of these errors may be onerous on performance for
+ simple implementations. Command Sequence Numbers and the above
+ mechanisms for connection drop and reestablishment help handle this
+ type of mapping errors.
+
+ All violations of iSCSI PDU exchange sequences specified in this
+ document are also protocol errors. This category of errors can only
+ be addressed by fixing the implementations; iSCSI defines Reject and
+ response codes to enable this.
+
+6.12. Connection Failures
+
+ iSCSI can keep a session in operation if it is able to
+ keep/establish at least one TCP connection between the initiator and
+ the target in a timely fashion. Targets and/or initiators may
+ recognize a failing connection by either transport level means (TCP),
+ a gap in the command sequence number, a response stream that is not
+ filled for a long time, or by a failing iSCSI NOP (acting as a ping).
+ The latter MAY be used periodically to increase the speed and
+ likelihood of detecting connection failures. Initiators and targets
+ MAY also use the keep-alive option on the TCP connection to enable
+ early link failure detection on otherwise idle links.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 82]
+
+RFC 3720 iSCSI April 2004
+
+
+ On connection failure, the initiator and target MUST do one of the
+ following:
+
+ - Attempt connection recovery within the session (Section 6.1.4.3
+ Connection Recovery).
+
+ - Logout the connection with the reason code "closes the
+ connection" (Section 10.14.5 Implicit termination of tasks),
+ re-issue missing commands, and implicitly terminate all active
+ commands. This option requires support for the
+ within-connection recovery class (Section 6.1.4.2 Recovery
+ Within-connection).
+
+ - Perform session recovery (Section 6.1.4.4 Session Recovery).
+
+ Either side may choose to escalate to session recovery (via the
+ initiator dropping all the connections, or via an Async Message that
+ announces the similar intent from a target), and the other side MUST
+ give it precedence. On a connection failure, a target MUST terminate
+ and/or discard all of the active immediate commands regardless of
+ which of the above options is used (i.e., immediate commands are not
+ recoverable across connection failures).
+
+6.13. Session Errors
+
+ If all of the connections of a session fail and cannot be
+ reestablished in a short time, or if initiators detect protocol
+ errors repeatedly, an initiator may choose to terminate a session and
+ establish a new session.
+
+ In this case, the initiator takes the following actions:
+
+ - Resets or closes all the transport connections.
+ - Terminates all outstanding requests with an appropriate
+ response before initiating a new session. If the same I_T
+ nexus is intended to be reestablished, the initiator MUST
+ employ session reinstatement (see section 5.3.5).
+
+ When the session timeout (the connection state timeout for the last
+ failed connection) happens on the target, it takes the following
+ actions:
+
+ - Resets or closes the TCP connections (closes the session).
+ - Terminates all active tasks that were allegiant to the
+ connection(s) that constituted the session.
+
+ A target MUST also be prepared to handle a session reinstatement
+ request from the initiator, that may be addressing session errors.
+
+
+
+Satran, et al. Standards Track [Page 83]
+
+RFC 3720 iSCSI April 2004
+
+
+7. State Transitions
+
+ iSCSI connections and iSCSI sessions go through several well-defined
+ states from the time they are created to the time they are cleared.
+
+ The connection state transitions are described in two separate but
+ dependent state diagrams for ease in understanding. The first
+ diagram, "standard connection state diagram", describes the
+ connection state transitions when the iSCSI connection is not waiting
+ for, or undergoing, a cleanup by way of an explicit or implicit
+ Logout. The second diagram, "connection cleanup state diagram",
+ describes the connection state transitions while performing the iSCSI
+ connection cleanup.
+
+ The "session state diagram" describes the state transitions an iSCSI
+ session would go through during its lifetime, and it depends on the
+ states of possibly multiple iSCSI connections that participate in the
+ session.
+
+ States and state transitions are described in the text, tables and
+ diagrams. The diagrams are used for illustration. The text and the
+ tables are the governing specification.
+
+7.1. Standard Connection State Diagrams
+
+7.1.1. State Descriptions for Initiators and Targets
+
+ State descriptions for the standard connection state diagram are as
+ follows:
+
+ -S1: FREE
+ -initiator: State on instantiation, or after successful
+ connection closure.
+ -target: State on instantiation, or after successful connection
+ closure.
+ -S2: XPT_WAIT
+ -initiator: Waiting for a response to its transport connection
+ establishment request.
+ -target: Illegal
+ -S3: XPT_UP
+ -initiator: Illegal
+ -target: Waiting for the Login process to commence.
+ -S4: IN_LOGIN
+ -initiator: Waiting for the Login process to conclude, possibly
+ involving several PDU exchanges.
+ -target: Waiting for the Login process to conclude, possibly
+ involving several PDU exchanges.
+
+
+
+
+Satran, et al. Standards Track [Page 84]
+
+RFC 3720 iSCSI April 2004
+
+
+ -S5: LOGGED_IN
+ -initiator: In Full Feature Phase, waiting for all internal,
+ iSCSI, and transport events.
+ -target: In Full Feature Phase, waiting for all internal, iSCSI,
+ and transport events.
+ -S6: IN_LOGOUT
+ -initiator: Waiting for a Logout response.
+ -target: Waiting for an internal event signaling completion of
+ logout processing.
+ -S7: LOGOUT_REQUESTED
+ -initiator: Waiting for an internal event signaling readiness to
+ proceed with Logout.
+ -target: Waiting for the Logout process to start after having
+ requested a Logout via an Async Message.
+ -S8: CLEANUP_WAIT
+ -initiator: Waiting for the context and/or resources to initiate
+ the cleanup processing for this CSM.
+ -target: Waiting for the cleanup process to start for this CSM.
+
+7.1.2. State Transition Descriptions for Initiators and Targets
+
+ -T1:
+ -initiator: Transport connect request was made (e.g., TCP SYN
+ sent).
+ -target: Illegal
+ -T2:
+ -initiator: Transport connection request timed out, a transport
+ reset was received, or an internal event of receiving a
+ Logout response (success) on another connection for a
+ "close the session" Logout request was received.
+ -target:Illegal
+ -T3:
+ -initiator: Illegal
+ -target: Received a valid transport connection request that
+ establishes the transport connection.
+ -T4:
+ -initiator: Transport connection established, thus prompting the
+ initiator to start the iSCSI Login.
+ -target: Initial iSCSI Login Request was received.
+ -T5:
+ -initiator: The final iSCSI Login Response with a Status-Class
+ of zero was received.
+ -target: The final iSCSI Login Request to conclude the Login
+ Phase was received, thus prompting the target to send the
+ final iSCSI Login Response with a Status-Class of zero.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 85]
+
+RFC 3720 iSCSI April 2004
+
+
+ -T6:
+ -initiator: Illegal
+ -target: Timed out waiting for an iSCSI Login, transport
+ disconnect indication was received, transport reset was
+ received, or an internal event indicating a transport
+ timeout was received. In all these cases, the connection is
+ to be closed.
+ -T7:
+ -initiator - one of the following events caused the transition:
+ - The final iSCSI Login Response was received with a
+ non-zero Status-Class.
+ - Login timed out.
+ - A transport disconnect indication was received.
+ - A transport reset was received.
+ - An internal event was received indicating a transport
+ timeout.
+ - An internal event of receiving a Logout response (success)
+ on another connection for a "close the session" Logout
+ request was received.
+
+ In all these cases, the transport connection is closed.
+
+ -target - one of the following events caused the transition:
+ - The final iSCSI Login Request to conclude the Login Phase
+ was received, prompting the target to send the final iSCSI
+ Login Response with a non-zero Status-Class.
+ - Login timed out.
+ - Transport disconnect indication was received.
+ - Transport reset was received.
+ - An internal event indicating a transport timeout was
+ received.
+ - On another connection a "close the session" Logout request
+ was received.
+ In all these cases, the connection is to be closed.
+ -T8:
+ -initiator: An internal event of receiving a Logout response
+ (success) on another connection for a "close the session"
+ Logout request was received, thus closing this connection
+ requiring no further cleanup.
+ -target: An internal event of sending a Logout response
+ (success) on another connection for a "close the session"
+ Logout request was received, or an internal event of a
+ successful connection/session reinstatement is received,
+ thus prompting the target to close this connection cleanly.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 86]
+
+RFC 3720 iSCSI April 2004
+
+
+ -T9, T10:
+ -initiator: An internal event that indicates the readiness to
+ start the Logout process was received, thus prompting an
+ iSCSI Logout to be sent by the initiator.
+ -target: An iSCSI Logout request was received.
+ -T11, T12:
+ -initiator: Async PDU with AsyncEvent "Request Logout" was
+ received.
+ -target: An internal event that requires the decommissioning of
+ the connection is received, thus causing an Async PDU with
+ an AsyncEvent "Request Logout" to be sent.
+ -T13:
+ -initiator: An iSCSI Logout response (success) was received, or
+ an internal event of receiving a Logout response (success)
+ on another connection for a "close the session" Logout
+ request was received.
+ -target: An internal event was received that indicates
+ successful processing of the Logout, which prompts an iSCSI
+ Logout response (success) to be sent; an internal event of
+ sending a Logout response (success) on another connection
+ for a "close the session" Logout request was received; or an
+ internal event of a successful connection/session
+ reinstatement is received. In all these cases, the
+ transport connection is closed.
+
+ -T14:
+ -initiator: Async PDU with AsyncEvent "Request Logout" was
+ received again.
+ -target: Illegal
+ -T15, T16:
+ -initiator: One or more of the following events caused this
+ transition:
+ -Internal event that indicates a transport connection
+ timeout was received thus prompting transport RESET or
+ transport connection closure.
+ -A transport RESET.
+ -A transport disconnect indication.
+ -Async PDU with AsyncEvent "Drop connection" (for this CID).
+ -Async PDU with AsyncEvent "Drop all connections".
+ -target: One or more of the following events caused this
+ transition:
+ -Internal event that indicates a transport connection
+ timeout was received, thus prompting transport RESET or
+ transport connection closure.
+ -An internal event of a failed connection/session
+ reinstatement is received.
+ -A transport RESET.
+ -A transport disconnect indication.
+
+
+
+Satran, et al. Standards Track [Page 87]
+
+RFC 3720 iSCSI April 2004
+
+
+ -Internal emergency cleanup event was received which prompts
+ an Async PDU with AsyncEvent "Drop connection" (for this
+ CID), or event "Drop all connections".
+ -T17:
+ -initiator: One or more of the following events caused this
+ transition:
+ -Logout response, (failure i.e., a non-zero status) was
+ received, or Logout timed out.
+ -Any of the events specified for T15 and T16.
+ -target: One or more of the following events caused this
+ transition:
+ -Internal event that indicates a failure of the Logout
+ processing was received, which prompts a Logout response
+ (failure, i.e., a non-zero status) to be sent.
+ -Any of the events specified for T15 and T16.
+ -T18:
+ -initiator: An internal event of receiving a Logout response
+ (success) on another connection for a "close the session"
+ Logout request was received.
+ -target: An internal event of sending a Logout response
+ (success) on another connection for a "close the session"
+ Logout request was received, or an internal event of a
+ successful connection/session reinstatement is received. In
+ both these cases, the connection is closed.
+
+ The CLEANUP_WAIT state (S8) implies that there are possible iSCSI
+ tasks that have not reached conclusion and are still considered busy.
+
+7.1.3. Standard Connection State Diagram for an Initiator
+
+ Symbolic names for States:
+
+ S1: FREE
+ S2: XPT_WAIT
+ S4: IN_LOGIN
+ S5: LOGGED_IN
+ S6: IN_LOGOUT
+ S7: LOGOUT_REQUESTED
+ S8: CLEANUP_WAIT
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 88]
+
+RFC 3720 iSCSI April 2004
+
+
+ States S5, S6, and S7 constitute the Full Feature Phase operation of
+ the connection.
+
+ The state diagram is as follows:
+
+ -------<-------------+
+ +--------->/ S1 \<----+ |
+ T13| +->\ /<-+ \ |
+ | / ---+--- \ \ |
+ | / | T2 \ | |
+ | T8 | |T1 | | |
+ | | | / |T7 |
+ | | | / | |
+ | | | / | |
+ | | V / / |
+ | | ------- / / |
+ | | / S2 \ / |
+ | | \ / / |
+ | | ---+--- / |
+ | | |T4 / |
+ | | V / | T18
+ | | ------- / |
+ | | / S4 \ |
+ | | \ / |
+ | | ---+--- | T15
+ | | |T5 +--------+---------+
+ | | | /T16+-----+------+ |
+ | | | / -+-----+--+ | |
+ | | | / / S7 \ |T12| |
+ | | | / +->\ /<-+ V V
+ | | | / / -+----- -------
+ | | | / /T11 |T10 / S8 \
+ | | V / / V +----+ \ /
+ | | ---+-+- ----+-- | -------
+ | | / S5 \T9 / S6 \<+ ^
+ | +-----\ /--->\ / T14 |
+ | ------- --+----+------+T17
+ +---------------------------+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 89]
+
+RFC 3720 iSCSI April 2004
+
+
+ The following state transition table represents the above diagram.
+ Each row represents the starting state for a given transition, which
+ after taking a transition marked in a table cell would end in the
+ state represented by the column of the cell. For example, from state
+ S1, the connection takes the T1 transition to arrive at state S2.
+ The fields marked "-" correspond to undefined transitions.
+
+ +----+---+---+---+---+----+---+
+ |S1 |S2 |S4 |S5 |S6 |S7 |S8 |
+ ---+----+---+---+---+---+----+---+
+ S1| - |T1 | - | - | - | - | - |
+ ---+----+---+---+---+---+----+---+
+ S2|T2 |- |T4 | - | - | - | - |
+ ---+----+---+---+---+---+----+---+
+ S4|T7 |- |- |T5 | - | - | - |
+ ---+----+---+---+---+---+----+---+
+ S5|T8 |- |- | - |T9 |T11 |T15|
+ ---+----+---+---+---+---+----+---+
+ S6|T13 |- |- | - |T14|- |T17|
+ ---+----+---+---+---+---+----+---+
+ S7|T18 |- |- | - |T10|T12 |T16|
+ ---+----+---+---+---+---+----+---+
+ S8| - |- |- | - | - | - | - |
+ ---+----+---+---+---+---+----+---+
+
+7.1.4. Standard Connection State Diagram for a Target
+
+ Symbolic names for States:
+
+ S1: FREE
+ S3: XPT_UP
+ S4: IN_LOGIN
+ S5: LOGGED_IN
+ S6: IN_LOGOUT
+ S7: LOGOUT_REQUESTED
+ S8: CLEANUP_WAIT
+
+ States S5, S6, and S7 constitute the Full Feature Phase operation of
+ the connection.
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 90]
+
+RFC 3720 iSCSI April 2004
+
+
+ The state diagram is as follows:
+
+ -------<-------------+
+ +--------->/ S1 \<----+ |
+ T13| +->\ /<-+ \ |
+ | / ---+--- \ \ |
+ | / | T6 \ | |
+ | T8 | |T3 | | |
+ | | | / |T7 |
+ | | | / | |
+ | | | / | |
+ | | V / / |
+ | | ------- / / |
+ | | / S3 \ / |
+ | | \ / / | T18
+ | | ---+--- / |
+ | | |T4 / |
+ | | V / |
+ | | ------- / |
+ | | / S4 \ |
+ | | \ / |
+ | | ---+--- T15 |
+ | | |T5 +--------+---------+
+ | | | /T16+-----+------+ |
+ | | | / -+-----+---+ | |
+ | | | / / S7 \ |T12| |
+ | | | / +->\ /<-+ V V
+ | | | / / -+----- -------
+ | | | / /T11 |T10 / S8 \
+ | | V / / V \ /
+ | | ---+-+- ------- -------
+ | | / S5 \T9 / S6 \ ^
+ | +-----\ /--->\ / |
+ | ------- --+----+--------+T17
+ +---------------------------+
+
+ The following state transition table represents the above diagram,
+ and follows the conventions described for the initiator diagram.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 91]
+
+RFC 3720 iSCSI April 2004
+
+
+ +----+---+---+---+---+----+---+
+ |S1 |S3 |S4 |S5 |S6 |S7 |S8 |
+ ---+----+---+---+---+---+----+---+
+ S1| - |T3 | - | - | - | - | - |
+ ---+----+---+---+---+---+----+---+
+ S3|T6 |- |T4 | - | - | - | - |
+ ---+----+---+---+---+---+----+---+
+ S4|T7 |- |- |T5 | - | - | - |
+ ---+----+---+---+---+---+----+---+
+ S5|T8 |- |- | - |T9 |T11 |T15|
+ ---+----+---+---+---+---+----+---+
+ S6|T13 |- |- | - |- |- |T17|
+ ---+----+---+---+---+---+----+---+
+ S7|T18 |- |- | - |T10|T12 |T16|
+ ---+----+---+---+---+---+----+---+
+ S8| - |- |- | - | - | - | - |
+ ---+----+---+---+---+---+----+---+
+
+7.2. Connection Cleanup State Diagram for Initiators and Targets
+
+ Symbolic names for states:
+
+ R1: CLEANUP_WAIT (same as S8)
+ R2: IN_CLEANUP
+ R3: FREE (same as S1)
+
+ Whenever a connection state machine (e.g., CSM-C) enters the
+ CLEANUP_WAIT state (S8), it must go through the state transitions
+ described in the connection cleanup state diagram either a) using a
+ separate full-feature phase connection (let's call it CSM-E) in the
+ LOGGED_IN state in the same session, or b) using a new transport
+ connection (let's call it CSM-I) in the FREE state that is to be
+ added to the same session. In the CSM-E case, an explicit logout for
+ the CID that corresponds to CSM-C (either as a connection or session
+ logout) needs to be performed to complete the cleanup. In the CSM-I
+ case, an implicit logout for the CID that corresponds to CSM-C needs
+ to be performed by way of connection reinstatement (section 5.3.4)
+ for that CID. In either case, the protocol exchanges on CSM-E or
+ CSM-I determine the state transitions for CSM-C. Therefore, this
+ cleanup state diagram is only applicable to the instance of the
+ connection in cleanup (i.e., CSM-C). In the case of an implicit
+ logout for example, CSM-C reaches FREE (R3) at the time CSM-I reaches
+ LOGGED_IN. In the case of an explicit logout, CSM-C reaches FREE
+ (R3) when CSM-E receives a successful logout response while
+ continuing to be in the LOGGED_IN state.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 92]
+
+RFC 3720 iSCSI April 2004
+
+
+ An initiator must initiate an explicit or implicit connection logout
+ for a connection in the CLEANUP_WAIT state, if the initiator intends
+ to continue using the associated iSCSI session.
+
+ The following state diagram applies to both initiators and targets.
+
+ -------
+ / R1 \
+ +--\ /<-+
+ / ---+---
+ / | \ M3
+ M1 | |M2 |
+ | | /
+ | | /
+ | | /
+ | V /
+ | ------- /
+ | / R2 \
+ | \ /
+ | -------
+ | |
+ | |M4
+ | |
+ | |
+ | |
+ | V
+ | -------
+ | / R3 \
+ +---->\ /
+ -------
+
+ The following state transition table represents the above diagram,
+ and follows the same conventions as in earlier sections.
+
+ +----+----+----+
+ |R1 |R2 |R3 |
+ -----+----+----+----+
+ R1 | - |M2 |M1 |
+ -----+----+----+----+
+ R2 |M3 | - |M4 |
+ -----+----+----+----+
+ R3 | - | - | - |
+ -----+----+----+----+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 93]
+
+RFC 3720 iSCSI April 2004
+
+
+7.2.1. State Descriptions for Initiators and Targets
+
+ -R1: CLEANUP_WAIT (Same as S8)
+ -initiator: Waiting for the internal event to initiate the
+ cleanup processing for CSM-C.
+ -target: Waiting for the cleanup process to start for CSM-C.
+ -R2: IN_CLEANUP
+ -initiator: Waiting for the connection cleanup process to
+ conclude for CSM-C.
+ -target: Waiting for the connection cleanup process to conclude
+ for CSM-C.
+ -R3: FREE (Same as S1)
+ -initiator: End state for CSM-C.
+ -target: End state for CSM-C.
+
+7.2.2. State Transition Descriptions for Initiators and Targets
+
+ -M1: One or more of the following events was received:
+ -initiator:
+ -An internal event that indicates connection state timeout.
+ -An internal event of receiving a successful Logout response
+ on a different connection for a "close the session"
+ Logout.
+ -target:
+ -An internal event that indicates connection state timeout.
+ -An internal event of sending a Logout response (success) on
+ a different connection for a "close the session" Logout
+ request.
+
+ -M2: An implicit/explicit logout process was initiated by the
+ initiator.
+ -In CSM-I usage:
+ -initiator: An internal event requesting the connection (or
+ session) reinstatement was received, thus prompting a
+ connection (or session) reinstatement Login to be sent
+ transitioning CSM-I to state IN_LOGIN.
+ -target: A connection/session reinstatement Login was
+ received while in state XPT_UP.
+ -In CSM-E usage:
+ -initiator: An internal event that indicates that an
+ explicit logout was sent for this CID in state LOGGED_IN.
+ -target: An explicit logout was received for this CID in
+ state LOGGED_IN.
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 94]
+
+RFC 3720 iSCSI April 2004
+
+
+ -M3: Logout failure detected
+ -In CSM-I usage:
+ -initiator: CSM-I failed to reach LOGGED_IN and arrived into
+ FREE instead.
+ -target: CSM-I failed to reach LOGGED_IN and arrived into
+ FREE instead.
+ -In CSM-E usage:
+ -initiator: CSM-E either moved out of LOGGED_IN, or Logout
+ timed out and/or aborted, or Logout response (failure)
+ was received.
+ -target: CSM-E either moved out of LOGGED_IN, Logout timed
+ out and/or aborted, or an internal event that indicates a
+ failed Logout processing was received. A Logout response
+ (failure) was sent in the last case.
+
+ -M4: Successful implicit/explicit logout was performed.
+
+ - In CSM-I usage:
+ -initiator: CSM-I reached state LOGGED_IN, or an internal
+ event of receiving a Logout response (success) on another
+ connection for a "close the session" Logout request was
+ received.
+ -target: CSM-I reached state LOGGED_IN, or an internal event
+ of sending a Logout response (success) on a different
+ connection for a "close the session" Logout request was
+ received.
+ - In CSM-E usage:
+ -initiator: CSM-E stayed in LOGGED_IN and received a Logout
+ response (success), or an internal event of receiving a
+ Logout response (success) on another connection for a
+ "close the session" Logout request was received.
+ -target: CSM-E stayed in LOGGED_IN and an internal event
+ indicating a successful Logout processing was received,
+ or an internal event of sending a Logout response
+ (success) on a different connection for a "close the
+ session" Logout request was received.
+
+7.3. Session State Diagrams
+
+7.3.1. Session State Diagram for an Initiator
+
+ Symbolic Names for States:
+
+ Q1: FREE
+ Q3: LOGGED_IN
+ Q4: FAILED
+
+ State Q3 represents the Full Feature Phase operation of the session.
+
+
+
+Satran, et al. Standards Track [Page 95]
+
+RFC 3720 iSCSI April 2004
+
+
+ The state diagram is as follows:
+
+ -------
+ / Q1 \
+ +------>\ /<-+
+ / ---+--- |
+ / | |N3
+ N6 | |N1 |
+ | | |
+ | N4 | |
+ | +--------+ | /
+ | | | | /
+ | | | | /
+ | | V V /
+ -+--+-- -----+-
+ / Q4 \ N5 / Q3 \
+ \ /<---\ /
+ ------- -------
+
+ The state transition table is as follows:
+
+ +----+----+----+
+ |Q1 |Q3 |Q4 |
+ -----+----+----+----+
+ Q1 | - |N1 | - |
+ -----+----+----+----+
+ Q3 |N3 | - |N5 |
+ -----+----+----+----+
+ Q4 |N6 |N4 | - |
+ -----+----+----+----+
+
+7.3.2. Session State Diagram for a Target
+
+ Symbolic Names for States:
+
+ Q1: FREE
+ Q2: ACTIVE
+ Q3: LOGGED_IN
+ Q4: FAILED
+ Q5: IN_CONTINUE
+
+ State Q3 represents the Full Feature Phase operation of the session.
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 96]
+
+RFC 3720 iSCSI April 2004
+
+
+ The state diagram is as follows:
+
+ -------
+ +------------------>/ Q1 \
+ / +-------------->\ /<-+
+ | | ---+--- |
+ | | ^ | |N3
+ N6 | |N11 N9| V N1 |
+ | | +------ |
+ | | / Q2 \ |
+ | | \ / |
+ | --+---- +--+--- |
+ | / Q5 \ | |
+ | \ / N10 | |
+ | +-+---+------------+ |N2 /
+ | ^ | | | /
+ |N7| |N8 | | /
+ | | | | V /
+ -+--+-V V----+-
+ / Q4 \ N5 / Q3 \
+ \ /<-------------\ /
+ ------- -------
+
+ The state transition table is as follows:
+
+ +----+----+----+----+----+
+ |Q1 |Q2 |Q3 |Q4 |Q5 |
+ -----+----+----+----+----+----+
+ Q1 | - |N1 | - | - | - |
+ -----+----+----+----+----+----+
+ Q2 |N9 | - |N2 | - | - |
+ -----+----+----+----+----+----+
+ Q3 |N3 | - | - |N5 | - |
+ -----+----+----+----+----+----+
+ Q4 |N6 | - | - | - |N7 |
+ -----+----+----+----+----+----+
+ Q5 |N11 | - |N10 |N8 | - |
+ -----+----+----+----+----+----+
+
+7.3.3. State Descriptions for Initiators and Targets
+
+ -Q1: FREE
+ -initiator: State on instantiation or after cleanup.
+ -target: State on instantiation or after cleanup.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 97]
+
+RFC 3720 iSCSI April 2004
+
+
+ -Q2: ACTIVE
+ -initiator: Illegal.
+ -target: The first iSCSI connection in the session transitioned
+ to IN_LOGIN, waiting for it to complete the login process.
+
+ -Q3: LOGGED_IN
+ -initiator: Waiting for all session events.
+ -target: Waiting for all session events.
+
+ -Q4: FAILED
+ -initiator: Waiting for session recovery or session
+ continuation.
+ -target: Waiting for session recovery or session continuation.
+
+ -Q5: IN_CONTINUE
+ -initiator: Illegal.
+ -target: Waiting for session continuation attempt to reach a
+ conclusion.
+
+7.3.4. State Transition Descriptions for Initiators and Targets
+
+ -N1:
+ -initiator: At least one transport connection reached the
+ LOGGED_IN state.
+ -target: The first iSCSI connection in the session had reached
+ the IN_LOGIN state.
+
+ -N2:
+ -initiator: Illegal.
+ -target: At least one iSCSI connection reached the LOGGED_IN
+ state.
+
+ -N3:
+ -initiator: Graceful closing of the session via session closure
+ (Section 5.3.6 Session Continuation and Failure).
+ -target: Graceful closing of the session via session closure
+ (Section 5.3.6 Session Continuation and Failure) or a
+ successful session reinstatement cleanly closed the session.
+
+ -N4:
+ -initiator: A session continuation attempt succeeded.
+ -target: Illegal.
+
+ -N5:
+ -initiator: Session failure (Section 5.3.6 Session Continuation
+ and Failure) occurred.
+ -target: Session failure (Section 5.3.6 Session Continuation and
+ Failure) occurred.
+
+
+
+Satran, et al. Standards Track [Page 98]
+
+RFC 3720 iSCSI April 2004
+
+
+ -N6:
+ -initiator: Session state timeout occurred, or a session
+ reinstatement cleared this session instance. This results
+ in the freeing of all associated resources and the session
+ state is discarded.
+ -target: Session state timeout occurred, or a session
+ reinstatement cleared this session instance. This results
+ in the freeing of all associated resources and the session
+ state is discarded.
+
+ -N7:
+ -initiator: Illegal.
+ -target: A session continuation attempt is initiated.
+
+ -N8:
+ -initiator: Illegal.
+ -target: The last session continuation attempt failed.
+
+ -N9:
+ -initiator: Illegal.
+ -target: Login attempt on the leading connection failed.
+
+ -N10:
+ -initiator: Illegal.
+ -target: A session continuation attempt succeeded.
+
+ -N11:
+ -initiator: Illegal.
+ -target: A successful session reinstatement cleanly closed the
+ session.
+
+8. Security Considerations
+
+ Historically, native storage systems have not had to consider
+ security because their environments offered minimal security risks.
+ That is, these environments consisted of storage devices either
+ directly attached to hosts or connected via a Storage Area Network
+ (SAN) distinctly separate from the communications network. The use
+ of storage protocols, such as SCSI, over IP-networks requires that
+ security concerns be addressed. iSCSI implementations MUST provide
+ means of protection against active attacks (e.g., pretending to be
+ another identity, message insertion, deletion, modification, and
+ replaying) and passive attacks (e.g., eavesdropping, gaining
+ advantage by analyzing the data sent over the line).
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 99]
+
+RFC 3720 iSCSI April 2004
+
+
+ Although technically possible, iSCSI SHOULD NOT be configured without
+ security. iSCSI configured without security should be confined, in
+ extreme cases, to closed environments without any security risk.
+ [RFC3723] specifies the mechanisms that must be used in order to
+ mitigate risks fully described in that document.
+
+ The following section describes the security mechanisms provided by
+ an iSCSI implementation.
+
+8.1. iSCSI Security Mechanisms
+
+ The entities involved in iSCSI security are the initiator, target,
+ and the IP communication end points. iSCSI scenarios in which
+ multiple initiators or targets share a single communication end point
+ are expected. To accommodate such scenarios, iSCSI uses two separate
+ security mechanisms: In-band authentication between the initiator and
+ the target at the iSCSI connection level (carried out by exchange of
+ iSCSI Login PDUs), and packet protection (integrity, authentication,
+ and confidentiality) by IPsec at the IP level. The two security
+ mechanisms complement each other. The in-band authentication
+ provides end-to-end trust (at login time) between the iSCSI initiator
+ and the target while IPsec provides a secure channel between the IP
+ communication end points.
+
+ Further details on typical iSCSI scenarios and the relation between
+ the initiators, targets, and the communication end points can be
+ found in [RFC3723].
+
+8.2. In-band Initiator-Target Authentication
+
+ During login, the target MAY authenticate the initiator and the
+ initiator MAY authenticate the target. The authentication is
+ performed on every new iSCSI connection by an exchange of iSCSI Login
+ PDUs using a negotiated authentication method.
+
+ The authentication method cannot assume an underlying IPsec
+ protection, because IPsec is optional to use. An attacker should
+ gain as little advantage as possible by inspecting the authentication
+ phase PDUs. Therefore, a method using clear text (or equivalent)
+ passwords is not acceptable; on the other hand, identity protection
+ is not strictly required.
+
+ The authentication mechanism protects against an unauthorized login
+ to storage resources by using a false identity (spoofing). Once the
+ authentication phase is completed, if the underlying IPsec is not
+ used, all PDUs are sent and received in clear. The authentication
+
+
+
+
+
+Satran, et al. Standards Track [Page 100]
+
+RFC 3720 iSCSI April 2004
+
+
+ mechanism alone (without underlying IPsec) should only be used when
+ there is no risk of eavesdropping, message insertion, deletion,
+ modification, and replaying.
+
+ Section 11 iSCSI Security Text Keys and Authentication Methods
+ defines several authentication methods and the exact steps that must
+ be followed in each of them, including the iSCSI-text-keys and their
+ allowed values in each step. Whenever an iSCSI initiator gets a
+ response whose keys, or their values, are not according to the step
+ definition, it MUST abort the connection. Whenever an iSCSI target
+ gets a response whose keys, or their values, are not according to the
+ step definition, it MUST answer with a Login reject with the
+ "Initiator Error" or "Missing Parameter" status. These statuses are
+ not intended for cryptographically incorrect values such as the CHAP
+ response, for which "Authentication Failure" status MUST be
+ specified. The importance of this rule can be illustrated in CHAP
+ with target authentication (see Section 11.1.4 Challenge Handshake
+ Authentication Protocol (CHAP)) where the initiator would have been
+ able to conduct a reflection attack by omitting his response key
+ (CHAP_R) using the same CHAP challenge as the target and reflecting
+ the target's response back to the target. In CHAP, this is prevented
+ because the target must answer the missing CHAP_R key with a Login
+ reject with the "Missing Parameter" status.
+
+ For some of the authentication methods, a key specifies the identity
+ of the iSCSI initiator or target for authentication purposes. The
+ value associated with that key MAY be different from the iSCSI name
+ and SHOULD be configurable. (CHAP_N, see Section 11.1.4 Challenge
+ Handshake Authentication Protocol (CHAP) and SRP_U, see Section
+ 11.1.3 Secure Remote Password (SRP)).
+
+8.2.1. CHAP Considerations
+
+ Compliant iSCSI initiators and targets MUST implement the CHAP
+ authentication method [RFC1994] (according to Section 11.1.4
+ Challenge Handshake Authentication Protocol (CHAP) including the
+ target authentication option).
+
+ When CHAP is performed over a non-encrypted channel, it is vulnerable
+ to an off-line dictionary attack. Implementations MUST support use
+ of up to 128 bit random CHAP secrets, including the means to generate
+ such secrets and to accept them from an external generation source.
+ Implementations MUST NOT provide secret generation (or expansion)
+ means other than random generation.
+
+ An administrative entity of an environment in which CHAP is used with
+ a secret that has less than 96 random bits MUST enforce IPsec
+ encryption (according to the implementation requirements in Section
+
+
+
+Satran, et al. Standards Track [Page 101]
+
+RFC 3720 iSCSI April 2004
+
+
+ 8.3.2 Confidentiality) to protect the connection. Moreover, in this
+ case IKE authentication with group pre-shared cryptographic keys
+ SHOULD NOT be used unless it is not essential to protect group
+ members against off-line dictionary attacks by other members.
+
+ CHAP secrets MUST be an integral number of bytes (octets). A
+ compliant implementation SHOULD NOT continue with the login step in
+ which it should send a CHAP response (CHAP_R, Section 11.1.4
+ Challenge Handshake Authentication Protocol (CHAP)) unless it can
+ verify that the CHAP secret is at least 96 bits, or that IPsec
+ encryption is being used to protect the connection.
+
+ Any CHAP secret used for initiator authentication MUST NOT be
+ configured for authentication of any target, and any CHAP secret used
+ for target authentication MUST NOT be configured for authentication
+ of any initiator. If the CHAP response received by one end of an
+ iSCSI connection is the same as the CHAP response that the receiving
+ endpoint would have generated for the same CHAP challenge, the
+ response MUST be treated as an authentication failure and cause the
+ connection to close (this ensures that the same CHAP secret is not
+ used for authentication in both directions). Also, if an iSCSI
+ implementation can function as both initiator and target, different
+ CHAP secrets and identities MUST be configured for these two roles.
+ The following is an example of the attacks prevented by the above
+ requirements:
+
+ Rogue wants to impersonate Storage to Alice, and knows that a
+ single secret is used for both directions of Storage-Alice
+ authentication.
+
+ Rogue convinces Alice to open two connections to Rogue, and Rogue
+ identifies itself as Storage on both connections.
+
+ Rogue issues a CHAP challenge on connection 1, waits for Alice to
+ respond, and then reflects Alice's challenge as the initial
+ challenge to Alice on connection 2.
+
+ If Alice doesn't check for the reflection across connections,
+ Alice's response on connection 2 enables Rogue to impersonate
+ Storage on connection 1, even though Rogue does not know the
+ Alice-Storage CHAP secret.
+
+ Originators MUST NOT reuse the CHAP challenge sent by the Responder
+ for the other direction of a bidirectional authentication.
+ Responders MUST check for this condition and close the iSCSI TCP
+ connection if it occurs.
+
+
+
+
+
+Satran, et al. Standards Track [Page 102]
+
+RFC 3720 iSCSI April 2004
+
+
+ The same CHAP secret SHOULD NOT be configured for authentication of
+ multiple initiators or multiple targets, as this enables any of them
+ to impersonate any other one of them, and compromising one of them
+ enables the attacker to impersonate any of them. It is recommended
+ that iSCSI implementations check for use of identical CHAP secrets by
+ different peers when this check is feasible, and take appropriate
+ measures to warn users and/or administrators when this is detected.
+
+ When an iSCSI initiator or target authenticates itself to
+ counterparts in multiple administrative domains, it SHOULD use a
+ different CHAP secret for each administrative domain to avoid
+ propagating security compromises across domains.
+
+ Within a single administrative domain:
+ - A single CHAP secret MAY be used for authentication of an initiator
+ to multiple targets.
+ - A single CHAP secret MAY be used for an authentication of a target
+ to multiple initiators when the initiators use an external server
+ (e.g., RADIUS) to verify the target's CHAP responses and do not know
+ the target's CHAP secret.
+
+ If an external response verification server (e.g., RADIUS) is not
+ used, employing a single CHAP secret for authentication of a target
+ to multiple initiators requires that all such initiators know that
+ target secret. Any of these initiators can impersonate the target to
+ any other such initiator, and compromise of such an initiator enables
+ an attacker to impersonate the target to all such initiators.
+ Targets SHOULD use separate CHAP secrets for authentication to each
+ initiator when such risks are of concern; in this situation it may be
+ useful to configure a separate logical iSCSI target with its own
+ iSCSI Node Name for each initiator or group of initiators among which
+ such separation is desired.
+
+8.2.2. SRP Considerations
+
+ The strength of the SRP authentication method (specified in
+ [RFC2945]) is dependent on the characteristics of the group being
+ used (i.e., the prime modulus N and generator g). As described in
+ [RFC2945], N is required to be a Sophie-German prime (of the form
+ N = 2q + 1, where q is also prime) and the generator g is a primitive
+ root of GF(n). In iSCSI authentication, the prime modulus N MUST be
+ at least 768 bits.
+
+ The list of allowed SRP groups is provided in [RFC3723].
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 103]
+
+RFC 3720 iSCSI April 2004
+
+
+8.3. IPsec
+
+ iSCSI uses the IPsec mechanism for packet protection (cryptographic
+ integrity, authentication, and confidentiality) at the IP level
+ between the iSCSI communicating end points. The following sections
+ describe the IPsec protocols that must be implemented for data
+ integrity and authentication, confidentiality, and cryptographic key
+ management.
+
+ An iSCSI initiator or target may provide the required IPsec support
+ fully integrated or in conjunction with an IPsec front-end device.
+ In the latter case, the compliance requirements with regard to IPsec
+ support apply to the "combined device". Only the "combined device"
+ is to be considered an iSCSI device.
+
+ Detailed considerations and recommendations for using IPsec for iSCSI
+ are provided in [RFC3723].
+
+8.3.1. Data Integrity and Authentication
+
+ Data authentication and integrity is provided by a cryptographic
+ keyed Message Authentication Code in every sent packet. This code
+ protects against message insertion, deletion, and modification.
+ Protection against message replay is realized by using a sequence
+ counter.
+
+ An iSCSI compliant initiator or target MUST provide data integrity
+ and authentication by implementing IPsec [RFC2401] with ESP [RFC2406]
+ in tunnel mode and MAY provide data integrity and authentication by
+ implementing IPsec with ESP in transport mode. The IPsec
+ implementation MUST fulfill the following iSCSI specific
+ requirements:
+
+ - HMAC-SHA1 MUST be implemented [RFC2404].
+ - AES CBC MAC with XCBC extensions SHOULD be implemented
+ [RFC3566].
+
+ The ESP anti-replay service MUST also be implemented.
+
+ At the high speeds iSCSI is expected to operate, a single IPsec SA
+ could rapidly cycle through the 32-bit IPsec sequence number space.
+ In view of this, it may be desirable in the future for an iSCSI
+ implementation that operates at speeds of 1 Gbps or greater to
+ implement the IPsec sequence number extension [SEQ-EXT].
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 104]
+
+RFC 3720 iSCSI April 2004
+
+
+8.3.2. Confidentiality
+
+ Confidentiality is provided by encrypting the data in every packet.
+ When confidentiality is used it MUST be accompanied by data integrity
+ and authentication to provide comprehensive protection against
+ eavesdropping, message insertion, deletion, modification, and
+ replaying.
+
+ An iSCSI compliant initiator or target MUST provide confidentiality
+ by implementing IPsec [RFC2401] with ESP [RFC2406] in tunnel mode and
+ MAY provide confidentiality by implementing IPsec with ESP in
+ transport mode, with the following iSCSI specific requirements:
+
+ - 3DES in CBC mode MUST be implemented [RFC2451].
+ - AES in Counter mode SHOULD be implemented [RFC3686].
+
+ DES in CBC mode SHOULD NOT be used due to its inherent weakness. The
+ NULL encryption algorithm MUST also be implemented.
+
+8.3.3. Policy, Security Associations, and Cryptographic Key Management
+
+ A compliant iSCSI implementation MUST meet the cryptographic key
+ management requirements of the IPsec protocol suite. Authentication,
+ security association negotiation, and cryptographic key management
+ MUST be provided by implementing IKE [RFC2409] using the IPsec DOI
+ [RFC2407] with the following iSCSI specific requirements:
+
+ - Peer authentication using a pre-shared cryptographic key MUST be
+ supported. Certificate-based peer authentication using digital
+ signatures MAY be supported. Peer authentication using the
+ public key encryption methods outlined in IKE sections 5.2 and
+ 5.3[7] SHOULD NOT be used.
+
+ - When digital signatures are used to achieve authentication, an
+ IKE negotiator SHOULD use IKE Certificate Request Payload(s) to
+ specify the certificate authority. IKE negotiators SHOULD check
+ the pertinent Certificate Revocation List (CRL) before accepting
+ a PKI certificate for use in IKE authentication procedures.
+
+ - Conformant iSCSI implementations MUST support IKE Main Mode and
+ SHOULD support Aggressive Mode. IKE main mode with pre-shared
+ key authentication method SHOULD NOT be used when either the
+ initiator or the target uses dynamically assigned IP addresses.
+ While in many cases pre-shared keys offer good security,
+ situations in which dynamically assigned addresses are used force
+ the use of a group pre-shared key, which creates vulnerability to
+ a man-in-the-middle attack.
+
+
+
+
+Satran, et al. Standards Track [Page 105]
+
+RFC 3720 iSCSI April 2004
+
+
+ - In the IKE Phase 2 Quick Mode, exchanges for creating the Phase 2
+ SA, the Identity Payload, fields MUST be present. ID_IPV4_ADDR,
+ ID_IPV6_ADDR (if the protocol stack supports IPv6) and ID_FQDN
+ Identity payloads MUST be supported; ID_USER_FQDN SHOULD be
+ supported. The IP Subnet, IP Address Range, ID_DER_ASN1_DN, and
+ ID_DER_ASN1_GN formats SHOULD NOT be used. The ID_KEY_ID
+ Identity Payload MUST NOT be used.
+
+ Manual cryptographic keying MUST NOT be used because it does not
+ provide the necessary re-keying support.
+
+ When IPsec is used, the receipt of an IKE Phase 2 delete message
+ SHOULD NOT be interpreted as a reason for tearing down the iSCSI TCP
+ connection. If additional traffic is sent on it, a new IKE Phase 2
+ SA will be created to protect it.
+
+ The method used by the initiator to determine whether the target
+ should be connected using IPsec is regarded as an issue of IPsec
+ policy administration, and thus not defined in the iSCSI standard.
+
+ If an iSCSI target is discovered via a SendTargets request in a
+ discovery session not using IPsec, the initiator should assume that
+ it does not need IPsec to establish a session to that target. If an
+ iSCSI target is discovered using a discovery session that does use
+ IPsec, the initiator SHOULD use IPsec when establishing a session to
+ that target.
+
+9. Notes to Implementers
+
+ This section notes some of the performance and reliability
+ considerations of the iSCSI protocol. This protocol was designed to
+ allow efficient silicon and software implementations. The iSCSI task
+ tag mechanism was designed to enable Direct Data Placement (DDP - a
+ DMA form) at the iSCSI level or lower.
+
+ The guiding assumption made throughout the design of this protocol is
+ that targets are resource constrained relative to initiators.
+
+ Implementers are also advised to consider the implementation
+ consequences of the iSCSI to SCSI mapping model as outlined in
+ Section 3.4.3 Consequences of the Model.
+
+9.1. Multiple Network Adapters
+
+ The iSCSI protocol allows multiple connections, not all of which need
+ to go over the same network adapter. If multiple network connections
+ are to be utilized with hardware support, the iSCSI protocol
+
+
+
+
+Satran, et al. Standards Track [Page 106]
+
+RFC 3720 iSCSI April 2004
+
+
+ command-data-status allegiance to one TCP connection ensures that
+ there is no need to replicate information across network adapters or
+ otherwise require them to cooperate.
+
+ However, some task management commands may require some loose form of
+ cooperation or replication at least on the target.
+
+9.1.1. Conservative Reuse of ISIDs
+
+ Historically, the SCSI model (and implementations and applications
+ based on that model) has assumed that SCSI ports are static, physical
+ entities. Recent extensions to the SCSI model have taken advantage
+ of persistent worldwide unique names for these ports. In iSCSI
+ however, the SCSI initiator ports are the endpoints of dynamically
+ created sessions, so the presumptions of "static and physical" do not
+ apply. In any case, the model clauses (particularly, Section 3.4.2
+ SCSI Architecture Model) provide for persistent, reusable names for
+ the iSCSI-type SCSI initiator ports even though there does not need
+ to be any physical entity bound to these names.
+
+ To both minimize the disruption of legacy applications and to better
+ facilitate the SCSI features that rely on persistent names for SCSI
+ ports, iSCSI implementations SHOULD attempt to provide a stable
+ presentation of SCSI Initiator Ports (both to the upper OS-layers and
+ to the targets to which they connect). This can be achieved in an
+ initiator implementation by conservatively reusing ISIDs. In other
+ words, the same ISID should be used in the Login process to multiple
+ target portal groups (of the same iSCSI Target or different iSCSI
+ Targets). The ISID RULE (Section 3.4.3 Consequences of the Model)
+ only prohibits reuse to the same target portal group. It does not
+ "preclude" reuse to other target portal groups. The principle of
+ conservative reuse "encourages" reuse to other target portal groups.
+ When a SCSI target device sees the same (InitiatorName, ISID) pair in
+ different sessions to different target portal groups, it can identify
+ the underlying SCSI Initiator Port on each session as the same SCSI
+ port. In effect, it can recognize multiple paths from the same
+ source.
+
+9.1.2. iSCSI Name, ISID, and TPGT Use
+
+ The designers of the iSCSI protocol envisioned there being one iSCSI
+ Initiator Node Name per operating system image on a machine. This
+ enables SAN resource configuration and authentication schemes based
+ on a system's identity. It supports the notion that it should be
+ possible to assign access to storage resources based on "initiator
+ device" identity.
+
+
+
+
+
+Satran, et al. Standards Track [Page 107]
+
+RFC 3720 iSCSI April 2004
+
+
+ When there are multiple hardware or software components coordinated
+ as a single iSCSI Node, there must be some (logical) entity that
+ represents the iSCSI Node that makes the iSCSI Node Name available to
+ all components involved in session creation and login. Similarly,
+ this entity that represents the iSCSI Node must be able to coordinate
+ session identifier resources (ISID for initiators) to enforce both
+ the ISID and TSIH RULES (see Section 3.4.3 Consequences of the
+ Model).
+
+ For targets, because of the closed environment, implementation of
+ this entity should be straightforward. However, vendors of iSCSI
+ hardware (e.g., NICs or HBAs) intended for targets, SHOULD provide
+ mechanisms for configuration of the iSCSI Node Name across the portal
+ groups instantiated by multiple instances of these components within
+ a target.
+
+ However, complex targets making use of multiple Target Portal Group
+ Tags may reconfigure them to achieve various quality goals. The
+ initiators have two mechanisms at their disposal to discover and/or
+ check reconfiguring targets - the discovery session type and a key
+ returned by the target during login to confirm the TPGT. An
+ initiator should attempt to "rediscover" the target configuration
+ anytime a session is terminated unexpectedly.
+
+ For initiators, in the long term, it is expected that operating
+ system vendors will take on the role of this entity and provide
+ standard APIs that can inform components of their iSCSI Node Name and
+ can configure and/or coordinate ISID allocation, use, and reuse.
+
+ Recognizing that such initiator APIs are not available today, other
+ implementations of the role of this entity are possible. For
+ example, a human may instantiate the (common) Node name as part of
+ the installation process of each iSCSI component involved in session
+ creation and login. This may be done either by pointing the
+ component to a vendor-specific location for this datum or to a
+ system-wide location. The structure of the ISID namespace (see
+ Section 10.12.5 ISID and [RFC3721]) facilitates implementation of the
+ ISID coordination by allowing each component vendor to independently
+ (of other vendor's components) coordinate allocation, use, and reuse
+ of its own partition of the ISID namespace in a vendor-specific
+ manner. Partitioning of the ISID namespace within initiator portal
+ groups managed by that vendor allows each such initiator portal group
+ to act independently of all other portal groups when selecting an
+ ISID for a login; this facilitates enforcement of the ISID RULE (see
+ Section 3.4.3 Consequences of the Model) at the initiator.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 108]
+
+RFC 3720 iSCSI April 2004
+
+
+ A vendor of iSCSI hardware (e.g., NICs or HBAs) intended for use in
+ initiators MUST implement a mechanism for configuring the iSCSI Node
+ Name. Vendors, and administrators must ensure that iSCSI Node Names
+ are unique worldwide. It is therefore important that when one
+ chooses to reuse the iSCSI Node Name of a disabled unit, not to
+ re-assign that name to the original unit unless its worldwide
+ uniqueness can be ascertained again.
+
+ In addition, a vendor of iSCSI hardware must implement a mechanism to
+ configure and/or coordinate ISIDs for all sessions managed by
+ multiple instances of that hardware within a given iSCSI Node. Such
+ configuration might be either permanently pre-assigned at the factory
+ (in a necessarily globally unique way), statically assigned (e.g.,
+ partitioned across all the NICs at initialization in a locally unique
+ way), or dynamically assigned (e.g., on-line allocator, also in a
+ locally unique way). In the latter two cases, the configuration may
+ be via public APIs (perhaps driven by an independent vendor's
+ software, such as the OS vendor) or via private APIs driven by the
+ vendor's own software.
+
+9.2. Autosense and Auto Contingent Allegiance (ACA)
+
+ Autosense refers to the automatic return of sense data to the
+ initiator in case a command did not complete successfully. iSCSI
+ initiators and targets MUST support and use autosense.
+
+ ACA helps preserve ordered command execution in the presence of
+ errors. As iSCSI can have many commands in-flight between initiator
+ and target, iSCSI initiators and targets SHOULD support ACA.
+
+9.3. iSCSI Timeouts
+
+ iSCSI recovery actions are often dependent on iSCSI time-outs being
+ recognized and acted upon before SCSI time-outs. Determining the
+ right time-outs to use for various iSCSI actions (command
+ acknowledgements expected, status acknowledgements, etc.) is very
+ much dependent on infrastructure (hardware, links, TCP/IP stack,
+ iSCSI driver). As a guide, the implementer may use an average
+ Nop-Out/Nop-In turnaround delay multiplied by a "safety factor"
+ (e.g., 4) as a good estimate for the basic delay of the iSCSI stack
+ for a given connection. The safety factor should account for the
+ network load variability. For connection teardown the implementer
+ may want to consider also the TCP common practice for the given
+ infrastructure.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 109]
+
+RFC 3720 iSCSI April 2004
+
+
+ Text negotiations MAY also be subject to either time-limits or limits
+ in the number of exchanges. Those SHOULD be generous enough to avoid
+ affecting interoperability (e.g., allowing each key to be negotiated
+ on a separate exchange).
+
+ The relation between iSCSI timeouts and SCSI timeouts should also be
+ considered. SCSI timeouts should be longer than iSCSI timeouts plus
+ the time required for iSCSI recovery whenever iSCSI recovery is
+ planned. Alternatively, an implementer may choose to interlock iSCSI
+ timeouts and recovery with SCSI timeouts so that SCSI recovery will
+ become active only where iSCSI is not planned to, or failed to,
+ recover.
+
+ The implementer may also want to consider the interaction between
+ various iSCSI exception events - such as a digest failure - and
+ subsequent timeouts. When iSCSI error recovery is active, a digest
+ failure is likely to result in discovering a missing command or data
+ PDU. In these cases, an implementer may want to lower the timeout
+ values to enable faster initiation for recovery procedures.
+
+9.4. Command Retry and Cleaning Old Command Instances
+
+ To avoid having old, retried command instances appear in a valid
+ command window after a command sequence number wrap around, the
+ protocol requires (see Section 3.2.2.1 Command Numbering and
+ Acknowledging) that on every connection on which a retry has been
+ issued, a non-immediate command be issued and acknowledged within a
+ 2**31-1 commands interval from the CmdSN of the retried command.
+ This requirement can be fulfilled by an implementation in several
+ ways.
+
+ The simplest technique to use is to send a (non-retry) non-immediate
+ SCSI command (or a NOP if no SCSI command is available for a while)
+ after every command retry on the connection on which the retry was
+ attempted. As errors are deemed rare events, this technique is
+ probably the most effective, as it does not involve additional checks
+ at the initiator when issuing commands.
+
+9.5. Synch and Steering Layer and Performance
+
+ While a synch and steering layer is optional, an initiator/target
+ that does not have it working against a target/initiator that demands
+ synch and steering may experience performance degradation caused by
+ packet reordering and loss. Providing a synch and steering mechanism
+ is recommended for all high-speed implementations.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 110]
+
+RFC 3720 iSCSI April 2004
+
+
+9.6. Considerations for State-dependent Devices and Long-lasting SCSI
+ Operations
+
+ Sequential access devices operate on the principle that the position
+ of the device is based on the last command processed. As such,
+ command processing order and knowledge of whether or not the previous
+ command was processed is of the utmost importance to maintain data
+ integrity. For example, inadvertent retries of SCSI commands when it
+ is not known if the previous SCSI command was processed is a
+ potential data integrity risk.
+
+ For a sequential access device, consider the scenario in which a SCSI
+ SPACE command to backspace one filemark is issued and then re-issued
+ due to no status received for the command. If the first SPACE
+ command was actually processed, the re-issued SPACE command, if
+ processed, will cause the position to change. Thus, a subsequent
+ write operation will write data to the wrong position and any
+ previous data at that position will be overwritten.
+
+ For a medium changer device, consider the scenario in which an
+ EXCHANGE MEDIUM command (the SOURCE ADDRESS and DESTINATION ADDRESS
+ are the same thus performing a swap) is issued and then re-issued due
+ to no status received for the command. If the first EXCHANGE MEDIUM
+ command was actually processed, the re-issued EXCHANGE MEDIUM
+ command, if processed, will perform the swap again. The net effect
+ is that a swap was not performed thus leaving a data integrity
+ exposure.
+
+ All commands that change the state of the device (as in SPACE
+ commands for sequential access devices, and EXCHANGE MEDIUM for
+ medium changer device), MUST be issued as non-immediate commands for
+ deterministic and in order delivery to iSCSI targets.
+
+ For many of those state changing commands, the execution model also
+ assumes that the command is executed exactly once. Devices
+ implementing READ POSITION and LOCATE provide a means for SCSI level
+ command recovery and new tape-class devices should support those
+ commands. In their absence a retry at SCSI level is difficult and
+ error recovery at iSCSI level is advisable.
+
+ Devices operating on long latency delivery subsystems and performing
+ long lasting SCSI operations may need mechanisms that enable
+ connection replacement while commands are running (e.g., during an
+ extended copy operation).
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 111]
+
+RFC 3720 iSCSI April 2004
+
+
+9.6.1. Determining the Proper ErrorRecoveryLevel
+
+ The implementation and use of a specific ErrorRecoveryLevel should be
+ determined based on the deployment scenarios of a given iSCSI
+ implementation. Generally, the following factors must be considered
+ before deciding on the proper level of recovery:
+
+ a) Application resilience to I/O failures.
+ b) Required level of availability in the face of transport
+ connection failures.
+ c) Probability of transport layer "checksum escape". This in
+ turn decides the iSCSI digest failure frequency, and thus the
+ criticality of iSCSI-level error recovery. The details of
+ estimating this probability are outside the scope of this
+ document.
+
+
+ A consideration of the above factors for SCSI tape devices as an
+ example suggests that implementations SHOULD use ErrorRecoveryLevel=1
+ when transport connection failure is not a concern and SCSI level
+ recovery is unavailable, and ErrorRecoveryLevel=2 when the connection
+ failure is also of high likelihood during a backup/retrieval.
+
+ For extended copy operations, implementations SHOULD use
+ ErrorRecoveryLevel=2 whenever there is a relatively high likelihood
+ of connection failure.
+
+10. iSCSI PDU Formats
+
+ All multi-byte integers that are specified in formats defined in this
+ document are to be represented in network byte order (i.e., big
+ endian). Any field that appears in this document assumes that the
+ most significant byte is the lowest numbered byte and the most
+ significant bit (within byte or field) is the lowest numbered bit
+ unless specified otherwise.
+
+ Any compliant sender MUST set all bits not defined and all reserved
+ fields to zero unless specified otherwise. Any compliant receiver
+ MUST ignore any bit not defined and all reserved fields unless
+ specified otherwise. Receipt of reserved code values in defined
+ fields MUST be reported as a protocol error.
+
+ Reserved fields are marked by the word "reserved", some abbreviation
+ of "reserved", or by "." for individual bits when no other form of
+ marking is technically feasible.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 112]
+
+RFC 3720 iSCSI April 2004
+
+
+10.1. iSCSI PDU Length and Padding
+
+ iSCSI PDUs are padded to the closest integer number of four byte
+ words. The padding bytes SHOULD be sent as 0.
+
+10.2. PDU Template, Header, and Opcodes
+
+ All iSCSI PDUs have one or more header segments and, optionally, a
+ data segment. After the entire header segment group a header-digest
+ MAY follow. The data segment MAY also be followed by a data-digest.
+
+ The Basic Header Segment (BHS) is the first segment in all of the
+ iSCSI PDUs. The BHS is a fixed-length 48-byte header segment. It
+ MAY be followed by Additional Header Segments (AHS), a Header-Digest,
+ a Data Segment, and/or a Data-Digest.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 113]
+
+RFC 3720 iSCSI April 2004
+
+
+ The overall structure of an iSCSI PDU is as follows:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0/ Basic Header Segment (BHS) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48/ Additional Header Segment 1 (AHS) (optional) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ / Additional Header Segment 2 (AHS) (optional) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ ----
+ +---------------+---------------+---------------+---------------+
+ / Additional Header Segment n (AHS) (optional) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ ----
+ +---------------+---------------+---------------+---------------+
+ k/ Header-Digest (optional) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ l/ Data Segment(optional) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ m/ Data-Digest (optional) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+
+ All PDU segments and digests are padded to the closest integer number
+ of four byte words. For example, all PDU segments and digests start
+ at a four byte word boundary and the padding ranges from 0 to 3
+ bytes. The padding bytes SHOULD be sent as 0.
+
+ iSCSI response PDUs do not have AH Segments.
+
+10.2.1. Basic Header Segment (BHS)
+
+ The BHS is 48 bytes long. The Opcode and DataSegmentLength fields
+ appear in all iSCSI PDUs. In addition, when used, the Initiator Task
+ Tag and Logical Unit Number always appear in the same location in the
+ header.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 114]
+
+RFC 3720 iSCSI April 2004
+
+
+ The format of the BHS is:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|I| Opcode |F| Opcode-specific fields |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| LUN or Opcode-specific fields |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20/ Opcode-specific fields /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48
+
+10.2.1.1 I
+
+ For request PDUs, the I bit set to 1 is an immediate delivery marker.
+
+10.2.1.2. Opcode
+
+ The Opcode indicates the type of iSCSI PDU the header encapsulates.
+
+ The Opcodes are divided into two categories: initiator opcodes and
+ target opcodes. Initiator opcodes are in PDUs sent by the initiator
+ (request PDUs). Target opcodes are in PDUs sent by the target
+ (response PDUs).
+
+ Initiators MUST NOT use target opcodes and targets MUST NOT use
+ initiator opcodes.
+
+ Initiator opcodes defined in this specification are:
+
+ 0x00 NOP-Out
+ 0x01 SCSI Command (encapsulates a SCSI Command Descriptor Block)
+ 0x02 SCSI Task Management function request
+ 0x03 Login Request
+ 0x04 Text Request
+ 0x05 SCSI Data-Out (for WRITE operations)
+ 0x06 Logout Request
+ 0x10 SNACK Request
+ 0x1c-0x1e Vendor specific codes
+
+
+
+Satran, et al. Standards Track [Page 115]
+
+RFC 3720 iSCSI April 2004
+
+
+
+ Target opcodes are:
+
+ 0x20 NOP-In
+ 0x21 SCSI Response - contains SCSI status and possibly sense
+ information or other response information.
+ 0x22 SCSI Task Management function response
+ 0x23 Login Response
+ 0x24 Text Response
+ 0x25 SCSI Data-In - for READ operations.
+ 0x26 Logout Response
+ 0x31 Ready To Transfer (R2T) - sent by target when it is ready
+ to receive data.
+ 0x32 Asynchronous Message - sent by target to indicate certain
+ special conditions.
+ 0x3c-0x3e Vendor specific codes
+ 0x3f Reject
+
+ All other opcodes are reserved.
+
+10.2.1.3. Final (F) bit
+
+ When set to 1 it indicates the final (or only) PDU of a sequence.
+
+10.2.1.4. Opcode-specific Fields
+
+ These fields have different meanings for different opcode types.
+
+10.2.1.5. TotalAHSLength
+
+ Total length of all AHS header segments in units of four byte words
+ including padding, if any.
+
+ The TotalAHSLength is only used in PDUs that have an AHS and MUST be
+ 0 in all other PDUs.
+
+10.2.1.6. DataSegmentLength
+
+ This is the data segment payload length in bytes (excluding padding).
+ The DataSegmentLength MUST be 0 whenever the PDU has no data segment.
+
+10.2.1.7. LUN
+
+ Some opcodes operate on a specific Logical Unit. The Logical Unit
+ Number (LUN) field identifies which Logical Unit. If the opcode does
+ not relate to a Logical Unit, this field is either ignored or may be
+ used in an opcode specific way. The LUN field is 64-bits and should
+
+
+
+
+Satran, et al. Standards Track [Page 116]
+
+RFC 3720 iSCSI April 2004
+
+
+ be formatted in accordance with [SAM2]. For example, LUN[0] from
+ [SAM2] is BHS byte 8 and so on up to LUN[7] from [SAM2], which is BHS
+ byte 15.
+
+10.2.1.8. Initiator Task Tag
+
+ The initiator assigns a Task Tag to each iSCSI task it issues. While
+ a task exists, this tag MUST uniquely identify the task session-wide.
+ SCSI may also use the initiator task tag as part of the SCSI task
+ identifier when the timespan during which an iSCSI initiator task tag
+ must be unique extends over the timespan during which a SCSI task tag
+ must be unique. However, the iSCSI Initiator Task Tag must exist and
+ be unique even for untagged SCSI commands.
+
+10.2.2. Additional Header Segment (AHS)
+
+ The general format of an AHS is:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0| AHSLength | AHSType | AHS-Specific |
+ +---------------+---------------+---------------+---------------+
+ 4/ AHS-Specific /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ x
+
+10.2.2.1. AHSType
+
+ The AHSType field is coded as follows:
+
+ bit 0-1 - Reserved
+
+ bit 2-7 - AHS code
+
+ 0 - Reserved
+ 1 - Extended CDB
+ 2 - Expected Bidirectional Read Data Length
+ 3 - 63 Reserved
+
+10.2.2.2. AHSLength
+
+ This field contains the effective length in bytes of the AHS
+ excluding AHSType and AHSLength and padding, if any. The AHS is
+ padded to the smallest integer number of 4 byte words (i.e., from 0
+ up to 3 padding bytes).
+
+
+
+Satran, et al. Standards Track [Page 117]
+
+RFC 3720 iSCSI April 2004
+
+
+10.2.2.3. Extended CDB AHS
+
+ The format of the Extended CDB AHS is:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0| AHSLength (CDBLength-15) | 0x01 | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4/ ExtendedCDB...+padding /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ x
+
+ This type of AHS MUST NOT be used if the CDBLength is less than 17.
+ The length includes the reserved byte 3.
+
+10.2.2.4. Bidirectional Expected Read-Data Length AHS
+
+ The format of the Bidirectional Read Expected Data Transfer Length
+ AHS is:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0| AHSLength (0x0005) | 0x02 | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4| Expected Read-Data Length |
+ +---------------+---------------+---------------+---------------+
+ 8
+
+10.2.3. Header Digest and Data Digest
+
+ Optional header and data digests protect the integrity of the header
+ and data, respectively. The digests, if present, are located,
+ respectively, after the header and PDU-specific data, and cover
+ respectively the header and the PDU data, each including the padding
+ bytes, if any.
+
+ The existence and type of digests are negotiated during the Login
+ Phase.
+
+ The separation of the header and data digests is useful in iSCSI
+ routing applications, in which only the header changes when a message
+ is forwarded. In this case, only the header digest should be
+ recalculated.
+
+
+
+Satran, et al. Standards Track [Page 118]
+
+RFC 3720 iSCSI April 2004
+
+
+ Digests are not included in data or header length fields.
+
+ A zero-length Data Segment also implies a zero-length data-digest.
+
+10.2.4. Data Segment
+
+ The (optional) Data Segment contains PDU associated data. Its
+ payload effective length is provided in the BHS field -
+ DataSegmentLength. The Data Segment is also padded to an integer
+ number of 4 byte words.
+
+10.3. SCSI Command
+
+ The format of the SCSI Command PDU is:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|I| 0x01 |F|R|W|. .|ATTR | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| Logical Unit Number (LUN) |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| Expected Data Transfer Length |
+ +---------------+---------------+---------------+---------------+
+ 24| CmdSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpStatSN |
+ +---------------+---------------+---------------+---------------+
+ 32/ SCSI Command Descriptor Block (CDB) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48/ AHS (Optional) /
+ +---------------+---------------+---------------+---------------+
+ x/ Header Digest (Optional) /
+ +---------------+---------------+---------------+---------------+
+ y/ (DataSegment, Command Data) (Optional) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ z/ Data Digest (Optional) /
+ +---------------+---------------+---------------+---------------+
+
+
+
+
+Satran, et al. Standards Track [Page 119]
+
+RFC 3720 iSCSI April 2004
+
+
+10.3.1. Flags and Task Attributes (byte 1)
+
+ The flags for a SCSI Command are:
+
+ bit 0 (F) is set to 1 when no unsolicited SCSI Data-Out PDUs follow
+ this PDU. When F=1 for a write and if Expected Data
+ Transfer Length is larger than the DataSegmentLength, the
+ target may solicit additional data through R2T.
+
+ bit 1 (R) is set to 1 when the command is expected to input data.
+
+ bit 2 (W) is set to 1 when the command is expected to output data.
+
+ bit 3-4 Reserved.
+
+ bit 5-7 contains Task Attributes.
+
+ Task Attributes (ATTR) have one of the following integer values (see
+ [SAM2] for details):
+
+ 0 - Untagged
+ 1 - Simple
+ 2 - Ordered
+ 3 - Head of Queue
+ 4 - ACA
+ 5-7 - Reserved
+
+ Setting both the W and the F bit to 0 is an error. Either or both of
+ R and W MAY be 1 when either the Expected Data Transfer Length and/or
+ Bidirectional Read Expected Data Transfer Length are 0, but they MUST
+ NOT both be 0 when the Expected Data Transfer Length and/or
+ Bidirectional Read Expected Data Transfer Length are not 0 (i.e.,
+ when some data transfer is expected the transfer direction is
+ indicated by the R and/or W bit).
+
+10.3.2. CmdSN - Command Sequence Number
+
+ Enables ordered delivery across multiple connections in a single
+ session.
+
+10.3.3. ExpStatSN
+
+ Command responses up to ExpStatSN-1 (mod 2**32) have been received
+ (acknowledges status) on the connection.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 120]
+
+RFC 3720 iSCSI April 2004
+
+
+10.3.4. Expected Data Transfer Length
+
+ For unidirectional operations, the Expected Data Transfer Length
+ field contains the number of bytes of data involved in this SCSI
+ operation. For a unidirectional write operation (W flag set to 1 and
+ R flag set to 0), the initiator uses this field to specify the number
+ of bytes of data it expects to transfer for this operation. For a
+ unidirectional read operation (W flag set to 0 and R flag set to 1),
+ the initiator uses this field to specify the number of bytes of data
+ it expects the target to transfer to the initiator. It corresponds
+ to the SAM2 byte count.
+
+ For bidirectional operations (both R and W flags are set to 1), this
+ field contains the number of data bytes involved in the write
+ transfer. For bidirectional operations, an additional header segment
+ MUST be present in the header sequence that indicates the
+ Bidirectional Read Expected Data Transfer Length. The Expected Data
+ Transfer Length field and the Bidirectional Read Expected Data
+ Transfer Length field correspond to the SAM2 byte count
+
+ If the Expected Data Transfer Length for a write and the length of
+ the immediate data part that follows the command (if any) are the
+ same, then no more data PDUs are expected to follow. In this case,
+ the F bit MUST be set to 1.
+
+ If the Expected Data Transfer Length is higher than the
+ FirstBurstLength (the negotiated maximum amount of unsolicited data
+ the target will accept), the initiator MUST send the maximum amount
+ of unsolicited data OR ONLY the immediate data, if any.
+
+ Upon completion of a data transfer, the target informs the initiator
+ (through residual counts) of how many bytes were actually processed
+ (sent and/or received) by the target.
+
+10.3.5. CDB - SCSI Command Descriptor Block
+
+ There are 16 bytes in the CDB field to accommodate the commonly used
+ CDBs. Whenever the CDB is larger than 16 bytes, an Extended CDB AHS
+ MUST be used to contain the CDB spillover.
+
+10.3.6. Data Segment - Command Data
+
+ Some SCSI commands require additional parameter data to accompany the
+ SCSI command. This data may be placed beyond the boundary of the
+ iSCSI header in a data segment. Alternatively, user data (e.g., from
+ a WRITE operation) can be placed in the data segment (both cases are
+
+
+
+
+
+Satran, et al. Standards Track [Page 121]
+
+RFC 3720 iSCSI April 2004
+
+
+ referred to as immediate data). These data are governed by the rules
+ for solicited vs. unsolicited data outlined in Section 3.2.4.2 Data
+ Transfer Overview.
+
+10.4. SCSI Response
+
+ The format of the SCSI Response PDU is:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x21 |1|. .|o|u|O|U|.| Response | Status |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| Reserved |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| SNACK Tag or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 24| StatSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 32| MaxCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 36| ExpDataSN or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 40| Bidirectional Read Residual Count or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 44| Residual Count or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+ / Data Segment (Optional) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ | Data-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 122]
+
+RFC 3720 iSCSI April 2004
+
+
+10.4.1. Flags (byte 1)
+
+ bit 1-2 Reserved.
+
+ bit 3 - (o) set for Bidirectional Read Residual Overflow. In this
+ case, the Bidirectional Read Residual Count indicates the number
+ of bytes that were not transferred to the initiator because the
+ initiator's Expected Bidirectional Read Data Transfer Length was
+ not sufficient.
+
+ bit 4 - (u) set for Bidirectional Read Residual Underflow. In this
+ case, the Bidirectional Read Residual Count indicates the number
+ of bytes that were not transferred to the initiator out of the
+ number of bytes expected to be transferred.
+
+ bit 5 - (O) set for Residual Overflow. In this case, the Residual
+ Count indicates the number of bytes that were not transferred
+ because the initiator's Expected Data Transfer Length was not
+ sufficient. For a bidirectional operation, the Residual Count
+ contains the residual for the write operation.
+
+ bit 6 - (U) set for Residual Underflow. In this case, the Residual
+ Count indicates the number of bytes that were not transferred out
+ of the number of bytes that were expected to be transferred. For
+ a bidirectional operation, the Residual Count contains the
+ residual for the write operation.
+
+ bit 7 - (0) Reserved.
+
+ Bits O and U and bits o and u are mutually exclusive (i.e., having
+ both o and u or O and U set to 1 is a protocol error). For a
+ response other than "Command Completed at Target", bits 3-6 MUST be
+ 0.
+
+10.4.2. Status
+
+ The Status field is used to report the SCSI status of the command (as
+ specified in [SAM2]) and is only valid if the Response Code is
+ Command Completed at target.
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 123]
+
+RFC 3720 iSCSI April 2004
+
+
+ Some of the status codes defined in [SAM2] are:
+
+ 0x00 GOOD
+ 0x02 CHECK CONDITION
+ 0x08 BUSY
+ 0x18 RESERVATION CONFLICT
+ 0x28 TASK SET FULL
+ 0x30 ACA ACTIVE
+ 0x40 TASK ABORTED
+
+ See [SAM2] for the complete list and definitions.
+
+ If a SCSI device error is detected while data from the initiator is
+ still expected (the command PDU did not contain all the data and the
+ target has not received a Data PDU with the final bit Set), the
+ target MUST wait until it receives a Data PDU with the F bit set in
+ the last expected sequence before sending the Response PDU.
+
+10.4.3. Response
+
+ This field contains the iSCSI service response.
+
+ iSCSI service response codes defined in this specification are:
+
+ 0x00 - Command Completed at Target
+ 0x01 - Target Failure
+ 0x80-0xff - Vendor specific
+
+ All other response codes are reserved.
+
+ The Response is used to report a Service Response. The mapping of
+ the response code into a SCSI service response code value, if needed,
+ is outside the scope of this document. However, in symbolic terms
+ response value 0x00 maps to the SCSI service response (see [SAM2] and
+ [SPC3]) of TASK COMPLETE or LINKED COMMAND COMPLETE. All other
+ Response values map to the SCSI service response of SERVICE DELIVERY
+ OR TARGET FAILURE.
+
+ If a PDU that includes SCSI status (Response PDU or Data-In PDU
+ including status) does not arrive before the session is terminated,
+ the SCSI service response is SERVICE DELIVERY OR TARGET FAILURE.
+
+ A non-zero Response field indicates a failure to execute the command
+ in which case the Status and Flag fields are undefined.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 124]
+
+RFC 3720 iSCSI April 2004
+
+
+10.4.4. SNACK Tag
+
+ This field contains a copy of the SNACK Tag of the last SNACK Tag
+ accepted by the target on the same connection and for the command for
+ which the response is issued. Otherwise it is reserved and should be
+ set to 0.
+
+ After issuing a R-Data SNACK the initiator must discard any SCSI
+ status unless contained in an SCSI Response PDU carrying the same
+ SNACK Tag as the last issued R-Data SNACK for the SCSI command on the
+ current connection.
+
+ For a detailed discussion on R-Data SNACK see Section 10.16 SNACK
+ Request.
+
+10.4.5. Residual Count
+
+ The Residual Count field MUST be valid in the case where either the U
+ bit or the O bit is set. If neither bit is set, the Residual Count
+ field is reserved. Targets may set the residual count and initiators
+ may use it when the response code is "completed at target" (even if
+ the status returned is not GOOD). If the O bit is set, the Residual
+ Count indicates the number of bytes that were not transferred because
+ the initiator's Expected Data Transfer Length was not sufficient. If
+ the U bit is set, the Residual Count indicates the number of bytes
+ that were not transferred out of the number of bytes expected to be
+ transferred.
+
+10.4.6. Bidirectional Read Residual Count
+
+ The Bidirectional Read Residual Count field MUST be valid in the case
+ where either the u bit or the o bit is set. If neither bit is set,
+ the Bidirectional Read Residual Count field is reserved. Targets may
+ set the Bidirectional Read Residual Count and initiators may use it
+ when the response code is "completed at target". If the o bit is
+ set, the Bidirectional Read Residual Count indicates the number of
+ bytes that were not transferred to the initiator because the
+ initiator's Expected Bidirectional Read Transfer Length was not
+ sufficient. If the u bit is set, the Bidirectional Read Residual
+ Count indicates the number of bytes that were not transferred to the
+ initiator out of the number of bytes expected to be transferred.
+
+10.4.7. Data Segment - Sense and Response Data Segment
+
+ iSCSI targets MUST support and enable autosense. If Status is CHECK
+ CONDITION (0x02), then the Data Segment MUST contain sense data for
+ the failed command.
+
+
+
+
+Satran, et al. Standards Track [Page 125]
+
+RFC 3720 iSCSI April 2004
+
+
+ For some iSCSI responses, the response data segment MAY contain some
+ response related information, (e.g., for a target failure, it may
+ contain a vendor specific detailed description of the failure).
+
+ If the DataSegmentLength is not 0, the format of the Data Segment is
+ as follows:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|SenseLength | Sense Data |
+ +---------------+---------------+---------------+---------------+
+ x/ Sense Data /
+ +---------------+---------------+---------------+---------------+
+ y/ Response Data /
+ / /
+ +---------------+---------------+---------------+---------------+
+ z|
+
+10.4.7.1. SenseLength
+
+ Length of Sense Data.
+
+10.4.7.2. Sense Data
+
+ The Sense Data contains detailed information about a check condition
+ and [SPC3] specifies the format and content of the Sense Data.
+
+ Certain iSCSI conditions result in the command being terminated at
+ the target (response Command Completed at Target) with a SCSI Check
+ Condition Status as outlined in the next table:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 126]
+
+RFC 3720 iSCSI April 2004
+
+
+ +--------------------------+----------+---------------------------+
+ | iSCSI Condition |Sense | Additional Sense Code & |
+ | |Key | Qualifier |
+ +--------------------------+----------+---------------------------+
+ | Unexpected unsolicited |Aborted | ASC = 0x0c ASCQ = 0x0c |
+ | data |Command-0B| Write Error |
+ +--------------------------+----------+---------------------------+
+ | Incorrect amount of data |Aborted | ASC = 0x0c ASCQ = 0x0d |
+ | |Command-0B| Write Error |
+ +--------------------------+----------+---------------------------+
+ | Protocol Service CRC |Aborted | ASC = 0x47 ASCQ = 0x05 |
+ | error |Command-0B| CRC Error Detected |
+ +--------------------------+----------+---------------------------+
+ | SNACK rejected |Aborted | ASC = 0x11 ASCQ = 0x13 |
+ | |Command-0B| Read Error |
+ +--------------------------+----------+---------------------------+
+
+ The target reports the "Incorrect amount of data" condition if during
+ data output the total data length to output is greater than
+ FirstBurstLength and the initiator sent unsolicited non-immediate
+ data but the total amount of unsolicited data is different than
+ FirstBurstLength. The target reports the same error when the amount
+ of data sent as a reply to an R2T does not match the amount
+ requested.
+
+10.4.8. ExpDataSN
+
+ The number of R2T and Data-In (read) PDUs the target has sent for the
+ command.
+
+ This field MUST be 0 if the response code is not Command Completed at
+ Target or the target sent no Data-In PDUs for the command.
+
+10.4.9. StatSN - Status Sequence Number
+
+ StatSN is a Sequence Number that the target iSCSI layer generates per
+ connection and that in turn, enables the initiator to acknowledge
+ status reception. StatSN is incremented by 1 for every
+ response/status sent on a connection except for responses sent as a
+ result of a retry or SNACK. In the case of responses sent due to a
+ retransmission request, the StatSN MUST be the same as the first time
+ the PDU was sent unless the connection has since been restarted.
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 127]
+
+RFC 3720 iSCSI April 2004
+
+
+10.4.10. ExpCmdSN - Next Expected CmdSN from this Initiator
+
+ ExpCmdSN is a Sequence Number that the target iSCSI returns to the
+ initiator to acknowledge command reception. It is used to update a
+ local variable with the same name. An ExpCmdSN equal to MaxCmdSN+1
+ indicates that the target cannot accept new commands.
+
+10.4.11. MaxCmdSN - Maximum CmdSN from this Initiator
+
+ MaxCmdSN is a Sequence Number that the target iSCSI returns to the
+ initiator to indicate the maximum CmdSN the initiator can send. It
+ is used to update a local variable with the same name. If MaxCmdSN
+ is equal to ExpCmdSN-1, this indicates to the initiator that the
+ target cannot receive any additional commands. When MaxCmdSN changes
+ at the target while the target has no pending PDUs to convey this
+ information to the initiator, it MUST generate a NOP-IN to carry the
+ new MaxCmdSN.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 128]
+
+RFC 3720 iSCSI April 2004
+
+
+10.5. Task Management Function Request
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|I| 0x02 |1| Function | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| Logical Unit Number (LUN) or Reserved |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| Referenced Task Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 24| CmdSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpStatSN |
+ +---------------+---------------+---------------+---------------+
+ 32| RefCmdSN or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 36| ExpDataSN or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 40/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+10.5.1. Function
+
+ The Task Management functions provide an initiator with a way to
+ explicitly control the execution of one or more Tasks (SCSI and iSCSI
+ tasks). The Task Management function codes are listed below. For a
+ more detailed description of SCSI task management, see [SAM2].
+
+ 1 - ABORT TASK - aborts the task identified by the Referenced Task
+ Tag field.
+
+ 2 - ABORT TASK SET - aborts all Tasks issued via this session on the
+ logical unit.
+
+ 3 - CLEAR ACA - clears the Auto Contingent Allegiance condition.
+
+
+
+
+
+Satran, et al. Standards Track [Page 129]
+
+RFC 3720 iSCSI April 2004
+
+
+ 4 - CLEAR TASK SET - aborts all Tasks in the appropriate task set as
+ defined by the TST field in the Control mode page (see [SPC3]).
+
+ 5 - LOGICAL UNIT RESET
+
+ 6 - TARGET WARM RESET
+
+ 7 - TARGET COLD RESET
+
+ 8 - TASK REASSIGN - reassigns connection allegiance for the task
+ identified by the Referenced Task Tag field to this connection,
+ thus resuming the iSCSI exchanges for the task.
+
+ For all these functions, the Task Management function response MUST
+ be returned as detailed in Section 10.6 Task Management Function
+ Response. All these functions apply to the referenced tasks
+ regardless of whether they are proper SCSI tasks or tagged iSCSI
+ operations. Task management requests must act on all the commands
+ from the same session having a CmdSN lower than the task management
+ CmdSN. LOGICAL UNIT RESET, TARGET WARM RESET and TARGET COLD RESET
+ may affect commands from other sessions or commands from the same
+ session with CmdSN equal or exceeding CmdSN.
+
+ If the task management request is marked for immediate delivery, it
+ must be considered immediately for execution, but the operations
+ involved (all or part of them) may be postponed to allow the target
+ to receive all relevant tasks. According to [SAM2], for all the
+ tasks covered by the Task Management response (i.e., with CmdSN lower
+ than the task management command CmdSN) but except the Task
+ Management response to a TASK REASSIGN, additional responses MUST NOT
+ be delivered to the SCSI layer after the Task Management response.
+ The iSCSI initiator MAY deliver to the SCSI layer all responses
+ received before the Task Management response (i.e., it is a matter of
+ implementation if the SCSI responses, received before the Task
+ Management response but after the task management request was issued,
+ are delivered to the SCSI layer by the iSCSI layer in the initiator).
+ The iSCSI target MUST ensure that no responses for the tasks covered
+ by a task management function are delivered to the iSCSI initiator
+ after the Task Management response except for a task covered by a
+ TASK REASSIGN.
+
+ For ABORT TASK SET and CLEAR TASK SET, the issuing initiator MUST
+ continue to respond to all valid target transfer tags (received via
+ R2T, Text Response, NOP-In, or SCSI Data-In PDUs) related to the
+ affected task set, even after issuing the task management request.
+ The issuing initiator SHOULD however terminate (i.e., by setting the
+ F-bit to 1) these response sequences as quickly as possible. The
+ target on its part MUST wait for responses on all affected target
+
+
+
+Satran, et al. Standards Track [Page 130]
+
+RFC 3720 iSCSI April 2004
+
+
+ transfer tags before acting on either of these two task management
+ requests. In case all or part of the response sequence is not
+ received (due to digest errors) for a valid TTT, the target MAY treat
+ it as a case of within-command error recovery class (see Section
+ 6.1.4.1 Recovery Within-command) if it is supporting
+ ErrorRecoveryLevel >= 1, or alternatively may drop the connection to
+ complete the requested task set function.
+
+ If an ABORT TASK is issued for a task created by an immediate command
+ then RefCmdSN MUST be that of the Task Management request itself
+ (i.e., CmdSN and RefCmdSN are equal); otherwise RefCmdSN MUST be set
+ to the CmdSN of the task to be aborted (lower than CmdSN).
+
+ If the connection is still active (it is not undergoing an implicit
+ or explicit logout), ABORT TASK MUST be issued on the same connection
+ to which the task to be aborted is allegiant at the time the Task
+ Management Request is issued. If the connection is implicitly or
+ explicitly logged out (i.e., no other request will be issued on the
+ failing connection and no other response will be received on the
+ failing connection), then an ABORT TASK function request may be
+ issued on another connection. This Task Management request will then
+ establish a new allegiance for the command to be aborted as well as
+ abort it (i.e., the task to be aborted will not have to be retried or
+ reassigned, and its status, if issued but not acknowledged, will be
+ reissued followed by the Task Management response).
+
+ At the target an ABORT TASK function MUST NOT be executed on a Task
+ Management request; such a request MUST result in Task Management
+ response of "Function rejected".
+
+ For the LOGICAL UNIT RESET function, the target MUST behave as
+ dictated by the Logical Unit Reset function in [SAM2].
+
+ The implementation of the TARGET WARM RESET function and the TARGET
+ COLD RESET function is OPTIONAL and when implemented, should act as
+ described below. The TARGET WARM RESET is also subject to SCSI
+ access controls on the requesting initiator as defined in [SPC3].
+ When authorization fails at the target, the appropriate response as
+ described in Section 10.6 Task Management Function Response MUST be
+ returned by the target. The TARGET COLD RESET function is not
+ subject to SCSI access controls, but its execution privileges may be
+ managed by iSCSI mechanisms such as login authentication.
+
+ When executing the TARGET WARM RESET and TARGET COLD RESET functions,
+ the target cancels all pending operations on all Logical Units known
+ by the issuing initiator. Both functions are equivalent to the
+ Target Reset function specified by [SAM2]. They can affect many
+ other initiators logged in with the servicing SCSI target port.
+
+
+
+Satran, et al. Standards Track [Page 131]
+
+RFC 3720 iSCSI April 2004
+
+
+ The target MUST treat the TARGET COLD RESET function additionally as
+ a power on event, thus terminating all of its TCP connections to all
+ initiators (all sessions are terminated). For this reason, the
+ Service Response (defined by [SAM2]) for this SCSI task management
+ function may not be reliably delivered to the issuing initiator port.
+
+ For the TASK REASSIGN function, the target should reassign the
+ connection allegiance to this new connection (and thus resume iSCSI
+ exchanges for the task). TASK REASSIGN MUST ONLY be received by the
+ target after the connection on which the command was previously
+ executing has been successfully logged-out. The Task Management
+ response MUST be issued before the reassignment becomes effective.
+ For additional usage semantics see Section 6.2 Retry and Reassign in
+ Recovery.
+
+ At the target a TASK REASSIGN function request MUST NOT be executed
+ to reassign the connection allegiance of a Task Management function
+ request, an active text negotiation task, or a Logout task; such a
+ request MUST result in Task Management response of "Function
+ rejected".
+
+ TASK REASSIGN MUST be issued as an immediate command.
+
+10.5.2. TotalAHSLength and DataSegmentLength
+
+ For this PDU TotalAHSLength and DataSegmentLength MUST be 0.
+
+10.5.3. LUN
+
+ This field is required for functions that address a specific LU
+ (ABORT TASK, CLEAR TASK SET, ABORT TASK SET, CLEAR ACA, LOGICAL UNIT
+ RESET) and is reserved in all others.
+
+10.5.4. Referenced Task Tag
+
+ The Initiator Task Tag of the task to be aborted for the ABORT TASK
+ function or reassigned for the TASK REASSIGN function. For all the
+ other functions this field MUST be set to the reserved value
+ 0xffffffff.
+
+10.5.5. RefCmdSN
+
+ If an ABORT TASK is issued for a task created by an immediate command
+ then RefCmdSN MUST be that of the Task Management request itself
+ (i.e., CmdSN and RefCmdSN are equal).
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 132]
+
+RFC 3720 iSCSI April 2004
+
+
+ For an ABORT TASK of a task created by non-immediate command RefCmdSN
+ MUST be set to the CmdSN of the task identified by the Referenced
+ Task Tag field. Targets must use this field as described in section
+ 10.6.1 when the task identified by the Referenced Task Tag field is
+ not with the target.
+
+ Otherwise, this field is reserved.
+
+10.5.6. ExpDataSN
+
+ For recovery purposes, the iSCSI target and initiator maintain a data
+ acknowledgement reference number - the first input DataSN number
+ unacknowledged by the initiator. When issuing a new command, this
+ number is set to 0. If the function is TASK REASSIGN, which
+ establishes a new connection allegiance for a previously issued Read
+ or Bidirectional command, ExpDataSN will contain an updated data
+ acknowledgement reference number or the value 0; the latter
+ indicating that the data acknowledgement reference number is
+ unchanged. The initiator MUST discard any data PDUs from the
+ previous execution that it did not acknowledge and the target MUST
+ transmit all Data-In PDUs (if any) starting with the data
+ acknowledgement reference number. The number of retransmitted PDUs
+ may or may not be the same as the original transmission depending on
+ if there was a change in MaxRecvDataSegmentLength in the
+ reassignment. The target MAY also send no more Data-In PDUs if all
+ data has been acknowledged.
+
+ The value of ExpDataSN MUST be 0 or higher than the DataSN of the
+ last acknowledged Data-In PDU, but not larger than DataSN+1 of the
+ last Data-In PDU sent by the target. Any other value MUST be ignored
+ by the target.
+
+ For other functions this field is reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 133]
+
+RFC 3720 iSCSI April 2004
+
+
+10.6. Task Management Function Response
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x22 |1| Reserved | Response | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------------------------------------------------------+
+ 8/ Reserved /
+ / /
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 24| StatSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 32| MaxCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 36/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+ For the functions ABORT TASK, ABORT TASK SET, CLEAR ACA, CLEAR TASK
+ SET, LOGICAL UNIT RESET, TARGET COLD RESET, TARGET WARM RESET and
+ TASK REASSIGN, the target performs the requested Task Management
+ function and sends a Task Management response back to the initiator.
+ For TASK REASSIGN, the new connection allegiance MUST ONLY become
+ effective at the target after the target issues the Task Management
+ Response.
+
+10.6.1. Response
+
+ The target provides a Response, which may take on the following
+ values:
+
+ a) 0 - Function complete.
+ b) 1 - Task does not exist.
+ c) 2 - LUN does not exist.
+ d) 3 - Task still allegiant.
+ e) 4 - Task allegiance reassignment not supported.
+
+
+
+
+Satran, et al. Standards Track [Page 134]
+
+RFC 3720 iSCSI April 2004
+
+
+ f) 5 - Task management function not supported.
+ g) 6 - Function authorization failed.
+ h) 255 - Function rejected.
+
+ All other values are reserved.
+
+ For a discussion on usage of response codes 3 and 4, see Section
+ 6.2.2 Allegiance Reassignment.
+
+ For the TARGET COLD RESET and TARGET WARM RESET functions, the target
+ cancels all pending operations across all Logical Units known to the
+ issuing initiator. For the TARGET COLD RESET function, the target
+ MUST then close all of its TCP connections to all initiators
+ (terminates all sessions).
+
+ The mapping of the response code into a SCSI service response code
+ value, if needed, is outside the scope of this document. However, in
+ symbolic terms Response values 0 and 1 map to the SCSI service
+ response of FUNCTION COMPLETE. All other Response values map to the
+ SCSI service response of FUNCTION REJECTED. If a Task Management
+ function response PDU does not arrive before the session is
+ terminated, the SCSI service response is SERVICE DELIVERY OR TARGET
+ FAILURE.
+
+ The response to ABORT TASK SET and CLEAR TASK SET MUST only be issued
+ by the target after all of the commands affected have been received
+ by the target, the corresponding task management functions have been
+ executed by the SCSI target, and the delivery of all responses
+ delivered until the task management function completion have been
+ confirmed (acknowledged through ExpStatSN) by the initiator on all
+ connections of this session. For the exact timeline of events, refer
+ to Section 10.6.2 Task Management Actions on Task Sets.
+
+ For the ABORT TASK function,
+
+ a) If the Referenced Task Tag identifies a valid task leading to
+ a successful termination, then targets must return the
+ "Function complete" response.
+ b) If the Referenced Task Tag does not identify an existing task,
+ but if the CmdSN indicated by the RefCmdSN field in the Task
+ Management function request is within the valid CmdSN window
+ and less than the CmdSN of the Task Management function
+ request itself, then targets must consider the CmdSN received
+ and return the "Function complete" response.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 135]
+
+RFC 3720 iSCSI April 2004
+
+
+ c) If the Referenced Task Tag does not identify an existing task
+ and if the CmdSN indicated by the RefCmdSN field in the Task
+ Management function request is outside the valid CmdSN window,
+ then targets must return the "Task does not exist" response.
+
+10.6.2. Task Management Actions on Task Sets
+
+ The execution of ABORT TASK SET and CLEAR TASK SET Task Management
+ function requests consists of the following sequence of events in the
+ specified order on each of the entities.
+
+ The initiator:
+
+ a) Issues ABORT TASK SET/CLEAR TASK SET request.
+ b) Continues to respond to each target transfer tag received
+ for the affected task set.
+ c) Receives any responses for the tasks in the affected task
+ set (may process them as usual because they are guaranteed
+ to be valid).
+ d) Receives the task set management response, thus concluding
+ all the tasks in the affected task set.
+
+ The target:
+
+ a) Receives the ABORT TASK SET/CLEAR TASK SET request.
+ b) Waits for all target transfer tags to be responded to and
+ for all affected tasks in the task set to be received.
+ c) Propagates the command to and receives the response from the
+ target SCSI layer.
+ d) Takes note of last-sent StatSN on each of the connections in
+ the iSCSI sessions (one or more) sharing the affected task
+ set, and waits for acknowledgement of each StatSN (may
+ solicit for acknowledgement by way of a NOP-In). If some
+ tasks originate from non-iSCSI I_T_L nexi then the means by
+ which the target insures that all affected tasks have
+ returned their status to the initiator are defined by the
+ specific protocol.
+
+ e) Sends the task set management response to the issuing
+ initiator.
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 136]
+
+RFC 3720 iSCSI April 2004
+
+
+10.6.3. TotalAHSLength and DataSegmentLength
+
+ For this PDU TotalAHSLength and DataSegmentLength MUST be 0.
+
+10.7. SCSI Data-Out & SCSI Data-In
+
+ The SCSI Data-Out PDU for WRITE operations has the following format:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x05 |F| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| LUN or Reserved |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| Target Transfer Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 24| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpStatSN |
+ +---------------+---------------+---------------+---------------+
+ 32| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 36| DataSN |
+ +---------------+---------------+---------------+---------------+
+ 40| Buffer Offset |
+ +---------------+---------------+---------------+---------------+
+ 44| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+ / DataSegment /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ | Data-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 137]
+
+RFC 3720 iSCSI April 2004
+
+
+ The SCSI Data-In PDU for READ operations has the following format:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x25 |F|A|0 0 0|O|U|S| Reserved |Status or Rsvd |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| LUN or Reserved |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| Target Transfer Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 24| StatSN or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 32| MaxCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 36| DataSN |
+ +---------------+---------------+---------------+---------------+
+ 40| Buffer Offset |
+ +---------------+---------------+---------------+---------------+
+ 44| Residual Count |
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+ / DataSegment /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ | Data-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+ Status can accompany the last Data-In PDU if the command did not end
+ with an exception (i.e., the status is "good status" - GOOD,
+ CONDITION MET or INTERMEDIATE CONDITION MET). The presence of status
+ (and of a residual count) is signaled though the S flag bit.
+ Although targets MAY choose to send even non-exception status in
+ separate responses, initiators MUST support non-exception status in
+ Data-In PDUs.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 138]
+
+RFC 3720 iSCSI April 2004
+
+
+10.7.1. F (Final) Bit
+
+ For outgoing data, this bit is 1 for the last PDU of unsolicited data
+ or the last PDU of a sequence that answers an R2T.
+
+ For incoming data, this bit is 1 for the last input (read) data PDU
+ of a sequence. Input can be split into several sequences, each
+ having its own F bit. Splitting the data stream into sequences does
+ not affect DataSN counting on Data-In PDUs. It MAY be used as a
+ "change direction" indication for Bidirectional operations that need
+ such a change.
+
+ DataSegmentLength MUST not exceed MaxRecvDataSegmentLength for the
+ direction it is sent and the total of all the DataSegmentLength of
+ all PDUs in a sequence MUST not exceed MaxBurstLength (or
+ FirstBurstLength for unsolicited data). However the number of
+ individual PDUs in a sequence (or in total) may be higher than the
+ MaxBurstLength (or FirstBurstLength) to MaxRecvDataSegmentLength
+ ratio (as PDUs may be limited in length by the sender capabilities).
+ Using DataSegmentLength of 0 may increase beyond what is reasonable
+ for the number of PDUs and should therefore be avoided.
+
+ For Bidirectional operations, the F bit is 1 for both the end of the
+ input sequences and the end of the output sequences.
+
+10.7.2. A (Acknowledge) Bit
+
+ For sessions with ErrorRecoveryLevel 1 or higher, the target sets
+ this bit to 1 to indicate that it requests a positive acknowledgement
+ from the initiator for the data received. The target should use the
+ A bit moderately; it MAY only set the A bit to 1 once every
+ MaxBurstLength bytes, or on the last Data-In PDU that concludes the
+ entire requested read data transfer for the task from the target's
+ perspective, and it MUST NOT do so more frequently. The target MUST
+ NOT set to 1 the A bit for sessions with ErrorRecoveryLevel=0. The
+ initiator MUST ignore the A bit set to 1 for sessions with
+ ErrorRecoveryLevel=0.
+
+ On receiving a Data-In PDU with the A bit set to 1 on a session with
+ ErrorRecoveryLevel greater than 0, if there are no holes in the read
+ data until that Data-In PDU, the initiator MUST issue a SNACK of type
+ DataACK except when it is able to acknowledge the status for the task
+ immediately via ExpStatSN on other outbound PDUs if the status for
+ the task is also received. In the latter case (acknowledgement
+ through ExpStatSN), sending a SNACK of type DataACK in response to
+ the A bit is OPTIONAL, but if it is done, it must not be sent after
+ the status acknowledgement through ExpStatSN. If the initiator has
+ detected holes in the read data prior to that Data-In PDU, it MUST
+
+
+
+Satran, et al. Standards Track [Page 139]
+
+RFC 3720 iSCSI April 2004
+
+
+ postpone issuing the SNACK of type DataACK until the holes are
+ filled. An initiator also MUST NOT acknowledge the status for the
+ task before those holes are filled. A status acknowledgement for a
+ task that generated the Data-In PDUs is considered by the target as
+ an implicit acknowledgement of the Data-In PDUs if such an
+ acknowledgement was requested by the target.
+
+10.7.3. Flags (byte 1)
+
+ The last SCSI Data packet sent from a target to an initiator for a
+ SCSI command that completed successfully (with a status of GOOD,
+ CONDITION MET, INTERMEDIATE or INTERMEDIATE CONDITION MET) may also
+ optionally contain the Status for the data transfer. As Sense Data
+ cannot be sent together with the Command Status, if the command is
+ completed with an error, then the response and sense data MUST be
+ sent in a SCSI Response PDU (i.e., MUST NOT be sent in a SCSI Data
+ packet). If Status is sent with the data, then a SCSI Response PDU
+ MUST NOT be sent as this would violate SCSI rules (a single status).
+ For Bidirectional commands, the status MUST be sent in a SCSI
+ Response PDU.
+
+ bit 2-4 - Reserved.
+
+ bit 5-6 - used the same as in a SCSI Response. These bits are
+ only valid when S is set to 1. For details see Section
+ 10.4.1 Flags (byte 1).
+
+ bit 7 S (status)- set to indicate that the Command Status field
+ contains status. If this bit is set to 1, the F bit
+ MUST also be set to 1.
+
+ The fields StatSN, Status, and Residual Count only have meaningful
+ content if the S bit is set to 1 and their values are defined in
+ Section 10.4 SCSI Response.
+
+10.7.4. Target Transfer Tag and LUN
+
+ On outgoing data, the Target Transfer Tag is provided to the target
+ if the transfer is honoring an R2T. In this case, the Target
+ Transfer Tag field is a replica of the Target Transfer Tag provided
+ with the R2T.
+
+ On incoming data, the Target Transfer Tag and LUN MUST be provided by
+ the target if the A bit is set to 1; otherwise they are reserved.
+ The Target Transfer Tag and LUN are copied by the initiator into the
+ SNACK of type DataACK that it issues as a result of receiving a SCSI
+ Data-In PDU with the A bit set to 1.
+
+
+
+
+Satran, et al. Standards Track [Page 140]
+
+RFC 3720 iSCSI April 2004
+
+
+ The Target Transfer Tag values are not specified by this protocol
+ except that the value 0xffffffff is reserved and means that the
+ Target Transfer Tag is not supplied. 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.
+
+10.7.5. DataSN
+
+ For input (read) or bidirectional Data-In PDUs, the DataSN is the
+ input PDU number within the data transfer for the command identified
+ by the Initiator Task Tag.
+
+ R2T and Data-In PDUs, in the context of bidirectional commands, share
+ the numbering sequence (see Section 3.2.2.3 Data Sequencing).
+
+ For output (write) data PDUs, the DataSN is the Data-Out PDU number
+ within the current output sequence. The current output sequence is
+ either identified by the Initiator Task Tag (for unsolicited data) or
+ is a data sequence generated for one R2T (for data solicited through
+ R2T).
+
+10.7.6. Buffer Offset
+
+ The Buffer Offset field contains the offset of this PDU payload data
+ within the complete data transfer. The sum of the buffer offset and
+ length should not exceed the expected transfer length for the
+ command.
+
+ The order of data PDUs within a sequence is determined by
+ DataPDUInOrder. When set to Yes, it means that PDUs have to be in
+ increasing Buffer Offset order and overlays are forbidden.
+
+ The ordering between sequences is determined by DataSequenceInOrder.
+ When set to Yes, it means that sequences have to be in increasing
+ Buffer Offset order and overlays are forbidden.
+
+10.7.7. DataSegmentLength
+
+ This is the data payload length of a SCSI Data-In or SCSI Data-Out
+ PDU. The sending of 0 length data segments should be avoided, but
+ initiators and targets MUST be able to properly receive 0 length data
+ segments.
+
+ The Data Segments of Data-In and Data-Out PDUs SHOULD be filled to
+ the integer number of 4 byte words (real payload) unless the F bit is
+ set to 1.
+
+
+
+
+Satran, et al. Standards Track [Page 141]
+
+RFC 3720 iSCSI April 2004
+
+
+10.8. Ready To Transfer (R2T)
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x31 |1| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| LUN |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| Target Transfer Tag |
+ +---------------+---------------+---------------+---------------+
+ 24| StatSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 32| MaxCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 36| R2TSN |
+ +---------------+---------------+---------------+---------------+
+ 40| Buffer Offset |
+ +---------------+---------------+---------------+---------------+
+ 44| Desired Data Transfer Length |
+ +---------------------------------------------------------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+ When an initiator has submitted a SCSI Command with data that passes
+ from the initiator to the target (WRITE), the target may specify
+ which blocks of data it is ready to receive. The target may request
+ that the data blocks be delivered in whichever order is convenient
+ for the target at that particular instant. This information is
+ passed from the target to the initiator in the Ready To Transfer
+ (R2T) PDU.
+
+ In order to allow write operations without an explicit initial R2T,
+ the initiator and target MUST have negotiated the key InitialR2T to
+ No during Login.
+
+ An R2T MAY be answered with one or more SCSI Data-Out PDUs with a
+ matching Target Transfer Tag. If an R2T is answered with a single
+ Data-Out PDU, the Buffer Offset in the Data PDU MUST be the same as
+
+
+
+Satran, et al. Standards Track [Page 142]
+
+RFC 3720 iSCSI April 2004
+
+
+ the one specified by the R2T, and the data length of the Data PDU
+ MUST be the same as the Desired Data Transfer Length specified in the
+ R2T. If the R2T is answered with a sequence of Data PDUs, the Buffer
+ Offset and Length MUST be within the range of those specified by R2T,
+ and the last PDU MUST have the F bit set to 1. If the last PDU
+ (marked with the F bit) is received before the Desired Data Transfer
+ Length is transferred, a target MAY choose to Reject that
+
+ PDU with "Protocol error" reason code. DataPDUInOrder governs the
+ Data-Out PDU ordering. If DataPDUInOrder is set to Yes, the Buffer
+ Offsets and Lengths for consecutive PDUs MUST form a continuous
+ non-overlapping range and the PDUs MUST be sent in increasing offset
+ order.
+
+ The target may send several R2T PDUs. It, therefore, can have a
+ number of pending data transfers. The number of outstanding R2T PDUs
+ are limited by the value of the negotiated key MaxOutstandingR2T.
+ Within a connection, outstanding R2Ts MUST be fulfilled by the
+ initiator in the order in which they were received.
+
+ R2T PDUs MAY also be used to recover Data Out PDUs. Such an R2T
+ (Recovery-R2T) is generated by a target upon detecting the loss of
+ one or more Data-Out PDUs due to:
+
+ - Digest error
+ - Sequence error
+ - Sequence reception timeout
+
+ A Recovery-R2T carries the next unused R2TSN, but requests part of or
+ the entire data burst that an earlier R2T (with a lower R2TSN) had
+ already requested.
+
+ DataSequenceInOrder governs the buffer offset ordering in consecutive
+ R2Ts. If DataSequenceInOrder is Yes, then consecutive R2Ts MUST
+ refer to continuous non-overlapping ranges except for Recovery-R2Ts.
+
+10.8.1. TotalAHSLength and DataSegmentLength
+
+ For this PDU TotalAHSLength and DataSegmentLength MUST be 0.
+
+10.8.2. R2TSN
+
+ R2TSN is the R2T PDU input PDU number within the command identified
+ by the Initiator Task Tag.
+
+ For bidirectional commands R2T and Data-In PDUs share the input PDU
+ numbering sequence (see Section 3.2.2.3 Data Sequencing).
+
+
+
+
+Satran, et al. Standards Track [Page 143]
+
+RFC 3720 iSCSI April 2004
+
+
+10.8.3. StatSN
+
+ The StatSN field will contain the next StatSN. The StatSN for this
+ connection is not advanced after this PDU is sent.
+
+10.8.4. Desired Data Transfer Length and Buffer Offset
+
+ The target specifies how many bytes it wants the initiator to send
+ because of this R2T PDU. The target may request the data from the
+ initiator in several chunks, not necessarily in the original order of
+ the data. The target, therefore, also specifies a Buffer Offset that
+ indicates the point at which the data transfer should begin, relative
+ to the beginning of the total data transfer. The Desired Data
+ Transfer Length MUST NOT be 0 and MUST not exceed MaxBurstLength.
+
+10.8.5. Target Transfer Tag
+
+ The target assigns its own tag to each R2T request that it sends to
+ the initiator. This tag can be used by the target to easily identify
+ the data it receives. The Target Transfer Tag and LUN are copied in
+ the outgoing data PDUs and are only used by the target. There is no
+ protocol rule about the Target Transfer Tag except that the value
+ 0xffffffff is reserved and MUST NOT be sent by a target in an R2T.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 144]
+
+RFC 3720 iSCSI April 2004
+
+
+10.9. Asynchronous Message
+
+ An Asynchronous Message may be sent from the target to the initiator
+ without correspondence to a particular command. The target specifies
+ the reason for the event and sense data.
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x32 |1| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| LUN or Reserved |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 20| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 24| StatSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 32| MaxCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 36| AsyncEvent | AsyncVCode | Parameter1 or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 40| Parameter2 or Reserved | Parameter3 or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 44| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+ / DataSegment - Sense Data and iSCSI Event Data /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ | Data-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+ Some Asynchronous Messages are strictly related to iSCSI while others
+ are related to SCSI [SAM2].
+
+ StatSN counts this PDU as an acknowledgeable event (StatSN is
+ advanced), which allows for initiator and target state
+ synchronization.
+
+
+
+Satran, et al. Standards Track [Page 145]
+
+RFC 3720 iSCSI April 2004
+
+
+10.9.1. AsyncEvent
+
+ The codes used for iSCSI Asynchronous Messages (events) are:
+
+ 0 - a SCSI Asynchronous Event is reported in the sense data.
+ Sense Data that accompanies the report, in the data segment,
+ identifies the condition. The sending of a SCSI Event
+ (Asynchronous Event Reporting in SCSI terminology) is
+ dependent on the target support for SCSI asynchronous event
+ reporting (see [SAM2]) as indicated in the standard INQUIRY
+ data (see [SPC3]). Its use may be enabled by parameters in
+ the SCSI Control mode page (see [SPC3]).
+
+ 1 - target requests Logout. This Async Message MUST be sent on
+ the same connection as the one requesting to be logged out.
+ The initiator MUST honor this request by issuing a Logout as
+ early as possible, but no later than Parameter3 seconds.
+ Initiator MUST send a Logout with a reason code of "Close the
+ connection" OR "Close the session" to close all the
+ connections. Once this message is received, the initiator
+ SHOULD NOT issue new iSCSI commands on the connection to be
+ logged out. The target MAY reject any new I/O requests that
+ it receives after this Message with the reason code "Waiting
+ for Logout". If the initiator does not Logout in Parameter3
+ seconds, the target should send an Async PDU with iSCSI event
+ code "Dropped the connection" if possible, or simply terminate
+ the transport connection. Parameter1 and Parameter2 are
+ reserved.
+
+ 2 - target indicates it will drop the connection. The Parameter1
+ field indicates the CID of the connection that is going to be
+ dropped.
+
+ The Parameter2 field (Time2Wait) indicates, in seconds, the
+ minimum time to wait before attempting to reconnect or
+ reassign.
+
+ The Parameter3 field (Time2Retain) indicates the maximum time
+ allowed to reassign commands after the initial wait (in
+ Parameter2).
+
+ If the initiator does not attempt to reconnect and/or reassign
+ the outstanding commands within the time specified by
+ Parameter3, or if Parameter3 is 0, the target will terminate
+ all outstanding commands on this connection. In this case, no
+ other responses should be expected from the target for the
+ outstanding commands on this connection.
+
+
+
+
+Satran, et al. Standards Track [Page 146]
+
+RFC 3720 iSCSI April 2004
+
+
+ A value of 0 for Parameter2 indicates that reconnect can be
+ attempted immediately.
+
+ 3 - target indicates it will drop all the connections of this
+ session.
+
+ Parameter1 field is reserved.
+
+ The Parameter2 field (Time2Wait) indicates, in seconds, the
+ minimum time to wait before attempting to reconnect. The
+ Parameter3 field (Time2Retain) indicates the maximum time
+ allowed to reassign commands after the initial wait (in
+ Parameter2).
+
+ If the initiator does not attempt to reconnect and/or reassign
+ the outstanding commands within the time specified by
+ Parameter3, or if Parameter3 is 0, the session is terminated.
+
+ In this case, the target will terminate all outstanding
+ commands in this session; no other responses should be
+ expected from the target for the outstanding commands in this
+ session. A value of 0 for Parameter2 indicates that reconnect
+ can be attempted immediately.
+
+ 4 - target requests parameter negotiation on this connection. The
+ initiator MUST honor this request by issuing a Text Request
+ (that can be empty) on the same connection as early as
+ possible, but no later than Parameter3 seconds, unless a Text
+ Request is already pending on the connection, or by issuing a
+ Logout Request. If the initiator does not issue a Text
+ Request the target may reissue the Asynchronous Message
+ requesting parameter negotiation.
+
+ 255 - vendor specific iSCSI Event. The AsyncVCode details the
+ vendor code, and data MAY accompany the report.
+
+ All other event codes are reserved.
+
+10.9.2. AsyncVCode
+
+ AsyncVCode is a vendor specific detail code that is only valid if the
+ AsyncEvent field indicates a vendor specific event. Otherwise, it is
+ reserved.
+
+10.9.3. LUN
+
+ The LUN field MUST be valid if AsyncEvent is 0. Otherwise, this
+ field is reserved.
+
+
+
+Satran, et al. Standards Track [Page 147]
+
+RFC 3720 iSCSI April 2004
+
+
+10.9.4. Sense Data and iSCSI Event Data
+
+ For a SCSI event, this data accompanies the report in the data
+ segment and identifies the condition.
+
+ For an iSCSI event, additional vendor-unique data MAY accompany the
+ Async event. Initiators MAY ignore the data when not understood
+ while processing the rest of the PDU.
+
+ If the DataSegmentLength is not 0, the format of the DataSegment is
+ as follows:
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|SenseLength | Sense Data |
+ +---------------+---------------+---------------+---------------+
+ x/ Sense Data /
+ +---------------+---------------+---------------+---------------+
+ y/ iSCSI Event Data /
+ / /
+ +---------------+---------------+---------------+---------------+
+ z|
+
+10.9.4.1. SenseLength
+
+ This is the length of Sense Data. When the Sense Data field is empty
+ (e.g., the event is not a SCSI event) SenseLength is 0.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 148]
+
+RFC 3720 iSCSI April 2004
+
+
+10.10. Text Request
+
+ The Text Request is provided to allow for the exchange of information
+ and for future extensions. It permits the initiator to inform a
+ target of its capabilities or to request some special operations.
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|I| 0x04 |F|C| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| LUN or Reserved |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| Target Transfer Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 24| CmdSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpStatSN |
+ +---------------+---------------+---------------+---------------+
+ 32/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+ / DataSegment (Text) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ | Data-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+ An initiator MUST have at most one outstanding Text Request on a
+ connection at any given time.
+
+ On a connection failure, an initiator must either explicitly abort
+ any active allegiant text negotiation task or must cause such a task
+ to be implicitly terminated by the target.
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 149]
+
+RFC 3720 iSCSI April 2004
+
+
+10.10.1. F (Final) Bit
+
+ When set to 1, indicates that this is the last or only text request
+ in a sequence of Text Requests; otherwise, it indicates that more
+ Text Requests will follow.
+
+10.10.2. C (Continue) Bit
+
+ When set to 1, indicates that the text (set of key=value pairs) in
+ this Text Request is not complete (it will be continued on subsequent
+ Text Requests); otherwise, it indicates that this Text Request ends a
+ set of key=value pairs. A Text Request with the C bit set to 1 MUST
+ have the F bit set to 0.
+
+10.10.3. Initiator Task Tag
+
+ The initiator assigned identifier for this Text Request. If the
+ command is sent as part of a sequence of text requests and responses,
+ the Initiator Task Tag MUST be the same for all the requests within
+ the sequence (similar to linked SCSI commands). The I bit for all
+ requests in a sequence also MUST be the same.
+
+10.10.4. Target Transfer Tag
+
+ When the Target Transfer Tag is set to the reserved value 0xffffffff,
+ it tells the target that this is a new request and the target resets
+ any internal state associated with the Initiator Task Tag (resets the
+ current negotiation state).
+
+ The target sets the Target Transfer Tag in a text response to a value
+ other than the reserved value 0xffffffff whenever it indicates that
+ it has more data to send or more operations to perform that are
+ associated with the specified Initiator Task Tag. It MUST do so
+ whenever it sets the F bit to 0 in the response. By copying the
+ Target Transfer Tag from the response to the next Text Request, the
+ initiator tells the target to continue the operation for the specific
+ Initiator Task Tag. The initiator MUST ignore the Target Transfer
+ Tag in the Text Response when the F bit is set to 1.
+
+ This mechanism allows the initiator and target to transfer a large
+ amount of textual data over a sequence of text-command/text-response
+ exchanges, or to perform extended negotiation sequences.
+
+ If the Target Transfer Tag is not 0xffffffff, the LUN field MUST be
+ sent by the target in the Text Response.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 150]
+
+RFC 3720 iSCSI April 2004
+
+
+ A target MAY reset its internal negotiation state if an exchange is
+ stalled by the initiator for a long time or if it is running out of
+ resources.
+
+ Long text responses are handled as in the following example:
+
+ I->T Text SendTargets=All (F=1,TTT=0xffffffff)
+ T->I Text <part 1> (F=0,TTT=0x12345678)
+ I->T Text <empty> (F=1, TTT=0x12345678)
+ T->I Text <part 2> (F=0, TTT=0x12345678)
+ I->T Text <empty> (F=1, TTT=0x12345678)
+ ...
+ T->I Text <part n> (F=1, TTT=0xffffffff)
+
+10.10.5. Text
+
+ The data lengths of a text request MUST NOT exceed the iSCSI target
+ MaxRecvDataSegmentLength (a per connection and per direction
+ negotiated parameter). The text format is specified in Section 5.2
+ Text Mode Negotiation.
+
+ Chapter 11 and Chapter 12 list some basic Text key=value pairs, some
+ of which can be used in Login Request/Response and some in Text
+ Request/Response.
+
+ A key=value pair can span Text request or response boundaries. A
+ key=value pair can start in one PDU and continue on the next. In
+ other words the end of a PDU does not necessarily signal the end of a
+ key=value pair.
+
+ The target responds by sending its response back to the initiator.
+ The response text format is similar to the request text format. The
+ text response MAY refer to key=value pairs presented in an earlier
+ text request and the text in the request may refer to earlier
+ responses.
+
+ Chapter 5 details the rules for the Text Requests and Responses.
+
+ Text operations are usually meant for parameter setting/
+ negotiations, but can also be used to perform some long lasting
+ operations.
+
+ Text operations that take a long time should be placed in their own
+ Text request.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 151]
+
+RFC 3720 iSCSI April 2004
+
+
+10.11. Text Response
+
+ The Text Response PDU contains the target's responses to the
+ initiator's Text request. The format of the Text field matches that
+ of the Text request.
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x24 |F|C| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| LUN or Reserved |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| Target Transfer Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 24| StatSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 32| MaxCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 36/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+ / DataSegment (Text) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ | Data-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+10.11.1. F (Final) Bit
+
+ When set to 1, in response to a Text Request with the Final bit set
+ to 1, the F bit indicates that the target has finished the whole
+ operation. Otherwise, if set to 0 in response to a Text Request with
+ the Final Bit set to 1, it indicates that the target has more work to
+ do (invites a follow-on text request). A Text Response with the F
+ bit set to 1 in response to a Text Request with the F bit set to 0 is
+ a protocol error.
+
+
+
+Satran, et al. Standards Track [Page 152]
+
+RFC 3720 iSCSI April 2004
+
+
+ A Text Response with the F bit set to 1 MUST NOT contain key=value
+ pairs that may require additional answers from the initiator.
+
+ A Text Response with the F bit set to 1 MUST have a Target Transfer
+ Tag field set to the reserved value of 0xffffffff.
+
+ A Text Response with the F bit set to 0 MUST have a Target Transfer
+ Tag field set to a value other than the reserved 0xffffffff.
+
+10.11.2. C (Continue) Bit
+
+ When set to 1, indicates that the text (set of key=value pairs) in
+ this Text Response is not complete (it will be continued on
+ subsequent Text Responses); otherwise, it indicates that this Text
+ Response ends a set of key=value pairs. A Text Response with the C
+ bit set to 1 MUST have the F bit set to 0.
+
+10.11.3. Initiator Task Tag
+
+ The Initiator Task Tag matches the tag used in the initial Text
+ Request.
+
+10.11.4. Target Transfer Tag
+
+ When a target has more work to do (e.g., cannot transfer all the
+ remaining text data in a single Text Response or has to continue the
+ negotiation) and has enough resources to proceed, it MUST set the
+ Target Transfer Tag to a value other than the reserved value of
+ 0xffffffff. Otherwise, the Target Transfer Tag MUST be set to
+ 0xffffffff.
+
+ When the Target Transfer Tag is not 0xffffffff, the LUN field may be
+ significant.
+
+ The initiator MUST copy the Target Transfer Tag and LUN in its next
+ request to indicate that it wants the rest of the data.
+
+ 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).
+
+ When a target cannot finish the operation in a single Text Response,
+ and does not have enough resources to continue, it rejects the Text
+ Request with the appropriate Reject code.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 153]
+
+RFC 3720 iSCSI April 2004
+
+
+ A target may reset its internal state associated with an Initiator
+ Task Tag (the current negotiation state), state expressed through the
+ Target Transfer Tag if the initiator fails to continue the exchange
+ for some time. The target may reject subsequent Text Requests with
+ the Target Transfer Tag set to the "stale" value.
+
+10.11.5. StatSN
+
+ The target StatSN variable is advanced by each Text Response sent.
+
+10.11.6. Text Response Data
+
+ The data lengths of a text response MUST NOT exceed the iSCSI
+ initiator MaxRecvDataSegmentLength (a per connection and per
+ direction negotiated parameter).
+
+ The text in the Text Response Data is governed by the same rules as
+ the text in the Text Request Data (see Section 10.10.5 Text).
+
+ Although the initiator is the requesting party and controls the
+ request-response initiation and termination, the target can offer
+ key=value pairs of its own as part of a sequence and not only in
+ response to the initiator.
+
+10.12. Login Request
+
+ After establishing a TCP connection between an initiator and a
+ target, the initiator MUST start a Login Phase to gain further access
+ to the target's resources.
+
+ The Login Phase (see Chapter 5) consists of a sequence of Login
+ Requests and Responses that carry the same Initiator Task Tag.
+
+ Login Requests are always considered as immediate.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 154]
+
+RFC 3720 iSCSI April 2004
+
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|1| 0x03 |T|C|.|.|CSG|NSG| Version-max | Version-min |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| ISID |
+ + +---------------+---------------+
+ 12| | TSIH |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| CID | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 24| CmdSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpStatSN or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 32| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 36| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 40/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48/ DataSegment - Login Parameters in Text request Format /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+
+10.12.1. T (Transit) Bit
+
+ If set to 1, indicates that the initiator is ready to transit to the
+ next stage.
+
+ If the T bit is set to 1 and NSG is FullFeaturePhase, then this also
+ indicates that the initiator is ready for the Final Login Response
+ (see Chapter 5).
+
+10.12.2. C (Continue) Bit
+
+ When set to 1, indicates that the text (set of key=value pairs) in
+ this Login Request is not complete (it will be continued on
+ subsequent Login Requests); otherwise, it indicates that this Login
+ Request ends a set of key=value pairs. A Login Request with the C
+ bit set to 1 MUST have the T bit set to 0.
+
+
+
+
+Satran, et al. Standards Track [Page 155]
+
+RFC 3720 iSCSI April 2004
+
+
+10.12.3. CSG and NSG
+
+ Through these fields, Current Stage (CSG) and Next Stage (NSG), the
+ Login negotiation requests and responses are associated with a
+ specific stage in the session (SecurityNegotiation,
+ LoginOperationalNegotiation, FullFeaturePhase) and may indicate the
+ next stage to which they want to move (see Chapter 5). The next
+ stage value is only valid when the T bit is 1; otherwise, it is
+ reserved.
+
+ The stage codes are:
+
+ - 0 - SecurityNegotiation
+ - 1 - LoginOperationalNegotiation
+ - 3 - FullFeaturePhase
+
+ All other codes are reserved.
+
+10.12.4. Version
+
+ The version number of the current draft is 0x00. As such, all
+ devices MUST carry version 0x00 for both Version-min and Version-max.
+
+10.12.4.1. Version-max
+
+ Maximum Version number supported.
+
+ All Login Requests within the Login Phase MUST carry the same
+ Version-max.
+
+ The target MUST use the value presented with the first Login Request.
+
+10.12.4.2. Version-min
+
+ All Login Requests within the Login Phase MUST carry the same
+ Version-min. The target MUST use the value presented with the first
+ Login Request.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 156]
+
+RFC 3720 iSCSI April 2004
+
+
+10.12.5. ISID
+
+ This is an initiator-defined component of the session identifier and
+ is structured as follows (see [RFC3721] and Section 9.1.1
+ Conservative Reuse of ISIDs for details):
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 8| T | A | B | C |
+ +---------------+---------------+---------------+---------------+
+ 12| D |
+ +---------------+---------------+
+
+ The T field identifies the format and usage of A, B, C, and D as
+ indicated below:
+
+ T
+
+ 00b OUI-Format
+ A&B are a 22 bit OUI
+ (the I/G & U/L bits are omitted)
+ C&D 24 bit qualifier
+ 01b EN - Format (IANA Enterprise Number)
+ A - Reserved
+ B&C EN (IANA Enterprise Number)
+ D - Qualifier
+ 10b "Random"
+ A - Reserved
+ B&C Random
+ D - Qualifier
+ 11b A,B,C&D Reserved
+
+ For the T field values 00b and 01b, a combination of A and B (for
+ 00b) or B and C (for 01b) identifies the vendor or organization whose
+ component (software or hardware) generates this ISID. A vendor or
+ organization with one or more OUIs, or one or more Enterprise
+ Numbers, MUST use at least one of these numbers and select the
+ appropriate value for the T field when its components generate ISIDs.
+ An OUI or EN MUST be set in the corresponding fields in network byte
+ order (byte big-endian).
+
+ If the T field is 10b, B and C are set to a random 24-bit unsigned
+ integer value in network byte order (byte big-endian). See [RFC3721]
+ for how this affects the principle of "conservative reuse".
+
+
+
+
+
+Satran, et al. Standards Track [Page 157]
+
+RFC 3720 iSCSI April 2004
+
+
+ The Qualifier field is a 16 or 24-bit unsigned integer value that
+ provides a range of possible values for the ISID within the selected
+ namespace. It may be set to any value within the constraints
+ specified in the iSCSI protocol (see Section 3.4.3 Consequences of
+ the Model and Section 9.1.1 Conservative Reuse of ISIDs).
+
+ The T field value of 11b is reserved.
+
+ If the ISID is derived from something assigned to a hardware adapter
+ or interface by a vendor, as a preset default value, it MUST be
+ configurable to a value assigned according to the SCSI port behavior
+ desired by the system in which it is installed (see Section 9.1.1
+ Conservative Reuse of ISIDs and Section 9.1.2 iSCSI Name, ISID, and
+ TPGT Use). The resultant ISID MUST also be persistent over power
+ cycles, reboot, card swap, etc.
+
+10.12.6. TSIH
+
+ TSIH must be set in the first Login Request. The reserved value 0
+ MUST be used on the first connection for a new session. Otherwise,
+ the TSIH sent by the target at the conclusion of the successful login
+ of the first connection for this session MUST be used. The TSIH
+ identifies to the target the associated existing session for this new
+ connection.
+
+ All Login Requests within a Login Phase MUST carry the same TSIH.
+
+ The target MUST check the value presented with the first Login
+ Request and act as specified in Section 5.3.1 Login Phase Start.
+
+10.12.7. Connection ID - CID
+
+ A unique ID for this connection within the session.
+
+ All Login Requests within the Login Phase MUST carry the same CID.
+
+ The target MUST use the value presented with the first Login Request.
+
+ A Login Request with a non-zero TSIH and a CID equal to that of an
+ existing connection implies a logout of the connection followed by a
+ Login (see Section 5.3.4 Connection Reinstatement). For the details
+ of the implicit Logout Request, see Section 10.14 Logout Request.
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 158]
+
+RFC 3720 iSCSI April 2004
+
+
+10.12.8. CmdSN
+
+ CmdSN is either the initial command sequence number of a session (for
+ the first Login Request of a session - the "leading" login), or the
+ command sequence number in the command stream if the login is for a
+ new connection in an existing session.
+
+ Examples:
+
+ - Login on a leading connection - if the leading login carries
+ the CmdSN 123, all other Login Requests in the same Login Phase
+ carry the CmdSN 123 and the first non-immediate command in
+ FullFeaturePhase also carries the CmdSN 123.
+
+ - Login on other than a leading connection - if the current CmdSN
+ at the time the first login on the connection is issued is 500,
+ then that PDU carries CmdSN=500. Subsequent Login Requests
+ that are needed to complete this Login Phase may carry a CmdSN
+ higher than 500 if non-immediate requests that were issued on
+ other connections in the same session advance CmdSN.
+
+ If the Login Request is a leading Login Request, the target MUST use
+ the value presented in CmdSN as the target value for ExpCmdSN.
+
+10.12.9. ExpStatSN
+
+ For the first Login Request on a connection this is ExpStatSN for the
+ old connection and this field is only valid if the Login Request
+ restarts a connection (see Section 5.3.4 Connection Reinstatement).
+
+ For subsequent Login Requests it is used to acknowledge the Login
+ Responses with their increasing StatSN values.
+
+10.12.10. Login Parameters
+
+ The initiator MUST provide some basic parameters in order to enable
+ the target to determine if the initiator may use the target's
+ resources and the initial text parameters for the security exchange.
+
+ All the rules specified in Section 10.10.5 Text for text requests
+ also hold for Login Requests. Keys and their explanations are listed
+ in Chapter 11 (security negotiation keys) and Chapter 12 (operational
+ parameter negotiation keys). All keys in Chapter 12, except for the
+ X extension formats, MUST be supported by iSCSI initiators and
+ targets. Keys in Chapter 11 only need to be supported when the
+ function to which they refer is mandatory to implement.
+
+
+
+
+
+Satran, et al. Standards Track [Page 159]
+
+RFC 3720 iSCSI April 2004
+
+
+10.13. Login Response
+
+ The Login Response indicates the progress and/or end of the Login
+ Phase.
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x23 |T|C|.|.|CSG|NSG| Version-max | Version-active|
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| ISID |
+ + +---------------+---------------+
+ 12| | TSIH |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 24| StatSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 32| MaxCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 36| Status-Class | Status-Detail | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 40/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48/ DataSegment - Login Parameters in Text request Format /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+
+10.13.1. Version-max
+
+ This is the highest version number supported by the target.
+
+ All Login Responses within the Login Phase MUST carry the same
+ Version-max.
+
+ The initiator MUST use the value presented as a response to the first
+ Login Request.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 160]
+
+RFC 3720 iSCSI April 2004
+
+
+10.13.2. Version-active
+
+ Indicates the highest version supported by the target and initiator.
+ If the target does not support a version within the range specified
+ by the initiator, the target rejects the login and this field
+ indicates the lowest version supported by the target.
+
+ All Login Responses within the Login Phase MUST carry the same
+ Version-active.
+
+ The initiator MUST use the value presented as a response to the first
+ Login Request.
+
+10.13.3. TSIH
+
+ The TSIH is the target assigned session identifying handle. Its
+ internal format and content are not defined by this protocol except
+ for the value 0 that is reserved. With the exception of the Login
+ Final-Response in a new session, this field should be set to the TSIH
+ provided by the initiator in the Login Request. For a new session,
+ the target MUST generate a non-zero TSIH and ONLY return it in the
+ Login Final-Response (see Section 5.3 Login Phase).
+
+10.13.4. StatSN
+
+ For the first Login Response (the response to the first Login
+ Request), this is the starting status Sequence Number for the
+ connection. The next response of any kind, including the next Login
+ Response, if any, in the same Login Phase, will carry this number +
+ 1. This field is only valid if the Status-Class is 0.
+
+10.13.5. Status-Class and Status-Detail
+
+ The Status returned in a Login Response indicates the execution
+ status of the Login Phase. The status includes:
+
+ Status-Class
+ Status-Detail
+
+ 0 Status-Class indicates success.
+
+ A non-zero Status-Class indicates an exception. In this case,
+ Status-Class is sufficient for a simple initiator to use when
+ handling exceptions, without having to look at the Status-Detail.
+ The Status-Detail allows finer-grained exception handling for more
+ sophisticated initiators and for better information for logging.
+
+
+
+
+
+Satran, et al. Standards Track [Page 161]
+
+RFC 3720 iSCSI April 2004
+
+
+ The status classes are as follows:
+
+ 0 - Success - indicates that the iSCSI target successfully
+ received, understood, and accepted the request. The numbering
+ fields (StatSN, ExpCmdSN, MaxCmdSN) are only valid if
+ Status-Class is 0.
+
+ 1 - Redirection - indicates that the initiator must take further
+ action to complete the request. This is usually due to the
+ target moving to a different address. All of the redirection
+ status class responses MUST return one or more text key
+ parameters of the type "TargetAddress", which indicates the
+ target's new address. A redirection response MAY be issued by
+ a target prior or after completing a security negotiation if a
+ security negotiation is required. A redirection SHOULD be
+ accepted by an initiator even without having the target
+ complete a security negotiation if any security negotiation is
+ required, and MUST be accepted by the initiator after the
+ completion of the security negotiation if any security
+ negotiation is required.
+
+ 2 - Initiator Error (not a format error) - indicates that the
+ initiator most likely caused the error. This MAY be due to a
+ request for a resource for which the initiator does not have
+ permission. The request should not be tried again.
+
+ 3 - Target Error - indicates that the target sees no errors in the
+ initiator's Login Request, but is currently incapable of
+ fulfilling the request. The initiator may re-try the same
+ Login Request later.
+
+ The table below shows all of the currently allocated status codes.
+ The codes are in hexadecimal; the first byte is the status class and
+ the second byte is the status detail.
+
+ -----------------------------------------------------------------
+ Status | Code | Description
+ |(hex) |
+ -----------------------------------------------------------------
+ Success | 0000 | Login is proceeding OK (*1).
+ -----------------------------------------------------------------
+ Target moved | 0101 | The requested iSCSI Target Name (ITN)
+ temporarily | | has temporarily moved
+ | | to the address provided.
+ -----------------------------------------------------------------
+ Target moved | 0102 | The requested ITN has permanently moved
+ permanently | | to the address provided.
+ -----------------------------------------------------------------
+
+
+
+Satran, et al. Standards Track [Page 162]
+
+RFC 3720 iSCSI April 2004
+
+
+ Initiator | 0200 | Miscellaneous iSCSI initiator
+ error | | errors.
+ ----------------------------------------------------------------
+ Authentication| 0201 | The initiator could not be
+ failure | | successfully authenticated or target
+ | | authentication is not supported.
+ -----------------------------------------------------------------
+ Authorization | 0202 | The initiator is not allowed access
+ failure | | to the given target.
+ -----------------------------------------------------------------
+ Not found | 0203 | The requested ITN does not
+ | | exist at this address.
+ -----------------------------------------------------------------
+ Target removed| 0204 | The requested ITN has been removed and
+ | |no forwarding address is provided.
+ -----------------------------------------------------------------
+ Unsupported | 0205 | The requested iSCSI version range is
+ version | | not supported by the target.
+ -----------------------------------------------------------------
+ Too many | 0206 | Too many connections on this SSID.
+ connections | |
+ -----------------------------------------------------------------
+ Missing | 0207 | Missing parameters (e.g., iSCSI
+ parameter | | Initiator and/or Target Name).
+ -----------------------------------------------------------------
+ Can't include | 0208 | Target does not support session
+ in session | | spanning to this connection (address).
+ -----------------------------------------------------------------
+ Session type | 0209 | Target does not support this type of
+ not supported | | of session or not from this Initiator.
+ -----------------------------------------------------------------
+ Session does | 020a | Attempt to add a connection
+ not exist | | to a non-existent session.
+ -----------------------------------------------------------------
+ Invalid during| 020b | Invalid Request type during Login.
+ login | |
+ -----------------------------------------------------------------
+ Target error | 0300 | Target hardware or software error.
+ -----------------------------------------------------------------
+ Service | 0301 | The iSCSI service or target is not
+ unavailable | | currently operational.
+ -----------------------------------------------------------------
+ Out of | 0302 | The target has insufficient session,
+ resources | | connection, or other resources.
+ -----------------------------------------------------------------
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 163]
+
+RFC 3720 iSCSI April 2004
+
+
+ (*1) If the response T bit is 1 in both the request and the matching
+ response, and the NSG is FullFeaturePhase in both the request and the
+ matching response, the Login Phase is finished and the initiator may
+ proceed to issue SCSI commands.
+
+ If the Status Class is not 0, the initiator and target MUST close the
+ TCP connection.
+
+ If the target wishes to reject the Login Request for more than one
+ reason, it should return the primary reason for the rejection.
+
+10.13.6. T (Transit) bit
+
+ The T bit is set to 1 as an indicator of the end of the stage. If
+ the T bit is set to 1 and NSG is FullFeaturePhase, then this is also
+ the Final Login Response (see Chapter 5). A T bit of 0 indicates a
+ "partial" response, which means "more negotiation needed".
+
+ A Login Response with a T bit set to 1 MUST NOT contain key=value
+ pairs that may require additional answers from the initiator within
+ the same stage.
+
+ If the status class is 0, the T bit MUST NOT be set to 1 if the T bit
+ in the request was set to 0.
+
+10.13.7. C (Continue) Bit
+
+ When set to 1, indicates that the text (set of key=value pairs) in
+ this Login Response is not complete (it will be continued on
+ subsequent Login Responses); otherwise, it indicates that this Login
+ Response ends a set of key=value pairs. A Login Response with the C
+ bit set to 1 MUST have the T bit set to 0.
+
+10.13.8. Login Parameters
+
+ The target MUST provide some basic parameters in order to enable the
+ initiator to determine if it is connected to the correct port and the
+ initial text parameters for the security exchange.
+
+ All the rules specified in Section 10.11.6 Text Response Data for
+ text responses also hold for Login Responses. Keys and their
+ explanations are listed in Chapter 11 (security negotiation keys) and
+ Chapter 12 (operational parameter negotiation keys). All keys in
+ Chapter 12, except for the X extension formats, MUST be supported by
+ iSCSI initiators and targets. Keys in Chapter 11, only need to be
+ supported when the function to which they refer is mandatory to
+ implement.
+
+
+
+
+Satran, et al. Standards Track [Page 164]
+
+RFC 3720 iSCSI April 2004
+
+
+10.14. Logout Request
+
+ The Logout Request is used to perform a controlled closing of a
+ connection.
+
+ An initiator MAY use a Logout Request to remove a connection from a
+ session or to close an entire session.
+
+ After sending the Logout Request PDU, an initiator MUST NOT send any
+ new iSCSI requests on the closing connection. If the Logout Request
+ is intended to close the session, new iSCSI requests MUST NOT be sent
+ on any of the connections participating in the session.
+
+ When receiving a Logout Request with the reason code of "close the
+ connection" or "close the session", the target MUST terminate all
+ pending commands, whether acknowledged via ExpCmdSN or not, on that
+ connection or session respectively.
+
+ When receiving a Logout Request with the reason code "remove
+ connection for recovery", the target MUST discard all requests not
+ yet acknowledged via ExpCmdSN that were issued on the specified
+ connection, and suspend all data/status/R2T transfers on behalf of
+ pending commands on the specified connection.
+
+ The target then issues the Logout Response and half-closes the TCP
+ connection (sends FIN). After receiving the Logout Response and
+ attempting to receive the FIN (if still possible), the initiator MUST
+ completely close the logging-out connection. For the terminated
+ commands, no additional responses should be expected.
+
+ A Logout for a CID may be performed on a different transport
+ connection when the TCP connection for the CID has already been
+ terminated. In such a case, only a logical "closing" of the iSCSI
+ connection for the CID is implied with a Logout.
+
+ All commands that were not terminated or not completed (with status)
+ and acknowledged when the connection is closed completely can be
+ reassigned to a new connection if the target supports connection
+ recovery.
+
+ If an initiator intends to start recovery for a failing connection,
+ it MUST use the Logout Request to "clean-up" the target end of a
+ failing connection and enable recovery to start, or the Login Request
+ with a non-zero TSIH and the same CID on a new connection for the
+ same effect (see Section 10.14.3 CID). In sessions with a single
+ connection, the connection can be closed and then a new connection
+ reopened. A connection reinstatement login can be used for recovery
+ (see Section 5.3.4 Connection Reinstatement).
+
+
+
+Satran, et al. Standards Track [Page 165]
+
+RFC 3720 iSCSI April 2004
+
+
+ A successful completion of a Logout Request with the reason code of
+ "close the connection" or "remove the connection for recovery"
+ results at the target in the discarding of unacknowledged commands
+ received on the connection being logged out. These are commands that
+ have arrived on the connection being logged out, but have not been
+ delivered to SCSI because one or more commands with a smaller CmdSN
+ has not been received by iSCSI. See Section 3.2.2.1 Command
+ Numbering and Acknowledging. The resulting holes the in command
+ sequence numbers will have to be handled by appropriate recovery (see
+ Chapter 6) unless the session is also closed.
+
+ The entire logout discussion in this section is also applicable for
+ an implicit Logout realized via a connection reinstatement or session
+ reinstatement. When a Login Request performs an implicit Logout, the
+ implicit Logout is performed as if having the reason codes specified
+ below:
+
+ Reason code Type of implicit Logout
+ -------------------------------------------
+ 0 session reinstatement
+ 1 connection reinstatement when
+ the operational ErrorRecoveryLevel < 2
+ 2 connection reinstatement when
+ the operational ErrorRecoveryLevel = 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 166]
+
+RFC 3720 iSCSI April 2004
+
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|I| 0x06 |1| Reason Code | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------------------------------------------------------+
+ 8/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| CID or Reserved | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 24| CmdSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpStatSN |
+ +---------------+---------------+---------------+---------------+
+ 32/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+10.14.1. Reason Code
+
+ Reason Code indicates the reason for Logout as follows:
+
+ 0 - close the session. All commands associated with the session
+ (if any) are terminated.
+
+ 1 - close the connection. All commands associated with connection
+ (if any) are terminated.
+
+ 2 - remove the connection for recovery. Connection is closed and
+ all commands associated with it, if any, are to be prepared
+ for a new allegiance.
+
+ All other values are reserved.
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 167]
+
+RFC 3720 iSCSI April 2004
+
+
+10.14.2. TotalAHSLength and DataSegmentLength
+
+ For this PDU TotalAHSLength and DataSegmentLength MUST be 0.
+
+10.14.3. CID
+
+ This is the connection ID of the connection to be closed (including
+ closing the TCP stream). This field is only valid if the reason code
+ is not "close the session".
+
+10.14.4. ExpStatSN
+
+ This is the last ExpStatSN value for the connection to be closed.
+
+10.14.5. Implicit termination of tasks
+
+ A target implicitly terminates the active tasks due to the iSCSI
+ protocol in the following cases:
+
+ a) When a connection is implicitly or explicitly logged out with
+ the reason code of "Close the connection" and there are active
+ tasks allegiant to that connection.
+
+ b) When a connection fails and eventually the connection state
+ times out (state transition M1 in Section 7.2.2 State
+ Transition Descriptions for Initiators and Targets) and there
+ are active tasks allegiant to that connection.
+
+ c) When a successful recovery Logout is performed while there are
+ active tasks allegiant to that connection, and those tasks
+ eventually time out after the Time2Wait and Time2Retain
+ periods without allegiance reassignment.
+
+ d) When a connection is implicitly or explicitly logged out with
+ the reason code of "Close the session" and there are active
+ tasks in that session.
+
+ If the tasks terminated in any of the above cases are SCSI tasks,
+ they must be internally terminated as if with CHECK CONDITION status.
+ This status is only meaningful for appropriately handling the
+ internal SCSI state and SCSI side effects with respect to ordering
+ because this status is never communicated back as a terminating
+ status to the initiator. However additional actions may have to be
+ taken at SCSI level depending on the SCSI context as defined by the
+ SCSI standards (e.g., queued commands and ACA, in cases a), b), and
+ c), after the tasks are terminated, the target MUST report a Unit
+ Attention condition on the next command processed on any connection
+ for each affected I_T_L nexus with the status of CHECK CONDITION, and
+
+
+
+Satran, et al. Standards Track [Page 168]
+
+RFC 3720 iSCSI April 2004
+
+
+ the ASC/ASCQ value of 47h/7Fh - "SOME COMMANDS CLEARED BY ISCSI
+ PROTOCOL EVENT" - etc. - see [SAM2] and [SPC3]).
+
+10.15. Logout Response
+
+ The Logout Response is used by the target to indicate if the cleanup
+ operation for the connection(s) has completed.
+
+ After Logout, the TCP connection referred by the CID MUST be closed
+ at both ends (or all connections must be closed if the logout reason
+ was session close).
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x26 |1| Reserved | Response | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------------------------------------------------------+
+ 8/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag |
+ +---------------+---------------+---------------+---------------+
+ 20| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 24| StatSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 32| MaxCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 36| Reserved |
+ +---------------------------------------------------------------+
+ 40| Time2Wait | Time2Retain |
+ +---------------+---------------+---------------+---------------+
+ 44| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 169]
+
+RFC 3720 iSCSI April 2004
+
+
+10.15.1. Response
+
+ Logout Response:
+
+ 0 - connection or session closed successfully.
+
+ 1 - CID not found.
+
+ 2 - connection recovery is not supported. If Logout reason code
+ was recovery and target does not support it as indicated by the
+ ErrorRecoveryLevel.
+
+ 3 - cleanup failed for various reasons.
+
+10.15.2. TotalAHSLength and DataSegmentLength
+
+ For this PDU TotalAHSLength and DataSegmentLength MUST be 0.
+
+10.15.3. Time2Wait
+
+ If the Logout Response code is 0 and if the operational
+ ErrorRecoveryLevel is 2, this is the minimum amount of time, in
+ seconds, to wait before attempting task reassignment. If the Logout
+ Response code is 0 and if the operational ErrorRecoveryLevel is less
+ than 2, this field is to be ignored.
+
+ This field is invalid if the Logout Response code is 1.
+
+ If the Logout response code is 2 or 3, this field specifies the
+ minimum time to wait before attempting a new implicit or explicit
+ logout.
+
+ If Time2Wait is 0, the reassignment or a new Logout may be attempted
+ immediately.
+
+10.15.4. Time2Retain
+
+ If the Logout response code is 0 and if the operational
+ ErrorRecoveryLevel is 2, this is the maximum amount of time, in
+ seconds, after the initial wait (Time2Wait), the target waits for the
+ allegiance reassignment for any active task after which the task
+ state is discarded. If the Logout response code is 0 and if the
+ operational ErrorRecoveryLevel is less than 2, this field is to be
+ ignored.
+
+ This field is invalid if the Logout response code is 1.
+
+
+
+
+
+Satran, et al. Standards Track [Page 170]
+
+RFC 3720 iSCSI April 2004
+
+
+ If the Logout response code is 2 or 3, this field specifies the
+ maximum amount of time, in seconds, after the initial wait
+ (Time2Wait), the target waits for a new implicit or explicit logout.
+
+ If it is the last connection of a session, the whole session state is
+ discarded after Time2Retain.
+
+ If Time2Retain is 0, the target has already discarded the connection
+ (and possibly the session) state along with the task states. No
+ reassignment or Logout is required in this case.
+
+10.16. SNACK Request
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x10 |1|.|.|.| Type | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| LUN or Reserved |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 20| Target Transfer Tag or SNACK Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 24| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpStatSN |
+ +---------------+---------------+---------------+---------------+
+ 32/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 40| BegRun |
+ +---------------------------------------------------------------+
+ 44| RunLength |
+ +---------------------------------------------------------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+ If the implementation supports ErrorRecoveryLevel greater than zero,
+ it MUST support all SNACK types.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 171]
+
+RFC 3720 iSCSI April 2004
+
+
+ The SNACK is used by the initiator to request the retransmission of
+ numbered-responses, data, or R2T PDUs from the target. The SNACK
+ request indicates the numbered-responses or data "runs" whose
+ retransmission is requested by the target, where the run starts with
+ the first StatSN, DataSN, or R2TSN whose retransmission is requested
+ and indicates the number of Status, Data, or R2T PDUs requested
+ including the first. 0 has special meaning when used as a starting
+ number and length:
+
+ - When used in RunLength, it means all PDUs starting with the
+ initial.
+ - When used in both BegRun and RunLength, it means all
+ unacknowledged PDUs.
+
+ The numbered-response(s) or R2T(s), requested by a SNACK, MUST be
+ delivered as exact replicas of the ones that the target transmitted
+ originally except for the fields ExpCmdSN, MaxCmdSN, and ExpDataSN,
+ which MUST carry the current values. R2T(s)requested by SNACK MUST
+ also carry the current value of StatSN.
+
+ The numbered Data-In PDUs, requested by a Data SNACK MUST be
+ delivered as exact replicas of the ones that the target transmitted
+ originally except for the fields ExpCmdSN and MaxCmdSN, which MUST
+ carry the current values and except for resegmentation (see Section
+ 10.16.3 Resegmentation).
+
+ Any SNACK that requests a numbered-response, Data, or R2T that was
+ not sent by the target or was already acknowledged by the initiator,
+ MUST be rejected with a reason code of "Protocol error".
+
+10.16.1. Type
+
+ This field encodes the SNACK function as follows:
+
+ 0-Data/R2T SNACK - requesting retransmission of one or more Data-
+ In or R2T PDUs.
+
+ 1-Status SNACK - requesting retransmission of one or more numbered
+ responses.
+
+ 2-DataACK - positively acknowledges Data-In PDUs.
+
+ 3-R-Data SNACK - requesting retransmission of Data-In PDUs with
+ possible resegmentation and status tagging.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 172]
+
+RFC 3720 iSCSI April 2004
+
+
+ All other values are reserved.
+
+ Data/R2T SNACK, Status SNACK, or R-Data SNACK for a command MUST
+ precede status acknowledgement for the given command.
+
+10.16.2. Data Acknowledgement
+
+ If an initiator operates at ErrorRecoveryLevel 1 or higher, it MUST
+ issue a SNACK of type DataACK after receiving a Data-In PDU with the
+ A bit set to 1. However, if the initiator has detected holes in the
+ input sequence, it MUST postpone issuing the SNACK of type DataACK
+ until the holes are filled. An initiator MAY ignore the A bit if it
+ deems that the bit is being set aggressively by the target (i.e.,
+ before the MaxBurstLength limit is reached).
+
+ The DataACK is used to free resources at the target and not to
+ request or imply data retransmission.
+
+ An initiator MUST NOT request retransmission for any data it had
+ already acknowledged.
+
+10.16.3. Resegmentation
+
+ If the initiator MaxRecvDataSegmentLength changed between the
+ original transmission and the time the initiator requests
+ retransmission, the initiator MUST issue a R-Data SNACK (see Section
+ 10.16.1 Type). With R-Data SNACK, the initiator indicates that it
+ discards all the unacknowledged data and expects the target to resend
+ it. It also expects resegmentation. In this case, the retransmitted
+ Data-In PDUs MAY be different from the ones originally sent in order
+ to reflect changes in MaxRecvDataSegmentLength. Their DataSN starts
+ with the BegRun of the last DataACK received by the target if any was
+ received; otherwise it starts with 0 and is increased by 1 for each
+ resent Data-In PDU.
+
+ A target that has received a R-Data SNACK MUST return a SCSI Response
+ that contains a copy of the SNACK Tag field from the R-Data SNACK in
+ the SCSI Response SNACK Tag field as its last or only Response. For
+ example, if it has already sent a response containing another value
+ in the SNACK Tag field or had the status included in the last Data-In
+ PDU, it must send a new SCSI Response PDU. If a target sends more
+ than one SCSI Response PDU due to this rule, all SCSI responses must
+ carry the same StatSN (see Section 10.4.4 SNACK Tag). If an
+ initiator attempts to recover a lost SCSI Response (with a
+ Status SNACK, see Section 10.16.1 Type) when more than one response
+ has been sent, the target will send the SCSI Response with the latest
+ content known to the target, including the last SNACK Tag for the
+ command.
+
+
+
+Satran, et al. Standards Track [Page 173]
+
+RFC 3720 iSCSI April 2004
+
+
+ For considerations in allegiance reassignment of a task to a
+ connection with a different MaxRecvDataSegmentLength, refer to
+ Section 6.2.2 Allegiance Reassignment.
+
+10.16.4. Initiator Task Tag
+
+ For Status SNACK and DataACK, the Initiator Task Tag MUST be set to
+ the reserved value 0xffffffff. In all other cases, the Initiator
+ Task Tag field MUST be set to the Initiator Task Tag of the
+ referenced command.
+
+10.16.5. Target Transfer Tag or SNACK Tag
+
+ For an R-Data SNACK, this field MUST contain a value that is
+ different from 0 or 0xffffffff and is unique for the task (identified
+ by the Initiator Task Tag). This value MUST be copied by the iSCSI
+ target in the last or only SCSI Response PDU it issues for the
+ command.
+
+ For DataACK, the Target Transfer Tag MUST contain a copy of the
+ Target Transfer Tag and LUN provided with the SCSI Data-In PDU with
+ the A bit set to 1.
+
+ In all other cases, the Target Transfer Tag field MUST be set to the
+ reserved value of 0xffffffff.
+
+10.16.6. BegRun
+
+ The DataSN, R2TSN, or StatSN of the first PDU whose retransmission is
+ requested (Data/R2T and Status SNACK), or the next expected DataSN
+ (DataACK SNACK).
+
+ BegRun 0 when used in conjunction with RunLength 0 means resend all
+ unacknowledged Data-In, R2T or Response PDUs.
+
+ BegRun MUST be 0 for a R-Data SNACK.
+
+10.16.7. RunLength
+
+ The number of PDUs whose retransmission is requested.
+
+ RunLength 0 signals that all Data-In, R2T, or Response PDUs carrying
+ the numbers equal to or greater than BegRun have to be resent.
+
+ The RunLength MUST also be 0 for a DataACK SNACK in addition to
+ R-Data SNACK.
+
+
+
+
+
+Satran, et al. Standards Track [Page 174]
+
+RFC 3720 iSCSI April 2004
+
+
+10.17. Reject
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x3f |1| Reserved | Reason | Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 16| 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 20| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 24| StatSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 32| MaxCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 36| DataSN/R2TSN or Reserved |
+ +---------------+---------------+---------------+---------------+
+ 40| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 44| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+ xx/ Complete Header of Bad PDU /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ yy/Vendor specific data (if any) /
+ / /
+ +---------------+---------------+---------------+---------------+
+ zz| Data-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+ Reject is used to indicate an iSCSI error condition (protocol,
+ unsupported option, etc.).
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 175]
+
+RFC 3720 iSCSI April 2004
+
+
+10.17.1. Reason
+
+ The reject Reason is coded as follows:
+
+ +------+----------------------------------------+------------------+
+ | Code | Explanation | Can the original |
+ | (hex)| | PDU be re-sent? |
+ +------+----------------------------------------+------------------+
+ | 0x01 | Reserved | no |
+ | | | |
+ | 0x02 | Data (payload) Digest Error | yes (Note 1) |
+ | | | |
+ | 0x03 | SNACK Reject | yes |
+ | | | |
+ | 0x04 | Protocol Error (e.g., SNACK request for| no |
+ | | a status that was already acknowledged)| |
+ | | | |
+ | 0x05 | Command not supported | no |
+ | | | |
+ | 0x06 | Immediate Command Reject - too many | yes |
+ | | immediate commands | |
+ | | | |
+ | 0x07 | Task in progress | no |
+ | | | |
+ | 0x08 | Invalid Data ACK | no |
+ | | | |
+ | 0x09 | Invalid PDU field | no (Note 2) |
+ | | | |
+ | 0x0a | Long Operation Reject - Can't generate | yes |
+ | | Target Transfer Tag - out of resources | |
+ | | | |
+ | 0x0b | Negotiation Reset | no |
+ | | | |
+ | 0x0c | Waiting for Logout | no |
+ +------+----------------------------------------+------------------+
+
+ Note 1: For iSCSI, Data-Out PDU retransmission is only done if the
+ target requests retransmission with a recovery R2T. However, if this
+ is the data digest error on immediate data, the initiator may choose
+ to retransmit the whole PDU including the immediate data.
+
+ Note 2: A target should use this reason code for all invalid values
+ of PDU fields that are meant to describe a task, a response, or a
+ data transfer. Some examples are invalid TTT/ITT, buffer offset, LUN
+ qualifying a TTT, and an invalid sequence number in a SNACK.
+
+ All other values for Reason are reserved.
+
+
+
+
+Satran, et al. Standards Track [Page 176]
+
+RFC 3720 iSCSI April 2004
+
+
+ In all the cases in which a pre-instantiated SCSI task is terminated
+ because of the reject, the target MUST issue a proper SCSI command
+ response with CHECK CONDITION as described in Section 10.4.3
+ Response. In these cases in which a status for the SCSI task was
+ already sent before the reject, no additional status is required. If
+ the error is detected while data from the initiator is still expected
+ (i.e., the command PDU did not contain all the data and the target
+ has not received a Data-Out PDU with the Final bit set to 1 for the
+ unsolicited data, if any, and all outstanding R2Ts, if any), the
+ target MUST wait until it receives the last expected Data-Out PDUs
+ with the F bit set to 1 before sending the Response PDU.
+
+ For additional usage semantics of Reject PDU, see Section 6.3 Usage
+ Of Reject PDU in Recovery.
+
+10.17.2. DataSN/R2TSN
+
+ This field is only valid if the rejected PDU is a Data/R2T SNACK and
+ the Reject reason code is "Protocol error" (see Section 10.16 SNACK
+ Request). The DataSN/R2TSN is the next Data/R2T sequence number that
+ the target would send for the task, if any.
+
+10.17.3. StatSN, ExpCmdSN and MaxCmdSN
+
+ These fields carry their usual values and are not related to the
+ rejected command. StatSN is advanced after a Reject.
+
+10.17.4. Complete Header of Bad PDU
+
+ The target returns the header (not including digest) of the PDU in
+ error as the data of the response.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 177]
+
+RFC 3720 iSCSI April 2004
+
+
+10.18. NOP-Out
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|I| 0x00 |1| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| LUN or Reserved |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 20| Target Transfer Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 24| CmdSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpStatSN |
+ +---------------+---------------+---------------+---------------+
+ 32/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+ / DataSegment - Ping Data (optional) /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ | Data-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+ A NOP-Out may be used by an initiator as a "ping request" to verify
+ that a connection/session is still active and all its components are
+ operational. The NOP-In response is the "ping echo".
+
+ A NOP-Out is also sent by an initiator in response to a NOP-In.
+
+ A NOP-Out may also be used to confirm a changed ExpStatSN if another
+ PDU will not be available for a long time.
+
+ Upon receipt of a NOP-In with the Target Transfer Tag set to a valid
+ value (not the reserved 0xffffffff), the initiator MUST respond with
+ a NOP-Out. In this case, the NOP-Out Target Transfer Tag MUST
+ contain a copy of the NOP-In Target Transfer Tag.
+
+
+
+
+
+Satran, et al. Standards Track [Page 178]
+
+RFC 3720 iSCSI April 2004
+
+
+10.18.1. Initiator Task Tag
+
+ The NOP-Out MUST have the Initiator Task Tag set to a valid value
+ only if a response in the form of NOP-In is requested (i.e., the
+ NOP-Out is used as a ping request). Otherwise, the Initiator Task
+ Tag MUST be set to 0xffffffff.
+
+ When a target receives the NOP-Out with a valid Initiator Task Tag,
+ it MUST respond with a Nop-In Response (see Section 10.19 NOP-In).
+
+ If the Initiator Task Tag contains 0xffffffff, the I bit MUST be set
+ to 1 and the CmdSN is not advanced after this PDU is sent.
+
+10.18.2. Target Transfer Tag
+
+ A target assigned identifier for the operation.
+
+ The NOP-Out MUST only have the Target Transfer Tag set if it is
+ issued in response to a NOP-In with a valid Target Transfer Tag. In
+ this case, it copies the Target Transfer Tag from the NOP-In PDU.
+ Otherwise, the Target Transfer Tag MUST be set to 0xffffffff.
+
+ When the Target Transfer Tag is set to a value other than 0xffffffff,
+ the LUN field MUST also be copied from the NOP-In.
+
+10.18.3. Ping Data
+
+ Ping data are reflected in the NOP-In Response. The length of the
+ reflected data are limited to MaxRecvDataSegmentLength. The length
+ of ping data are indicated by the DataSegmentLength. 0 is a valid
+ value for the DataSegmentLength and indicates the absence of ping
+ data.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 179]
+
+RFC 3720 iSCSI April 2004
+
+
+10.19. NOP-In
+
+ Byte/ 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0|.|.| 0x20 |1| Reserved |
+ +---------------+---------------+---------------+---------------+
+ 4|TotalAHSLength | DataSegmentLength |
+ +---------------+---------------+---------------+---------------+
+ 8| LUN or Reserved |
+ + +
+ 12| |
+ +---------------+---------------+---------------+---------------+
+ 16| Initiator Task Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 20| Target Transfer Tag or 0xffffffff |
+ +---------------+---------------+---------------+---------------+
+ 24| StatSN |
+ +---------------+---------------+---------------+---------------+
+ 28| ExpCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 32| MaxCmdSN |
+ +---------------+---------------+---------------+---------------+
+ 36/ Reserved /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ 48| Header-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+ / DataSegment - Return Ping Data /
+ +/ /
+ +---------------+---------------+---------------+---------------+
+ | Data-Digest (Optional) |
+ +---------------+---------------+---------------+---------------+
+
+ NOP-In is either sent by a target as a response to a NOP-Out, as a
+ "ping" to an initiator, or as a means to carry a changed ExpCmdSN
+ and/or MaxCmdSN if another PDU will not be available for a long time
+ (as determined by the target).
+
+ When a target receives the NOP-Out with a valid Initiator Task Tag
+ (not the reserved value 0xffffffff), it MUST respond with a NOP-In
+ with the same Initiator Task Tag that was provided in the NOP-Out
+ request. It MUST also duplicate up to the first
+ MaxRecvDataSegmentLength bytes of the initiator provided Ping Data.
+ For such a response, the Target Transfer Tag MUST be 0xffffffff.
+
+
+
+
+
+Satran, et al. Standards Track [Page 180]
+
+RFC 3720 iSCSI April 2004
+
+
+ Otherwise, when a target sends a NOP-In that is not a response to a
+ Nop-Out received from the initiator, the Initiator Task Tag MUST be
+ set to 0xffffffff and the Data Segment MUST NOT contain any data
+ (DataSegmentLength MUST be 0).
+
+10.19.1. Target Transfer Tag
+
+ If the target is responding to a NOP-Out, this is set to the reserved
+ value 0xffffffff.
+
+ If the target is sending a NOP-In as a Ping (intending to receive a
+ corresponding NOP-Out), this field is set to a valid value (not the
+ reserved 0xffffffff).
+
+ If the target is initiating a NOP-In without wanting to receive a
+ corresponding NOP-Out, this field MUST hold the reserved value of
+ 0xffffffff.
+
+10.19.2. StatSN
+
+ The StatSN field will always contain the next StatSN. However, when
+ the Initiator Task Tag is set to 0xffffffff, StatSN for the
+ connection is not advanced after this PDU is sent.
+
+10.19.3. LUN
+
+ A LUN MUST be set to a correct value when the Target Transfer Tag is
+ valid (not the reserved value 0xffffffff).
+
+11. iSCSI Security Text Keys and Authentication Methods
+
+ Only the following keys are used during the SecurityNegotiation stage
+ of the Login Phase:
+
+ SessionType
+ InitiatorName
+ TargetName
+ TargetAddress
+ InitiatorAlias
+ TargetAlias
+ TargetPortalGroupTag
+ AuthMethod and the keys used by the authentication methods
+ specified under Section 11.1 AuthMethod along with all of
+ their associated keys as well as Vendor Specific
+ Authentication Methods.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 181]
+
+RFC 3720 iSCSI April 2004
+
+
+ Other keys MUST NOT be used.
+
+ SessionType, InitiatorName, TargetName, InitiatorAlias, TargetAlias,
+ and TargetPortalGroupTag are described in Chapter 12 as they can be
+ used also in the OperationalNegotiation stage.
+
+ All security keys have connection-wide applicability.
+
+11.1. AuthMethod
+
+ Use: During Login - Security Negotiation Senders: Initiator and
+ Target Scope: connection
+
+ AuthMethod = <list-of-values>
+
+ The main item of security negotiation is the authentication method
+ (AuthMethod).
+
+ The authentication methods that can be used (appear in the
+ list-of-values) are either those listed in the following table or are
+ vendor-unique methods:
+
+ +------------------------------------------------------------+
+ | Name | Description |
+ +------------------------------------------------------------+
+ | KRB5 | Kerberos V5 - defined in [RFC1510] |
+ +------------------------------------------------------------+
+ | SPKM1 | Simple Public-Key GSS-API Mechanism |
+ | | defined in [RFC2025] |
+ +------------------------------------------------------------+
+ | SPKM2 | Simple Public-Key GSS-API Mechanism |
+ | | defined in [RFC2025] |
+ +------------------------------------------------------------+
+ | SRP | Secure Remote Password |
+ | | defined in [RFC2945] |
+ +------------------------------------------------------------+
+ | CHAP | Challenge Handshake Authentication Protocol|
+ | | defined in [RFC1994] |
+ +------------------------------------------------------------+
+ | None | No authentication |
+ +------------------------------------------------------------+
+
+ The AuthMethod selection is followed by an "authentication exchange"
+ specific to the authentication method selected.
+
+ The authentication method proposal may be made by either the
+ initiator or the target. However the initiator MUST make the first
+ step specific to the selected authentication method as soon as it is
+
+
+
+Satran, et al. Standards Track [Page 182]
+
+RFC 3720 iSCSI April 2004
+
+
+ selected. It follows that if the target makes the authentication
+ method proposal the initiator sends the first keys(s) of the exchange
+ together with its authentication method selection.
+
+ The authentication exchange authenticates the initiator to the
+ target, and optionally, the target to the initiator. Authentication
+ is OPTIONAL to use but MUST be supported by the target and initiator.
+
+ The initiator and target MUST implement CHAP. All other
+ authentication methods are OPTIONAL.
+
+ Private or public extension algorithms MAY also be negotiated for
+ authentication methods. Whenever a private or public extension
+ algorithm is part of the default offer (the offer made in absence of
+ explicit administrative action) the implementer MUST ensure that CHAP
+ is listed as an alternative in the default offer and "None" is not
+ part of the default offer.
+
+ Extension authentication methods MUST be named using one of the
+ following two formats:
+
+ a) Z-reversed.vendor.dns_name.do_something=
+ b) Z<#><IANA-registered-string>=
+
+ Authentication methods named using the Z- format are used as private
+ extensions. Authentication methods named using the Z# format are
+ used as public extensions that must be registered with IANA and MUST
+ be described by an informational RFC.
+
+ For all of the public or private extension authentication methods,
+ the method specific keys MUST conform to the format specified in
+ Section 5.1 Text Format for standard-label.
+
+ To identify the vendor for private extension authentication methods,
+ we suggest you use the reversed DNS-name as a prefix to the proper
+ digest names.
+
+ The part of digest-name following Z- and Z# MUST conform to the
+ format for standard-label specified in Section 5.1 Text Format.
+
+ Support for public or private extension authentication methods is
+ OPTIONAL.
+
+ The following subsections define the specific exchanges for each of
+ the standardized authentication methods. As mentioned earlier the
+ first step is always done by the initiator.
+
+
+
+
+
+Satran, et al. Standards Track [Page 183]
+
+RFC 3720 iSCSI April 2004
+
+
+11.1.1. Kerberos
+
+ For KRB5 (Kerberos V5) [RFC1510] and [RFC1964], the initiator MUST
+ use:
+
+ KRB_AP_REQ=<KRB_AP_REQ>
+
+ where KRB_AP_REQ is the client message as defined in [RFC1510].
+
+ The default principal name assumed by an iSCSI initiator or target
+ (prior to any administrative configuration action) MUST be the iSCSI
+ Initiator Name or iSCSI Target Name respectively, prefixed by the
+ string "iscsi/".
+
+ If the initiator authentication fails, the target MUST respond with a
+ Login reject with "Authentication Failure" status. Otherwise, if the
+ initiator has selected the mutual authentication option (by setting
+ MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the
+ target MUST reply with:
+
+ KRB_AP_REP=<KRB_AP_REP>
+
+ where KRB_AP_REP is the server's response message as defined in
+ [RFC1510].
+
+ If mutual authentication was selected and target authentication
+ fails, the initiator MUST close the connection.
+
+ KRB_AP_REQ and KRB_AP_REP are binary-values and their binary length
+ (not the length of the character string that represents them in
+ encoded form) MUST not exceed 65536 bytes.
+
+11.1.2. Simple Public-Key Mechanism (SPKM)
+
+ For SPKM1 and SPKM2 [RFC2025], the initiator MUST use:
+
+ SPKM_REQ=<SPKM-REQ>
+
+ where SPKM-REQ is the first initiator token as defined in [RFC2025].
+
+ [RFC2025] defines situations where each side may send an error token
+ that may cause the peer to re-generate and resend its last token.
+ This scheme is followed in iSCSI, and the error token syntax is:
+
+ SPKM_ERROR=<SPKM-ERROR>
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 184]
+
+RFC 3720 iSCSI April 2004
+
+
+ However, SPKM-DEL tokens that are defined by [RFC2025] for fatal
+ errors will not be used by iSCSI. If the target needs to send a
+ SPKM-DEL token, it will, instead, send a Login "login reject" message
+ with the "Authentication Failure" status and terminate the
+ connection. If the initiator needs to send a SPKM-DEL token, it will
+ close the connection.
+
+ In the following sections, we assume that no SPKM-ERROR tokens are
+ required.
+
+ If the initiator authentication fails, the target MUST return an
+ error. Otherwise, if the AuthMethod is SPKM1 or if the initiator has
+ selected the mutual authentication option (by setting mutual-state
+ bit in the options field of the REQ-TOKEN in the SPKM-REQ), the
+ target MUST reply with:
+
+ SPKM_REP_TI=<SPKM-REP-TI>
+
+ where SPKM-REP-TI is the target token as defined in [RFC2025].
+
+ If mutual authentication was selected and target authentication
+ fails, the initiator MUST close the connection. Otherwise, if the
+ AuthMethod is SPKM1, the initiator MUST continue with:
+
+ SPKM_REP_IT=<SPKM-REP-IT>
+
+ where SPKM-REP-IT is the second initiator token as defined in
+ [RFC2025]. If the initiator authentication fails, the target MUST
+ answer with a Login reject with "Authentication Failure" status.
+
+ SPKM requires support for very long authentication items.
+
+ All the SPKM-* tokens are binary-values and their binary length (not
+ the length of the character string that represents them in encoded
+ form) MUST not exceed 65536 bytes.
+
+11.1.3. Secure Remote Password (SRP)
+
+ For SRP [RFC2945], the initiator MUST use:
+
+ SRP_U=<U> TargetAuth=Yes /* or TargetAuth=No */
+
+ The target MUST answer with a Login reject with the "Authorization
+ Failure" status or reply with:
+
+ SRP_GROUP=<G1,G2...> SRP_s=<s>
+
+ Where G1,G2... are proposed groups, in order of preference.
+
+
+
+Satran, et al. Standards Track [Page 185]
+
+RFC 3720 iSCSI April 2004
+
+
+ The initiator MUST either close the connection or continue with:
+
+ SRP_A=<A> SRP_GROUP=<G>
+
+ Where G is one of G1,G2... that were proposed by the target.
+
+ The target MUST answer with a Login reject with the "Authentication
+ Failure" status or reply with:
+
+ SRP_B=<B>
+
+ The initiator MUST close the connection or continue with:
+
+ SRP_M=<M>
+
+ If the initiator authentication fails, the target MUST answer with a
+ Login reject with "Authentication Failure" status. Otherwise, if the
+ initiator sent TargetAuth=Yes in the first message (requiring target
+ authentication), the target MUST reply with:
+
+ SRP_HM=<H(A | M | K)>
+
+ If the target authentication fails, the initiator MUST close the
+ connection.
+
+ Where U, s, A, B, M, and H(A | M | K) are defined in [RFC2945] (using
+ the SHA1 hash function, such as SRP-SHA1) and G,Gn (Gn stands for
+ G1,G2...) are identifiers of SRP groups specified in [RFC3723]. G,
+ Gn, and U are text strings, s,A,B,M, and H(A | M | K) are
+ binary-values. The length of s,A,B,M and H(A | M | K) in binary form
+ (not the length of the character string that represents them in
+ encoded form) MUST not exceed 1024 bytes.
+
+ For the SRP_GROUP, all the groups specified in [RFC3723] up to 1536
+ bits (i.e., SRP-768, SRP-1024, SRP-1280, SRP-1536) must be supported
+ by initiators and targets. To guarantee interoperability, targets
+ MUST always offer "SRP-1536" as one of the proposed groups.
+
+11.1.4. Challenge Handshake Authentication Protocol (CHAP)
+
+ For CHAP [RFC1994], in the first step, the initiator MUST send:
+
+ CHAP_A=<A1,A2...>
+
+ Where A1,A2... are proposed algorithms, in order of preference.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 186]
+
+RFC 3720 iSCSI April 2004
+
+
+ In the second step, the target MUST answer with a Login reject with
+ the "Authentication Failure" status or reply with:
+
+ CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
+
+ Where A is one of A1,A2... that were proposed by the initiator.
+
+ In the third step, the initiator MUST continue with:
+
+ CHAP_N=<N> CHAP_R=<R>
+
+ or, if it requires target authentication, with:
+
+ CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C>
+
+ If the initiator authentication fails, the target MUST answer with a
+ Login reject with "Authentication Failure" status. Otherwise, if the
+ initiator required target authentication, the target MUST either
+ answer with a Login reject with "Authentication Failure" or reply
+ with:
+
+ CHAP_N=<N> CHAP_R=<R>
+
+ If target authentication fails, the initiator MUST close the
+ connection.
+
+ Where N, (A,A1,A2), I, C, and R are (correspondingly) the Name,
+ Algorithm, Identifier, Challenge, and Response as defined in
+ [RFC1994], N is a text string, A,A1,A2, and I are numbers, and C and
+ R are large-binary-values and their binary length (not the length of
+ the character string that represents them in encoded form) MUST not
+ exceed 1024 bytes.
+
+ For the Algorithm, as stated in [RFC1994], one value is required to
+ be implemented:
+
+ 5 (CHAP with MD5)
+
+ To guarantee interoperability, initiators MUST always offer it as one
+ of the proposed algorithms.
+
+12. Login/Text Operational Text Keys
+
+ Some session specific parameters MUST only be carried on the leading
+ connection and cannot be changed after the leading connection login
+ (e.g., MaxConnections, the maximum number of connections). This
+
+
+
+
+
+Satran, et al. Standards Track [Page 187]
+
+RFC 3720 iSCSI April 2004
+
+
+ holds for a single connection session with regard to connection
+ restart. The keys that fall into this category have the use: LO
+ (Leading Only).
+
+ Keys that can only be used during login have the use: IO (initialize
+ only), while those that can be used in both the Login Phase and Full
+ Feature Phase have the use: ALL.
+
+ Keys that can only be used during Full Feature Phase use FFPO (Full
+ Feature Phase only).
+
+ Keys marked as Any-Stage may also appear in the SecurityNegotiation
+ stage while all other keys described in this chapter are operational
+ keys.
+
+ Keys that do not require an answer are marked as Declarative.
+
+ Key scope is indicated as session-wide (SW) or connection-only (CO).
+
+ Result function, wherever mentioned, states the function that can be
+ applied to check the validity of the responder selection. Minimum
+ means that the selected value cannot exceed the offered value.
+ Maximum means that the selected value cannot be lower than the
+ offered value. AND means that the selected value must be a possible
+ result of a Boolean "and" function with an arbitrary Boolean value
+ (e.g., if the offered value is No the selected value must be No). OR
+ means that the selected value must be a possible result of a Boolean
+ "or" function with an arbitrary Boolean value (e.g., if the offered
+ value is Yes the selected value must be Yes).
+
+12.1. HeaderDigest and DataDigest
+
+ Use: IO
+ Senders: Initiator and Target
+ Scope: CO
+
+ HeaderDigest = <list-of-values>
+ DataDigest = <list-of-values>
+
+ Default is None for both HeaderDigest and DataDigest.
+
+ Digests enable the checking of end-to-end, non-cryptographic data
+ integrity beyond the integrity checks provided by the link layers and
+ the covering of the whole communication path including all elements
+ that may change the network level PDUs such as routers, switches, and
+ proxies.
+
+
+
+
+
+Satran, et al. Standards Track [Page 188]
+
+RFC 3720 iSCSI April 2004
+
+
+ The following table lists cyclic integrity checksums that can be
+ negotiated for the digests and that MUST be implemented by every
+ iSCSI initiator and target. These digest options only have error
+ detection significance.
+
+ +---------------------------------------------+
+ | Name | Description | Generator |
+ +---------------------------------------------+
+ | CRC32C | 32 bit CRC |0x11edc6f41|
+ +---------------------------------------------+
+ | None | no digest |
+ +---------------------------------------------+
+
+ The generator polynomial for this digest is given in
+ hex-notation (e.g., 0x3b stands for 0011 1011 and the polynomial is
+ x**5+X**4+x**3+x+1).
+
+ When the Initiator and Target agree on a digest, this digest MUST be
+ used for every PDU in Full Feature Phase.
+
+ Padding bytes, when present in a segment covered by a CRC, SHOULD be
+ set to 0 and are included in the CRC.
+
+ The CRC MUST be calculated by a method that produces the same
+ results as the following process:
+
+ - The PDU bits are considered as the coefficients of a
+ polynomial M(x) of degree n-1; bit 7 of the lowest numbered
+ byte is considered the most significant bit (x^n-1), followed
+ by bit 6 of the lowest numbered byte through bit 0 of the
+ highest numbered byte (x^0).
+
+ - The most significant 32 bits are complemented.
+
+ - The polynomial is multiplied by x^32 then divided by G(x). The
+ generator polynomial produces a remainder R(x) of degree <= 31.
+
+ - The coefficients of R(x) are considered a 32 bit sequence.
+
+ - The bit sequence is complemented and the result is the CRC.
+
+ - The CRC bits are mapped into the digest word. The x^31
+ coefficient in bit 7 of the lowest numbered byte of the digest
+ continuing through to the byte up to the x^24 coefficient in
+ bit 0 of the lowest numbered byte, continuing with the x^23
+ coefficient in bit 7 of next byte through x^0 in bit 0 of the
+ highest numbered byte.
+
+
+
+
+Satran, et al. Standards Track [Page 189]
+
+RFC 3720 iSCSI April 2004
+
+
+ - Computing the CRC over any segment (data or header) extended
+ to include the CRC built using the generator 0x11edc6f41 will
+ always get the value 0x1c2d19ed as its final remainder (R(x)).
+ This value is given here in its polynomial form (i.e., not
+ mapped as the digest word).
+
+ For a discussion about selection criteria for the CRC, see
+ [RFC3385]. For a detailed analysis of the iSCSI polynomial, see
+ [Castagnoli93].
+
+ Private or public extension algorithms MAY also be negotiated for
+ digests. Whenever a private or public digest extension algorithm is
+ part of the default offer (the offer made in absence of explicit
+ administrative action) the implementer MUST ensure that CRC32C is
+ listed as an alternative in the default offer and "None" is not
+ part of the default offer.
+
+ Extension digest algorithms MUST be named using one of the following
+ two formats:
+
+ a) Y-reversed.vendor.dns_name.do_something=
+ b) Y<#><IANA-registered-string>=
+
+ Digests named using the Y- format are used for private purposes
+ (unregistered). Digests named using the Y# format (public extension)
+ must be registered with IANA and MUST be described by an
+ informational RFC.
+
+ For private extension digests, to identify the vendor, we suggest
+ you use the reversed DNS-name as a prefix to the proper digest
+ names.
+
+ The part of digest-name following Y- and Y# MUST conform to the
+ format for standard-label specified in Section 5.1 Text Format.
+
+ Support for public or private extension digests is OPTIONAL.
+
+12.2. MaxConnections
+
+ Use: LO
+ Senders: Initiator and Target
+ Scope: SW
+ Irrelevant when: SessionType=Discovery
+
+ MaxConnections=<numerical-value-from-1-to-65535>
+
+ Default is 1.
+ Result function is Minimum.
+
+
+
+Satran, et al. Standards Track [Page 190]
+
+RFC 3720 iSCSI April 2004
+
+
+
+ Initiator and target negotiate the maximum number of connections
+ requested/acceptable.
+
+12.3. SendTargets
+
+ Use: FFPO
+ Senders: Initiator
+ Scope: SW
+
+ For a complete description, see Appendix D. - SendTargets
+ Operation -.
+
+12.4. TargetName
+
+ Use: IO by initiator, FFPO by target - only as response to a
+ SendTargets, Declarative, Any-Stage
+
+ Senders: Initiator and Target
+ Scope: SW
+
+ TargetName=<iSCSI-name-value>
+
+ Examples:
+
+ TargetName=iqn.1993-11.com.disk-vendor:diskarrays.sn.45678
+ TargetName=eui.020000023B040506
+
+ The initiator of the TCP connection MUST provide this key to the
+ remote endpoint in the first login request if the initiator is not
+ establishing a discovery session. The iSCSI Target Name specifies
+ the worldwide unique name of the target.
+
+ The TargetName key may also be returned by the "SendTargets" text
+ request (which is its only use when issued by a target).
+
+ TargetName MUST not be redeclared within the login phase.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 191]
+
+RFC 3720 iSCSI April 2004
+
+
+12.5. InitiatorName
+
+ Use: IO, Declarative, Any-Stage
+ Senders: Initiator
+ Scope: SW
+
+ InitiatorName=<iSCSI-name-value>
+
+ Examples:
+
+ InitiatorName=iqn.1992-04.com.os-vendor.plan9:cdrom.12345
+ InitiatorName=iqn.2001-02.com.ssp.users:customer235.host90
+
+ The initiator of the TCP connection MUST provide this key to the
+ remote endpoint at the first Login of the Login Phase for every
+ connection. The InitiatorName key enables the initiator to identify
+ itself to the remote endpoint.
+
+ InitiatorName MUST not be redeclared within the login phase.
+
+12.6. TargetAlias
+
+ Use: ALL, Declarative, Any-Stage
+ Senders: Target
+ Scope: SW
+
+ TargetAlias=<iSCSI-local-name-value>
+
+ Examples:
+
+ TargetAlias=Bob-s Disk
+ TargetAlias=Database Server 1 Log Disk
+ TargetAlias=Web Server 3 Disk 20
+
+ If a target has been configured with a human-readable name or
+ description, this name SHOULD be communicated to the initiator during
+ a Login Response PDU if SessionType=Normal (see Section 12.21
+ SessionType). This string is not used as an identifier, nor is it
+ meant to be used for authentication or authorization decisions. It
+ can be displayed by the initiator's user interface in a list of
+ targets to which it is connected.
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 192]
+
+RFC 3720 iSCSI April 2004
+
+
+12.7. InitiatorAlias
+
+ Use: ALL, Declarative, Any-Stage
+ Senders: Initiator
+ Scope: SW
+
+ InitiatorAlias=<iSCSI-local-name-value>
+
+ Examples:
+
+ InitiatorAlias=Web Server 4
+ InitiatorAlias=spyalley.nsa.gov
+ InitiatorAlias=Exchange Server
+
+ If an initiator has been configured with a human-readable name or
+ description, it SHOULD be communicated to the target during a Login
+ Request PDU. If not, the host name can be used instead. This string
+ is not used as an identifier, nor is meant to be used for
+ authentication or authorization decisions. It can be displayed by
+ the target's user interface in a list of initiators to which it is
+ connected.
+
+12.8. TargetAddress
+
+ Use: ALL, Declarative, Any-Stage
+ Senders: Target
+ Scope: SW
+
+ TargetAddress=domainname[:port][,portal-group-tag]
+
+ The domainname can be specified as either a DNS host name, a
+ dotted-decimal IPv4 address, or a bracketed IPv6 address as specified
+ in [RFC2732].
+
+ If the TCP port is not specified, it is assumed to be the
+ IANA-assigned default port for iSCSI (see Section 13 IANA
+ Considerations).
+
+ If the TargetAddress is returned as the result of a redirect status
+ in a login response, the comma and portal group tag MUST be omitted.
+
+ If the TargetAddress is returned within a SendTargets response, the
+ portal group tag MUST be included.
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 193]
+
+RFC 3720 iSCSI April 2004
+
+
+ Examples:
+
+ TargetAddress=10.0.0.1:5003,1
+ TargetAddress=[1080:0:0:0:8:800:200C:417A],65
+ TargetAddress=[1080::8:800:200C:417A]:5003,1
+ TargetAddress=computingcenter.example.com,23
+
+ Use of the portal-group-tag is described in Appendix D.
+ - SendTargets Operation -. The formats for the port and
+ portal-group-tag are the same as the one specified in Section 12.9
+ TargetPortalGroupTag.
+
+12.9. TargetPortalGroupTag
+
+ Use: IO by target, Declarative, Any-Stage
+ Senders: Target
+ Scope: SW
+
+ TargetPortalGroupTag=<16-bit-binary-value>
+
+ Examples:
+ TargetPortalGroupTag=1
+
+ The target portal group tag is a 16-bit binary-value that uniquely
+ identifies a portal group within an iSCSI target node. This key
+ carries the value of the tag of the portal group that is servicing
+ the Login request. The iSCSI target returns this key to the
+ initiator in the Login Response PDU to the first Login Request PDU
+ that has the C bit set to 0 when TargetName is given by the
+ initiator.
+
+ For the complete usage expectations of this key see Section 5.3 Login
+ Phase.
+
+12.10. InitialR2T
+
+ Use: LO
+ Senders: Initiator and Target
+ Scope: SW
+ Irrelevant when: SessionType=Discovery
+
+ InitialR2T=<boolean-value>
+
+ Examples:
+
+ I->InitialR2T=No
+ T->InitialR2T=No
+
+
+
+
+Satran, et al. Standards Track [Page 194]
+
+RFC 3720 iSCSI April 2004
+
+
+ Default is Yes.
+ Result function is OR.
+
+ The InitialR2T key is used to turn off the default use of R2T for
+ unidirectional and the output part of bidirectional commands, thus
+ allowing an initiator to start sending data to a target as if it has
+ received an initial R2T with Buffer Offset=Immediate Data Length and
+ Desired Data Transfer Length=(min(FirstBurstLength, Expected Data
+ Transfer Length) - Received Immediate Data Length).
+
+ The default action is that R2T is required, unless both the initiator
+ and the target send this key-pair attribute specifying InitialR2T=No.
+ Only the first outgoing data burst (immediate data and/or separate
+ PDUs) can be sent unsolicited (i.e., not requiring an explicit R2T).
+
+12.11. ImmediateData
+
+ Use: LO
+ Senders: Initiator and Target
+ Scope: SW
+ Irrelevant when: SessionType=Discovery
+
+ ImmediateData=<boolean-value>
+
+ Default is Yes.
+ Result function is AND.
+
+ The initiator and target negotiate support for immediate data. To
+ turn immediate data off, the initiator or target must state its
+ desire to do so. ImmediateData can be turned on if both the
+ initiator and target have ImmediateData=Yes.
+
+ If ImmediateData is set to Yes and InitialR2T is set to Yes
+ (default), then only immediate data are accepted in the first burst.
+
+ If ImmediateData is set to No and InitialR2T is set to Yes, then the
+ initiator MUST NOT send unsolicited data and the target MUST reject
+ unsolicited data with the corresponding response code.
+
+ If ImmediateData is set to No and InitialR2T is set to No, then the
+ initiator MUST NOT send unsolicited immediate data, but MAY send one
+ unsolicited burst of Data-Out PDUs.
+
+ If ImmediateData is set to Yes and InitialR2T is set to No, then the
+ initiator MAY send unsolicited immediate data and/or one unsolicited
+ burst of Data-Out PDUs.
+
+
+
+
+
+Satran, et al. Standards Track [Page 195]
+
+RFC 3720 iSCSI April 2004
+
+
+ The following table is a summary of unsolicited data options:
+
+ +----------+-------------+------------------+--------------+
+ |InitialR2T|ImmediateData| Unsolicited |Immediate Data|
+ | | | Data Out PDUs | |
+ +----------+-------------+------------------+--------------+
+ | No | No | Yes | No |
+ +----------+-------------+------------------+--------------+
+ | No | Yes | Yes | Yes |
+ +----------+-------------+------------------+--------------+
+ | Yes | No | No | No |
+ +----------+-------------+------------------+--------------+
+ | Yes | Yes | No | Yes |
+ +----------+-------------+------------------+--------------+
+
+12.12. MaxRecvDataSegmentLength
+
+ Use: ALL, Declarative
+ Senders: Initiator and Target
+ Scope: CO
+
+ MaxRecvDataSegmentLength=<numerical-value-512-to-(2**24-1)>
+
+ Default is 8192 bytes.
+
+ The initiator or target declares the maximum data segment length in
+ bytes it can receive in an iSCSI PDU.
+
+ The transmitter (initiator or target) is required to send PDUs with a
+ data segment that does not exceed MaxRecvDataSegmentLength of the
+ receiver.
+
+ A target receiver is additionally limited by MaxBurstLength for
+ solicited data and FirstBurstLength for unsolicited data. An
+ initiator MUST NOT send solicited PDUs exceeding MaxBurstLength nor
+ unsolicited PDUs exceeding FirstBurstLength (or
+ FirstBurstLength-Immediate Data Length if immediate data were sent).
+
+12.13. MaxBurstLength
+
+ Use: LO
+ Senders: Initiator and Target
+ Scope: SW
+ Irrelevant when: SessionType=Discovery
+
+ MaxBurstLength=<numerical-value-512-to-(2**24-1)>
+
+
+
+
+
+Satran, et al. Standards Track [Page 196]
+
+RFC 3720 iSCSI April 2004
+
+
+ Default is 262144 (256 Kbytes).
+ Result function is Minimum.
+
+ The initiator and target negotiate maximum SCSI data payload in bytes
+ in a Data-In or a solicited Data-Out iSCSI sequence. A sequence
+ consists of one or more consecutive Data-In or Data-Out PDUs that end
+ with a Data-In or Data-Out PDU with the F bit set to one.
+
+12.14. FirstBurstLength
+
+ Use: LO
+ Senders: Initiator and Target
+ Scope: SW
+ Irrelevant when: SessionType=Discovery
+ Irrelevant when: ( InitialR2T=Yes and ImmediateData=No )
+
+ FirstBurstLength=<numerical-value-512-to-(2**24-1)>
+
+ Default is 65536 (64 Kbytes).
+ Result function is Minimum.
+
+ The initiator and target negotiate the maximum amount in bytes of
+ unsolicited data an iSCSI initiator may send to the target during the
+ execution of a single SCSI command. This covers the immediate data
+ (if any) and the sequence of unsolicited Data-Out PDUs (if any) that
+ follow the command.
+
+ FirstBurstLength MUST NOT exceed MaxBurstLength.
+
+12.15. DefaultTime2Wait
+
+ Use: LO
+ Senders: Initiator and Target
+ Scope: SW
+
+ DefaultTime2Wait=<numerical-value-0-to-3600>
+
+ Default is 2.
+ Result function is Maximum.
+
+ The initiator and target negotiate the minimum time, in seconds, to
+ wait before attempting an explicit/implicit logout or an active task
+ reassignment after an unexpected connection termination or a
+ connection reset.
+
+ A value of 0 indicates that logout or active task reassignment can be
+ attempted immediately.
+
+
+
+
+Satran, et al. Standards Track [Page 197]
+
+RFC 3720 iSCSI April 2004
+
+
+12.16. DefaultTime2Retain
+
+ Use: LO Senders: Initiator and Target Scope: SW
+
+ DefaultTime2Retain=<numerical-value-0-to-3600>
+
+ Default is 20. Result function is Minimum.
+
+ The initiator and target negotiate the maximum time, in seconds after
+ an initial wait (Time2Wait), before which an active task reassignment
+ is still possible after an unexpected connection termination or a
+ connection reset.
+
+ This value is also the session state timeout if the connection in
+ question is the last LOGGED_IN connection in the session.
+
+ A value of 0 indicates that connection/task state is immediately
+ discarded by the target.
+
+12.17. MaxOutstandingR2T
+
+ Use: LO
+ Senders: Initiator and Target
+ Scope: SW
+
+ MaxOutstandingR2T=<numerical-value-from-1-to-65535>
+ Irrelevant when: SessionType=Discovery
+
+ Default is 1.
+ Result function is Minimum.
+
+ Initiator and target negotiate the maximum number of outstanding R2Ts
+ per task, excluding any implied initial R2T that might be part of
+ that task. An R2T is considered outstanding until the last data PDU
+ (with the F bit set to 1) is transferred, or a sequence reception
+ timeout (Section 6.1.4.1 Recovery Within-command) is encountered for
+ that data sequence.
+
+12.18. DataPDUInOrder
+
+ Use: LO
+ Senders: Initiator and Target
+ Scope: SW
+ Irrelevant when: SessionType=Discovery
+
+ DataPDUInOrder=<boolean-value>
+
+
+
+
+
+Satran, et al. Standards Track [Page 198]
+
+RFC 3720 iSCSI April 2004
+
+
+ Default is Yes.
+ Result function is OR.
+
+ No is used by iSCSI to indicate that the data PDUs within sequences
+ can be in any order. Yes is used to indicate that data PDUs within
+ sequences have to be at continuously increasing addresses and
+ overlays are forbidden.
+
+12.19. DataSequenceInOrder
+
+ Use: LO
+ Senders: Initiator and Target
+ Scope: SW
+ Irrelevant when: SessionType=Discovery
+
+ DataSequenceInOrder=<boolean-value>
+
+ Default is Yes.
+ Result function is OR.
+
+ A Data Sequence is a sequence of Data-In or Data-Out PDUs that end
+ with a Data-In or Data-Out PDU with the F bit set to one. A Data-Out
+ sequence is sent either unsolicited or in response to an R2T.
+ Sequences cover an offset-range.
+
+ If DataSequenceInOrder is set to No, Data PDU sequences may be
+ transferred in any order.
+
+ If DataSequenceInOrder is set to Yes, Data Sequences MUST be
+ transferred using continuously non-decreasing sequence offsets (R2T
+ buffer offset for writes, or the smallest SCSI Data-In buffer offset
+ within a read data sequence).
+
+ If DataSequenceInOrder is set to Yes, a target may retry at most the
+ last R2T, and an initiator may at most request retransmission for the
+ last read data sequence. For this reason, if ErrorRecoveryLevel is
+ not 0 and DataSequenceInOrder is set to Yes then MaxOustandingR2T
+ MUST be set to 1.
+
+12.20. ErrorRecoveryLevel
+
+ Use: LO
+ Senders: Initiator and Target
+ Scope: SW
+
+ ErrorRecoveryLevel=<numerical-value-0-to-2>
+
+
+
+
+
+Satran, et al. Standards Track [Page 199]
+
+RFC 3720 iSCSI April 2004
+
+
+ Default is 0.
+ Result function is Minimum.
+
+ The initiator and target negotiate the recovery level supported.
+
+ Recovery levels represent a combination of recovery capabilities.
+ Each recovery level includes all the capabilities of the lower
+ recovery levels and adds some new ones to them.
+
+ In the description of recovery mechanisms, certain recovery classes
+ are specified. Section 6.1.5 Error Recovery Hierarchy describes the
+ mapping between the classes and the levels.
+
+12.21. SessionType
+
+ Use: LO, Declarative, Any-Stage
+ Senders: Initiator
+ Scope: SW
+
+ SessionType= <Discovery|Normal>
+
+ Default is Normal.
+
+ The initiator indicates the type of session it wants to create. The
+ target can either accept it or reject it.
+
+ A discovery session indicates to the Target that the only purpose of
+ this Session is discovery. The only requests a target accepts in
+ this type of session are a text request with a SendTargets key and a
+ logout request with reason "close the session".
+
+ The discovery session implies MaxConnections = 1 and overrides both
+ the default and an explicit setting.
+
+12.22. The Private or Public Extension Key Format
+
+ Use: ALL
+ Senders: Initiator and Target
+ Scope: specific key dependent
+
+ X-reversed.vendor.dns_name.do_something=
+
+ or
+
+ X<#><IANA-registered-string>=
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 200]
+
+RFC 3720 iSCSI April 2004
+
+
+ Keys with this format are used for public or private extension
+ purposes. These keys always start with X- if unregistered with IANA
+ (private) or X# if registered with IANA (public).
+
+ For unregistered keys, to identify the vendor, we suggest you use the
+ reversed DNS-name as a prefix to the key-proper.
+
+ The part of key-name following X- and X# MUST conform to the format
+ for key-name specified in Section 5.1 Text Format.
+
+ For IANA registered keys the string following X# must be registered
+ with IANA and the use of the key MUST be described by an
+ informational RFC.
+
+ Vendor specific keys MUST ONLY be used in normal sessions.
+
+ Support for public or private extension keys is OPTIONAL.
+
+13. IANA Considerations
+
+ This section conforms to [RFC2434].
+
+ The well-known user TCP port number for iSCSI connections assigned by
+ IANA is 3260 and this is the default iSCSI port. Implementations
+ needing a system TCP port number may use port 860, the port assigned
+ by IANA as the iSCSI system port; however in order to use port 860,
+ it MUST be explicitly specified - implementations MUST NOT default to
+ use of port 860, as 3260 is the only allowed default.
+
+ Extension keys, authentication methods, or digest types for which a
+ vendor or group of vendors intend to provide publicly available
+ descriptions MUST be described by an RFC and MUST be registered with
+ IANA.
+
+ The IANA has set up the following three registries:
+
+ a) iSCSI extended key registry
+ b) iSCSI authentication methods registry
+ c) iSCSI digests registry
+
+ [RFC3723] also instructs IANA to maintain a registry for the values
+ of the SRP_GROUP key. The format of these values must conform to the
+ one specified for iSCSI extension item-label in Section 13.5.4
+ Standard iSCSI extension item-label format.
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 201]
+
+RFC 3720 iSCSI April 2004
+
+
+ For the iSCSI authentication methods registry and the iSCSI digests
+ registry, IANA MUST also assign a 16-bit unsigned integer number (the
+ method number for the authentication method and the digest number for
+ the digest).
+
+ The following initial values for the registry for authentication
+ methods are specified by the standards action of this document:
+
+ Authentication Method | Number |
+ +----------------------------------------+--------+
+ | CHAP | 1 |
+ +----------------------------------------+--------+
+ | SRP | 2 |
+ +----------------------------------------+--------+
+ | KRB5 | 3 |
+ +----------------------------------------+--------+
+ | SPKM1 | 4 |
+ +----------------------------------------+--------+
+ | SPKM2 | 5 |
+ +----------------------------------------+--------+
+
+ All other record numbers from 0 to 255 are reserved. IANA will
+ register numbers above 255.
+
+ Authentication methods with numbers above 255 MUST be unique within
+ the registry and MUST be used with the prefix Z#.
+
+
+ The following initial values for the registry for digests are
+ specified by the standards action of this document:
+
+ Digest | Number |
+ +----------------------------------------+--------+
+ | CRC32C | 1 |
+ +----------------------------------------+--------+
+
+ All other record numbers from 0 to 255 are reserved. IANA will
+ register numbers above 255.
+
+ Digests with numbers above 255 MUST be unique within the registry and
+ MUST be used with the prefix Y#.
+
+ The RFC that describes the item to be registered MUST indicate in the
+ IANA Considerations section the string and iSCSI registry to which it
+ should be recorded.
+
+ Extension Keys, Authentication Methods, and digests (iSCSI extension
+ items) must conform to a number of requirements as described below.
+
+
+
+Satran, et al. Standards Track [Page 202]
+
+RFC 3720 iSCSI April 2004
+
+
+13.1. Naming Requirements
+
+ Each iSCSI extension item must have a unique name in its category.
+ This name will be used as a standard-label for the key, access
+ method, or digest and must conform to the syntax specified in Section
+ 13.5.4 Standard iSCSI extension item-label format for iSCSI extension
+ item-labels.
+
+13.2. Mechanism Specification Requirements
+
+ For iSCSI extension items all of the protocols and procedures used by
+ a given iSCSI extension item must be described, either in the
+ specification of the iSCSI extension item itself or in some other
+ publicly available specification, in sufficient detail for the iSCSI
+ extension item to be implemented by any competent implementor. Use
+ of secret and/or proprietary methods in iSCSI extension items are
+ expressly prohibited. In addition, the restrictions imposed by
+ [RFC1602] on the standardization of patented algorithms must be
+ respected.
+
+13.3. Publication Requirements
+
+ All iSCSI extension items must be described by an RFC. The RFC may
+ be informational rather than Standards-Track, although Standards
+ Track review and approval are encouraged for all iSCSI extension
+ items.
+
+13.4. Security Requirements
+
+ Any known security issues that arise from the use of the iSCSI
+ extension item must be completely and fully described. It is not
+ required that the iSCSI extension item be secure or that it be free
+ from risks, but that the known risks be identified. Publication of a
+ new iSCSI extension item does not require an exhaustive security
+ review, and the security considerations section is subject to
+ continuing evaluation.
+
+ Additional security considerations should be addressed by publishing
+ revised versions of the iSCSI extension item specification.
+
+ For each of these registries, IANA must record the registered string,
+ which MUST conform to the format rules described in Section 13.5.4
+ Standard iSCSI extension item-label format for iSCSI extension
+ item-labels, and the RFC number that describes it. The key prefix
+ (X#, Y# or Z#) is not part of the recorded string.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 203]
+
+RFC 3720 iSCSI April 2004
+
+
+13.5. Registration Procedure
+
+ Registration of a new iSCSI extension item starts with the
+ construction of an Internet Draft to become an RFC.
+
+13.5.1. Present the iSCSI extension item to the Community
+
+ Send a proposed access type specification to the IPS WG mailing list,
+ or if the IPS WG is disbanded at the registration time, to a mailing
+ list designated by the IETF Transport Area Director for a review
+ period of a month. The intent of the public posting is to solicit
+ comments and feedback on the iSCSI extension item specification and a
+ review of any security considerations.
+
+13.5.2. iSCSI extension item review and IESG approval
+
+ When the one month period has passed, the IPS WG chair or a person
+ nominated by the IETF Transport Area Director (the iSCSI extension
+ item reviewer) forwards the Internet Draft to the IESG for
+ publication as an informational RFC or rejects it. If the
+ specification is a standards track document, the usual IETF
+ procedures for such documents are followed.
+
+ Decisions made by the iSCSI extension item reviewer must be published
+ within two weeks after the month-long review period. Decisions made
+ by the iSCSI extension item reviewer can be appealed through the IESG
+ appeal process.
+
+13.5.3. IANA Registration
+
+ Provided that the iSCSI extension item has either passed review or
+ has been successfully appealed to the IESG, and the specification is
+ published as an RFC, then IANA will register the iSCSI extension item
+ and make the registration available to the community.
+
+13.5.4. Standard iSCSI extension item-label format
+
+ The following character symbols are used iSCSI extension item-labels
+ (the hexadecimal values represent Unicode code points):
+
+ (a-z, A-Z) - letters
+ (0-9) - digits
+ "." (0x2e) - dot
+ "-" (0x2d) - minus
+ "+" (0x2b) - plus
+ "@" (0x40) - commercial at
+ "_" (0x5f) - underscore
+
+
+
+
+Satran, et al. Standards Track [Page 204]
+
+RFC 3720 iSCSI April 2004
+
+
+ An iSCSI extension item-label is a string of one or more characters
+ that consist of letters, digits, dot, minus, plus, commercial at, or
+ underscore. An iSCSI extension item-label MUST begin with a capital
+ letter and must not exceed 63 characters.
+
+13.6. IANA Procedures for Registering iSCSI extension items
+
+ The identity of the iSCSI extension item reviewer is communicated to
+ the IANA by the IESG. Then, the IANA only acts in response to iSCSI
+ extension item definitions that are approved by the iSCSI extension
+ item reviewer and forwarded by the reviewer to the IANA for
+ registration, or in response to a communication from the IESG that an
+ iSCSI extension item definition appeal has overturned the iSCSI
+ extension item reviewer's ruling.
+
+References
+
+Normative References
+
+ [CAM] ANSI X3.232-199X, Common Access Method-3.
+
+ [EUI] "Guidelines for 64-bit Global Identifier (EUI-64)",
+ http:
+ //standards.ieee.org/regauth/oui/tutorials/EUI64.html
+
+ [OUI] "IEEE OUI and Company_Id Assignments",
+ http://standards.ieee.org/regauth/oui
+
+ [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791,
+ September 1981.
+
+ [RFC793] Postel, J., "Transmission Control Protocol", STD 7,
+ RFC 793, September 1981.
+
+ [RFC1035] Mockapetris, P., "Domain Names - Implementation and
+ Specification", STD 13, RFC 1035, November 1987.
+
+ [RFC1122] Braden, R., Ed., "Requirements for Internet Hosts-
+ Communication Layer", STD 3, RFC 1122, October 1989.
+
+ [RFC1510] Kohl, J. and C. Neuman, "The Kerberos Network
+ Authentication Service (V5)", RFC 1510, September
+ 1993.
+
+ [RFC1737] Sollins, K. and L. Masinter "Functional Requirements
+ for Uniform Resource Names"RFC 1737, December 1994.
+
+
+
+
+
+Satran, et al. Standards Track [Page 205]
+
+RFC 3720 iSCSI April 2004
+
+
+ [RFC1964] Linn, J., "The Kerberos Version 5 GSS-API Mechanism",
+ RFC 1964, June 1996.
+
+ [RFC1982] Elz, R. and R. Bush, "Serial Number Arithmetic", RFC
+ 1982, August 1996.
+
+ [RFC1994] Simpson, W., "PPP Challenge Handshake Authentication
+ Protocol (CHAP)", RFC 1994, August 1996.
+
+ [RFC2025] Adams, C., "The Simple Public-Key GSS-API Mechanism
+ (SPKM)", RFC 2025, October 1996.
+
+ [RFC2045] Borenstein, N. and N. Freed, "MIME (Multipurpose
+ Internet Mail Extensions) Part One: Mechanisms for
+ Specifying and Describing the Format of Internet
+ Message Bodies", RFC 2045, November 1996.
+
+ [RFC2119] Bradner, S. "Key Words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2279] Yergeau, F., "UTF-8, a Transformation Format of ISO
+ 10646", RFC 2279 October 1996.
+
+ [RFC2373] Hinden, R. and S. Deering, "IP Version 6 Addressing
+ Architecture", RFC 2373, July 1998.
+
+ [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter "Uniform
+ Resource Identifiers", RFC 2396, August 1998.
+
+ [RFC2401] Kent, S. and R. Atkinson, "Security Architecture for
+ the Internet Protocol", RFC 2401, November 1998.
+
+ [RFC2404] Madson, C. and R. Glenn, "The Use of HMAC-SHA-1-96
+ within ESP and AH", RFC 2404, November 1998.
+
+ [RFC2406] Kent, S. and R. Atkinson, "IP Encapsulating Security
+ Payload (ESP)", RFC 2406, November 1998.
+
+ [RFC2407] Piper, D., "The Internet IP Security Domain of
+ Interpretation of ISAKMP", RFC 2407, November 1998.
+
+ [RFC2409] Harkins, D. and D. Carrel, "The Internet Key Exchange
+ (IKE)", RFC2409, November 1998.
+
+ [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing
+ an IANA Considerations Section in RFCs.", BCP 26, RFC
+ 2434, October 1998.
+
+
+
+
+Satran, et al. Standards Track [Page 206]
+
+RFC 3720 iSCSI April 2004
+
+
+ [RFC2451] Pereira, R. and R. Adams " The ESP CBC-Mode Cipher
+ Algorithms", RFC 2451, November 1998.
+
+ [RFC2732] Hinden, R., Carpenter, B. and L. Masinter, "Format for
+ Literal IPv6 Addresses in URL's", RFC 2451, December
+ 1999.
+
+ [RFC2945] Wu, T., "The SRP Authentication and Key Exchange
+ System", RFC 2945, September 2000.
+
+ [RFC3066] Alvestrand, H., "Tags for the Identification of
+ Languages", STD 47, RFC 3066, January 2001.
+
+ [RFC3454] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [RFC3566] Frankel, S. and H. Herbert, "The AES-XCBC-MAC-96
+ Algorithm and Its Use With IPsec", RFC 3566, September
+ 2003.
+
+ [RFC3686] Housley, R., "Using Advanced Encryption Standard (AES)
+ Counter Mode with IPsec Encapsulating Security Payload
+ (ESP)", RFC 3686, January 2004.
+
+ [RFC3722] Bakke, M., "String Profile for Internet Small Computer
+ Systems Interface (iSCSI) Names", RFC 3722, March
+ 2004.
+
+ [RFC3723] Aboba, B., Tseng, J., Walker, J., Rangan, V. and F.
+ Travostino, "Securing Block Storage Protocols over
+ IP", RFC 3723, March 2004.
+
+ [SAM2] T10/1157D, SCSI Architecture Model - 2 (SAM-2).
+
+ [SBC] NCITS.306-1998, SCSI-3 Block Commands (SBC).
+
+ [SPC3] T10/1416-D, SCSI Primary Commands-3.
+
+ [UNICODE] Unicode Standard Annex #15, "Unicode Normalization
+ Forms", http://www.unicode.org/unicode/reports/tr15
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 207]
+
+RFC 3720 iSCSI April 2004
+
+
+Informative References
+
+ [BOOT] P. Sarkar, et al., "Bootstrapping Clients using the
+ iSCSI Protocol", Work in Progress, July 2003.
+
+ [Castagnoli93] G. Castagnoli, S. Braeuer and M. Herrman "Optimization
+ of Cyclic Redundancy-Check Codes with 24 and 32 Parity
+ Bits", IEEE Transact. on Communications, Vol. 41, No.
+ 6, June 1993.
+
+ [CORD] Chadalapaka, M. and R. Elliott, "SCSI Command
+ Ordering Considerations with iSCSI", Work in Progress.
+
+ [RFC3347] Krueger, M., Haagens, R., Sapuntzakis, C. and M.
+ Bakke, "Small Computer Systems Interface protocol over
+ the Internet (iSCSI) Requirements and Design
+ Considerations", RFC 3347, July 2002.
+
+ [RFC3385] Sheinwald, D., Staran, J., Thaler, P. and V. Cavanna,
+ "Internet Protocol Small Computer System Interface
+ (iSCSI) Cyclic Redundancy Check (CRC)/Checksum
+ Considerations", RFC 3385, September 2002.
+
+ [RFC3721] Bakke M., Hafner, J., Hufferd, J., Voruganti, K. and
+ M. Krueger, "Internet Small Computer Systems Interface
+ (iSCSI) Naming and Discovery, RFC 3721, March 2004.
+
+ [SEQ-EXT] Kent, S., "IP Encapsulating Security Payload (ESP)",
+ Work in Progress, July 2002.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 208]
+
+RFC 3720 iSCSI April 2004
+
+
+Appendix A. Sync and Steering with Fixed Interval Markers
+
+ This appendix presents a simple scheme for synchronization (PDU
+ boundary retrieval). It uses markers that include synchronization
+ information placed at fixed intervals in the TCP stream.
+
+ A Marker consists of:
+
+ Byte / 0 | 1 | 2 | 3 |
+ / | | | |
+ |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+ +---------------+---------------+---------------+---------------+
+ 0| Next-iSCSI-PDU-start pointer - copy #1 |
+ +---------------+---------------+---------------+---------------+
+ 4| Next-iSCSI-PDU-start pointer - copy #2 |
+ +---------------+---------------+---------------+---------------+
+
+ The Marker scheme uses payload byte stream counting that includes
+ every byte placed by iSCSI in the TCP stream except for the markers
+ themselves. It also excludes any bytes that TCP counts but are not
+ originated by iSCSI.
+
+ Markers MUST NOT be included in digest calculation.
+
+ The Marker indicates the offset to the next iSCSI PDU header. The
+ Marker is eight bytes in length and contains two 32-bit offset fields
+ that indicate how many bytes to skip in the TCP stream in order to
+ find the next iSCSI PDU header. The marker uses two copies of the
+ pointer so that a marker that spans a TCP packet boundary should
+ leave at least one valid copy in one of the packets.
+
+ The structure and semantics of an inserted marker are independent of
+ the marker interval.
+
+ The use of markers is negotiable. The initiator and target MAY
+ indicate their readiness to receive and/or send markers during login
+ separately for each connection. The default is No.
+
+A.1. Markers At Fixed Intervals
+
+ A marker is inserted at fixed intervals in the TCP byte stream.
+ During login, each end of the iSCSI session specifies the interval at
+ which it is willing to receive the marker, or it disables the marker
+ altogether. If a receiver indicates that it desires a marker, the
+ sender MAY agree (during negotiation) and provide the marker at the
+ desired interval. However, in certain environments, a sender that
+ does not provide markers to a receiver that wants markers may suffer
+ an appreciable performance degradation.
+
+
+
+Satran, et al. Standards Track [Page 209]
+
+RFC 3720 iSCSI April 2004
+
+
+ The marker interval and the initial marker-less interval are counted
+ in terms of the bytes placed in the TCP stream data by iSCSI.
+
+ When reduced to iSCSI terms, markers MUST indicate the offset to a
+ 4-byte word boundary in the stream. The least significant two bits
+ of each marker word are reserved and are considered 0 for offset
+ computation.
+
+ Padding iSCSI PDU payloads to 4-byte word boundaries simplifies
+ marker manipulation.
+
+A.2. Initial Marker-less Interval
+
+ To enable the connection setup including the Login Phase negotiation,
+ marking (if any) is only started at the first marker interval after
+ the end of the Login Phase. However, in order to enable the marker
+ inclusion and exclusion mechanism to work without knowledge of the
+ length of the Login Phase, the first marker will be placed in the TCP
+ stream as if the Marker-less interval had included markers.
+
+ Thus, all markers appear in the stream at locations conforming to the
+ formula: [(MI + 8) * n - 8] where MI = Marker Interval, n = integer
+ number.
+
+ For example, if the marker interval is 512 bytes and the login ended
+ at byte 1003 (first iSCSI placed byte is 0), the first marker will be
+ inserted after byte 1031 in the stream.
+
+A.3. Negotiation
+
+ The following operational key=value pairs are used to negotiate the
+ fixed interval markers. The direction (output or input) is relative
+ to the initiator.
+
+A.3.1. OFMarker, IFMarker
+
+ Use: IO
+ Senders: Initiator and Target
+ Scope: CO
+
+ OFMarker=<boolean-value>
+ IFMarker=<boolean-value>
+
+ Default is No.
+
+ Result function is AND.
+
+
+
+
+
+Satran, et al. Standards Track [Page 210]
+
+RFC 3720 iSCSI April 2004
+
+
+ OFMarker is used to turn on or off the initiator to target markers
+ on the connection. IFMarker is used to turn on or off the target to
+ initiator markers on the connection.
+
+ Examples:
+
+ I->OFMarker=Yes,IFMarker=Yes
+ T->OFMarker=Yes,IFMarker=Yes
+
+ Results in the Marker being used in both directions while:
+
+ I->OFMarker=Yes,IFMarker=Yes
+ T->OFMarker=Yes,IFMarker=No
+
+ Results in Marker being used from the initiator to the target, but
+ not from the target to initiator.
+
+A.3.2. OFMarkInt, IFMarkInt
+
+ Use: IO
+ Senders: Initiator and Target
+ Scope: CO
+ OFMarkInt is Irrelevant when: OFMarker=No
+ IFMarkInt is Irrelevant when: IFMarker=No
+
+ Offering:
+
+ OFMarkInt=<numeric-range-from-1-to-65535>
+ IFMarkInt=<numeric-range-from-1-to-65535>
+
+ Responding:
+
+ OFMarkInt=<numeric-value-from-1-to-65535>|Reject
+ IFMarkInt=<numeric-value-from-1-to-65535>|Reject
+
+ OFMarkInt is used to set the interval for the initiator to target
+ markers on the connection. IFMarkInt is used to set the interval for
+ the target to initiator markers on the connection.
+
+ For the offering, the initiator or target indicates the minimum to
+ maximum interval (in 4-byte words) it wants the markers for one or
+ both directions. In case it only wants a specific value, only a
+ single value has to be specified. The responder selects a value
+ within the minimum and maximum offered or the only value offered or
+ indicates through the xFMarker key=value its inability to set and/or
+ receive markers. When the interval is unacceptable the responder
+ answers with "Reject". Reject is resetting the marker function in
+ the specified direction (Output or Input) to No.
+
+
+
+Satran, et al. Standards Track [Page 211]
+
+RFC 3720 iSCSI April 2004
+
+
+ The interval is measured from the end of a marker to the beginning of
+ the next marker. For example, a value of 1024 means 1024 words (4096
+ bytes of iSCSI payload between markers).
+
+ The default is 2048.
+
+Appendix B. Examples
+
+B.1. Read Operation Example
+
+ +------------------+-----------------------+----------------------+
+ |Initiator Function| PDU Type | Target Function |
+ +------------------+-----------------------+----------------------+
+ | Command request |SCSI Command (READ)>>> | |
+ | (read) | | |
+ +------------------+-----------------------+----------------------+
+ | | |Prepare Data Transfer |
+ +------------------+-----------------------+----------------------+
+ | Receive Data | <<< SCSI Data-In | Send Data |
+ +------------------+-----------------------+----------------------+
+ | Receive Data | <<< SCSI Data-In | Send Data |
+ +------------------+-----------------------+----------------------+
+ | Receive Data | <<< SCSI Data-In | Send Data |
+ +------------------+-----------------------+----------------------+
+ | | <<< SCSI Response |Send Status and Sense |
+ +------------------+-----------------------+----------------------+
+ | Command Complete | | |
+ +------------------+-----------------------+----------------------+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 212]
+
+RFC 3720 iSCSI April 2004
+
+
+B.2. Write Operation Example
+
+ +------------------+-----------------------+---------------------+
+ |Initiator Function| PDU Type | Target Function |
+ +------------------+-----------------------+---------------------+
+ | Command request |SCSI Command (WRITE)>>>| Receive command |
+ | (write) | | and queue it |
+ +------------------+-----------------------+---------------------+
+ | | | Process old commands|
+ +------------------+-----------------------+---------------------+
+ | | | Ready to process |
+ | | <<< R2T | WRITE command |
+ +------------------+-----------------------+---------------------+
+ | Send Data | SCSI Data-Out >>> | Receive Data |
+ +------------------+-----------------------+---------------------+
+ | | <<< R2T | Ready for data |
+ +------------------+-----------------------+---------------------+
+ | | <<< R2T | Ready for data |
+ +------------------+-----------------------+---------------------+
+ | Send Data | SCSI Data-Out >>> | Receive Data |
+ +------------------+-----------------------+---------------------+
+ | Send Data | SCSI Data-Out >>> | Receive Data |
+ +------------------+-----------------------+---------------------+
+ | | <<< SCSI Response |Send Status and Sense|
+ +------------------+-----------------------+---------------------+
+ | Command Complete | | |
+ +------------------+-----------------------+---------------------+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 213]
+
+RFC 3720 iSCSI April 2004
+
+
+B.3. R2TSN/DataSN Use Examples
+
+ Output (write) data DataSN/R2TSN Example
+
+ +------------------+-----------------------+----------------------+
+ |Initiator Function| PDU Type & Content | Target Function |
+ +------------------+-----------------------+----------------------+
+ | Command request |SCSI Command (WRITE)>>>| Receive command |
+ | (write) | | and queue it |
+ +------------------+-----------------------+----------------------+
+ | | | Process old commands |
+ +------------------+-----------------------+----------------------+
+ | | <<< R2T | Ready for data |
+ | | R2TSN = 0 | |
+ +------------------+-----------------------+----------------------+
+ | | <<< R2T | Ready for more data |
+ | | R2TSN = 1 | |
+ +------------------+-----------------------+----------------------+
+ | Send Data | SCSI Data-Out >>> | Receive Data |
+ | for R2TSN 0 | DataSN = 0, F=0 | |
+ +------------------+-----------------------+----------------------+
+ | Send Data | SCSI Data-Out >>> | Receive Data |
+ | for R2TSN 0 | DataSN = 1, F=1 | |
+ +------------------+-----------------------+----------------------+
+ | Send Data | SCSI Data >>> | Receive Data |
+ | for R2TSN 1 | DataSN = 0, F=1 | |
+ +------------------+-----------------------+----------------------+
+ | | <<< SCSI Response |Send Status and Sense |
+ | | ExpDataSN = 0 | |
+ +------------------+-----------------------+----------------------+
+ | Command Complete | | |
+ +------------------+-----------------------+----------------------+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 214]
+
+RFC 3720 iSCSI April 2004
+
+
+ Input (read) data DataSN Example
+
+ +------------------+-----------------------+----------------------+
+ |Initiator Function| PDU Type | Target Function |
+ +------------------+-----------------------+----------------------+
+ | Command request |SCSI Command (READ)>>> | |
+ | (read) | | |
+ +------------------+-----------------------+----------------------+
+ | | | Prepare Data Transfer|
+ +------------------+-----------------------+----------------------+
+ | Receive Data | <<< SCSI Data-In | Send Data |
+ | | DataSN = 0, F=0 | |
+ +------------------+-----------------------+----------------------+
+ | Receive Data | <<< SCSI Data-In | Send Data |
+ | | DataSN = 1, F=0 | |
+ +------------------+-----------------------+----------------------+
+ | Receive Data | <<< SCSI Data-In | Send Data |
+ | | DataSN = 2, F=1 | |
+ +------------------+-----------------------+----------------------+
+ | | <<< SCSI Response |Send Status and Sense |
+ | | ExpDataSN = 3 | |
+ +------------------+-----------------------+----------------------+
+ | Command Complete | | |
+ +------------------+-----------------------+----------------------+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 215]
+
+RFC 3720 iSCSI April 2004
+
+
+ Bidirectional DataSN Example
+
+ +------------------+-----------------------+----------------------+
+ |Initiator Function| PDU Type | Target Function |
+ +------------------+-----------------------+----------------------+
+ | Command request |SCSI Command >>> | |
+ | (Read-Write) | Read-Write | |
+ +------------------+-----------------------+----------------------+
+ | | | Process old commands |
+ +------------------+-----------------------+----------------------+
+ | | <<< R2T | Ready to process |
+ | | R2TSN = 0 | WRITE command |
+ +------------------+-----------------------+----------------------+
+ | * Receive Data | <<< SCSI Data-In | Send Data |
+ | | DataSN = 1, F=0 | |
+ +------------------+-----------------------+----------------------+
+ | * Receive Data | <<< SCSI Data-In | Send Data |
+ | | DataSN = 2, F=1 | |
+ +------------------+-----------------------+----------------------+
+ | * Send Data | SCSI Data-Out >>> | Receive Data |
+ | for R2TSN 0 | DataSN = 0, F=1 | |
+ +------------------+-----------------------+----------------------+
+ | | <<< SCSI Response |Send Status and Sense |
+ | | ExpDataSN = 3 | |
+ +------------------+-----------------------+----------------------+
+ | Command Complete | | |
+ +------------------+-----------------------+----------------------+
+
+ *) Send data and Receive Data may be transferred simultaneously as in
+ an atomic Read-Old-Write-New or sequentially as in an atomic
+ Read-Update-Write (in the latter case the R2T may follow the received
+ data).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 216]
+
+RFC 3720 iSCSI April 2004
+
+
+ Unsolicited and immediate output (write) data with DataSN Example
+
+ +------------------+-----------------------+----------------------+
+ |Initiator Function| PDU Type & Content | Target Function |
+ +------------------+-----------------------+----------------------+
+ | Command request |SCSI Command (WRITE)>>>| Receive command |
+ | (write) |F=0 | and data |
+ |+ Immediate data | | and queue it |
+ +------------------+-----------------------+----------------------+
+ | Send Unsolicited | SCSI Write Data >>> | Receive more Data |
+ | Data | DataSN = 0, F=1 | |
+ +------------------+-----------------------+----------------------+
+ | | | Process old commands |
+ +------------------+-----------------------+----------------------+
+ | | <<< R2T | Ready for more data |
+ | | R2TSN = 0 | |
+ +------------------+-----------------------+----------------------+
+ | Send Data | SCSI Write Data >>> | Receive Data |
+ | for R2TSN 0 | DataSN = 0, F=1 | |
+ +------------------+-----------------------+----------------------+
+ | | <<< SCSI Response |Send Status and Sense |
+ | | | |
+ +------------------+-----------------------+----------------------+
+ | Command Complete | | |
+ +------------------+-----------------------+----------------------+
+
+B.4. CRC Examples
+
+ N.B. all Values are Hexadecimal
+
+ 32 bytes of zeroes:
+
+ Byte: 0 1 2 3
+
+ 0: 00 00 00 00
+ ...
+ 28: 00 00 00 00
+
+ CRC: aa 36 91 8a
+
+ 32 bytes of ones:
+
+ Byte: 0 1 2 3
+
+ 0: ff ff ff ff
+ ...
+ 28: ff ff ff ff
+
+
+
+
+Satran, et al. Standards Track [Page 217]
+
+RFC 3720 iSCSI April 2004
+
+
+ CRC: 43 ab a8 62
+
+ 32 bytes of incrementing 00..1f:
+
+ Byte: 0 1 2 3
+
+ 0: 00 01 02 03
+ ...
+ 28: 1c 1d 1e 1f
+
+ CRC: 4e 79 dd 46
+
+ 32 bytes of decrementing 1f..00:
+
+ Byte: 0 1 2 3
+
+ 0: 1f 1e 1d 1c
+ ...
+ 28: 03 02 01 00
+
+ CRC: 5c db 3f 11
+
+ An iSCSI - SCSI Read (10) Command PDU
+
+ Byte: 0 1 2 3
+
+ 0: 01 c0 00 00
+ 4: 00 00 00 00
+ 8: 00 00 00 00
+ 12: 00 00 00 00
+ 16: 14 00 00 00
+ 20: 00 00 04 00
+ 24: 00 00 00 14
+ 28: 00 00 00 18
+ 32: 28 00 00 00
+ 36: 00 00 00 00
+ 40: 02 00 00 00
+ 44: 00 00 00 00
+
+ CRC: 56 3a 96 d9
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 218]
+
+RFC 3720 iSCSI April 2004
+
+
+Appendix C. Login Phase Examples
+
+ In the first example, the initiator and target authenticate each
+ other via Kerberos:
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ InitiatorName=iqn.1999-07.com.os:hostid.77
+ TargetName=iqn.1999-07.com.example:diskarray.sn.88
+ AuthMethod=KRB5,SRP,None
+
+ T-> Login (CSG,NSG=0,0 T=0)
+ AuthMethod=KRB5
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ KRB_AP_REQ=<krb_ap_req>
+
+ (krb_ap_req contains the Kerberos V5 ticket and authenticator
+ with MUTUAL-REQUIRED set in the ap-options field)
+
+ If the authentication is successful, the target proceeds with:
+
+ T-> Login (CSG,NSG=0,1 T=1)
+ KRB_AP_REP=<krb_ap_rep>
+
+ (krb_ap_rep is the Kerberos V5 mutual authentication reply)
+
+ If the authentication is successful, the initiator may proceed
+ with:
+
+ I-> Login (CSG,NSG=1,0 T=0) FirstBurstLength=8192
+ T-> Login (CSG,NSG=1,0 T=0) FirstBurstLength=4096
+ MaxBurstLength=8192
+ I-> Login (CSG,NSG=1,0 T=0) MaxBurstLength=8192
+ ... more iSCSI Operational Parameters
+
+ T-> Login (CSG,NSG=1,0 T=0)
+ ... more iSCSI Operational Parameters
+
+ And at the end:
+
+ I-> Login (CSG,NSG=1,3 T=1)
+ optional iSCSI parameters
+
+ T-> Login (CSG,NSG=1,3 T=1) "login accept"
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 219]
+
+RFC 3720 iSCSI April 2004
+
+
+ If the initiator's authentication by the target is not
+ successful, the target responds with:
+
+ T-> Login "login reject"
+
+ instead of the Login KRB_AP_REP message, and terminates the
+ connection.
+
+ If the target's authentication by the initiator is not
+ successful, the initiator terminates the connection (without
+ responding to the Login KRB_AP_REP message).
+
+ In the next example only the initiator is authenticated by the
+ target via Kerberos:
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ InitiatorName=iqn.1999-07.com.os:hostid.77
+ TargetName=iqn.1999-07.com.example:diskarray.sn.88
+ AuthMethod=SRP,KRB5,None
+
+ T-> Login-PR (CSG,NSG=0,0 T=0)
+ AuthMethod=KRB5
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ KRB_AP_REQ=krb_ap_req
+
+ (MUTUAL-REQUIRED not set in the ap-options field of krb_ap_req)
+
+ If the authentication is successful, the target proceeds with:
+
+ T-> Login (CSG,NSG=0,1 T=1)
+
+ I-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ T-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ . . .
+
+ T-> Login (CSG,NSG=1,3 T=1)"login accept"
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 220]
+
+RFC 3720 iSCSI April 2004
+
+
+ In the next example, the initiator and target authenticate each
+ other via SPKM1:
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ InitiatorName=iqn.1999-07.com.os:hostid.77
+ TargetName=iqn.1999-07.com.example:diskarray.sn.88
+ AuthMethod=SPKM1,KRB5,None
+
+ T-> Login (CSG,NSG=0,0 T=0)
+ AuthMethod=SPKM1
+
+ I-> Login (CSG,NSG=0,0 T=0)
+ SPKM_REQ=<spkm-req>
+
+ (spkm-req is the SPKM-REQ token with the mutual-state bit in the
+ options field of the REQ-TOKEN set)
+
+ T-> Login (CSG,NSG=0,0 T=0)
+ SPKM_REP_TI=<spkm-rep-ti>
+
+ If the authentication is successful, the initiator proceeds:
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ SPKM_REP_IT=<spkm-rep-it>
+
+ If the authentication is successful, the target proceeds with:
+
+ T-> Login (CSG,NSG=0,1 T=1)
+
+ The initiator may proceed:
+
+ I-> Login (CSG,NSG=1,0 T=0) ... iSCSI parameters
+ T-> Login (CSG,NSG=1,0 T=0) ... iSCSI parameters
+
+ And at the end:
+
+ I-> Login (CSG,NSG=1,3 T=1)
+ optional iSCSI parameters
+
+ T-> Login (CSG,NSG=1,3 T=1) "login accept"
+
+
+ If the target's authentication by the initiator is not
+ successful, the initiator terminates the connection (without
+ responding to the Login SPKM_REP_TI message).
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 221]
+
+RFC 3720 iSCSI April 2004
+
+
+ If the initiator's authentication by the target is not
+ successful, the target responds with:
+
+ T-> Login "login reject"
+
+ instead of the Login "proceed and change stage" message, and
+ terminates the connection.
+
+
+ In the next example, the initiator and target authenticate each
+ other via SPKM2:
+
+ I-> Login (CSG,NSG=0,0 T=0)
+ InitiatorName=iqn.1999-07.com.os:hostid.77
+ TargetName=iqn.1999-07.com.example:diskarray.sn.88
+ AuthMethod=SPKM1,SPKM2
+
+ T-> Login-PR (CSG,NSG=0,0 T=0)
+ AuthMethod=SPKM2
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ SPKM_REQ=<spkm-req>
+
+ (spkm-req is the SPKM-REQ token with the mutual-state bit in the
+ options field of the REQ-TOKEN not set)
+
+ If the authentication is successful, the target proceeds with:
+
+ T-> Login (CSG,NSG=0,1 T=1)
+
+ The initiator may proceed:
+
+ I-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ T-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ And at the end:
+
+ I-> Login (CSG,NSG=1,3 T=1)
+ optional iSCSI parameters
+
+ T-> Login (CSG,NSG=1,3 T=1) "login accept"
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 222]
+
+RFC 3720 iSCSI April 2004
+
+
+ In the next example, the initiator and target authenticate each
+ other via SRP:
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ InitiatorName=iqn.1999-07.com.os:hostid.77
+ TargetName=iqn.1999-07.com.example:diskarray.sn.88
+ AuthMethod=KRB5,SRP,None
+
+ T-> Login-PR (CSG,NSG=0,0 T=0)
+ AuthMethod=SRP
+
+ I-> Login (CSG,NSG=0,0 T=0)
+ SRP_U=<user>
+ TargetAuth=Yes
+
+ T-> Login (CSG,NSG=0,0 T=0)
+ SRP_GROUP=SRP-1536,SRP-1024
+ SRP_s=<s>
+
+ I-> Login (CSG,NSG=0,0 T=0)
+ SRP_GROUP=SRP-1536
+ SRP_A=<A>
+
+ T-> Login (CSG,NSG=0,0 T=0)
+ SRP_B=<B>
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ SRP_M=<M>
+
+ If the initiator authentication is successful, the target
+ proceeds:
+
+ T-> Login (CSG,NSG=0,1 T=1)
+ SRP_HM=<H(A | M | K)>
+
+ Where N, g, s, A, B, M, and H(A | M | K) are defined in [RFC2945].
+
+ If the target authentication is not successful, the initiator
+ terminates the connection; otherwise, it proceeds.
+
+ I-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ T-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 223]
+
+RFC 3720 iSCSI April 2004
+
+
+ And at the end:
+
+ I-> Login (CSG,NSG=1,3 T=1)
+ optional iSCSI parameters
+
+ T-> Login (CSG,NSG=1,3 T=1) "login accept"
+
+ If the initiator authentication is not successful, the target
+ responds with:
+
+ T-> Login "login reject"
+
+ Instead of the T-> Login SRP_HM=<H(A | M | K)> message and
+ terminates the connection.
+
+ In the next example, the initiator and target authenticate each
+ other via SRP:
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ InitiatorName=iqn.1999-07.com.os:hostid.77
+ TargetName=iqn.1999-07.com.example:diskarray.sn.88
+ AuthMethod=KRB5,SRP,None
+
+ T-> Login-PR (CSG,NSG=0,0 T=0)
+ AuthMethod=SRP
+
+ I-> Login (CSG,NSG=0,0 T=0)
+ SRP_U=<user>
+ TargetAuth=No
+
+ T-> Login (CSG,NSG=0,0 T=0)
+ SRP_GROUP=SRP-1536
+ SRP_s=<s>
+
+ I-> Login (CSG,NSG=0,0 T=0)
+ SRP_GROUP=SRP-1536
+ SRP_A=<A>
+
+ T-> Login (CSG,NSG=0,0 T=0)
+ SRP_B=<B>
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ SRP_M=<M>
+
+ If the initiator authentication is successful, the target
+ proceeds:
+
+ T-> Login (CSG,NSG=0,1 T=1)
+
+
+
+Satran, et al. Standards Track [Page 224]
+
+RFC 3720 iSCSI April 2004
+
+
+
+ I-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ T-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ And at the end:
+
+ I-> Login (CSG,NSG=1,3 T=1)
+ optional iSCSI parameters
+
+ T-> Login (CSG,NSG=1,3 T=1) "login accept"
+
+ In the next example the initiator and target authenticate each other
+ via CHAP:
+
+ I-> Login (CSG,NSG=0,0 T=0)
+ InitiatorName=iqn.1999-07.com.os:hostid.77
+ TargetName=iqn.1999-07.com.example:diskarray.sn.88
+ AuthMethod=KRB5,CHAP,None
+
+ T-> Login-PR (CSG,NSG=0,0 T=0)
+ AuthMethod=CHAP
+
+ I-> Login (CSG,NSG=0,0 T=0)
+ CHAP_A=<A1,A2>
+
+ T-> Login (CSG,NSG=0,0 T=0)
+ CHAP_A=<A1>
+ CHAP_I=<I>
+ CHAP_C=<C>
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ CHAP_N=<N>
+ CHAP_R=<R>
+ CHAP_I=<I>
+ CHAP_C=<C>
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 225]
+
+RFC 3720 iSCSI April 2004
+
+
+ If the initiator authentication is successful, the target
+ proceeds:
+
+ T-> Login (CSG,NSG=0,1 T=1)
+ CHAP_N=<N>
+ CHAP_R=<R>
+
+ If the target authentication is not successful, the initiator
+ aborts the connection; otherwise, it proceeds.
+
+ I-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+ T-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ And at the end:
+
+ I-> Login (CSG,NSG=1,3 T=1)
+ optional iSCSI parameters
+
+ T-> Login (CSG,NSG=1,3 T=1) "login accept"
+
+ If the initiator authentication is not successful, the target
+ responds with:
+
+ T-> Login "login reject"
+
+ Instead of the Login CHAP_R=<response> "proceed and change
+ stage" message and terminates the connection.
+
+ In the next example, only the initiator is authenticated by the
+ target via CHAP:
+
+ I-> Login (CSG,NSG=0,1 T=0)
+ InitiatorName=iqn.1999-07.com.os:hostid.77
+ TargetName=iqn.1999-07.com.example:diskarray.sn.88
+ AuthMethod=KRB5,CHAP,None
+
+ T-> Login-PR (CSG,NSG=0,0 T=0)
+ AuthMethod=CHAP
+
+ I-> Login (CSG,NSG=0,0 T=0)
+ CHAP_A=<A1,A2>
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 226]
+
+RFC 3720 iSCSI April 2004
+
+
+ T-> Login (CSG,NSG=0,0 T=0)
+ CHAP_A=<A1>
+ CHAP_I=<I>
+ CHAP_C=<C>
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ CHAP_N=<N>
+ CHAP_R=<R>
+
+ If the initiator authentication is successful, the target
+ proceeds:
+
+ T-> Login (CSG,NSG=0,1 T=1)
+
+ I-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ T-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ And at the end:
+
+ I-> Login (CSG,NSG=1,3 T=1)
+ optional iSCSI parameters
+
+ T-> Login (CSG,NSG=1,3 T=1) "login accept"
+
+ In the next example, the initiator does not offer any security
+ parameters. It therefore may offer iSCSI parameters on the Login PDU
+ with the T bit set to 1, and the target may respond with a final
+ Login Response PDU immediately:
+
+ I-> Login (CSG,NSG=1,3 T=1)
+ InitiatorName=iqn.1999-07.com.os:hostid.77
+ TargetName=iqn.1999-07.com.example:diskarray.sn.88
+ ... iSCSI parameters
+
+ T-> Login (CSG,NSG=1,3 T=1) "login accept"
+ ... ISCSI parameters
+
+ In the next example, the initiator does offer security
+ parameters on the Login PDU, but the target does not choose
+ any (i.e., chooses the "None" values):
+
+ I-> Login (CSG,NSG=0,1 T=1)
+ InitiatorName=iqn.1999-07.com.os:hostid.77
+ TargetName=iqn.1999-07.com.example:diskarray.sn.88
+ AuthMethod=KRB5,SRP,None
+
+
+
+Satran, et al. Standards Track [Page 227]
+
+RFC 3720 iSCSI April 2004
+
+
+
+ T-> Login-PR (CSG,NSG=0,1 T=1)
+ AuthMethod=None
+
+ I-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ T-> Login (CSG,NSG=1,0 T=0)
+ ... iSCSI parameters
+
+ And at the end:
+
+ I-> Login (CSG,NSG=1,3 T=1)
+ optional iSCSI parameters
+
+ T-> Login (CSG,NSG=1,3 T=1) "login accept"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 228]
+
+RFC 3720 iSCSI April 2004
+
+
+Appendix D. SendTargets Operation
+
+ To reduce the amount of configuration required on an initiator, iSCSI
+ provides the SendTargets text request. The initiator uses the
+ SendTargets request to get a list of targets to which it may have
+ access, as well as the list of addresses (IP address and TCP port) on
+ which these targets may be accessed.
+
+ To make use of SendTargets, an initiator must first establish one of
+ two types of sessions. If the initiator establishes the session
+ using the key "SessionType=Discovery", the session is a discovery
+ session, and a target name does not need to be specified. Otherwise,
+ the session is a normal, operational session. The SendTargets
+ command MUST only be sent during the Full Feature Phase of a normal
+ or discovery session.
+
+ A system that contains targets MUST support discovery sessions on
+ each of its iSCSI IP address-port pairs, and MUST support the
+ SendTargets command on the discovery session. In a discovery
+ session, a target MUST return all path information (target name and
+ IP address-port pairs and portal group tags) for the targets on the
+ target network entity which the requesting initiator is authorized to
+ access.
+
+ A target MUST support the SendTargets command on operational
+ sessions; these will only return path information about the target to
+ which the session is connected, and do not need to return information
+ about other target names that may be defined in the responding
+ system.
+
+ An initiator MAY make use of the SendTargets as it sees fit.
+
+ A SendTargets command consists of a single Text request PDU. This
+ PDU contains exactly one text key and value. The text key MUST be
+ SendTargets. The expected response depends upon the value, as well
+ as whether the session is a discovery or operational session.
+
+ The value must be one of:
+
+ All
+
+ The initiator is requesting that information on all relevant
+ targets known to the implementation be returned. This value
+ MUST be supported on a discovery session, and MUST NOT be
+ supported on an operational session.
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 229]
+
+RFC 3720 iSCSI April 2004
+
+
+ <iSCSI-target-name>
+
+ If an iSCSI target name is specified, the session should respond
+ with addresses for only the named target, if possible. This
+ value MUST be supported on discovery sessions. A discovery
+ session MUST be capable of returning addresses for those
+ targets that would have been returned had value=All had been
+ designated.
+
+ <nothing>
+
+ The session should only respond with addresses for the target to
+ which the session is logged in. This MUST be supported on
+ operational sessions, and MUST NOT return targets other than
+ the one to which the session is logged in.
+
+ The response to this command is a text response that contains a list
+ of zero or more targets and, optionally, their addresses. Each
+ target is returned as a target record. A target record begins with
+ the TargetName text key, followed by a list of TargetAddress text
+ keys, and bounded by the end of the text response or the next
+ TargetName key, which begins a new record. No text keys other than
+ TargetName and TargetAddress are permitted within a SendTargets
+ response.
+
+ For the format of the TargetName, see Section 12.4 TargetName.
+
+ In a discovery session, a target MAY respond to a SendTargets request
+ with its complete list of targets, or with a list of targets that is
+ based on the name of the initiator logged in to the session.
+
+ A SendTargets response MUST NOT contain target names if there are no
+ targets for the requesting initiator to access.
+
+ Each target record returned includes zero or more TargetAddress
+ fields.
+
+ Each target record starts with one text key of the form:
+
+ TargetName=<target-name-goes-here>
+
+ Followed by zero or more address keys of the form:
+
+ TargetAddress=<hostname-or-ipaddress>[:<tcp-port>],
+ <portal-group-tag>
+
+ The hostname-or-ipaddress contains a domain name, IPv4 address, or
+ IPv6 address, as specified for the TargetAddress key.
+
+
+
+Satran, et al. Standards Track [Page 230]
+
+RFC 3720 iSCSI April 2004
+
+
+ A hostname-or-ipaddress duplicated in TargetAddress responses for a
+ given node (the port is absent or equal) would probably indicate that
+ multiple address families are in use at once (IPV6 and IPV4).
+
+ Each TargetAddress belongs to a portal group, identified by its
+ numeric portal group tag (as in Section 12.9 TargetPortalGroupTag).
+ The iSCSI target name, together with this tag, constitutes the SCSI
+ port identifier; the tag only needs to be unique within a given
+ target's name list of addresses.
+
+ Multiple-connection sessions can span iSCSI addresses that belong to
+ the same portal group.
+
+ Multiple-connection sessions cannot span iSCSI addresses that belong
+ to different portal groups.
+
+ If a SendTargets response reports an iSCSI address for a target, it
+ SHOULD also report all other addresses in its portal group in the
+ same response.
+
+ A SendTargets text response can be longer than a single Text Response
+ PDU, and makes use of the long text responses as specified.
+
+ After obtaining a list of targets from the discovery target session,
+ an iSCSI initiator may initiate new sessions to log in to the
+ discovered targets for full operation. The initiator MAY keep the
+ discovery session open, and MAY send subsequent SendTargets commands
+ to discover new targets.
+
+ Examples:
+
+ This example is the SendTargets response from a single target that
+ has no other interface ports.
+
+ Initiator sends text request that contains:
+
+ SendTargets=All
+
+ Target sends a text response that contains:
+
+ TargetName=iqn.1993-11.com.example:diskarray.sn.8675309
+
+ All the target had to return in the simple case was the target name.
+ It is assumed by the initiator that the IP address and TCP port for
+ this target are the same as used on the current connection to the
+ default iSCSI target.
+
+
+
+
+
+Satran, et al. Standards Track [Page 231]
+
+RFC 3720 iSCSI April 2004
+
+
+ The next example has two internal iSCSI targets, each accessible via
+ two different ports with different IP addresses. The following is
+ the text response:
+
+ TargetName=iqn.1993-11.com.example:diskarray.sn.8675309
+ TargetAddress=10.1.0.45:3000,1 TargetAddress=10.1.1.45:3000,2
+ TargetName=iqn.1993-11.com.example:diskarray.sn.1234567
+ TargetAddress=10.1.0.45:3000,1 TargetAddress=10.1.1.45:3000,2
+
+ Both targets share both addresses; the multiple addresses are likely
+ used to provide multi-path support. The initiator may connect to
+ either target name on either address. Each of the addresses has its
+ own portal group tag; they do not support spanning
+ multiple-connection sessions with each other. Keep in mind that the
+ portal group tags for the two named targets are independent of one
+ another; portal group "1" on the first target is not necessarily the
+ same as portal group "1" on the second target.
+
+ In the above example, a DNS host name or an IPv6 address could have
+ been returned instead of an IPv4 address.
+
+ The next text response shows a target that supports spanning sessions
+ across multiple addresses, and further illustrates the use of the
+ portal group tags:
+
+ TargetName=iqn.1993-11.com.example:diskarray.sn.8675309
+
+ TargetAddress=10.1.0.45:3000,1 TargetAddress=10.1.1.46:3000,1
+ TargetAddress=10.1.0.47:3000,2 TargetAddress=10.1.1.48:3000,2
+ TargetAddress=10.1.1.49:3000,3
+
+ In this example, any of the target addresses can be used to reach the
+ same target. A single-connection session can be established to any
+ of these TCP addresses. A multiple-connection session could span
+ addresses .45 and .46 or .47 and .48, but cannot span any other
+ combination. A TargetAddress with its own tag (.49) cannot be
+ combined with any other address within the same session.
+
+ This SendTargets response does not indicate whether .49 supports
+ multiple connections per session; it is communicated via the
+ MaxConnections text key upon login to the target.
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 232]
+
+RFC 3720 iSCSI April 2004
+
+
+Appendix E. Algorithmic Presentation of Error Recovery Classes
+
+ This appendix illustrates the error recovery classes using a
+ pseudo-programming-language. The procedure names are chosen to be
+ obvious to most implementers. Each of the recovery classes described
+ has initiator procedures as well as target procedures. These
+ algorithms focus on outlining the mechanics of error recovery
+ classes, and do not exhaustively describe all other aspects/cases.
+ Examples of this approach are:
+
+
+ - Handling for only certain Opcode types is shown.
+
+ - Only certain reason codes (e.g., Recovery in Logout command)
+ are outlined.
+
+ - Resultant cases, such as recovery of Synchronization on a
+ header digest error are considered out-of-scope in these
+ algorithms. In this particular example, a header digest error
+ may lead to connection recovery if some type of sync and
+ steering layer is not implemented.
+
+ These algorithms strive to convey the iSCSI error recovery concepts
+ in the simplest terms, and are not designed to be optimal.
+
+E.1. General Data Structure and Procedure Description
+
+ This section defines the procedures and data structures that are
+ commonly used by all the error recovery algorithms. The structures
+ may not be the exhaustive representations of what is required for a
+ typical implementation.
+
+ Data structure definitions -
+ struct TransferContext {
+ int TargetTransferTag;
+ int ExpectedDataSN;
+ };
+
+ struct TCB { /* task control block */
+ Boolean SoFarInOrder;
+ int ExpectedDataSN; /* used for both R2Ts, and Data */
+ int MissingDataSNList[MaxMissingDPDU];
+ Boolean FbitReceived;
+ Boolean StatusXferd;
+ Boolean CurrentlyAllegiant;
+ int ActiveR2Ts;
+ int Response;
+ char *Reason;
+
+
+
+Satran, et al. Standards Track [Page 233]
+
+RFC 3720 iSCSI April 2004
+
+
+ struct TransferContext
+ TransferContextList[MaxOutStandingR2T];
+ int InitiatorTaskTag;
+ int CmdSN;
+
+ int SNACK_Tag;
+
+ };
+
+ struct Connection {
+ struct Session SessionReference;
+ Boolean SoFarInOrder;
+ int CID;
+ int State;
+
+ int CurrentTimeout;
+ int ExpectedStatSN;
+ int MissingStatSNList[MaxMissingSPDU];
+ Boolean PerformConnectionCleanup;
+ };
+
+ struct Session {
+ int NumConnections;
+ int CmdSN;
+ int Maxconnections;
+ int ErrorRecoveryLevel;
+ struct iSCSIEndpoint OtherEndInfo;
+ struct Connection ConnectionList[MaxSupportedConns];
+ };
+
+ Procedure descriptions -
+ Receive-a-In-PDU(transport connection, inbound PDU);
+ check-basic-validity(inbound PDU);
+ Start-Timer(timeout handler, argument, timeout value);
+ Build-And-Send-Reject(transport connection, bad PDU, reason code);
+
+E.2. Within-command Error Recovery Algorithms
+
+E.2.1. Procedure Descriptions
+
+ Recover-Data-if-Possible(last required DataSN, task control
+ block);
+ Build-And-Send-DSnack(task control block);
+ Build-And-Send-RDSnack(task control block);
+ Build-And-Send-Abort(task control block);
+ SCSI-Task-Completion(task control block);
+ Build-And-Send-A-Data-Burst(transport connection, data-descriptor,
+ task control block);
+
+
+
+Satran, et al. Standards Track [Page 234]
+
+RFC 3720 iSCSI April 2004
+
+
+ Build-And-Send-R2T(transport connection, data-descriptor,
+ task control block);
+ Build-And-Send-Status(transport connection, task control block);
+ Transfer-Context-Timeout-Handler(transfer context);
+
+
+ Notes:
+
+ - One procedure used in this section: Handle-Status-SNACK-
+ request is defined in Within-connection recovery algorithms.
+
+ - The Response processing pseudo-code, shown in the target
+ algorithms, applies to all solicited PDUs that carry StatSN -
+ SCSI Response, Text Response etc.
+
+E.2.2. Initiator Algorithms
+
+Recover-Data-if-Possible(LastRequiredDataSN, TCB)
+{
+ if (operational ErrorRecoveryLevel > 0) {
+ if (# of missing PDUs is trackable) {
+ Note the missing DataSNs in TCB.
+ if (the task spanned a change in
+ MaxRecvDataSegmentLength) {
+ if (TCB.StatusXferd is TRUE)
+ drop the status PDU;
+ Build-And-Send-RDSnack(TCB);
+ } else {
+ Build-And-Send-DSnack(TCB);
+ }
+ } else {
+ TCB.Reason = "Protocol service CRC error";
+ }
+ } else {
+ TCB.Reason = "Protocol service CRC error";
+ }
+ if (TCB.Reason == "Protocol service CRC error") {
+ Clear the missing PDU list in the TCB.
+ if (TCB.StatusXferd is not TRUE)
+ Build-And-Send-Abort(TCB);
+ }
+}
+
+Receive-a-In-PDU(Connection, CurrentPDU)
+{
+ check-basic-validity(CurrentPDU);
+ if (Header-Digest-Bad) discard, return;
+ Retrieve TCB for CurrentPDU.InitiatorTaskTag.
+
+
+
+Satran, et al. Standards Track [Page 235]
+
+RFC 3720 iSCSI April 2004
+
+
+ if ((CurrentPDU.type == Data)
+ or (CurrentPDU.type = R2T)) {
+ if (Data-Digest-Bad for Data) {
+ send-data-SNACK = TRUE;
+ LastRequiredDataSN = CurrentPDU.DataSN;
+ } else {
+ if (TCB.SoFarInOrder = TRUE) {
+ if (current DataSN is expected) {
+ Increment TCB.ExpectedDataSN.
+ } else {
+
+ TCB.SoFarInOrder = FALSE;
+ send-data-SNACK = TRUE;
+ }
+ } else {
+ if (current DataSN was considered missing) {
+ remove current DataSN from missing PDU list.
+ } else if (current DataSN is higher than expected)
+{
+ send-data-SNACK = TRUE;
+ } else {
+ discard, return;
+ }
+ Adjust TCB.ExpectedDataSN if appropriate.
+ }
+ LastRequiredDataSN = CurrentPDU.DataSN - 1;
+ }
+ if (send-data-SNACK is TRUE and
+ task is not already considered failed) {
+ Recover-Data-if-Possible(LastRequiredDataSN, TCB);
+ }
+ if (missing data PDU list is empty) {
+ TCB.SoFarInOrder = TRUE;
+ }
+ if (CurrentPDU.type == R2T) {
+ Increment ActiveR2Ts for this task.
+
+ Create a data-descriptor for the data burst.
+ Build-And-Send-A-Data-Burst(Connection, data-descriptor,
+
+ TCB);
+ }
+ } else if (CurrentPDU.type == Response) {
+ if (Data-Digest-Bad) {
+ send-status-SNACK = TRUE;
+ } else {
+ TCB.StatusXferd = TRUE;
+ Store the status information in TCB.
+
+
+
+Satran, et al. Standards Track [Page 236]
+
+RFC 3720 iSCSI April 2004
+
+
+ if (ExpDataSN does not match) {
+ TCB.SoFarInOrder = FALSE;
+ Recover-Data-if-Possible(current DataSN, TCB);
+ }
+ if (missing data PDU list is empty) {
+ TCB.SoFarInOrder = TRUE;
+ }
+ }
+ } else { /* REST UNRELATED TO WITHIN-COMMAND-RECOVERY, NOT
+ SHOWN */
+ }
+ if ((TCB.SoFarInOrder == TRUE) and
+ (TCB.StatusXferd == TRUE)) {
+ SCSI-Task-Completion(TCB);
+ }
+}
+
+E.2.3. Target Algorithms
+
+Receive-a-In-PDU(Connection, CurrentPDU)
+{
+ check-basic-validity(CurrentPDU);
+ if (Header-Digest-Bad) discard, return;
+ Retrieve TCB for CurrentPDU.InitiatorTaskTag.
+ if (CurrentPDU.type == Data) {
+ Retrieve TContext from CurrentPDU.TargetTransferTag;
+ if (Data-Digest-Bad) {
+ Build-And-Send-Reject(Connection, CurrentPDU,
+ Payload-Digest-Error);
+ Note the missing data PDUs in MissingDataRange[].
+ send-recovery-R2T = TRUE;
+ } else {
+ if (current DataSN is not expected) {
+ Note the missing data PDUs in MissingDataRange[].
+ send-recovery-R2T = TRUE;
+ }
+ if (CurrentPDU.Fbit == TRUE) {
+ if (current PDU is solicited) {
+ Decrement TCB.ActiveR2Ts.
+ }
+ if ((current PDU is unsolicited and
+ data received is less than I/O length and
+ data received is less than FirstBurstLength)
+ or (current PDU is solicited and the length of
+ this burst is less than expected)) {
+ send-recovery-R2T = TRUE;
+ Note the missing data in MissingDataRange[].
+ }
+
+
+
+Satran, et al. Standards Track [Page 237]
+
+RFC 3720 iSCSI April 2004
+
+
+ }
+ }
+ Increment TContext.ExpectedDataSN.
+ if (send-recovery-R2T is TRUE and
+ task is not already considered failed) {
+ if (operational ErrorRecoveryLevel > 0) {
+ Increment TCB.ActiveR2Ts.
+ Create a data-descriptor for the data burst
+ from MissingDataRange.
+ Build-And-Send-R2T(Connection, data-descriptor, TCB);
+ } else {
+ if (current PDU is the last unsolicited)
+ TCB.Reason = "Not enough unsolicited data";
+ else
+ TCB.Reason = "Protocol service CRC error";
+ }
+ }
+ if (TCB.ActiveR2Ts == 0) {
+ Build-And-Send-Status(Connection, TCB);
+ }
+ } else if (CurrentPDU.type == SNACK) {
+ snack-failure = FALSE;
+ if (operational ErrorRecoveryLevel > 0) {
+ if (CurrentPDU.type == Data/R2T) {
+ if (the request is satisfiable) {
+
+ if (request for Data) {
+ Create a data-descriptor for the data burst
+ from BegRun and RunLength.
+ Build-And-Send-A-Data-Burst(Connection,
+
+ data-descriptor, TCB);
+ } else { /* R2T */
+ Create a data-descriptor for the data burst
+ from BegRun and RunLength.
+ Build-And-Send-R2T(Connection, data-descriptor,
+ TCB);
+ }
+ } else {
+ snack-failure = TRUE;
+ }
+ } else if (CurrentPDU.type == status) {
+ Handle-Status-SNACK-request(Connection, CurrentPDU);
+ } else if (CurrentPDU.type == DataACK) {
+ Consider all data upto CurrentPDU.BegRun as
+ acknowledged.
+ Free up the retransmission resources for that data.
+ } else if (CurrentPDU.type == R-Data SNACK) {
+
+
+
+Satran, et al. Standards Track [Page 238]
+
+RFC 3720 iSCSI April 2004
+
+
+ Create a data descriptor for a data burst covering
+ all unacknowledged data.
+ Build-And-Send-A-Data-Burst(Connection,
+ data-descriptor, TCB);
+ TCB.SNACK_Tag = CurrentPDU.SNACK_Tag;
+ if (there's no more data to send) {
+ Build-And-Send-Status(Connection, TCB);
+ }
+ }
+ } else { /* operational ErrorRecoveryLevel = 0 */
+ snack-failure = TRUE;
+
+ }
+ if (snack-failure == TRUE) {
+ Build-And-Send-Reject(Connection, CurrentPDU,
+ SNACK-Reject);
+ if (TCB.StatusXferd != TRUE) {
+ TCB.Reason = "SNACK Rejected";
+ Build-And-Send-Status(Connection, TCB);
+ }
+ }
+
+ } else { /* REST UNRELATED TO WITHIN-COMMAND-RECOVERY, NOT SHOWN */
+ }
+}
+
+Transfer-Context-Timeout-Handler(TContext)
+{
+ Retrieve TCB and Connection from TContext.
+ Decrement TCB.ActiveR2Ts.
+ if (operational ErrorRecoveryLevel > 0 and
+ task is not already considered failed) {
+ Note the missing data PDUs in MissingDataRange[].
+ Create a data-descriptor for the data burst
+ from MissingDataRange[].
+ Build-And-Send-R2T(Connection, data-descriptor, TCB);
+ } else {
+ TCB.Reason = "Protocol service CRC error";
+ if (TCB.ActiveR2Ts = 0) {
+ Build-And-Send-Status(Connection, TCB);
+ }
+ }
+}
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 239]
+
+RFC 3720 iSCSI April 2004
+
+
+E.3. Within-connection Recovery Algorithms
+
+E.3.1. Procedure Descriptions
+
+Procedure descriptions:
+Recover-Status-if-Possible(transport connection,
+ currently received PDU);
+Evaluate-a-StatSN(transport connection, currently received PDU);
+Retransmit-Command-if-Possible(transport connection, CmdSN);
+Build-And-Send-SSnack(transport connection);
+Build-And-Send-Command(transport connection, task control block);
+Command-Acknowledge-Timeout-Handler(task control block);
+Status-Expect-Timeout-Handler(transport connection);
+Build-And-Send-Nop-Out(transport connection);
+Handle-Status-SNACK-request(transport connection, status SNACK
+PDU);
+Retransmit-Status-Burst(status SNACK, task control block);
+Is-Acknowledged(beginning StatSN, run length);
+
+Implementation-specific tunables:
+InitiatorProactiveSNACKEnabled
+
+ Notes:
+
+ - The initiator algorithms only deal with unsolicited Nop-In PDUs
+ for generating status SNACKs. A solicited Nop-In PDU has an
+ assigned StatSN, which, when out of order, could trigger the
+ out of order StatSN handling in Within-command algorithms,
+ again leading to Recover-Status-if-Possible.
+
+
+ - The pseudo-code shown may result in the retransmission of
+ unacknowledged commands in more cases than necessary. This
+ will not, however, affect the correctness of the operation
+ because the target is required to discard the duplicate CmdSNs.
+
+ - The procedure Build-And-Send-Async is defined in the Connection
+ recovery algorithms.
+
+ - The procedure Status-Expect-Timeout-Handler describes how
+ initiators may proactively attempt to retrieve the Status if
+ they so choose. This procedure is assumed to be triggered much
+ before the standard ULP timeout.
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 240]
+
+RFC 3720 iSCSI April 2004
+
+
+E.3.2. Initiator Algorithms
+
+Recover-Status-if-Possible(Connection, CurrentPDU)
+{
+ if ((Connection.state == LOGGED_IN) and
+ connection is not already considered failed) {
+ if (operational ErrorRecoveryLevel > 0) {
+ if (# of missing PDUs is trackable) {
+ Note the missing StatSNs in Connection
+ that were not already requested with SNACK;
+ Build-And-Send-SSnack(Connection);
+ } else {
+ Connection.PerformConnectionCleanup = TRUE;
+ }
+ } else {
+ Connection.PerformConnectionCleanup = TRUE;
+ }
+ if (Connection.PerformConnectionCleanup == TRUE) {
+ Start-Timer(Connection-Cleanup-Handler, Connection, 0);
+ }
+ }
+}
+
+Retransmit-Command-if-Possible(Connection, CmdSN)
+{
+
+ if (operational ErrorRecoveryLevel > 0) {
+ Retrieve the InitiatorTaskTag, and thus TCB for the CmdSN.
+ Build-And-Send-Command(Connection, TCB);
+ }
+}
+
+Evaluate-a-StatSN(Connection, CurrentPDU)
+{
+ send-status-SNACK = FALSE;
+ if (Connection.SoFarInOrder == TRUE) {
+ if (current StatSN is the expected) {
+ Increment Connection.ExpectedStatSN.
+ } else {
+ Connection.SoFarInOrder = FALSE;
+ send-status-SNACK = TRUE;
+ }
+ } else {
+ if (current StatSN was considered missing) {
+ remove current StatSN from the missing list.
+ } else {
+ if (current StatSN is higher than expected){
+ send-status-SNACK = TRUE;
+
+
+
+Satran, et al. Standards Track [Page 241]
+
+RFC 3720 iSCSI April 2004
+
+
+ } else {
+ send-status-SNACK = FALSE;
+ discard the PDU;
+ }
+ }
+ Adjust Connection.ExpectedStatSN if appropriate.
+ if (missing StatSN list is empty) {
+ Connection.SoFarInOrder = TRUE;
+ }
+ }
+ return send-status-SNACK;
+}
+
+Receive-a-In-PDU(Connection, CurrentPDU)
+{
+ check-basic-validity(CurrentPDU);
+ if (Header-Digest-Bad) discard, return;
+ Retrieve TCB for CurrentPDU.InitiatorTaskTag.
+ if (CurrentPDU.type == Nop-In) {
+ if (the PDU is unsolicited) {
+ if (current StatSN is not expected) {
+ Recover-Status-if-Possible(Connection,
+ CurrentPDU);
+ }
+ if (current ExpCmdSN is not Session.CmdSN) {
+ Retransmit-Command-if-Possible(Connection,
+ CurrentPDU.ExpCmdSN);
+ }
+ }
+ } else if (CurrentPDU.type == Reject) {
+ if (it is a data digest error on immediate data) {
+ Retransmit-Command-if-Possible(Connection,
+ CurrentPDU.BadPDUHeader.CmdSN);
+ }
+ } else if (CurrentPDU.type == Response) {
+ send-status-SNACK = Evaluate-a-StatSN(Connection,
+ CurrentPDU);
+ if (send-status-SNACK == TRUE)
+ Recover-Status-if-Possible(Connection, CurrentPDU);
+ } else { /* REST UNRELATED TO WITHIN-CONNECTION-RECOVERY,
+ * NOT SHOWN */
+ }
+}
+
+Command-Acknowledge-Timeout-Handler(TCB)
+{
+ Retrieve the Connection for TCB.
+ Retransmit-Command-if-Possible(Connection, TCB.CmdSN);
+
+
+
+Satran, et al. Standards Track [Page 242]
+
+RFC 3720 iSCSI April 2004
+
+
+}
+
+Status-Expect-Timeout-Handler(Connection)
+{
+ if (operational ErrorRecoveryLevel > 0) {
+ Build-And-Send-Nop-Out(Connection);
+ } else if (InitiatorProactiveSNACKEnabled){
+ if ((Connection.state == LOGGED_IN) and
+ connection is not already considered failed) {
+ Build-And-Send-SSnack(Connection);
+ }
+ }
+}
+
+E.3.3. Target Algorithms
+
+Handle-Status-SNACK-request(Connection, CurrentPDU)
+{
+ if (operational ErrorRecoveryLevel > 0) {
+ if (request for an acknowledged run) {
+ Build-And-Send-Reject(Connection, CurrentPDU,
+ Protocol-Error);
+ } else if (request for an untransmitted run) {
+ discard, return;
+ } else {
+ Retransmit-Status-Burst(CurrentPDU, TCB);
+ } else {
+ Build-And-Send-Async(Connection, DroppedConnection,
+ DefaultTime2Wait,
+ DefaultTime2Retain);
+ }
+}
+
+E.4. Connection Recovery Algorithms
+
+E.4.1. Procedure Descriptions
+
+Build-And-Send-Async(transport connection, reason code,
+ minimum time, maximum time);
+Pick-A-Logged-In-Connection(session);
+Build-And-Send-Logout(transport connection, logout connection
+ identifier, reason code);
+PerformImplicitLogout(transport connection, logout connection
+ identifier, target information);
+PerformLogin(transport connection, target information);
+CreateNewTransportConnection(target information);
+Build-And-Send-Command(transport connection, task control block);
+Connection-Cleanup-Handler(transport connection);
+
+
+
+Satran, et al. Standards Track [Page 243]
+
+RFC 3720 iSCSI April 2004
+
+
+Connection-Resource-Timeout-Handler(transport connection);
+Quiesce-And-Prepare-for-New-Allegiance(session, task control
+block);
+Build-And-Send-Logout-Response(transport connection,
+ CID of connection in recovery, reason
+code);
+Build-And-Send-TaskMgmt-Response(transport connection,
+ task mgmt command PDU, response code);
+Establish-New-Allegiance(task control block, transport
+connection);
+Schedule-Command-To-Continue(task control block);
+
+Notes:
+ - Transport exception conditions, such as unexpected connection
+ termination, connection reset, and hung connection while the
+ connection is in the full-feature phase, are all assumed to be
+ asynchronously signaled to the iSCSI layer using the
+ Transport_Exception_Handler procedure.
+
+E.4.2. Initiator Algorithms
+
+ Receive-a-In-PDU(Connection, CurrentPDU) {
+ check-basic-validity(CurrentPDU);
+ if (Header-Digest-Bad) discard, return;
+
+ Retrieve TCB from CurrentPDU.InitiatorTaskTag.
+ if (CurrentPDU.type == Async) {
+ if (CurrentPDU.AsyncEvent == ConnectionDropped) {
+ Retrieve the AffectedConnection for
+ CurrentPDU.Parameter1.
+ AffectedConnection.CurrentTimeout =
+ CurrentPDU.Parameter3;
+ AffectedConnection.State = CLEANUP_WAIT;
+ Start-Timer(Connection-Cleanup-Handler,
+ AffectedConnection,
+ CurrentPDU.Parameter2);
+ } else if (CurrentPDU.AsyncEvent == LogoutRequest)) {
+ AffectedConnection = Connection;
+ AffectedConnection.State = LOGOUT_REQUESTED;
+ AffectedConnection.PerformConnectionCleanup = TRUE;
+ AffectedConnection.CurrentTimeout =
+ CurrentPDU.Parameter3;
+ Start-Timer(Connection-Cleanup-Handler,
+ AffectedConnection, 0);
+ } else if (CurrentPDU.AsyncEvent == SessionDropped)) {
+ for (each Connection) {
+ Connection.State = CLEANUP_WAIT;
+ Connection.CurrentTimeout = CurrentPDU.Parameter3;
+
+
+
+Satran, et al. Standards Track [Page 244]
+
+RFC 3720 iSCSI April 2004
+
+
+ Start-Timer(Connection-Cleanup-Handler,
+ Connection, CurrentPDU.Parameter2);
+ }
+ Session.state = FAILED;
+ }
+
+ } else if (CurrentPDU.type == LogoutResponse) {
+ Retrieve the CleanupConnection for CurrentPDU.CID.
+ if (CurrentPDU.Response = failure) {
+ CleanupConnection.State = CLEANUP_WAIT;
+ } else {
+ CleanupConnection.State = FREE;
+ }
+ } else if (CurrentPDU.type == LoginResponse) {
+ if (this is a response to an implicit Logout) {
+ Retrieve the CleanupConnection.
+ if (successful) {
+ CleanupConnection.State = FREE;
+ Connection.State = LOGGED_IN;
+ } else {
+ CleanupConnection.State = CLEANUP_WAIT;
+ DestroyTransportConnection(Connection);
+ }
+ }
+ } else { /* REST UNRELATED TO CONNECTION-RECOVERY,
+
+ * NOT SHOWN */
+ }
+ if (CleanupConnection.State == FREE) {
+ for (each command that was active on CleanupConnection) {
+ /* Establish new connection allegiance */
+ NewConnection = Pick-A-Logged-In-Connection(Session);
+ Build-And-Send-Command(NewConnection, TCB);
+ }
+ } }
+
+ Connection-Cleanup-Handler(Connection) {
+ Retrieve Session from Connection.
+ if (Connection can still exchange iSCSI PDUs) {
+ NewConnection = Connection;
+ } else {
+ Start-Timer(Connection-Resource-Timeout-Handler,
+ Connection, Connection.CurrentTimeout);
+ if (there are other logged-in connections) {
+ NewConnection = Pick-A-Logged-In-
+ Connection(Session);
+ } else {
+ NewConnection =
+
+
+
+Satran, et al. Standards Track [Page 245]
+
+RFC 3720 iSCSI April 2004
+
+
+ CreateTransportConnection(Session.OtherEndInfo);
+ Initiate an implicit Logout on NewConnection for
+ Connection.CID.
+ return;
+ }
+ }
+ Build-And-Send-Logout(NewConnection, Connection.CID,
+ RecoveryRemove); }
+
+ Transport_Exception_Handler(Connection) {
+ Connection.PerformConnectionCleanup = TRUE;
+ if (the event is an unexpected transport disconnect) {
+ Connection.State = CLEANUP_WAIT;
+
+ Connection.CurrentTimeout = DefaultTime2Retain;
+ Start-Timer(Connection-Cleanup-Handler, Connection,
+ DefaultTime2Wait);
+
+ } else {
+ Connection.State = FREE;
+ } }
+
+E.4.3. Target Algorithms
+
+ Receive-a-In-PDU(Connection, CurrentPDU)
+ {
+ check-basic-validity(CurrentPDU);
+ if (Header-Digest-Bad) discard, return;
+ else if (Data-Digest-Bad) {
+ Build-And-Send-Reject(Connection, CurrentPDU,
+ Payload-Digest-Error);
+ discard, return;
+ }
+ Retrieve TCB and Session.
+ if (CurrentPDU.type == Logout) {
+ if (CurrentPDU.ReasonCode = RecoveryRemove) {
+ Retrieve the CleanupConnection from CurrentPDU.CID).
+ for (each command active on CleanupConnection) {
+ Quiesce-And-Prepare-for-New-Allegiance(Session,
+ TCB);
+ TCB.CurrentlyAllegiant = FALSE;
+ }
+ Cleanup-Connection-State(CleanupConnection);
+ if ((quiescing successful) and (cleanup successful)) {
+ Build-And-Send-Logout-Response(Connection,
+ CleanupConnection.CID, Success);
+ } else {
+ Build-And-Send-Logout-Response(Connection,
+
+
+
+Satran, et al. Standards Track [Page 246]
+
+RFC 3720 iSCSI April 2004
+
+
+ CleanupConnection.CID, Failure);
+ }
+ }
+ } else if ((CurrentPDU.type == Login) and
+ operational ErrorRecoveryLevel == 2) {
+ Retrieve the CleanupConnection from CurrentPDU.CID).
+ for (each command active on CleanupConnection) {
+ Quiesce-And-Prepare-for-New-Allegiance(Session, TCB);
+ TCB.CurrentlyAllegiant = FALSE;
+ }
+ Cleanup-Connection-State(CleanupConnection);
+ if ((quiescing successful) and (cleanup successful)) {
+ Continue with the rest of the Login processing;
+ } else {
+ Build-And-Send-Login-Response(Connection,
+ CleanupConnection.CID, Target Error);
+ }
+ }
+
+ } else if (CurrentPDU.type == TaskManagement) {
+ if (CurrentPDU.function == "TaskReassign") {
+ if (Session.ErrorRecoveryLevel < 2) {
+ Build-And-Send-TaskMgmt-Response(Connection,
+ CurrentPDU, "Allegiance reassignment
+ not supported");
+ } else if (task is not found) {
+ Build-And-Send-TaskMgmt-Response(Connection,
+ CurrentPDU, "Task not in task set");
+ } else if (task is currently allegiant) {
+ Build-And-Send-TaskMgmt-Response(Connection,
+ CurrentPDU, "Task still allegiant");
+ } else {
+ Establish-New-Allegiance(TCB, Connection);
+ TCB.CurrentlyAllegiant = TRUE;
+ Schedule-Command-To-Continue(TCB);
+ }
+ }
+ } else { /* REST UNRELATED TO CONNECTION-RECOVERY,
+ * NOT SHOWN */
+ }
+ }
+
+ Transport_Exception_Handler(Connection)
+ {
+ Connection.PerformConnectionCleanup = TRUE;
+ if (the event is an unexpected transport disconnect) {
+ Connection.State = CLEANUP_WAIT;
+ Start-Timer(Connection-Resource-Timeout-Handler,
+
+
+
+Satran, et al. Standards Track [Page 247]
+
+RFC 3720 iSCSI April 2004
+
+
+ Connection,
+
+ (DefaultTime2Wait+DefaultTime2Retain));
+ if (this Session has full-feature phase connections
+ left)
+ {
+ DifferentConnection =
+ Pick-A-Logged-In-Connection(Session);
+ Build-And-Send-Async(DifferentConnection,
+ DroppedConnection, DefaultTime2Wait,
+ DefaultTime2Retain);
+ }
+ } else {
+ Connection.State = FREE;
+ }
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 248]
+
+RFC 3720 iSCSI April 2004
+
+
+Appendix F. Clearing Effects of Various Events on Targets
+
+F.1. Clearing Effects on iSCSI Objects
+
+ The following tables describe the target behavior on receiving the
+ events specified in the rows of the table. The second table is an
+ extension of the first table and defines clearing actions for more
+ objects on the same events. The legend is:
+
+ Y = Yes (cleared/discarded/reset on the event specified in the
+ row). Unless otherwise noted, the clearing action is only
+ applicable for the issuing initiator port.
+ N = No (not affected on the event specified in the row, i.e.,
+ stays at previous value).
+ NA = Not Applicable or Not Defined.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 249]
+
+RFC 3720 iSCSI April 2004
+
+
+ +-----+-----+-----+-----+-----+
+ |IT(1)|IC(2)|CT(5)|ST(6)|PP(7)|
+ +---------------------+-----+-----+-----+-----+-----+
+ |connection failure(8)|Y |Y |N |N |Y |
+ +---------------------+-----+-----+-----+-----+-----+
+ |connection state |NA |NA |Y |N |NA |
+ |timeout (9) | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |session timeout/ |Y |Y |Y |Y |Y(14)|
+ |closure/reinstatement| | | | | |
+ |(10) | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |session continuation |NA |NA |N(11)|N |NA |
+ |(12) | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |successful connection|Y |Y |Y |N |Y(13)|
+ |close logout | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |session failure (18) |Y |Y |N |N |Y |
+ +---------------------+-----+-----+-----+-----+-----+
+ |successful recovery |Y |Y |N |N |Y(13)|
+ |Logout | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |failed Logout |Y |Y |N |N |Y |
+ +---------------------+-----+-----+-----+-----+-----+
+ |connection Login |NA |NA |NA |Y(15)|NA |
+ |(leading) | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |connection Login |NA |NA |N(11)|N |Y |
+ |(non-leading) | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |target cold reset(16)|Y |Y |Y |Y |Y |
+ +---------------------+-----+-----+-----+-----+-----+
+ |target warm reset(16)|Y |Y |Y |Y |Y |
+ +---------------------+-----+-----+-----+-----+-----+
+ |LU reset(19) |Y |Y |Y |Y |Y |
+ +---------------------+-----+-----+-----+-----+-----+
+ |powercycle(16) |Y |Y |Y |Y |Y |
+ +---------------------+-----+-----+-----+-----+-----+
+
+ 1. Incomplete TTTs - Target Transfer Tags on which the target is
+ still expecting PDUs to be received. Examples include TTTs received
+ via R2T, NOP-IN, etc.
+
+ 2. Immediate Commands - immediate commands, but waiting for
+ execution on a target. For example, Abort Task Set.
+
+
+
+
+
+Satran, et al. Standards Track [Page 250]
+
+RFC 3720 iSCSI April 2004
+
+
+ 5. Connection Tasks - tasks that are active on the iSCSI connection
+ in question.
+
+ 6. Session Tasks - tasks that are active on the entire iSCSI
+ session. A union of "connection tasks" on all participating
+ connections.
+
+ 7. Partial PDUs (if any) - PDUs that are partially sent and waiting
+ for transport window credit to complete the transmission.
+
+ 8. Connection failure is a connection exception condition - one of
+ the transport connections shutdown, transport connections reset, or
+ transport connections timed out, which abruptly terminated the iSCSI
+ full-feature phase connection. A connection failure always takes the
+ connection state machine to the CLEANUP_WAIT state.
+
+ 9. Connection state timeout happens if a connection spends more time
+ that agreed upon during Login negotiation in the CLEANUP_WAIT state,
+ and this takes the connection to the FREE state (M1 transition in
+ connection cleanup state diagram).
+
+ 10. These are defined in Section 5.3.5 Session Reinstatement,
+ Closure, and Timeout.
+
+ 11. This clearing effect is "Y" only if it is a connection
+ reinstatement and the operational ErrorRecoveryLevel is less than 2.
+
+ 12. Session continuation is defined in Section 5.3.6 Session
+ Continuation and Failure.
+
+ 13. This clearing effect is only valid if the connection is being
+ logged out on a different connection and when the connection being
+ logged out on the target may have some partial PDUs pending to be
+ sent. In all other cases, the effect is "NA".
+
+ 14. This clearing effect is only valid for a "close the session"
+ logout in a multi-connection session. In all other cases, the effect
+ is "NA".
+
+ 15. Only applicable if this leading connection login is a session
+ reinstatement. If this is not the case, it is "NA".
+
+ 16. This operation affects all logged-in initiators.
+
+ 18. Session failure is defined in Section 5.3.6 Session Continuation
+ and Failure.
+
+
+
+
+
+Satran, et al. Standards Track [Page 251]
+
+RFC 3720 iSCSI April 2004
+
+
+ 19. This operation affects all logged-in initiators and the clearing
+ effects are only applicable to the LU being reset.
+
+ +-----+-----+-----+-----+-----+
+ |DC(1)|DD(2)|SS(3)|CS(4)|DS(5)|
+ +---------------------+-----+-----+-----+-----+-----+
+ |connection failure |N |Y |N |N |N |
+ +---------------------+-----+-----+-----+-----+-----+
+ |connection state |Y |NA |Y |N |NA |
+ |timeout | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |session timeout/ |Y |Y |Y(7) |Y |NA |
+ |closure/reinstatement| | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |session continuation |N(11)|NA*12|NA |N |NA*13|
+ +---------------------+-----+-----+-----+-----+-----+
+ |successful connection|Y |Y |Y |N |NA |
+ |close Logout | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |session failure |N |Y |N |N |N |
+ +---------------------+-----+-----+-----+-----+-----+
+ |successful recovery |Y |Y |Y |N |N |
+ |Logout | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |failed Logout |N |Y(9) |N |N |N |
+ +---------------------+-----+-----+-----+-----+-----+
+ |connection Login |NA |NA |N(8) |N(8) |NA |
+ |(leading | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |connection Login |N(11)|NA*12|N(8) |N |NA*13|
+ |(non-leading) | | | | | |
+ +---------------------+-----+-----+-----+-----+-----+
+ |target cold reset |Y |Y |Y |Y(10)|NA |
+ +---------------------+-----+-----+-----+-----+-----+
+ |target warm reset |Y |Y |N |N |NA |
+ +---------------------+-----+-----+-----+-----+-----+
+ |LU reset |N |Y |N |N |N |
+ +---------------------+-----+-----+-----+-----+-----+
+ |powercycle |Y |Y |Y |Y(10)|NA |
+ +---------------------+-----+-----+-----+-----+-----+
+
+ 1. Discontiguous Commands - commands allegiant to the connection in
+ question and waiting to be reordered in the iSCSI layer. All "Y"s in
+ this column assume that the task causing the event (if indeed the
+ event is the result of a task) is issued as an immediate command,
+ because the discontiguities can be ahead of the task.
+
+
+
+
+
+Satran, et al. Standards Track [Page 252]
+
+RFC 3720 iSCSI April 2004
+
+
+ 2. Discontiguous Data - data PDUs received for the task in question
+ and waiting to be reordered due to prior discontiguities in DataSN.
+
+ 3. StatSN
+
+ 4. CmdSN
+
+ 5. DataSN
+
+ 7. It clears the StatSN on all the connections.
+
+ 8. This sequence number is instantiated on this event.
+
+ 9. A logout failure drives the connection state machine to the
+ CLEANUP_WAIT state, similar to the connection failure event. Hence,
+ it has a similar effect on this and several other protocol aspects.
+
+ 10. This is cleared by virtue of the fact that all sessions with all
+ initiators are terminated.
+
+ 11. This clearing effect is "Y" if it is a connection reinstatement.
+
+ 12. This clearing effect is "Y" only if it is a connection
+ reinstatement and the operational ErrorRecoveryLevel is 2.
+
+ 13. This clearing effect is "N" only if it is a connection
+ reinstatement and the operational ErrorRecoveryLevel is 2.
+
+F.2. Clearing Effects on SCSI Objects
+
+ The only iSCSI protocol action that can effect clearing actions on
+ SCSI objects is the "I_T nexus loss" notification (Section 4.3.5.1
+ Loss of Nexus notification). [SPC3] describes the clearing effects
+ of this notification on a variety of SCSI attributes. In addition,
+ SCSI standards documents (such as [SAM2] and [SBC]) define additional
+ clearing actions that may take place for several SCSI objects on SCSI
+ events such as LU resets and power-on resets.
+
+ Since iSCSI defines a target cold reset as a protocol-equivalent to a
+ target power-cycle, the iSCSI target cold reset must also be
+ considered as the power-on reset event in interpreting the actions
+ defined in the SCSI standards.
+
+ When the iSCSI session is reconstructed (between the same SCSI ports
+ with the same nexus identifier) reestablishing the same I_T nexus,
+ all SCSI objects that are defined to not clear on the "I_T nexus
+ loss" notification event, such as persistent reservations, are
+ automatically associated to this new session.
+
+
+
+Satran, et al. Standards Track [Page 253]
+
+RFC 3720 iSCSI April 2004
+
+
+Acknowledgements
+
+ This protocol was developed by a design team that, in addition to the
+ authors, included Daniel Smith, Ofer Biran, Jim Hafner and John
+ Hufferd (IBM), Mark Bakke (Cisco), Randy Haagens (HP), Matt Wakeley
+ (Agilent, now Sierra Logic), Luciano Dalle Ore (Quantum), and Paul
+ Von Stamwitz (Adaptec, now TrueSAN Networks).
+
+ Furthermore, a large group of people contributed to this work through
+ their review, comments, and valuable insights. We are grateful to
+ all of them. We especially thank those people who found the time and
+ patience to take part in our weekly phone conferences and
+ intermediate meetings in Almaden and Haifa, which helped shape this
+ document: Prasenjit Sarkar, Meir Toledano, John Dowdy, Steve Legg,
+ Alain Azagury (IBM), Dave Nagle (CMU), David Black (EMC), John Matze
+ (Veritas - now Okapi Software), Steve DeGroote, Mark Schrandt
+ (Cisco), Gabi Hecht (Gadzoox), Robert Snively and Brian Forbes
+ (Brocade), Nelson Nachum (StorAge), and Uri Elzur (Broadcom). Many
+ others helped edit and improve this document within the IPS working
+ group. We are especially grateful to David Robinson and Raghavendra
+ Rao (Sun), Charles Monia, Joshua Tseng (Nishan), Somesh Gupta
+ (Silverback), Michael Krause, Pierre Labat, Santosh Rao, Matthew
+ Burbridge, Bob Barry, Robert Elliott, Nick Martin (HP), Stephen
+ Bailey (Sandburst), Steve Senum, Ayman Ghanem, Dave Peterson (Cisco),
+ Barry Reinhold (Trebia Networks), Bob Russell (UNH), Eddy Quicksall
+ (iVivity, Inc.), Bill Lynn and Michael Fischer (Adaptec), Vince
+ Cavanna, Pat Thaler (Agilent), Jonathan Stone (Stanford), Luben
+ Tuikov (Splentec), Paul Koning (EqualLogic), Michael Krueger
+ (Windriver), Martins Krikis (Intel), Doug Otis (Sanlight), John
+ Marberg (IBM), Robert Griswold and Bill Moody (Crossroads), Bill
+ Studenmund (Wasabi Systems), Elizabeth Rodriguez (Brocade) and Yaron
+ Klein (Sanrad). The recovery chapter was enhanced with the help of
+ Stephen Bailey (Sandburst), Somesh Gupta (Silverback), and Venkat
+ Rangan (Rhapsody Networks). Eddy Quicksall contributed some examples
+ and began the Definitions section. Michael Fischer and Bob Barry
+ started the Acronyms section. Last, but not least, we thank Ralph
+ Weber for keeping us in line with T10 (SCSI) standardization.
+
+ We would like to thank Steve Hetzler for his unwavering support and
+ for coming up with such a good name for the protocol, and Micky
+ Rodeh, Jai Menon, Clod Barrera, and Andy Bechtolsheim for helping
+ make this work happen.
+
+ In addition to this document, we recommend you acquaint yourself with
+ the following in order to get a full understanding of the iSCSI
+ specification: "iSCSI Naming & Discovery"[RFC3721], "Bootstrapping
+ Clients using the iSCSI Protocol" [BOOT], "Securing Block Storage
+ Protocols over IP" [RFC3723] documents, "iSCSI Requirements and
+
+
+
+Satran, et al. Standards Track [Page 254]
+
+RFC 3720 iSCSI April 2004
+
+
+ Design Considerations" [RFC3347] and "SCSI Command Ordering
+ Considerations with iSCSI" [CORD].
+
+ The "iSCSI Naming & Discovery" document is authored by:
+
+ Mark Bakke (Cisco), Jim Hafner, John Hufferd, Kaladhar Voruganti
+ (IBM), and Marjorie Krueger (HP).
+
+ The "Bootstrapping Clients using the iSCSI Protocol" document is
+ authored by:
+
+ Prasenjit Sarkar (IBM), Duncan Missimer (HP), and Costa
+ Sapuntzakis (Cisco).
+
+ The "Securing Block Storage Protocols over IP" document is authored
+ by:
+
+ Bernard Aboba (Microsoft), Joshua Tseng (Nishan), Jesse Walker
+ (Intel), Venkat Rangan (Rhapsody Networks), and Franco
+ Travostino (Nortel Networks).
+
+ The "iSCSI Requirements and Design Considerations" document is
+ authored by:
+
+ Marjorie Krueger, Randy Haagens (HP), Costa Sapuntzakis, and Mark
+ Bakke (Cisco).
+
+ The "SCSI Command Ordering Considerations with iSCSI" document is
+ authored by:
+
+ Mallikarjun Chadalapaka, Rob Elliot (HP)
+
+ We are grateful to all of them for their good work and for helping us
+ correlate this document with the ones they produced.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 255]
+
+RFC 3720 iSCSI April 2004
+
+
+Authors' Addresses
+
+ Julian Satran
+ IBM Research Laboratory in Haifa
+ Haifa University Campus - Mount Carmel
+ Haifa 31905, Israel
+
+ Phone +972.4.829.6264
+ EMail: Julian_Satran@il.ibm.com
+
+
+ Kalman Meth
+ IBM Research Laboratory in Haifa
+ Haifa University Campus - Mount Carmel
+ Haifa 31905, Israel
+
+ Phone +972.4.829.6341
+ EMail: meth@il.ibm.com
+
+
+ Costa Sapuntzakis
+ Stanford University
+ 353 Serra Mall Dr #407
+ Stanford, CA 94305
+
+ Phone: +1.650.723.2458
+ EMail: csapuntz@alum.mit.edu
+
+
+ Efri Zeidner
+ XIV Ltd.
+ 1 Azrieli Center,
+ Tel-Aviv 67021, Israel
+
+ Phone: +972.3.607.4722
+ EMail: efri@xiv.co.il
+
+
+ Mallikarjun Chadalapaka
+ Hewlett-Packard Company
+ 8000 Foothills Blvd.
+ Roseville, CA 95747-5668, USA
+
+ Phone: +1.916.785.5621
+ EMail: cbm@rose.hp.com
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 256]
+
+RFC 3720 iSCSI April 2004
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2004). This document is subject
+ to the rights, licenses and restrictions contained in BCP 78, and
+ except as set forth therein, the authors retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at ietf-
+ ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+Satran, et al. Standards Track [Page 257]
+
diff --git a/utils/open-isns/doc/rfc3722.txt b/utils/open-isns/doc/rfc3722.txt
new file mode 100644
index 0000000..0eb44ff
--- /dev/null
+++ b/utils/open-isns/doc/rfc3722.txt
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group M. Bakke
+Request for Comments: 3722 Cisco
+Category: Standards Track April 2004
+
+
+ String Profile for Internet Small Computer
+ Systems Interface (iSCSI) Names
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2004). All Rights Reserved.
+
+Abstract
+
+ This document describes how to prepare internationalized iSCSI names
+ to increase the likelihood that name input and comparison work in
+ ways that make sense for typical users throughout the world.
+
+ The Internet Small Computer Systems Interface (iSCSI) protocol
+ provides a way for hosts to access SCSI devices over an IP network.
+ The iSCSI end-points, called initiators and targets, each have a
+ globally-unique name that must be transcribable, as well as easily
+ compared.
+
+1. Introduction
+
+ The iSCSI protocol [RFC3720] provides a way for hosts to access SCSI
+ [SAM2] devices over an IP network. The iSCSI end-points, called
+ initiators and targets, each have a globally-unique name, defined in
+ [RFC3721].
+
+ An iSCSI name is a string of UTF-8 [RFC3629] characters that includes
+ a type designator, a naming authority based on domain names, and a
+ unique part within the naming authority. The unique part may be
+ generated based on anything the naming authority deems useful, and
+ may include user input.
+
+ These names may need to be transcribed (sent between two
+ administrators via email, voice, paper, etc), so a case-insensitive
+ comparison would be desirable. However, these names must often be
+
+
+
+Bakke Standards Track [Page 1]
+
+RFC 3722 String Profile for iSCSI Names April 2004
+
+
+ compared by initiator and target implementations, most of which are
+ done in simple, embedded software. This makes case-sensitive
+ comparison highly desirable for these implementors.
+
+ However, a completely case-sensitive implementation would result in
+ identifiers such as "example-name" and "Example-Name" being
+ different, which could lead to confusion as these names are
+ transcribed.
+
+ The goal, then, is to generate iSCSI names that can be transcribed
+ and entered by users, and also compared byte-for-byte, with minimal
+ confusion. To attain these goals, iSCSI names are generalized using
+ a normalized character set (converted to lower case or equivalent),
+ with no white space allowed, and very limited punctuation.
+
+ For those using only ASCII characters (U+0000 to U+007F), the
+ following characters are allowed:
+
+ - ASCII dash character ('-' = U+002d)
+ - ASCII dot character ('.' = U+002e)
+ - ASCII colon character (':' = U+003a)
+ - ASCII lower-case characters ('a'..'z' = U+0061..U+007a)
+ - ASCII digit characters ('0'..'9' = U+0030..U+0039)
+
+ In addition, any upper-case characters input via a user interface
+ MUST be mapped to their lower-case equivalents.
+
+ This document specifies the valid character set for iSCSI names,
+ along with the rules for normalizing and generating iSCSI names based
+ on user input or other information that contains international
+ characters.
+
+ In particular, it defines the following, as required by [RFC3454]:
+
+ - The intended applicability of the profile: internationalized iSCSI
+ names.
+
+ - The character repertoire that is the input and output to
+ stringprep: Unicode 3.2, specified in section 3.
+
+ - The mappings used: specified in section 4.
+
+ - The Unicode normalization used: specified in section 5.
+
+ - The characters that are prohibited as output: specified in section
+ 6.
+
+ This profile MUST be used with the iSCSI protocol.
+
+
+
+Bakke Standards Track [Page 2]
+
+RFC 3722 String Profile for iSCSI Names April 2004
+
+
+2. Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC2119].
+
+ Examples in this document use the notation for code points and names
+ from the Unicode Standard [Unicode3.2] and ISO/IEC 10646 [ISO10646].
+ For example, the letter "a" may be represented as either "U+0061" or
+ "LATIN SMALL LETTER A". In the lists of prohibited characters, the
+ "U+" is left off to make the lists easier to read. The comments for
+ character ranges are shown in square brackets (such as "[SYMBOLS]")
+ and do not come from the standards.
+
+3. Character Repertoire
+
+ This profile uses Unicode 3.2, as defined in [RFC3454] Appendix A.
+
+4. Mapping
+
+ This profile specifies mapping using the following tables from
+ [RFC3454]. The following mapping tables MUST be used when generating
+ iSCSI names from Unicode characters.
+
+ Table B.1
+ Table B.2
+
+5. Normalization
+
+ Unicode normalization form KC MUST be used with this profile, as
+ described in [RFC3454].
+
+6. Prohibited Output
+
+ This profile specifies prohibiting using the following tables from
+ [RFC3454]. Characters appearing within these tables MUST NOT be used
+ within an iSCSI name.
+
+ Table C.1.1
+ Table C.1.2
+ Table C.2.1
+ Table C.2.2
+ Table C.3
+ Table C.4
+ Table C.5
+ Table C.6
+
+
+
+
+
+Bakke Standards Track [Page 3]
+
+RFC 3722 String Profile for iSCSI Names April 2004
+
+
+ Table C.7
+ Table C.8
+ Table C.9
+
+ Important note: this profile MUST be used with the iSCSI protocol.
+ The iSCSI protocol has additional naming rules that are checked
+ outside of this profile.
+
+ In addition, this profile adds the following prohibitions. The full
+ set of prohibited characters are those from the tables above plus
+ those listed individually below.
+
+6.1. Inappropriate Characters from Common Input Mechanisms
+
+ u+3002 is used as if it were u+002e in many domain name input
+ mechanisms used by applications, particularly in Asia. The character
+ u+3002 MUST NOT be used in an iSCSI name.
+
+ 3002; ideographic full stop
+
+6.2. Currently-prohibited ASCII characters
+
+ Some of the ASCII characters that are currently prohibited in iSCSI
+ names by [RFC3721] are also used in protocol elements such as URIs.
+ Some examples are described in [RFC2396] and [RFC2732]. Note that
+ there are many other RFCs that define additional URI schemes.
+
+ The other characters in the range U+0000 to U+007F that are not
+ currently allowed are prohibited in iSCSI names to reserve them for
+ future use in protocol elements. Note that the dash (U+002D), dot
+ (U+002E), and colon (U+003A) are not prohibited.
+
+ The following characters MUST NOT be used in iSCSI names:
+
+ 0000-002C; [ASCII CONTROL CHARACTERS and SPACE through ,]
+ 002F; [ASCII /]
+ 003B-0040; [ASCII ; through @]
+ 005B-0060; [ASCII [ through `]
+ 007B-007F; [ASCII { through DEL]
+
+7. Bidirectional Characters
+
+ This profile specifies checking bidirectional strings as described in
+ [RFC3454] section 6.
+
+
+
+
+
+
+
+Bakke Standards Track [Page 4]
+
+RFC 3722 String Profile for iSCSI Names April 2004
+
+
+8. Unassigned Code Points in Internationalized Domain Names
+
+ If the processing in [RFC3720] specifies that a list of unassigned
+ code points be used, the system uses table A.1 from [RFC3454] as its
+ list of unassigned code points.
+
+9. Security Considerations
+
+ ISO/IEC 10646 has many characters that look similar. In many cases,
+ users of security protocols might do visual matching, such as when
+ comparing the names of trusted third parties. This profile does
+ nothing to map similar-looking characters together.
+
+ iSCSI names may be used by an initiator to verify that a target it
+ has discovered is the correct one, and by a target to verify that an
+ initiator is to be allowed access. If these names are interpreted
+ and compared differently by different iSCSI implementations, an
+ initiator could gain access to the wrong target, or could be denied
+ access to a legitimate target.
+
+10. IANA Considerations
+
+ This is a profile of stringprep. It has been registered in the IANA
+ "Stringprep Profiles" registry. This process is described in the
+ IANA Considerations section of [RFC3454].
+
+11. Summary
+
+ This document describes a stringprep profile to be used with programs
+ generating names for iSCSI initiators and targets.
+
+12. Acknowledgements
+
+ This document was produced as a result of discussions on iSCSI name
+ formats with Joe Czap, Jim Hafner, Howard Hall, Jack Harwood, John
+ Hufferd, Marjorie Krueger, Lawrence Lamers, Todd Sperry, Joshua
+ Tseng, and Kaladhar Voruganti, as well as discussions on the
+ normalization of names into identifiers with Paul Hoffman and Marc
+ Blanchet.
+
+ Thanks also to Bob Snively for suggesting the use of the nameprep
+ process for iSCSI name normalization.
+
+ Most of this document was copied from the stringprep profile for
+ Internationalized Domain Names [RFC3491], written by Paul Hoffman and
+ Marc Blanchet.
+
+
+
+
+
+Bakke Standards Track [Page 5]
+
+RFC 3722 String Profile for iSCSI Names April 2004
+
+
+13. References
+
+13.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3454] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [RFC3720] Satran, J., Meth, K., Sapuntzakis, C. Chadalapaka, M.
+ and E. Zeidner, "Internet Small Computer Systems
+ Interface (iSCSI)", RFC 3720, April 2004.
+
+13.2. Informative References
+
+ [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform
+ Resource Identifiers", RFC 2396, August 1998.
+
+ [RFC2732] Hinden, R., Carpenter, B. and L. Masinter, "Format for
+ Literal IPv6 Addresses in URL's", RFC 2732, December
+ 1999.
+
+ [RFC3491] Hoffman, P. and M. Blanchet, "Nameprep: A Stringprep
+ Profile for Internationalized Domain Names", RFC 3491,
+ March 2003.
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC3721] Bakke, M., Hafner, J., Hufferd, J., Voruganti, K. and M.
+ Krueger, "Internet Small Computer Systems Interface
+ (iSCSI) Naming and Discovery", RFC 3721, April 2004.
+
+ [SAM2] ANSI T10. "SCSI Architectural Model 2", March 2000.
+
+ [Unicode3.2] The Unicode Standard, Version 3.2.0: The Unicode
+ Consortium. The Unicode Standard, Version 3.2.0 is
+ defined by The Unicode Standard, Version 3.0 (Reading,
+ MA, Addison-Wesley, 2000. ISBN 0-201-61633-5), as
+ amended by the Unicode Standard Annex #27: Unicode 3.1
+ (http://www.unicode.org/unicode/reports/tr27/) and by
+ the Unicode Standard Annex #28: Unicode 3.2
+ (http://www.unicode.org/unicode/reports/tr28/).
+
+
+
+
+
+
+
+Bakke Standards Track [Page 6]
+
+RFC 3722 String Profile for iSCSI Names April 2004
+
+
+ [ISO10646] ISO/IEC 10646-1:2000. International Standard --
+ Information technology -- Universal Multiple-Octet Coded
+ Character Set (UCS) -- Part 1: Architecture and Basic
+ Multilingual Plane.
+
+14. Author's Address
+
+ Mark Bakke
+ Cisco Systems, Inc.
+ 6450 Wedgwood Road
+ Maple Grove, MN
+ USA 55311
+
+ Voice: +1 763-398-1000
+ EMail: mbakke@cisco.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Bakke Standards Track [Page 7]
+
+RFC 3722 String Profile for iSCSI Names April 2004
+
+
+15. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2004). This document is subject
+ to the rights, licenses and restrictions contained in BCP 78, and
+ except as set forth therein, the authors retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at ietf-
+ ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+Bakke Standards Track [Page 8]
+
diff --git a/utils/open-isns/doc/rfc4018.txt b/utils/open-isns/doc/rfc4018.txt
new file mode 100644
index 0000000..15df58d
--- /dev/null
+++ b/utils/open-isns/doc/rfc4018.txt
@@ -0,0 +1,1291 @@
+
+
+
+
+
+
+Network Working Group M. Bakke
+Request for Comments: 4018 Cisco
+Category: Standards Track J. Hufferd
+ K. Voruganti
+ IBM
+ M. Krueger
+ HP
+ T. Sperry
+ Adaptec
+ April 2005
+
+
+ Finding Internet Small Computer Systems Interface (iSCSI) Targets
+ and Name Servers by Using Service Location Protocol version 2 (SLPv2)
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2005).
+
+Abstract
+
+ The iSCSI protocol provides a way for hosts to access SCSI devices
+ over an IP network. This document defines the use of the Service
+ Location Protocol (SLP) by iSCSI hosts, devices, and management
+ services, along with the SLP service type templates that describe the
+ services they provide.
+
+Table of Contents
+
+ 1. Introduction................................................ 2
+ 2. Notation Conventions........................................ 2
+ 3. Terminology................................................. 3
+ 4. Using SLP for iSCSI Service Discovery....................... 4
+ 5. iSCSI SLP Templates......................................... 11
+ 6. Security Considerations..................................... 18
+ 7. IANA Considerations......................................... 19
+ 8. Summary..................................................... 19
+ 9. Normative References........................................ 19
+ 10. Informative References...................................... 20
+ 11. Acknowledgements............................................ 21
+
+
+
+Bakke & Hufferd Standards Track [Page 1]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+1. Introduction
+
+ iSCSI [RFC3720] is a protocol used to transport SCSI [SAM2] commands,
+ data, and status across an IP network. This protocol is connection-
+ oriented and is currently defined over TCP. iSCSI uses a client-
+ server relationship. The client end of the connection is an
+ initiator, and it sends SCSI commands; the server end of the
+ connection is called a target, and it receives and executes the
+ commands.
+
+ There are several methods an iSCSI initiator can use to find the
+ targets to which it should connect. Two of these methods can be
+ accomplished without the use of SLP:
+
+ - Each target and its address can be statically configured on the
+ initiator.
+
+ - Each address providing targets can be configured on the initiator;
+ iSCSI provides a mechanism by which the initiator can query the
+ address for a list of targets.
+
+ The above methods are further defined in "iSCSI Naming and Discovery
+ Requirements" [RFC3721].
+
+ Each of the above methods requires a small amount of configuration to
+ be done on each initiator. The ability to discover targets and name
+ services without having to configure initiators is a desirable
+ feature. The Service Location Protocol (SLP) [RFC2608] is an IETF
+ standards track protocol providing several features that will
+ simplify locating iSCSI services. This document describes how SLP
+ can be used in iSCSI environments to discover targets, addresses
+ providing targets, and storage management servers.
+
+2. Notation Conventions
+
+ In this document, the key words "MUST", "MUST NOT", "REQUIRED",
+ "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY",
+ and "OPTIONAL" are to be interpreted as described in [RFC2119].
+
+
+
+
+
+
+
+
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 2]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+3. Terminology
+
+ Here are some definitions that may aid readers who are unfamiliar
+ with SLP, SCSI, or iSCSI. Some of these definitions have been
+ reproduced from [RFC2608] and "Finding an RSIP Server with SLP"
+ [RFC3105].
+
+ User Agent (UA) A process working on the client's behalf
+ to establish contact with some service.
+ The UA retrieves service information from
+ the Service Agents or Directory Agents.
+
+ Service Agent (SA) A process working on behalf of one or more
+ services to advertise the services and
+ their capabilities.
+
+ Directory Agent (DA) A process that collects service
+ advertisements. There can only be one DA
+ present per given host.
+
+ Scope A named set of services, typically making
+ up a logical administrative group.
+
+ Service Advertisement A URL, attributes, and a lifetime
+ (indicating how long the advertisement is
+ valid) providing service access
+ information and capabilities description
+ for a particular service.
+
+ Initiator A logical entity, typically within a host,
+ that sends SCSI commands to targets to be
+ executed. An initiator is usually present
+ in the form of a device driver.
+
+ Target A logical entity, typically within a
+ storage controller or gateway that
+ receives SCSI commands from an initiator
+ and executes them. A target includes one
+ or more Logical Units (LUs); each LU is a
+ SCSI device, such as a disk or tape drive.
+
+ iSCSI Name A UTF-8 character string that serves as a
+ unique identifier for iSCSI initiators and
+ targets. Its format and usage is further
+ defined in [RFC3721].
+
+ iSCSI Client A logical entity, typically a host that
+ includes at least one iSCSI Initiator.
+
+
+
+Bakke & Hufferd Standards Track [Page 3]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ iSCSI Server A logical entity, typically a storage
+ controller or gateway that includes at
+ least one iSCSI Target.
+
+ Storage Management Server An addressable entity that provides
+ management services that benefit an iSCSI
+ environment. "Storage management server"
+ is used as a generic term and does not
+ indicate a specific protocol or service.
+
+4. Using SLP for iSCSI Service Discovery
+
+ Two entities are involved in iSCSI discovery. The end result is that
+ an iSCSI initiator (e.g., a host) discovers iSCSI targets, usually
+ provided by storage controllers or gateways.
+
+ iSCSI targets are registered with SLP as a set of service URLs, one
+ for each address on which the target may be accessed. Initiators
+ discover these targets by using SLP service requests. Targets that
+ do not directly support SLP or that are under the control of a
+ management service may be registered by a proxy service agent as part
+ of the software providing this service.
+
+ iSCSI entities may also use SLP to discover higher-level management
+ services when these are needed.
+
+ This section first describes the use of SLP for discovery of targets
+ by iSCSI initiators, it then describes the use of SLP to discover
+ storage management servers.
+
+ This document assumes that SLPv2 will be used for discovering iSCSI-
+ related services; no attempt is made to include support for SLPv1.
+
+4.1. Discovering iSCSI Targets with SLP
+
+ The following diagram shows the relationship among iSCSI clients,
+ servers, initiators, and targets. An iSCSI client includes at least
+ one iSCSI initiator, and an SLP user agent (UA). An iSCSI server
+ includes at least one iSCSI target an SLP service agent (SA). Some
+ entities, such as extended copy engines, include both initiators and
+ targets. These include both an SA, for its targets to be discovered,
+ and a UA, for its initiator(s) to discover other targets.
+
+
+
+
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 4]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ +---------------------------------+
+ | iSCSI Client |
+ | +-----------+ |
+ | | iSCSI | |
+ | | initiator | |
+ | | "myhost" | |
+ | +-----------+ |
+ | |
+ +--------------------------+------+
+ | iSCSI Driver | UA |
+ +--------------------------+------+
+ | TCP/UDP/IP |
+ +----------------+----------------+
+ | Interface 1 | Interface 2 |
+ +----------------+----------------+
+ | |
+ +------------+ | | +------------+
+ | SLP DA | | | | SLP DA |
+ | (optional) |----+ IP Networks +----| (optional) |
+ +------------+ | | +------------+
+ | |
+ +-----------------+-----------------|
+ | Interface 1 | Interface 2 |
+ | 192.0.2.131 | 192.0.2.3 |
+ +-----------------+-----------------+
+ | TCP/UDP/IP |
+ +---------------------------+-------+
+ | iSCSI Driver | SA |
+ +---------------------------+-------|
+ | |
+ | +--------+ +--------+ +---------+ |
+ | | iSCSI | | iSCSI | | iSCSI | |
+ | | target | | target | | target | |
+ | | "one" | | "two" | | "three" | |
+ | +--------+ +--------+ +---------+ |
+ | iSCSI Server |
+ +-----------------------------------+
+
+ In the above drawing, the iSCSI server has three iSCSI targets that
+ the client could discover, named "one", "two" and "three". The iSCSI
+ client has an iSCSI initiator with the name "myhost". The iSCSI
+ client may use the initiator name in its SLP Service Requests as a
+ filter to discover only targets that are configured to accept iSCSI
+ connections from "myhost".
+
+ Each iSCSI target and initiator has a unique name, called an iSCSI
+ Name. This identifier is the same regardless of the network path
+ (through adapter cards, networks, and interfaces on the storage
+
+
+
+Bakke & Hufferd Standards Track [Page 5]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ device) over which the target is discovered and accessed. For this
+ example, the iSCSI names "one", "two", and "three" are used for the
+ targets; the initiator uses the name "myhost". An actual iSCSI name
+ would incorporate more structure, including a naming authority, and
+ is not described here.
+
+ Each of the iSCSI targets in the drawing can appear at two addresses,
+ since two network interfaces are present. Each target would have two
+ service URLs, unless a single service URL included a DNS host name
+ mapping to both addresses.
+
+ An iSCSI target URL consists of its fully qualified host name or IP
+ address, the TCP port on which it is listening, and its iSCSI name.
+ An iSCSI server must register each of its individual targets at each
+ of its network addresses.
+
+ The iSCSI server constructs a service advertisement of the type
+ "service:iscsi:target" for each of the service URLs it wishes to
+ register. The advertisement contains a lifetime, along with other
+ attributes that are defined in the service template.
+
+ If the server in the above drawing is listening at TCP port 3260 for
+ both network addresses, the service URLs registered would be
+
+ - 192.0.2.131:3260/one
+
+ - 192.0.2.131:3260/two
+
+ - 192.0.2.131:3260/three
+
+ - 192.0.2.3:3260/one
+
+ - 192.0.2.3:3260/two
+
+ - 192.0.2.3:3260/three
+
+ The remainder of the discovery procedure is identical to that used by
+ any client/server pair implementing SLP:
+
+ 1. If an SLP DA is found, the SA contacts the DA and registers the
+ service advertisement. Whether or not one or more SLPv2 DAs are
+ discovered, the SA maintains the advertisement itself and answers
+ multicast UA queries directly.
+
+ 2. When the iSCSI initiator requires contact information for an
+ iSCSI target, the UA either contacts the DA by using unicast or
+ the SA by using multicast. If a UA is configured with the
+ address of the SA, it may avoid multicast and may contact an SA
+
+
+
+Bakke & Hufferd Standards Track [Page 6]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ by using unicast. The UA includes a query based on the
+ attributes to indicate the characteristics of the target(s) it
+ requires.
+
+ 3. Once the UA has the host name or address of the iSCSI server, as
+ well as the port number and iSCSI Target Name, it can begin the
+ normal iSCSI login to the target.
+
+ As information contained in the iSCSI target template may exceed
+ common network datagram sizes, the SLP implementation for both UAs
+ and SAs supporting this template MUST implement SLP over TCP.
+
+4.1.1. Finding Targets Based on Initiator Credentials
+
+ To be allowed access to an iSCSI target, an initiator must be
+ authenticated. The initiator may be required by the target to
+ produce one or more of the following credentials:
+
+ - An iSCSI Initiator Name
+
+ - An IP address
+
+ - A CHAP, SRP, or Kerberos credential
+
+ - Any combination of the above
+
+ Most iSCSI targets allow access to only one or two initiators. In
+ the ideal discovery scenario, an initiator would send an SLP request
+ and receive responses ONLY for targets to which the initiator is
+ guaranteed a successful login. To achieve this goal, the iSCSI
+ target template contains the following attributes, each of which
+ allows a list of values:
+
+ 1. auth-name: This attribute contains the list of initiator names
+ allowed to access this target, or the value "any", indicating
+ that no specific initiator name is required.
+
+ 2. auth-addr: This attribute contains the list of host names
+ and/or IP addresses that will be allowed access to this target,
+ or the value "any", indicating that no specific address or
+ host name is required. If a large number of addresses is to
+ be allowed (perhaps a subnet), this attribute may contain the
+ value "any".
+
+
+
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 7]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ 3. auth-cred: This attribute contains a list of "method/identifier"
+ credentials that will be allowed access to the target, provided
+ they can produce the correct password or other verifier during
+ the login process. If no specific credentials are required, the
+ value "any" is used.
+
+ The list of valid method strings for auth-cred are defined in
+ [RFC3720], section 11.1, "AuthMethod". The identifier used after the
+ "/" is defined by the specific AuthMethod, also in [RFC3720].
+ Examples showing initiator searches based on auth-xxxx attributes are
+ shown in the target-specific template section below.
+
+ Also note that the auth-xxxx attributes are considered security
+ policy information. If these attributes are distributed, IPsec MUST
+ be implemented as specified in the Security Implementation section
+ below.
+
+4.1.2. Supporting Access by Multiple Identities to the Same Target
+
+ If a target is to allow access to multiple host identities, more than
+ one combination of auth-xxxx attributes will have to be allowed. In
+ some of these cases, it is not possible to express the entire set of
+ valid combinations of auth-xxxx attributes within a single registered
+ service URL. For example, if a target can be addressed by
+
+ auth-name=myhost1 AND auth-cred=CHAP/user1 (identity1)
+
+ OR
+
+ auth-name-myhost2 AND auth-cred=CHAP/user2 (identity2)
+
+ the above cannot be specified in a single registered service URL,
+ since (auth-name=myhost1, auth-name=myhost2, auth-cred=CHAP/user1,
+ auth-cred=CHAP/user2) would allow either auth-name to be used with
+ either auth-cred. This necessitates the ability to register a target
+ and address under more than one service URL; one for (identity1) and
+ one for (identity2).
+
+ Because service URLs must be unique, (identity1) and (identity2) must
+ each be registered under a unique service URL. For systems that
+ support the configuration of multiple identities to access a target,
+ the service URL must contain an additional, opaque string defining
+ the identity. This appears after the iSCSI name in the URL string
+ and is separated by a "/". Each registered (target-address, target-
+ name, initiator-identity) tuple can then register a set of auth-xxxx
+ attributes.
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 8]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+4.1.3. Using SLP in a Non-multicast Environment
+
+ In some networks, the use of multicast for discovery purposes is
+ either unavailable or not allowed. These include public or service-
+ provider networks that are placed between an iSCSI client and a
+ server. These are probably most common between two iSCSI gateways,
+ one at a storage service provider site, and one at a customer site.
+
+ In these networks, an initiator may allow the addresses of one or
+ more SAs to be configured instead of or in addition to its DA
+ configuration. The initiator would then make unicast SLP service
+ requests directly to these SAs, without the use of multicast to
+ discover them first.
+
+ This functionality is well within the scope of the current SLP
+ protocol. The main consequence for implementors is that an initiator
+ configured to make direct unicast requests to an SA will have to add
+ this to the SLP API, if it is following the service location API
+ defined in [RFC2614].
+
+4.2. Discovering Storage Management Services with SLP
+
+ Storage management servers can be built to manage and control access
+ to targets in a variety of ways. They can provide extended services
+ beyond discovery, which could include storage allocation and
+ management. None of these services are defined here; the intent of
+ this document is to allow these services to be discovered by both
+ clients and servers, in addition to the target discovery already
+ being performed.
+
+ The following drawing shows an iSCSI client, an iSCSI server, and a
+ storage management server. To simplify the drawing, the second IP
+ network is not shown but is assumed to exist. The storage management
+ server would use its own protocol (smsp) to provide capabilities to
+ iSCSI clients and servers; these clients and servers can both use SLP
+ to discover the storage management server.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 9]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ +---------------------------+
+ | iSCSI Client |
+ | |
+ | +-----------+ |
+ | | iSCSI | |
+ | | initiator | |
+ | +-----------+ |
+ | |
+ +---------------+------+----+ +------------+
+ | iSCSI Driver | smsp | UA | | SLP DA |
+ +---------------+------+----+ | |
+ | TCP/UDP/IP | | (optional) |
+ +---------------+------+----+ +------------+
+ | |
+ | IP Network |
+ ------------------------------------------
+ | |
+ | |
+ +---------------+-----------+ +---------------------+
+ | TCP/UDP/IP | | TCP/UDP/IP |
+ +---------------+------+----+ +---------------------+
+ | iSCSI Driver | smsp | UA | | SA | smsp |
+ +---------------+------+----+ +---------------------+
+ | | | |
+ | +--------+ +--------+ | | storage mgmt server |
+ | | iSCSI | | iSCSI | | | |
+ | | target | | target | | +---------------------+
+ | | 1 | | 2 | |
+ | +--------+ +--------+ |
+ | |
+ | iSCSI Server |
+ +---------------------------+
+
+ Note the difference between the storage management server model and
+ the previously defined target discovery model. When target discovery
+ was used, the iSCSI Server implemented an SA, to be discovered by the
+ initiator's UA. In the storage management server model, the iSCSI
+ clients and servers both implement UAs, and the management server
+ implements the SA.
+
+ A storage management server's URL contains the domain name or IP
+ address and TCP or UDP port number. No other information is
+ required.
+
+ The storage management server constructs a service advertisement of
+ the type "service:iscsi:sms" for each of the addresses at which it
+ appears. The advertisement contains the URL and a lifetime, along
+ with other attributes that are defined in the service template.
+
+
+
+Bakke & Hufferd Standards Track [Page 10]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ The remainder of the discovery procedure is identical to that used to
+ discover iSCSI targets, except that both initiators and targets would
+ normally be "clients" of the storage management service.
+
+ Targets that support a storage management service implement a UA in
+ addition to the SA. A target may alternatively just implement the UA
+ and allow the storage management service to advertise its targets
+ appropriately by providing an SA and registering the appropriate
+ service:iscsi:target registrations on the target's behalf: The target
+ device would not have to advertise its own targets. This has no
+ impact on the initiator.
+
+ This allows the initiators' discovery of targets to be completely
+ interoperable regardless of which storage management service is used,
+ or whether one is used at all, or whether the target registrations
+ are provided directly by the target or by the management service.
+
+4.3. Internationalization Considerations
+
+ SLP allows internationalized strings to be registered and retrieved.
+ Attributes in the template that are not marked with an 'L' (literal)
+ will be registered in a localized manner. An "en" (English)
+ localization MUST be registered, and others MAY be registered.
+
+ Attributes that include non-ASCII characters will be encoded by using
+ UTF-8, as discussed in [RFC3722] and [RFC3491].
+
+5. iSCSI SLP Templates
+
+ Three templates are provided: an iSCSI target template, a management
+ service template, and an abstract template to encapsulate the two.
+
+5.1. The iSCSI Abstract Service Type Template
+
+ This template defines the abstract service "service:iscsi". It is
+ used as a top-level service to encapsulate all other iSCSI-related
+ services.
+
+ Name of submitter: Mark Bakke
+ Language of service template: en
+ Security Considerations: See section 6.
+
+ Template Text:
+ -------------------------template begins here-----------------------
+ template-type=iscsi
+ template-version=1.0
+
+ template-description=
+
+
+
+Bakke & Hufferd Standards Track [Page 11]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ This is an abstract service type. The purpose of the iscsi
+ service type is to encompass all of the services used to support
+ the iSCSI protocol.
+
+ template-url-syntax=
+ url-path= ; Depends on the concrete service type.
+
+ --------------------------template ends here------------------------
+
+5.2. The iSCSI Target Concrete Service Type Template
+
+ This template defines the service "service:iscsi:target". An entity
+ containing iSCSI targets that wishes them discovered via SLP would
+ register each of them, with each of their addresses, as this service
+ type.
+
+ Initiators (and perhaps management services) wishing to discover
+ targets in this way will generally use one of the following queries:
+
+ 1. Find a specific target, given its iSCSI Target Name:
+
+ Service: service:iscsi:target
+ Scope: initiator-scope-list
+ Query: (iscsi-name=iqn.2001-04.com.example:sn.456)
+
+ 2. Find all of the iSCSI Target Names that may allow access to a
+ given initiator:
+
+ Service: service:iscsi:target
+ Scope: initiator-scope-list
+ Query: (auth-name=iqn.1998-03.com.example:hostid.045A7B)
+
+ 3. Find all of the iSCSI Target Names that may allow access to
+ any initiator:
+
+ Service: service:iscsi:target
+ Scope: initiator-scope-list
+ Query: (auth-name=any)
+
+ 4. Find all of the iSCSI Target Names that may allow access to
+ this initiator, or that will allow access to any initiator:
+
+ Service: service:iscsi:target
+ Scope: initiator-scope-list
+ Query: &(auth-name=iqn.1998-03.com.example:hostid.045A7B)
+ (auth-name=any)
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 12]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ 5. Find all of the iSCSI Target Names that may allow access to
+ a given CHAP user name:
+
+ Service: service:iscsi:target
+ Scope: initiator-scope-list
+ Query: (auth-cred=chap/my-user-name)
+
+ 6. Find all of the iSCSI Target Names that may allow access to a
+ given initiator that supports two IP addresses, a CHAP credential
+ and SRP credential, and an initiator name:
+
+ Service: service:iscsi:target
+ Scope: initiator-scope-list
+ Query: &(|(auth-name=iqn.com.example:host47)(auth-name=any)
+ |(auth-addr=192.0.2.3)(auth-addr=192.0.2.131)(auth-addr=any)
+ |(auth-cred=chap/foo)(auth-cred=srp/my-user-name)
+ (auth-cred=any))
+
+ 7. Find the iSCSI Target Names from which the given initiator is
+ allowed to boot:
+
+ Service: service:iscsi:target
+ Scope: initiator-scope-list
+ Query: (boot-list=iqn.1998-03.com.example:hostid.045A7B)
+
+ 8. In addition, a management service may wish to discover all
+ targets:
+
+ Service: service:iscsi:target
+ Scope: management-server-scope-list
+ Query: <empty-string>
+
+ More details on booting from an iSCSI target are defined in [BOOT].
+
+ Name of submitter: Mark Bakke
+ Language of service template: en
+ Security Considerations: see section 6.
+
+ Template Text:
+ -------------------------template begins here-----------------------
+ template-type=iscsi:target
+ template-version=1.0
+
+ template-description=
+
+ This is a concrete service type. The iscsi:target service type is
+ used to register individual target addresses to be discovered
+ by others. UAs will generally search for these by including one of
+
+
+
+Bakke & Hufferd Standards Track [Page 13]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ the following:
+
+ - the iSCSI target name
+ - iSCSI initiator identifiers (iSCSI name, credential, IP address)
+ - the service URL
+
+ template-url-syntax=
+ url-path = hostport "/" iscsi-name [ "/" identity ]
+ hostport = host [ ":" port ]
+ host = hostname / hostnumber ; DNS name or IP address
+ hostname = *( domainlabel "." ) toplabel
+ alphanum = ALPHA / DIGIT
+ domainlabel = alphanum / alphanum *[alphanum / "-"] alphanum
+ toplabel = ALPHA / ALPHA *[ alphanum / "-" ] alphanum
+ hostnumber = ipv4-number / ipv6-addr ; IPv4 or IPv6 address
+ ipv4-number = 1*3DIGIT 3("." 1*3DIGIT)
+ ipv6-addr = "[" ipv6-number "]"
+ ipv6-number = 6( h16 ":" ) ls32
+ / "::" 5( h16 ":" ) ls32
+ / [ h16 ] "::" 4( h16 ":" ) ls32
+ / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
+ / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
+ / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
+ / [ *4( h16 ":" ) h16 ] "::" ls32
+ / [ *5( h16 ":" ) h16 ] "::" h16
+ / [ *6( h16 ":" ) h16 ] "::"
+ ls32 = ( h16 ":" h16 ) / ipv4-number
+ ; least-significant 32 bits of ipv6 address
+ h16 = 1*4HEXDIG
+ port = 1*DIGIT
+ iscsi-name = iscsi-char ; iSCSI target name
+ identity = iscsi-char ; optional identity string
+ iscsi-char = ALPHA / DIGIT / escaped / ":" / "-" / "."
+ ; Intended to allow UTF-8 encoded strings
+ escaped = 1*("\" HEXDIG HEXDIG)
+ ;
+ ; The iscsi-name part of the URL is required and must be the iSCSI
+ ; name of the target being registered.
+ ; A device representing multiple targets must individually
+ ; register each target/address combination with SLP.
+ ; The identity part of the URL is optional, and is used to
+ ; indicate an identity that is allowed to access this target.
+ ;
+ ; Example (split into two lines for clarity):
+ ; service:iscsi:target://192.0.2.3:3260/
+ ; iqn.2001-04.com.example:sn.45678
+ ;
+ ; IPv6 addresses are also supported; they use the notation
+
+
+
+Bakke & Hufferd Standards Track [Page 14]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ ; specified above and in [RFC3513], section 2.2
+
+ iscsi-name = string
+ # The iSCSI Name of this target.
+ # This must match the iscsi-name in the url-path.
+
+ portal-group = integer
+ # The iSCSI portal group tag for this address. Addresses sharing
+ # the same iscsi-name and portal-group tag can be used within the
+ # same iSCSI session. Portal groups are described in [RFC3720].
+
+ transports = string M L
+ tcp
+ # This is a list of transport protocols that the registered
+ # entity supports. iSCSI is currently supported over TCP,
+ # but it is anticipated that it could be supported over other
+ # transports, such as SCTP, in the future.
+ tcp
+
+ mgmt-entity = string O
+ # The fully qualified domain name, or IP address in dotted-decimal
+ # notation, of the management interface of the entity containing
+ # this target.
+ #
+
+ alias = string O
+ # The alias string contains a descriptive name of the target.
+
+ auth-name = string M X
+ # A list of iSCSI Initiator Names that can access this target.
+ # Normal iSCSI names will be 80 characters or less; max length
+ # is 255.
+ # Normally, only one or a few values will be in the list.
+ # Using the equivalence search on this will evaluate to "true"
+ # if any one of the items in this list matches the query.
+ # If this list contains the default name "any", any initiator
+ # is allowed to access this target, provided it matches
+ # the other auth-xxx attributes.
+ #
+ # This attribute contains security policy information. If this
+ # attribute is distributed via an Attribute Reply message,
+ # IPsec MUST be implemented.
+
+ auth-addr = string M X
+ # A list of initiator IP addresses (or host names) which will
+ # be allowed access to this target. If this list contains the
+ # default name "any", any IP address is allowed access to this
+ # target, provided it matches the other auth-xxx attributes.
+
+
+
+Bakke & Hufferd Standards Track [Page 15]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ #
+ # This attribute contains security policy information. If this
+ # attribute is distributed via an Attribute Reply message,
+ # IPsec MUST be implemented.
+
+ auth-cred = string M X
+ # A list of credentials which will be allowed access to the target
+ # (provided they can provide the correct password or other
+ # authenticator). Entries in this list are of the form
+ # "method/identifier", where the currently defined methods are
+ # "chap" and "srp", both of which take usernames as their
+ # identifiers.
+ #
+ # This attribute contains security policy information. If this
+ # attribute is distributed via an Attribute Reply message,
+ # IPsec MUST be implemented.
+
+ boot-list = string M O
+ # A list of iSCSI Initiator Names that can boot from this target.
+ # This list works precisely like the auth-name attribute. A name
+ # appearing in this list must either appear in the access-list,
+ # or the access-list must contain the initiator name "iscsi".
+ # Otherwise, an initiator will be unable to find its boot
+ # target. If boot-list contains the name "iscsi", any host can boot
+ # from it, but I am not sure if this is useful to anyone. If this
+ # attribute is not registered, this target is not "bootable".
+ #
+ # Note that the LUN the host boots from is not specified here; a
+ # host will generally attempt to boot from LUN 0.
+ #
+ # It is quite possible that other attributes will need to be defined
+ # here for booting as well.
+ #
+ # This attribute contains security policy information. If this
+ # attribute is distributed via an Attribute Reply message,
+ # IPsec MUST be implemented.
+
+ --------------------------template ends here------------------------
+
+5.3. iSCSI Storage Management Service Templates
+
+ This template defines the service "service:iscsi:sms". An entity
+ supporting one or more iSCSI management service protocols may
+ register itself with SLP as this service type. iSCSI clients and
+ servers wishing to discover storage management services using SLP
+ will usually search for them by the protocol(s) they support:
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 16]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ Service: service:iscsi:sms
+ Scope: initiator-scope-list
+ Query: (protocols=isns)
+
+ Name of submitter: Mark Bakke
+ Language of service template: en
+ Security Considerations: see section 6.
+
+ Template Text:
+ -------------------------template begins here-----------------------
+ template-type=iscsi:sms
+ template-version=1.0
+
+ template-description=
+ This is a concrete service type. The iscsi:sms service type
+ provides the capability for entities supporting iSCSI to discover
+ appropriate management services.
+
+ template-url-syntax=
+ url-path = ; The URL of the management service [RFC2608].
+
+ protocols = string M
+ # The list of protocols supported by this name service. This
+ # list may be expanded in the future. There is no default.
+ #
+ # "isns" - This management service supports the use of the iSNS
+ # protocol for access management, health monitoring, and
+ # discovery management services. This protocol is defined
+ # in [ISNS].
+ isns
+
+ transports = string M L
+ tcp
+ # This is a list of transport protocols that the registered
+ # entity supports.
+ tcp, udp
+
+ server-priority = integer
+ # The priority a client should give this server, when choosing
+ # between multiple servers with the same protocol type.
+ # When multiple servers are discovered for a given protocol type,
+ # this parameter indicates their relative precedence. Server
+ # precedence is protocol-specific; for some protocols, the primary
+ # server may have the highest server-priority value, while for
+
+
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 17]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ # others it may have the lowest. For example, with iSNS, the primary
+ # server has the lowest value (value 0).
+
+ --------------------------template ends here------------------------
+
+6. Security Considerations
+
+ The SLPv2 security model as specified in [RFC2608] does not provide
+ confidentiality but does provide an authentication mechanism for UAs
+ to ensure that service advertisements only come from trusted SAs,
+ with the exception that it does not provide a mechanism to
+ authenticate "zero-result responses". See [RFC3723] for a discussion
+ of the SLPv2 [RFC2608] security model.
+
+ Once a target or management server is discovered, authentication and
+ authorization are handled by the iSCSI protocol, or by the management
+ server's protocol. It is the responsibility of the providers of
+ these services to ensure that an inappropriately advertised or
+ discovered service does not compromise their security.
+
+ When no security is used for SLPv2, there is a risk of distribution
+ of false discovery information. The primary countermeasure for this
+ risk is authentication. When this risk is a significant concern,
+ IPsec SAs and iSCSI in-band authentication SHOULD be used for iSCSI
+ traffic subject to this risk to ensure that iSCSI traffic only flows
+ between endpoints that have participated in IKE authentication and
+ iSCSI in-band authentication. For example, if an attacker
+ distributes discovery information falsely claiming that it is an
+ iSCSI target, it will lack the secret information necessary to
+ complete IKE authentication or iSCSI in-band authentication
+ successfully and therefore will be prevented from falsely sending or
+ receiving iSCSI traffic.
+
+ A risk remains of a denial of service attack based on repeated use of
+ false discovery information that will cause initiation of IKE
+ negotiation. The countermeasures for this are administrative
+ configuration of each iSCSI Target to limit the peers it is willing
+ to communicate with (i.e., by IP address range and/or DNS domain),
+ and maintenance of a negative authentication cache to avoid
+ repeatedly contacting an iSCSI Target that fails to authenticate.
+ These three measures (i.e., IP address range limits, DNS domain
+ limits, negative authentication cache) MUST be implemented.
+
+ The auth-name, auth-addr, auth-cred, and boot-list attributes
+ comprise security policy information. When these are distributed,
+ IPsec MUST be implemented.
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 18]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+6.1. Security Implementation
+
+ Security for SLPv2 in an IP storage environment is specified in
+ [RFC3723]. IPsec is mandatory-to-implement for IPS clients and
+ servers. Thus, all IP storage clients, including those invoking SLP,
+ can be assumed to support IPsec. SLP servers, however, cannot be
+ assumed to implement IPsec, since there is no such requirement in
+ standard SLP. In particular, SLP Directory Agents (DA) may be
+ running on machines other than those running the IPS protocols.
+
+ IPsec SHOULD be implemented for SLPv2 as specified in [RFC3723]; this
+ includes ESP with a non-null transform to provide both authentication
+ and confidentiality.
+
+ When SLPv2 can be used to distribute auth-name, auth-addr, auth-cred,
+ and boot-list information (see section 5.2 above), IPsec MUST be
+ implemented, as these items are considered sensitive security policy
+ information. If IPsec is not implemented, auth-name, auth-addr,
+ auth-cred, and boot-list information MUST NOT be distributed via
+ SLPv2 and MUST NOT be used if discovered via SLPv2.
+
+ Because the IP storage services have their own authentication
+ capabilities when located, SLPv2 authentication is OPTIONAL to
+ implement and use (as discussed in more detail in [RFC3723]).
+
+7. IANA Considerations
+
+ This document describes three SLP Templates. They have been reviewed
+ and approved by the IESG and registered in the IANA's "SVRLOC
+ Templates" registry. This process is described in the IANA
+ Considerations section of [RFC2609].
+
+8. Summary
+
+ This document describes how SLP can be used by iSCSI initiators to
+ find iSCSI targets and storage management servers. Service type
+ templates for iSCSI targets and storage management servers are
+ presented.
+
+9. Normative References
+
+ [RFC2608] Guttman, E., Perkins, C., Veizades, J., and M. Day,
+ "Service Location Protocol, Version 2", RFC 2608, June
+ 1999.
+
+ [RFC2609] Guttman, E., Perkins, C., and J. Kempf, "Service
+ Templates and Service: Schemes", RFC 2609, June 1999.
+
+
+
+
+Bakke & Hufferd Standards Track [Page 19]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3491] Hoffman, P. and M. Blanchet, "Nameprep: A Stringprep
+ Profile for Internationalized Domain Names (IDN)", RFC
+ 3491, March 2003.
+
+ [RFC3513] Hinden, R. and S. Deering, "Internet Protocol Version 6
+ (IPv6) Addressing Architecture", RFC 3513, April 2003.
+
+ [RFC3720] Satran, J., Meth, K., Sapuntzakis, C., Chadalapaka, M.,
+ and E. Zeidner, "Internet Small Computer Systems
+ Interface (iSCSI)", RFC 3720, April 2004.
+
+ [RFC3722] Bakke, M., "String Profile for Internet Small Computer
+ Systems Interface (iSCSI) Names", RFC 3722, April 2004.
+
+ [RFC3723] Aboba, B., Tseng, J., Walker, J., Rangan, V., and F.
+ Travostino, "Securing Block Storage Protocols over IP",
+ RFC 3723, April 2004.
+
+10. Informative References
+
+ [RFC2614] Kempf, J. and E. Guttman, "An API for Service Location",
+ RFC 2614, June 1999.
+
+ [SAM2] ANSI T10. "SCSI Architectural Model 2", March 2000.
+
+ [RFC3721] Bakke, M., Hafner, J., Hufferd, J., Voruganti, K., and M.
+ Krueger, "Internet Small Computer Systems Interface
+ (iSCSI) Naming and Discovery", RFC 3721, April 2004.
+
+ [ISNS] Tseng, J., Gibbons, K., Travostino, F., Du Laney, C. and
+ J. Souza, "Internet Storage Name Service", Work in
+ Progress, February 2004.
+
+ [BOOT] Sarkar, P., Missimer, D. and C. Sapuntzakis, "A Standard
+ for Bootstrapping Clients using the iSCSI Protocol", Work
+ in Progress, March 2004.
+
+ [RFC3105] Kempf, J. and G. Montenegro, "Finding an RSIP Server with
+ SLP", RFC 3105, October 2001.
+
+
+
+
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 20]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+11. Acknowledgements
+
+ This document was produced by the iSCSI Naming and Discovery team,
+ including Joe Czap, Jim Hafner, John Hufferd, and Kaladhar Voruganti
+ (IBM), Howard Hall (Pirus), Jack Harwood (EMC), Yaron Klein (Sanrad),
+ Marjorie Krueger (HP), Lawrence Lamers (San Valley), Todd Sperry
+ (Adaptec), and Joshua Tseng (Nishan). Thanks also to Julian Satran
+ (IBM) for suggesting the use of SLP for iSCSI discovery, and to Matt
+ Peterson (Caldera) and James Kempf (Sun) for reviewing the document
+ from an SLP perspective.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 21]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+Authors' Addresses
+
+ Mark Bakke
+ Cisco Systems, Inc.
+ 7900 International Drive, Suite 400
+ Bloomington, MN
+ USA 55425
+
+ EMail: mbakke@cisco.com
+
+
+ Kaladhar Voruganti
+ IBM Almaden Research Center
+ 650 Harry Road
+ San Jose, CA 95120
+
+ EMail: kaladhar@us.ibm.com
+
+
+ John L. Hufferd
+ IBM Storage Systems Group
+ 5600 Cottle Road
+ San Jose, CA 95193
+
+ Phone: +1 408 997-6136
+ EMail: jlhufferd@comcast.net
+
+
+ Marjorie Krueger
+ Hewlett-Packard Corporation
+ 8000 Foothills Blvd
+ Roseville, CA 95747-5668, USA
+
+ Phone: +1 916 785-2656
+ EMail: marjorie_krueger@hp.com
+
+
+ Todd Sperry
+ Adaptec, Inc.
+ 691 South Milpitas Boulevard
+ Milpitas, Ca. 95035
+
+ Phone: +1 408 957-4980
+ EMail: todd_sperry@adaptec.com
+
+
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 22]
+
+RFC 4018 iSCSI and SLPv2 April 2005
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2005).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at ietf-
+ ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+Bakke & Hufferd Standards Track [Page 23]
+
diff --git a/utils/open-isns/doc/rfc4171.txt b/utils/open-isns/doc/rfc4171.txt
new file mode 100644
index 0000000..c02487c
--- /dev/null
+++ b/utils/open-isns/doc/rfc4171.txt
@@ -0,0 +1,6891 @@
+
+
+
+
+
+
+Network Working Group J. Tseng
+Request for Comments: 4171 Riverbed Technology
+Category: Standards Track K. Gibbons
+ McDATA Corporation
+ F. Travostino
+ Nortel
+ C. Du Laney
+ Rincon Research Corporation
+ J. Souza
+ Microsoft
+ September 2005
+
+
+ Internet Storage Name Service (iSNS)
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2005).
+
+Abstract
+
+ This document specifies the Internet Storage Name Service (iSNS)
+ protocol, used for interaction between iSNS servers and iSNS clients,
+ which facilitates automated discovery, management, and configuration
+ of iSCSI and Fibre Channel devices (using iFCP gateways) on a TCP/IP
+ network. iSNS provides intelligent storage discovery and management
+ services comparable to those found in Fibre Channel networks,
+ allowing a commodity IP network to function in a capacity similar to
+ that of a storage area network. iSNS facilitates a seamless
+ integration of IP and Fibre Channel networks due to its ability to
+ emulate Fibre Channel fabric services and to manage both iSCSI and
+ Fibre Channel devices. iSNS thereby provides value in any storage
+ network comprised of iSCSI devices, Fibre Channel devices (using iFCP
+ gateways), or any combination thereof.
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 1]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+Table of Contents
+
+ 1. Introduction................................................... 6
+ 1.1. Conventions Used in This Document........................ 6
+ 1.2. Purpose of This Document................................. 6
+ 2. iSNS Overview.................................................. 6
+ 2.1. iSNS Architectural Components ........................... 7
+ 2.1.1. iSNS Protocol (iSNSP) ........................... 7
+ 2.1.2. iSNS Client...................................... 7
+ 2.1.3. iSNS Server...................................... 7
+ 2.1.4. iSNS Database ................................... 7
+ 2.1.5. iSCSI............................................ 7
+ 2.1.6. iFCP............................................. 7
+ 2.2. iSNS Functional Overview................................. 8
+ 2.2.1. Name Registration Service........................ 8
+ 2.2.2. Discovery Domain and Login Control Service....... 8
+ 2.2.3. State Change Notification Service............... 10
+ 2.2.4. Open Mapping between
+ Fibre Channel and iSCSI Devices................. 11
+ 2.3. iSNS Usage Model........................................ 11
+ 2.3.1. iSCSI Initiator................................. 12
+ 2.3.2. iSCSI Target.................................... 12
+ 2.3.3. iSCSI-FC Gateway................................ 12
+ 2.3.4. iFCP Gateway.................................... 12
+ 2.3.5. Management Station.............................. 12
+ 2.4. Administratively Controlled iSNS Settings............... 13
+ 2.5. iSNS Server Discovery .................................. 14
+ 2.5.1. Service Location Protocol (SLP)................. 14
+ 2.5.2. Dynamic Host Configuration Protocol (DHCP)...... 14
+ 2.5.3. iSNS Heartbeat Message.......................... 14
+ 2.6. iSNS and Network Address Translation (NAT).............. 14
+ 2.7. Transfer of iSNS Database Records between iSNS Servers.. 15
+ 2.8. Backup iSNS Servers..................................... 17
+ 2.9. Transport Protocols..................................... 19
+ 2.9.1. Use of TCP for iSNS Communication............... 19
+ 2.9.2. Use of UDP for iSNS Communication............... 20
+ 2.9.3. iSNS Multicast and Broadcast Messages........... 20
+ 2.10. Simple Network Management Protocol (SNMP) Requirements.. 21
+ 3. iSNS Object Model............................................. 21
+ 3.1. Network Entity Object .................................. 22
+ 3.2. Portal Object .......................................... 22
+ 3.3. Storage Node Object..................................... 22
+ 3.4. Portal Group Object..................................... 23
+ 3.5. FC Device Object........................................ 24
+ 3.6. Discovery Domain Object................................. 24
+ 3.7. Discovery Domain Set Object............................. 24
+ 3.8. iSNS Database Model..................................... 24
+ 4. iSNS Implementation Requirements.............................. 25
+
+
+
+Tseng, et al. Standards Track [Page 2]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ 4.1. iSCSI Requirements...................................... 25
+ 4.1.1. Required Attributes for Support of iSCSI........ 26
+ 4.1.2. Examples: iSCSI Object Model Diagrams........... 28
+ 4.1.3. Required Commands and
+ Response Messages for Support of iSCSI.......... 30
+ 4.2. iFCP Requirements....................................... 31
+ 4.2.1. Required Attributes for Support of iFCP......... 31
+ 4.2.2. Example: iFCP Object Model Diagram.............. 32
+ 4.2.3. Required Commands and
+ Response Messages for Support of iFCP........... 34
+ 5. iSNSP Message Format.......................................... 35
+ 5.1. iSNSP PDU Header........................................ 35
+ 5.1.1. iSNSP Version................................... 36
+ 5.1.2. iSNSP Function ID............................... 36
+ 5.1.3. iSNSP PDU Length................................ 36
+ 5.1.4. iSNSP Flags..................................... 36
+ 5.1.5. iSNSP Transaction ID............................ 36
+ 5.1.6. iSNSP Sequence ID............................... 37
+ 5.2. iSNSP Message Segmentation and Reassembly............... 37
+ 5.3. iSNSP PDU Payload....................................... 37
+ 5.3.1. Attribute Value 4-Byte Alignment................ 38
+ 5.4. iSNSP Response Status Codes............................. 39
+ 5.5. Authentication for iSNS Multicast and Broadcast Messages 39
+ 5.6. Registration and Query Messages......................... 41
+ 5.6.1. Source Attribute................................ 42
+ 5.6.2. Message Key Attributes.......................... 42
+ 5.6.3. Delimiter Attribute............................. 42
+ 5.6.4. Operating Attributes............................ 43
+ 5.6.5. Registration and Query Request Message Types ... 44
+ 5.7. Response Messages....................................... 66
+ 5.7.1. Status Code..................................... 66
+ 5.7.2. Message Key Attributes in Response.............. 66
+ 5.7.3. Delimiter Attribute in Response................. 67
+ 5.7.4. Operating Attributes in Response................ 67
+ 5.7.5. Registration and Query Response Message Type.... 67
+ 5.8. Vendor-Specific Messages................................ 72
+ 6. iSNS Attributes............................................... 73
+ 6.1. iSNS Attribute Summary.................................. 73
+ 6.2. Entity Identifier-Keyed Attributes...................... 76
+ 6.2.1. Entity Identifier (EID)......................... 76
+ 6.2.2. Entity Protocol................................. 76
+ 6.2.3. Management IP Address .......................... 77
+ 6.2.4. Entity Registration Timestamp .................. 77
+ 6.2.5. Protocol Version Range.......................... 77
+ 6.2.6. Registration Period............................. 78
+ 6.2.7. Entity Index.................................... 78
+ 6.2.8. Entity Next Index............................... 79
+ 6.2.9. Entity ISAKMP Phase-1 Proposals................. 79
+
+
+
+Tseng, et al. Standards Track [Page 3]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ 6.2.10. Entity Certificate.............................. 79
+ 6.3. Portal-Keyed Attributes................................. 80
+ 6.3.1. Portal IP Address............................... 80
+ 6.3.2. Portal TCP/UDP Port............................. 80
+ 6.3.3. Portal Symbolic Name............................ 80
+ 6.3.4. Entity Status Inquiry Interval.................. 81
+ 6.3.5. ESI Port........................................ 82
+ 6.3.6. Portal Index.................................... 82
+ 6.3.7. SCN Port........................................ 82
+ 6.3.8. Portal Next Index............................... 83
+ 6.3.9. Portal Security Bitmap.......................... 83
+ 6.3.10. Portal ISAKMP Phase-1 Proposals................. 84
+ 6.3.11. Portal ISAKMP Phase-2 Proposals................. 84
+ 6.3.12. Portal Certificate.............................. 84
+ 6.4. iSCSI Node-Keyed Attributes............................. 84
+ 6.4.1. iSCSI Name...................................... 85
+ 6.4.2. iSCSI Node Type................................. 85
+ 6.4.3. iSCSI Node Alias................................ 86
+ 6.4.4. iSCSI Node SCN Bitmap .......................... 86
+ 6.4.5. iSCSI Node Index................................ 87
+ 6.4.6. WWNN Token...................................... 87
+ 6.4.7. iSCSI Node Next Index .......................... 89
+ 6.4.8. iSCSI AuthMethod................................ 89
+ 6.5. Portal Group (PG) Object-Keyed Attributes............... 89
+ 6.5.1. Portal Group iSCSI Name......................... 90
+ 6.5.2. PG Portal IP Addr............................... 90
+ 6.5.3. PG Portal TCP/UDP Port.......................... 90
+ 6.5.4. Portal Group Tag (PGT).......................... 90
+ 6.5.5. Portal Group Index.............................. 90
+ 6.5.6. Portal Group Next Index......................... 91
+ 6.6. FC Port Name-Keyed Attributes .......................... 91
+ 6.6.1. FC Port Name (WWPN)............................. 91
+ 6.6.2. Port ID (FC_ID)................................. 91
+ 6.6.3. FC Port Type.................................... 92
+ 6.6.4. Symbolic Port Name.............................. 92
+ 6.6.5. Fabric Port Name (FWWN)......................... 92
+ 6.6.6. Hard Address.................................... 92
+ 6.6.7. Port IP Address................................. 92
+ 6.6.8. Class of Service (COS).......................... 93
+ 6.6.9. FC-4 Types...................................... 93
+ 6.6.10. FC-4 Descriptor................................. 93
+ 6.6.11. FC-4 Features .................................. 93
+ 6.6.12. iFCP SCN Bitmap................................. 93
+ 6.6.13. Port Role....................................... 94
+ 6.6.14. Permanent Port Name (PPN)....................... 95
+ 6.7. Node-Keyed Attributes .................................. 95
+ 6.7.1. FC Node Name (WWNN)............................. 95
+ 6.7.2. Symbolic Node Name.............................. 95
+
+
+
+Tseng, et al. Standards Track [Page 4]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ 6.7.3. Node IP Address................................. 95
+ 6.7.4. Node IPA........................................ 96
+ 6.7.5. Proxy iSCSI Name................................ 96
+ 6.8. Other Attributes........................................ 96
+ 6.8.1. FC-4 Type Code.................................. 96
+ 6.8.2. iFCP Switch Name................................ 96
+ 6.8.3. iFCP Transparent Mode Commands.................. 97
+ 6.9. iSNS Server-Specific Attributes......................... 97
+ 6.9.1. iSNS Server Vendor OUI.......................... 98
+ 6.10. Vendor-Specific Attributes.............................. 98
+ 6.10.1. Vendor-Specific Server Attributes............... 98
+ 6.10.2. Vendor-Specific Entity Attributes............... 98
+ 6.10.3. Vendor-Specific Portal Attributes............... 99
+ 6.10.4. Vendor-Specific iSCSI Node Attributes........... 99
+ 6.10.5. Vendor-Specific FC Port Name Attributes......... 99
+ 6.10.6. Vendor-Specific FC Node Name Attributes......... 99
+ 6.10.7. Vendor-Specific Discovery Domain Attributes..... 99
+ 6.10.8. Vendor-Specific Discovery Domain Set Attributes. 99
+ 6.10.9. Other Vendor-Specific Attributes................ 99
+ 6.11. Discovery Domain Registration Attributes............... 100
+ 6.11.1. DD Set ID Keyed Attributes..................... 100
+ 6.11.2. DD ID Keyed Attributes......................... 101
+ 7. Security Considerations...................................... 103
+ 7.1. iSNS Security Threat Analysis ......................... 103
+ 7.2. iSNS Security Implementation and Usage Requirements.... 104
+ 7.3. Discovering Security Requirements of Peer Devices...... 105
+ 7.4. Configuring Security Policies of iFCP/iSCSI Devices.... 106
+ 7.5. Resource Issues........................................ 107
+ 7.6. iSNS Interaction with IKE and IPSec.................... 107
+ 8. IANA Considerations.......................................... 107
+ 8.1. Registry of Block Storage Protocols.................... 107
+ 8.2. Registry of Standard iSNS Attributes .................. 108
+ 8.3. Block Structure Descriptor (BSD) Registry.............. 108
+ 9. Normative References......................................... 109
+ 10. Informative References....................................... 110
+ Appendix A: iSNS Examples........................................ 112
+ A.1. iSCSI Initialization Example........................... 112
+ A.1.1. Simple iSCSI Target Registration............... 112
+ A.1.2. Target Registration and DD Configuration....... 114
+ A.1.3. Initiator Registration and Target Discovery.... 117
+ Acknowledgements................................................. 121
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 5]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+1. Introduction
+
+1.1. Conventions Used in This Document
+
+ "iSNS" refers to the storage network model and associated services
+ covered in the text of this document.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC2119].
+
+ All frame formats are in big endian network byte order.
+
+ All unused fields and bitmaps, including those that are RESERVED,
+ SHOULD be set to zero when sending and ignored when receiving.
+
+1.2. Purpose of This Document
+
+ This is a standards track document containing normative text
+ specifying the iSNS Protocol, used by iSCSI and iFCP devices to
+ communicate with the iSNS server. This document focuses on the
+ interaction between iSNS servers and iSNS clients; interactions among
+ multiple authoritative primary iSNS servers are a potential topic for
+ future work.
+
+2. iSNS Overview
+
+ iSNS facilitates scalable configuration and management of iSCSI and
+ Fibre Channel (FCP) storage devices in an IP network by providing a
+ set of services comparable to that available in Fibre Channel
+ networks. iSNS thus allows a commodity IP network to function at a
+ level of intelligence comparable to a Fibre Channel fabric. iSNS
+ allows the administrator to go beyond a simple device-by-device
+ management model, where each storage device is manually and
+ individually configured with its own list of known initiators and
+ targets. Using the iSNS, each storage device subordinates its
+ discovery and management responsibilities to the iSNS server. The
+ iSNS server thereby serves as the consolidated configuration point
+ through which management stations can configure and manage the entire
+ storage network, including both iSCSI and Fibre Channel devices.
+
+ iSNS can be implemented to support iSCSI and/or iFCP protocols as
+ needed; an iSNS implementation MAY provide support for one or both of
+ these protocols as desired by the implementor. Implementation
+ requirements within each of these protocols are further discussed in
+ Section 5. Use of iSNS is OPTIONAL for iSCSI and REQUIRED for iFCP.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 6]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+2.1. iSNS Architectural Components
+
+2.1.1. iSNS Protocol (iSNSP)
+
+ The iSNS Protocol (iSNSP) is a flexible and lightweight protocol that
+ specifies how iSNS clients and servers communicate. It is suitable
+ for various platforms, including switches and targets as well as
+ server hosts.
+
+2.1.2. iSNS Client
+
+ iSNS clients initiate transactions with iSNS servers using the iSNSP.
+ iSNS clients are processes that are co-resident in the storage
+ device, and that can register device attribute information, download
+ information about other registered clients in a common Discovery
+ Domain (DD), and receive asynchronous notification of events that
+ occur in their DD(s). Management stations are a special type of iSNS
+ client that have access to all DDs stored in the iSNS.
+
+2.1.3. iSNS Server
+
+ iSNS servers respond to iSNS protocol queries and requests, and
+ initiate iSNS protocol State Change Notifications. Properly
+ authenticated information submitted by a registration request is
+ stored in an iSNS database.
+
+2.1.4. iSNS Database
+
+ The iSNS database is the information repository for the iSNS
+ server(s). It maintains information about iSNS client attributes. A
+ directory-enabled implementation of iSNS may store client attributes
+ in an LDAP directory infrastructure.
+
+2.1.5. iSCSI
+
+ iSCSI (Internet SCSI) is an encapsulation of SCSI for a new
+ generation of storage devices interconnected with TCP/IP [iSCSI].
+
+2.1.6. iFCP
+
+ iFCP (Internet FCP) is a gateway-to-gateway protocol designed to
+ interconnect existing Fibre Channel and SCSI devices using TCP/IP.
+ iFCP maps the existing FCP standard and associated Fibre Channel
+ services to TCP/IP [iFCP].
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 7]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+2.2. iSNS Functional Overview
+
+ There are four main functions of the iSNS:
+
+ 1) A Name Service Providing Storage Resource Discovery
+
+ 2) Discovery Domain (DD) and Login Control Service
+
+ 3) State Change Notification Service
+
+ 4) Open Mapping of Fibre Channel and iSCSI Devices
+
+2.2.1. Name Registration Service
+
+ The iSNS provides a registration function to allow all entities in a
+ storage network to register and query the iSNS database. Both
+ targets and initiators can register in the iSNS database, as well as
+ query for information about other initiators and targets. This
+ allows, for example, a client initiator to obtain information about
+ target devices from the iSNS server. This service is modeled on the
+ Fibre Channel Generic Services Name Server described in FC-GS-4, with
+ extensions, operating within the context of an IP network.
+
+ The naming registration service also provides the ability to obtain a
+ network-unique Domain ID for iFCP gateways when one is required.
+
+2.2.2. Discovery Domain and Login Control Service
+
+ The Discovery Domain (DD) Service facilitates the partitioning of
+ Storage Nodes into more manageable groupings for administrative and
+ login control purposes. It allows the administrator to limit the
+ login process of each host to the more appropriate subset of targets
+ registered in the iSNS. This is particularly important for reducing
+ the number of unnecessary logins (iSCSI logins or Fibre Channel Port
+ Logins), and for limiting the amount of time that the host spends
+ initializing login relationships as the size of the storage network
+ scales up. Storage Nodes must be in at least one common enabled DD
+ in order to obtain information about each other. Devices can be
+ members of multiple DDs simultaneously.
+
+ Login Control allows targets to delegate their access
+ control/authorization policies to the iSNS server. This is
+ consistent with the goal of centralizing management of those storage
+ devices using the iSNS server. The target node or device downloads
+ the list of authorized initiators from the iSNS. Each node or device
+ is uniquely identified by an iSCSI Name or FC Port Name. Only
+
+
+
+
+
+Tseng, et al. Standards Track [Page 8]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ initiators that match the required identification and authorization
+ provided by the iSNS will be allowed access by that target Node
+ during session establishment.
+
+ Placing Portals of a Network Entity into Discovery Domains allows
+ administrators to indicate the preferred IP Portal interface through
+ which storage traffic should access specific Storage Nodes of that
+ Network Entity. If no Portals of a Network Entity have been placed
+ into a DD, then queries scoped to that DD SHALL report all Portals of
+ that Network Entity. If one or more Portals of a Network Entity have
+ been placed into a DD, then queries scoped to that DD SHALL report
+ only those Portals that have been explicitly placed in the DD.
+
+ DDs can be managed offline through a separate management workstation
+ using the iSNSP or SNMP. If the target opts to use the Login Control
+ feature of the iSNS, the target delegates management of access
+ control policy (i.e., the list of initiators allowed to log in to
+ that target) to the management workstations that are managing the
+ configuration in the iSNS database.
+
+ If administratively authorized, a target can upload its own Login
+ Control list. This is accomplished using the DDReg message and
+ listing the iSCSI name of each initiator to be registered in the
+ target's DD.
+
+ An implementation MAY decide that newly registered devices that have
+ not explicitly been placed into a DD by the management station will
+ be placed into a "default DD" contained in a "default DDS" whose
+ initial DD Set Status value is "enabled". This makes them visible to
+ other devices in the default DD. Other implementations MAY decide
+ that they are registered with no DD, making them inaccessible to
+ source-scoped iSNSP messages.
+
+ The iSNS server uses the Source Attribute of each iSNSP message to
+ determine the originator of the request and to scope the operation to
+ a set of Discovery Domains. In addition, the Node Type (specified in
+ the iFCP or iSCSI Node Type bitmap field) may also be used to
+ determine authorization for the specified iSNS operation. For
+ example, only Control Nodes are authorized to create or delete
+ discovery domains.
+
+ Valid and active Discovery Domains (DDs) belong to at least one
+ active Discovery Domain Set (DDS). Discovery Domains that do not
+ belong to an activated DDS are not enabled. The iSNS server MUST
+ maintain the state of DD membership for all Storage Nodes, even for
+ those that have been deregistered. DD membership is persistent
+ regardless of whether a Storage Node is actively registered in the
+ iSNS database.
+
+
+
+Tseng, et al. Standards Track [Page 9]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+2.2.3. State Change Notification Service
+
+ The State Change Notification (SCN) service allows the iSNS Server to
+ issue notifications about network events that affect the operational
+ state of Storage Nodes. The iSNS client may register for
+ notifications on behalf of its Storage Nodes for notification of
+ events detected by the iSNS Server. SCNs notify iSNS clients of
+ explicit or implicit changes to the iSNS database; they do not
+ necessarily indicate the state of connectivity to peer storage
+ devices in the network. The response of a storage device to receipt
+ of an SCN is implementation-specific; the policy for responding to
+ SCNs is outside of the scope of this document.
+
+ There are two types of SCN registrations: regular registrations and
+ management registrations. Management registrations result in
+ management SCNs, whereas regular registrations result in regular
+ SCNs. The type of registration and SCN message is indicated in the
+ SCN bitmap (see Sections 6.4.4 and 6.6.12).
+
+ A regular SCN registration indicates that the Discovery Domain
+ Service SHALL be used to control the distribution of SCN messages.
+ Receipt of regular SCNs is limited to the discovery domains in which
+ the SCN-triggering event takes place. Regular SCNs do not contain
+ information about discovery domains.
+
+ A management SCN registration can only by requested by Control Nodes.
+ Management SCNs resulting from management registrations are not bound
+ by the Discovery Domain service. Authorization to request management
+ SCN registrations may be administratively controlled.
+
+ The iSNS server SHOULD be implemented with hardware and software
+ resources sufficient to support the expected number of iSNS clients.
+ However, if resources are unexpectedly exhausted, then the iSNS
+ server MAY refuse SCN service by returning an SCN Registration
+ Rejected (Status Code 17). The rejection might occur in situations
+ where the network size or current number of SCN registrations has
+ passed an implementation-specific threshold. A client not allowed to
+ register for SCNs may decide to monitor its sessions with other
+ storage devices directly.
+
+
+ The specific notification mechanism by which the iSNS server learns
+ of the events that trigger SCNs is implementation-specific, but can
+ include examples such as explicit notification messages from an iSNS
+ client to the iSNS server, or a hardware interrupt to a switch-hosted
+ iSNS server as a result of link failure.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 10]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+2.2.4. Open Mapping between Fibre Channel and iSCSI Devices
+
+ The iSNS database stores naming and discovery information about both
+ Fibre Channel and iSCSI devices. This allows the iSNS server to
+ store mappings of a Fibre Channel device to a proxy iSCSI device
+ "image" in the IP network. Similarly, mappings of an iSCSI device to
+ a "proxy WWN" can be stored under the WWNN Token field for that iSCSI
+ device.
+
+ Furthermore, through use of iSCSI-FC gateways, Fibre Channel-aware
+ management stations can interact with the iSNS server to retrieve
+ information about Fibre Channel devices, and use this information to
+ manage Fibre Channel and iSCSI devices. This allows management
+ functions such as Discovery Domains and State Change Notifications to
+ be applied seamlessly to both iSCSI and Fibre Channel devices,
+ facilitating integration of IP networks with Fibre Channel devices
+ and fabrics.
+
+ Note that Fibre Channel attributes are stored as iFCP attributes, and
+ that the ability to store this information in the iSNS server is
+ useful even if the iFCP protocol is not implemented. In particular,
+ tag 101 can be used to store a "Proxy iSCSI Name" for Fibre Channel
+ devices registered in the iSNS server. This field is used to
+ associate the FC device with an iSCSI registration entry that is used
+ for the Fibre Channel device to communicate with iSCSI devices in the
+ IP network. Conversely, tag 37 (see Section 6.1) contains a WWNN
+ Token field, which can be used to store an FC Node Name (WWNN) value
+ used by iSCSI-FC gateways to represent an iSCSI device in the Fibre
+ Channel domain.
+
+ By storing the mapping between Fibre Channel and iSCSI devices in the
+ iSNS server, this information becomes open to any authorized iSNS
+ client wishing to retrieve and use this information. In many cases,
+ this provides advantages over storing the information internally
+ within an iSCSI-FC gateway, where the mapping is inaccessible to
+ other devices except by proprietary mechanisms.
+
+2.3. iSNS Usage Model
+
+ The following is a high-level description of how each type of device
+ in a storage network can utilize iSNS. Each type of device interacts
+ with the iSNS server as an iSNS client and must register itself in
+ the iSNS database in order to access services provided by the iSNS.
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 11]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+2.3.1. iSCSI Initiator
+
+ An iSCSI initiator will query the iSNS server to discover the
+ presence and location of iSCSI target devices. It may also request
+ state change notifications (SCNs) so that it can be notified of new
+ targets that appear on the network after the initial bootup and
+ discovery. SCNs can also inform the iSCSI initiator of targets that
+ have been removed from or no longer available in the storage network,
+ so that incomplete storage sessions can be gracefully terminated and
+ resources for non-existent targets can be reallocated.
+
+2.3.2. iSCSI Target
+
+ An iSCSI target allows itself to be discovered by iSCSI initiators by
+ registering its presence in the iSNS server. It may also register
+ for SCNs in order to detect the addition or removal of initiators for
+ resource allocation purposes. The iSCSI target device may also
+ register for Entity Status Inquiry (ESI) messages, which allow the
+ iSNS to monitor the target device's availability in the storage
+ network.
+
+2.3.3. iSCSI-FC Gateway
+
+ An iSCSI-FC gateway bridges devices in a Fibre Channel network to an
+ iSCSI/IP network. It may use the iSNS server to store FC device
+ attributes discovered in the FC name server, as well as mappings of
+ FC device identifiers to iSCSI device identifiers. iSNS has the
+ capability to store all attributes of both iSCSI and Fibre Channel
+ devices; iSCSI devices are managed through direct interaction using
+ iSNS, while FC devices can be indirectly managed through iSNS
+ interactions with the iSCSI-FC gateway. This allows both iSCSI and
+ Fibre Channel devices to be managed in a seamless management
+ framework.
+
+2.3.4. iFCP Gateway
+
+ An iFCP gateway uses iSNS to emulate the services provided by a Fibre
+ Channel name server for FC devices in its gateway region. iSNS
+ provides basic discovery and zoning configuration information to be
+ enforced by the iFCP gateway. When queried, iSNS returns information
+ on the N_Port network address used to establish iFCP sessions between
+ FC devices supported by iFCP gateways.
+
+2.3.5. Management Station
+
+ A management station uses iSNS to monitor storage devices and to
+ enable or disable storage sessions by configuring discovery domains.
+ A management station usually interacts with the iSNS server as a
+
+
+
+Tseng, et al. Standards Track [Page 12]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Control Node endowed with access to all iSNS database records and
+ with special privileges to configure discovery domains. Through
+ manipulation of discovery domains, the management station controls
+ the scope of device discovery for iSNS clients querying the iSNS
+ server.
+
+2.4. Administratively Controlled iSNS Settings
+
+ Some important operational settings for the iSNS server are
+ configured using administrative means, such as a configuration file,
+ a console port, an SNMP, or another implementation-specific method.
+ These administratively-controlled settings cannot be configured using
+ the iSNS Protocol, and therefore the iSNS server implementation MUST
+ provide for such an administrative control interface.
+
+ The following is a list of parameters that are administratively
+ controlled for the iSNS server. In the absence of alternative
+ settings provided by the administrator, the following specified
+ default settings MUST be used.
+
+ Setting Default Setting
+ ------- ---------------
+ ESI Non-Response Threshold 3 (see 5.6.5.13)
+ Management SCNs (Control Nodes only) enabled (see 5.6.5.8)
+ Default DD/DDS disabled
+ DD/DDS Modification
+ - Control Node enabled
+ - iSCSI Target Node Type disabled
+ - iSCSI Initiator Node Type disabled
+ - iFCP Target Port Role disabled
+ - iFCP Initiator Port Role disabled
+ Authorized Control Nodes N/A
+
+ ESI Non-Response Threshold: determines the number of ESI messages
+ sent without receiving a response before the network
+ entity is deregistered from the iSNS database.
+
+ Management SCN for Control Node: determines whether a registered
+ Control Node is permitted to register to receive
+ Management SCNs.
+
+ Default DD/DDS: determines whether a newly registered device not
+ explicitly placed into a discovery domain (DD) and
+ discovery domain set (DDS) is placed into a default
+ DD/DDS.
+
+ DD/DDS Modification: determines whether the specified type of Node is
+ allowed to add, delete or update DDs and DDSs.
+
+
+
+Tseng, et al. Standards Track [Page 13]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Authorized Control Nodes: a list of Nodes identified by iSCSI Name or
+ FC Port Name WWPN that are authorized to register as
+ Control Nodes.
+
+2.5. iSNS Server Discovery
+
+2.5.1. Service Location Protocol (SLP)
+
+ The Service Location Protocol (SLP) provides a flexible and scalable
+ framework for providing hosts with access to information about the
+ existence, location, and configuration of networked services,
+ including the iSNS server. SLP can be used by iSNS clients to
+ discover the IP address or FQDN of the iSNS server. To implement
+ discovery through SLP, a Service Agent (SA) should be cohosted in the
+ iSNS server, and a User Agent (UA) should be in each iSNS client.
+ Each client multicasts a discovery message requesting the IP address
+ of the iSNS server(s). The SA responds to this request. Optionally,
+ the location of the iSNS server can be stored in the SLP Directory
+ Agent (DA).
+
+ Note that a complete description and specification of SLP can be
+ found in [RFC2608], and is beyond the scope of this document. A
+ service template for using SLP to locate iSNS servers can be found in
+ [iSCSI-SLP].
+
+2.5.2. Dynamic Host Configuration Protocol (DHCP)
+
+ The IP address of the iSNS server can be stored in a DHCP server to
+ be downloaded by iSNS clients using a DHCP option. The DHCP option
+ number to be used for distributing the iSNS server location is found
+ in [iSNSOption].
+
+2.5.3. iSNS Heartbeat Message
+
+ The iSNS heartbeat message is described in Section 5.6.5.14. It
+ allows iSNS clients within the broadcast or multicast domain of the
+ iSNS server to discover the location of the active iSNS server and
+ any backup servers.
+
+2.6. iSNS and Network Address Translation (NAT)
+
+ The existence of NAT will have an impact upon information retrieved
+ from the iSNS server. If the iSNS client exists in an addressing
+ domain different from that of the iSNS server, then IP address
+ information stored in the iSNS server may not be correct when
+ interpreted in the domain of the iSNS client.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 14]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ There are several possible approaches to allow operation of iSNS
+ within a NAT network. The first approach is to require use of the
+ canonical TCP port number by both targets and initiators when
+ addressing targets across a NAT boundary, and for the iSNS client not
+ to query for nominal IP addresses. Rather, the iSNS client queries
+ for the DNS Fully Qualified Domain Name stored in the Entity
+ Identifier field when seeking addressing information. Once
+ retrieved, the DNS name can be interpreted in each address domain and
+ mapped to the appropriate IP address by local DNS servers.
+
+ A second approach is to deploy a distributed network of iSNS servers.
+ Local iSNS servers are deployed inside and outside NAT boundaries,
+ with each local server storing relevant IP addresses for their
+ respective NAT domains. Updates among the network of decentralized,
+ local iSNS servers are handled using LDAP and appropriate NAT
+ translation rules implemented within the update mechanism in each
+ server.
+
+ Finally, note that it is possible for an iSNS server in the private
+ addressing domain behind a NAT boundary to exclusively support iSNS
+ clients that are operating in the global IP addressing domain. If
+ this is the case, the administrator only needs to ensure that the
+ appropriate mappings are configured on the NAT gateways to allow the
+ iSNS clients to initiate iSNSP sessions to the iSNS server. All
+ registered addresses contained in the iSNS server are thus public IP
+ addresses for use outside the NAT boundary. Care should be taken to
+ ensure that there are no iSNS clients querying the server from inside
+ the NAT boundary.
+
+2.7. Transfer of iSNS Database Records between iSNS Servers
+
+ Transfer of iSNS database records between iSNS servers has important
+ applications, including the following:
+
+ 1) An independent organization needs to transfer storage information
+ to a different organization. Each organization independently
+ maintains its own iSNS infrastructure. To facilitate discovery
+ of storage assets of the peer organization using IP, iSNS
+ database records can be transferred between authoritative iSNS
+ servers from each organization. This allows storage sessions to
+ be established directly between devices residing in each
+ organization's storage network infrastructure over a common IP
+ network.
+
+ 2) Multiple iSNS servers are desired for redundancy. Backup servers
+ need to maintain copies of the primary server's dynamically
+ changing database.
+
+
+
+
+Tseng, et al. Standards Track [Page 15]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ To support the above applications, information in an iSNS server can
+ be distributed to other iSNS servers either using the iSNS protocol,
+ or through out-of-band mechanisms using non-iSNS protocols. The
+ following examples illustrate possible methods for transferring data
+ records between iSNS servers. In the first example, a back-end LDAP
+ information base is used to support the iSNS server, and the data is
+ transferred using the LDAP protocol. Once the record transfer of the
+ remote device is completed, it becomes visible and accessible to
+ local devices using the local iSNS server. This allows local devices
+ to establish sessions with remote devices (provided that firewall
+ boundaries can be negotiated).
+
+ +-------------------------+ +-------------------------+
+ |+------+ iSNSP | | iSNSP +-----+ |
+ ||dev A |<----->+------+ | | +------+<----->|dev C| |
+ |+------+ | | | | | | +-----+ |
+ |+------+ iSNSP |local | | | |remote| iSNSP +-----+ |
+ ||dev B |<----->| iSNS | | | | iSNS |<----->|dev D| |
+ |+------+ |server| | | |server| +-----+ |
+ |........ +--+---+ | WAN | +---+--+ |
+ |.dev C'. | | Link | | |
+ |........ | ============= | |
+ | | | | | |
+ | +--+---+ | | +---+--+ |
+ | | local|<--- <--- <--- <-|remote| |
+ | | LDAP | | LDAP: | | LDAP | |
+ | +------+ Xfer "dev C"| +------+ |
+ +-------------------------+ +-------------------------+
+ Enterprise Enterprise
+ Network A Network B
+
+ In the above diagram, two business partners wish to share storage
+ "dev C". Using LDAP, the record for "dev C" can be transferred from
+ Network B to Network A. Once accessible to the local iSNS server in
+ Network A, local devices A and B can now discover and connect to "dev
+ C".
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 16]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ +-------------------------+ +-------------------------+
+ |+------+ iSNSP | | iSNSP +-----+ |
+ ||dev A |<----->+------+ | | +------+<----->|dev C| |
+ |+------+ | | | | | | +-----+ |
+ |+------+ iSNSP |local | | | |remote| iSNSP +-----+ |
+ ||dev B |<----->| iSNS | | | | iSNS |<----->|dev D| |
+ |+------+ |server| | | |server| +-----+ |
+ |........ +------+ | WAN | +---+--+ |
+ |.dev C'. ^ | Link | | |
+ |........ | ============= v |
+ | | | | |SNMP |
+ | | | | | |
+ | +--+----+ | | v |
+ | | SNMP |<--- <--- <--- <---- |
+ | | Mgmt | | SNMP: Xfer "dev C" |
+ | |Station| | | |
+ | +-------+ | | |
+ +-------------------------+ +-------------------------+
+ Enterprise Enterprise
+ Network A Network B
+
+ The above diagram illustrates a second example of how iSNS records
+ can be shared. This method uses an SNMP-based management station to
+ retrieve (GET) the desired record for "dev C" manually, and then to
+ store (SET) it on the local iSNS server directly. Once the record is
+ transferred to the local iSNS server in Network A, "dev C" becomes
+ visible and accessible (provided that firewall boundaries can be
+ negotiated) to other devices in Network A.
+
+ Other methods, including proprietary protocols, can be used to
+ transfer device records between iSNS servers. Further discussion and
+ explanation of these methodologies is beyond the scope of this
+ document.
+
+2.8. Backup iSNS Servers
+
+ This section offers a broad framework for implementation and
+ deployment of iSNS backup servers. Server failover and recovery are
+ topics of continuing research, and adequate resolution of issues such
+ as split brain and primary server selection is dependent on the
+ specific implementation requirements and deployment needs. The
+ failover mechanisms discussed in this document focus on the
+ interaction between iSNS clients and iSNS servers. Specifically,
+ what is covered in this document includes the following:
+
+ - iSNS client behavior and the iSNS protocol interaction between the
+ client and multiple iSNS servers, some of which are backup
+ servers.
+
+
+
+Tseng, et al. Standards Track [Page 17]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ - Required failover behaviors of the collection of iSNS servers that
+ includes active and backup servers.
+
+ However, note that this document does not specify the complete
+ functional failover requirements of each iSNS server. In particular,
+ it does not specify the complete set of protocol interactions among
+ the iSNS servers that are required to achieve stable failover
+ operation in an interoperable manner.
+
+ For the purposes of this discussion, the specified backup mechanisms
+ pertain to interaction among different logical iSNS servers. Note
+ that it is possible to create multiple physical iSNS servers to form
+ a single logical iSNS server cluster, and thus to distribute iSNS
+ transaction processing among multiple physical servers. However, a
+ more detailed discussion of the interactions between physical servers
+ within a logical iSNS server cluster is beyond the scope of this
+ document.
+
+ Multiple logical iSNS servers can be used to provide redundancy in
+ the event that the active iSNS server fails or is removed from the
+ network. The methods described in Section 2.7 above can be used to
+ transfer name server records to backup iSNS servers. Each backup
+ server maintains a redundant copy of the name server database found
+ in the primary iSNS server, and can respond to iSNS protocol messages
+ in the same way as the active server. Each backup server SHOULD
+ monitor the health and status of the active iSNS server, including
+ checking to make sure its own database is synchronized with the
+ active server's database. How each backup server accomplishes this
+ is implementation-dependent, and may (or may not) include using the
+ iSNS protocol. If the iSNS protocol is used, then the backup server
+ MAY register itself in the active server's iSNS database as a Control
+ Node, allowing it to receive state-change notifications.
+
+ Generally, the administrator or some automated election process is
+ responsible for initial and subsequent designation of the primary
+ server and each backup server.
+
+ A maximum of one logical backup iSNS server SHALL exist at any
+ individual IP address, in order to avoid conflicts from multiple
+ servers listening on the same canonical iSNS TCP or UDP port number.
+
+ The iSNS heartbeat can also be used to coordinate the designation and
+ selection of primary and backup iSNS servers.
+
+ Each backup server MUST note its relative precedence in the active
+ server's list of backup servers. If its precedence is not already
+ known, each backup server MAY learn it from the iSNS heartbeat
+ message, by noting the position of its IP address in the ordered list
+
+
+
+Tseng, et al. Standards Track [Page 18]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ of backup server IP addresses. For example, if it is the first
+ backup listed in the heartbeat message, then its backup precedence is
+ 1. If it is the third backup server listed, then its backup
+ precedence is 3.
+
+ If a backup server establishes that it has lost connectivity to the
+ active server and other backup servers of higher precedence, then it
+ SHOULD assume that it is the active server. The method of
+ determining whether connectivity has been lost is implementation-
+ specific. One possible approach is to assume that if the backup
+ server does not receive iSNS heartbeat messages for a period of time,
+ then connectivity to the active server has been lost. Alternatively,
+ the backup server may establish TCP connections to the active server
+ and other backup servers, with loss of connectivity determined
+ through non-response to periodic echo or polling messages (using
+ iSNSP, SNMP, or other protocols).
+
+ When a backup server becomes the active server, it SHALL assume all
+ active server responsibilities, including (if used) transmission of
+ the iSNS heartbeat message. If transmitting the iSNS heartbeat, the
+ backup server replaces the active Server IP Address and TCP/UDP Port
+ entries with its own IP address and TCP/UDP Port, and begins
+ incrementing the counter field from the last known value from the
+ previously-active iSNS server. However, it MUST NOT change the
+ original ordered list of backup server IP Address and TCP/UDP Port
+ entries. If the primary backup server or other higher-precedence
+ backup server returns, then the existing active server is responsible
+ for ensuring that the new active server's database is up-to-date
+ before demoting itself to its original status as backup.
+
+ Since the primary and backup iSNS servers maintain a coordinated
+ database, no re-registration by an iSNS Client is required when a
+ backup server takes the active server role. Likewise, no re-
+ registration by an iSNS Client is required when the previous primary
+ server returns to the active server role.
+
+2.9. Transport Protocols
+
+ The iSNS Protocol is transport-neutral. Query and registration
+ messages are transported over TCP or UDP. iSNS heartbeat messages
+ are transported using IP multicast or broadcast.
+
+2.9.1. Use of TCP for iSNS Communication
+
+ It MUST be possible to use TCP for iSNS communication. The iSNS
+ server MUST accept TCP connections for client registrations. To
+ receive Entity Status Inquiry (ESI) (see Section 5.6.5.13) monitoring
+ the use of TCP, the client registers the Portal ESI Interval and the
+
+
+
+Tseng, et al. Standards Track [Page 19]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ port number of the TCP port that will be used to receive ESI
+ messages. The iSNS server initiates the TCP connection used to
+ deliver the ESI message. This TCP connection does not need to be
+ continuously open.
+
+ To receive SCN notifications using TCP, the client registers the
+ iSCSI or iFCP SCN Bitmap and the port number of the TCP port in the
+ Portal used to receive SCNs. The iSNS server initiates the TCP
+ connection used to deliver the SCN message. This TCP connection does
+ not need to be continuously open.
+
+ It is possible for an iSNS client to use the same TCP connection for
+ SCN, ESI, and iSNS queries. Alternatively, separate connections may
+ be used.
+
+2.9.2. Use of UDP for iSNS Communication
+
+ The iSNS server MAY accept UDP messages for client registrations.
+ The iSNS server MUST accept registrations from clients requesting
+ UDP-based ESI and SCN messages.
+
+ To receive UDP-based ESI monitoring messages, the client registers
+ the port number of the UDP port in at least one Portal to be used to
+ receive and respond to ESI messages from the iSNS server. If a
+ Network Entity has multiple Portals with registered ESI UDP Ports,
+ then ESI messages SHALL be delivered to every Portal registered to
+ receive such messages.
+
+ To receive UDP-based SCN notification messages, the client registers
+ the port number of the UDP port in at least one Portal to be used to
+ receive SCN messages from the iSNS server. If a Network Entity has
+ multiple Portals with registered SCN UDP Ports, then SCN messages
+ SHALL be delivered to each Portal registered to receive such
+ messages.
+
+ When using UDP to transport iSNS messages, each UDP datagram MUST
+ contain exactly one iSNS PDU (see Section 5).
+
+2.9.3. iSNS Multicast and Broadcast Messages
+
+ iSNS multicast messages are transported using IP multicast or
+ broadcast. The iSNS heartbeat is the only iSNS multicast or
+ broadcast message. This message is originated by the iSNS server and
+ sent to all iSNS clients that are listening on the IP multicast
+ address allocated for the iSNS heartbeat.
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 20]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+2.10. Simple Network Management Protocol (SNMP) Requirements
+
+ The iSNS Server may be managed via the iSNS MIB [iSNSMIB] using an
+ SNMP management framework [RFC3411]. For a detailed overview of the
+ documents that describe the current Internet-Standard Management
+ Framework, please refer to Section 7 of RFC 3410 [RFC3410]. The iSNS
+ MIB provides the ability to configure and monitor an iSNS server
+ without using the iSNS protocol directly. SNMP management frameworks
+ have several requirements for object indexing in order for objects to
+ be accessed or added.
+
+ SNMP uses an Object Identifier (OID) for object identification. The
+ size of each OID is restricted to a maximum of 128 sub-identifiers.
+ Both the iSCSI and iFCP protocol contain identifiers, such as the
+ iSCSI Name, that are greater the 128 characters in length. Using
+ such identifiers as an index would result in more than 128 sub-
+ identifiers per OID. In order to support objects that have key
+ identifiers whose maximum length is longer than the maximum SNMP-
+ supported length, the iSNS server provides secondary non-zero integer
+ index identifiers. These indexes SHALL be persistent for as long as
+ the server is active. Furthermore, index values for recently
+ deregistered objects SHOULD NOT be reused in the short term. Object
+ attributes, including indexes, are described in detail in Section 6.
+
+ For SNMP based management applications to create a new entry in a
+ table of objects, a valid OID must be available to specify the table
+ row. The iSNS server supports this by providing, for each type of
+ object that can be added via SNMP, an object attribute that returns
+ the next available non-zero integer index. This allows an SNMP
+ client to request an OID to be used for registering a new object in
+ the server. Object attributes, including next available index
+ attributes, are described in detail in Section 6.
+
+3. iSNS Object Model
+
+ iSNS provides the framework for the registration, discovery, and
+ management of iSCSI devices and Fibre Channel-based devices (using
+ iFCP). This architecture framework provides elements needed to
+ describe various storage device objects and attributes that may exist
+ on an IP storage network. Objects defined in this architecture
+ framework include Network Entity, Portal, Storage Node, FC Device,
+ Discovery Domain, and Discovery Domain Set. Each of these objects is
+ described in greater detail in the following sections.
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 21]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+3.1. Network Entity Object
+
+ The Network Entity object is a container of Storage Node objects and
+ Portal objects. It represents the infrastructure supporting access
+ to a unique set of one or more Storage Nodes. The Entity Identifier
+ attribute uniquely distinguishes a Network Entity, and is the key
+ used to register a Network Entity object in an iSNS server. All
+ Storage Nodes and Portals contained within a single Network Entity
+ object operate as a cohesive unit.
+
+ Note that it is possible for a single physical device or gateway to
+ be represented by more than one logical Network Entity in the iSNS
+ database. For example, one of the Storage Nodes on a physical device
+ may be accessible from only a subset of the network interfaces (i.e.,
+ Portals) available on that device. In this case, a logical network
+ entity (i.e., a "shadow entity") is created and used to contain the
+ Portals and Storage Nodes that can operate cooperatively. No object
+ (Portals, Storage Nodes, etc.) can be contained in more than one
+ logical Network Entity.
+
+ Similarly, it is possible for a logical Network Entity to be
+ supported by more than one physical device or gateway. For example,
+ multiple FC-iSCSI gateways may be used to bridge FC devices in a
+ single Fibre Channel network. Collectively, the multiple gateways
+ can be used to support a single logical Network Entity that is used
+ to contain all the devices in that Fibre Channel network.
+
+3.2. Portal Object
+
+ The Portal object is an interface through which access to Storage
+ Nodes within the Network Entity can be obtained. The IP address and
+ TCP/UDP Port number attributes uniquely distinguish a Portal object,
+ and combined are the key used to register a Portal object in an iSNS
+ server. A Portal is contained in one and only one Network Entity,
+ and may be contained in one or more DDs (see Section 3.6).
+
+3.3. Storage Node Object
+
+ The Storage Node object is the logical endpoint of an iSCSI or iFCP
+ session. In iFCP, the session endpoint is represented by the World
+ Wide Port Name (WWPN). In iSCSI, the session endpoint is represented
+ by the iSCSI Name of the device. For iSCSI, the iSCSI Name attribute
+ uniquely distinguishes a Storage Node, and is the key used to
+ register a Storage Node object in an iSNS Server. For iFCP, the FC
+ Port Name (WWPN) attribute uniquely distinguishes a Storage Node, and
+ is the key used to register a Storage Node object in the iSNS Server.
+ Storage Node is contained in only one Network Entity object and may
+ be contained in one or more DDs (see Section 3.6).
+
+
+
+Tseng, et al. Standards Track [Page 22]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+3.4. Portal Group Object
+
+ The Portal Group (PG) object represents an association between a
+ Portal and an iSCSI Node. Each Portal and iSCSI Storage Node
+ registered in an Entity can be associated using a Portal Group (PG)
+ object. The PG Tag (PGT), if non-NULL, indicates that the associated
+ Portal provides access to the associated iSCSI Storage Node in the
+ Entity. All Portals that have the same PGT value for a specific
+ iSCSI Storage Node allow coordinated access to that node.
+
+ A PG object MAY be registered when a Portal or iSCSI Storage Node is
+ registered. Each Portal to iSCSI Node association is represented by
+ one and only one PG object. In order for a Portal to provide access
+ to an iSCSI Node, the PGT of the PG object MUST be non-NULL. If the
+ PGT value registered for a specified Portal and iSCSI Node is NULL,
+ or if no PGT value is registered, then the Portal does not provide
+ access to that iSCSI Node in the Entity.
+
+ The PGT value indicates whether access to an iSCSI Node can be
+ coordinated across multiple Portals. All Portals that have the same
+ PGT value for a specific iSCSI Node can provide coordinated access to
+ that iSCSI Node. According to the iSCSI Specification, coordinated
+ access to an iSCSI node indicates the capability of coordinating an
+ iSCSI session with connections that span these Portals [iSCSI].
+
+ The PG object is uniquely distinguished by the iSCSI Name, Portal IP
+ Address, and Portal TCP Port values of the associated Storage Node
+ and Portal objects. These are represented in the iSNS Server by the
+ PG iSCSI Name, PG Portal IP Address, and PG Portal TCP/UDP Port
+ attributes, respectively. The PG object is also uniquely
+ distinguished in the iSNS Server by the PG Index value.
+
+ A new PG object can only be registered by referencing its associated
+ iSCSI Storage Node or Portal object. A pre-existing PG object can be
+ modified or queried by using its Portal Group Index as message key,
+ or by referencing its associated iSCSI Storage Node or Portal object.
+ A 0-length Tag, Length, Value TLV is used to register a PGT NULL
+ value.
+
+ The PG object is deregistered if and only if its associated iSCSI
+ Node and Portal objects are both removed.
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 23]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+3.5. Device Object
+
+ The FC Device represents the Fibre Channel Node. This object
+ contains information that may be useful in the management of the
+ Fibre Channel device. The FC Node Name (WWNN) attribute uniquely
+ distinguishes an FC Device, and is the key used to register an FC
+ Device object in the iSNS Server.
+
+ The FC Device is contained in one or more Storage Node objects.
+
+3.6. Discovery Domain Object
+
+ Discovery Domains (DD) are a security and management mechanism used
+ to administer access and connectivity to storage devices. For query
+ and registration purposes, they are considered containers for Storage
+ Node and Portal objects. A query by an iSNS client that is not from
+ a Control Node only returns information about objects with which it
+ shares at least one active DD. The only exception to this rule is
+ with Portals; if Storage Nodes of a Network Entity are registered in
+ the DD without Portals, then all Portals of that Network Entity are
+ implicit members of that DD. The Discovery Domain ID (DD_ID)
+ attribute uniquely distinguishes a Discovery Domain object, and is
+ the key used to register a Discovery Domain object in the iSNS
+ Server.
+
+ A DD is considered active if it is a member of at least one active DD
+ Set. DDs that are not members of at least one enabled DDS are
+ considered disabled. A Storage Node can be a member of one or more
+ DDs. An enabled DD establishes connectivity among the Storage Nodes
+ in that DD.
+
+3.7. Discovery Domain Set Object
+
+ The Discovery Domain Set (DDS) is a container object for Discovery
+ Domains (DDs). DDSs may contain one or more DDs. Similarly, each DD
+ can be a member of one or more DDSs. DDSs are a mechanism to store
+ coordinated sets of DD mappings in the iSNS server. Active DDs are
+ members of at least one active DD Set. Multiple DDSs may be
+ considered active at the same time. The Discovery Domain Set ID
+ (DDS_ID) attribute uniquely distinguishes a Discovery Domain Set
+ object, and is the key used to register a Discovery Domain Set object
+ in the iSNS Server.
+
+3.8. Database Model
+
+ As presented to the iSNS client, each object of a specific type in
+ the iSNS database MUST have an implicit internal linear ordering
+ based on the key(s) for that object type. This ordering provides the
+
+
+
+Tseng, et al. Standards Track [Page 24]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ ability to respond to DevGetNext queries (see Section 5.6.5.3). The
+ ordering of objects in the iSNS database SHOULD NOT be changed with
+ respect to that implied ordering, as a consequence of object
+ insertions and deletions. That is, the relative order of surviving
+ object entries in the iSNS database SHOULD be preserved so that the
+ DevGetNext message encounters generally reasonable behavior.
+
+ The following diagram shows the various objects described above and
+ their relationship to each other.
+
+ +--------------+ +-----------+
+ | NETWORK |1 *| |
+ | ENTITY |----| PORTAL |
+ | | | |
+ +--------------+ +-----------+
+ |1 |1 |*
+ | | |
+ | |* |
+ | +----------+ |
+ | | PORTAL | |
+ | | GROUP | |
+ | +----------+ |
+ | |* |
+ | | |
+ |* |1 |*
+ +-----------+ +--------------+ +-----------+ +-----------+
+ | FC |1 *| STORAGE |* *| DISCOVERY |* *| DISCOVERY |
+ | DEVICE |----| NODE |----| DOMAIN |----| DOMAIN |
+ | | | | | | | SET |
+ +-----------+ +--------------+ +-----------+ +-----------+
+
+ * represents 0 to many possible relationships
+
+4. iSNS Implementation Requirements
+
+ This section details specific requirements for support of each of
+ these IP storage protocols. Implementation requirements for security
+ are described in Section 7.
+
+4.1. iSCSI Requirements
+
+ Use of iSNS in support of iSCSI is OPTIONAL. iSCSI devices MAY be
+ manually configured with the iSCSI Name and IP address of peer
+ devices, without the aid or intervention of iSNS. iSCSI devices may
+ also use SLP [RFC2608] to discover peer iSCSI devices. However, iSNS
+ is useful for scaling a storage network to a larger number of iSCSI
+ devices.
+
+
+
+
+Tseng, et al. Standards Track [Page 25]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+4.1.1. Required Attributes for Support of iSCSI
+
+ The following attributes are available to support iSCSI. Attributes
+ indicated in the REQUIRED for Server column MUST be implemented by an
+ iSNS server used to support iSCSI. Attributes indicated in the
+ REQUIRED for Client column MUST be implemented by an iSCSI device
+ that elects to use the iSNS. Attributes indicated in the K (Key)
+ column uniquely identify the object type in the iSNS Server. A more
+ detailed description of each attribute is found in Section 6.
+
+ REQUIRED for:
+ Object Attribute K Server Client
+ ------ --------- - ------ ------
+ NETWORK ENTITY Entity Identifier * * *
+ Entity Protocol * *
+ Management IP Address *
+ Timestamp *
+ Protocol Version Range *
+ Registration Period *
+ Entity Index *
+ Entity IKE Phase-1 Proposal
+ Entity Certificate
+
+ PORTAL IP Address * * *
+ TCP/UDP Port * * *
+ Portal Symbolic Name *
+ ESI Interval *
+ ESI Port *
+ Portal Index *
+ SCN Port *
+ Portal Security Bitmap *
+ Portal IKE Phase-1 Proposal
+ Portal IKE Phase-2 Proposal
+ Portal Certificate
+
+ PORTAL GROUP PG iSCSI Name * * *
+ PG IP Address * * *
+ PG TCP/UDP Port * * *
+ PG Tag * *
+ PG Index *
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 26]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ STORAGE NODE iSCSI Name * * *
+ iSCSI Node Type * *
+ Alias *
+ iSCSI SCN Bitmap *
+ iSCSI Node Index *
+ WWNN Token
+ iSCSI AuthMethod
+ iSCSI Node Certificate
+
+ DISCOVERY DOMAIN DD ID * * *
+ DD Symbolic Name *
+ DD Member iSCSI Node Index *
+ DD Member iSCSI Name *
+ DD Member Portal Index *
+ DD Member Portal IP Addr *
+ DD Member Portal TCP/UDP *
+ DD Features *
+
+ DISCOVERY DOMAIN DDS Identifier * *
+ SET DDS Symbolic Name *
+ DDS Status *
+
+ All iSCSI user-specified and vendor-specified attributes are OPTIONAL
+ to implement and use.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 27]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+4.1.2. Examples: iSCSI Object Model Diagrams
+
+ The following diagram models how a simple iSCSI-based initiator and
+ target is represented using database objects stored in the iSNS
+ server. In this implementation, each target and initiator is
+ attached to a single Portal.
+
+ +----------------------------------------------------------------+
+ | IP Network |
+ +------------+--------------------------------------+------------+
+ | |
+ | |
+ +-----+------+------+-----+ +-----+------+------+-----+
+ | | PORTAL | | | | PORTAL | |
+ | | -IP Addr 1 | | | | -IP Addr 2 | |
+ | | -TCP Port 1 | | | | -TCP Port 2 | |
+ | +-----+ +-----+ | | +-----+ +-----+ |
+ | | | | | | | |
+ | +-----+ +-----+ | | +-----+ +-----+ |
+ | | PORTAL GROUP| | | | PORTAL GROUP| |
+ | | -Prtl Tag 1 | | | | -Prtl Tag 2 | |
+ | +-----+ +-----+ | | +-----+ +-----+ |
+ | | | | | | | |
+ | +--------+ +--------+ | | +-------+ +--------+ |
+ | | | | | | | |
+ | | STORAGE NODE | | | | STORAGE NODE | |
+ | | -iSCSI Name | | | | -iSCSI Name | |
+ | | -Alias: "server1"| | | | -Alias: "disk1"| |
+ | | -Type: initiator | | | | -Type: target | |
+ | | | | | | | |
+ | +-------------------+ | | +------------------+ |
+ | | | |
+ | NETWORK ENTITY | | NETWORK ENTITY |
+ | -Entity ID (FQDN): | | -Entity ID (FQDN): |
+ | "strg1.example.com" | | "strg2.example.net" |
+ | -Protocol: iSCSI | | -Protocol: iSCSI |
+ | | | |
+ +-------------------------+ +-------------------------+
+
+ The object model can be expanded to describe more complex devices,
+ such as an iSCSI device with more than one storage controller, in
+ which each controller is accessible through any of multiple Portal
+ interfaces, possibly using multiple Portal Groups. The storage
+ controllers on this device can be accessed through alternate Portal
+ interfaces if any original interface should fail. The following
+ diagram describes such a device:
+
+
+
+
+
+Tseng, et al. Standards Track [Page 28]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ +---------------------------------------------------------------+
+ | IP Network |
+ +-------------------+-----------------------+-------------------+
+ | |
+ | |
+ +------------+------+------+---------+------+------+------------+
+ | | PORTAL 1 | | PORTAL 2 | |
+ | | -IP Addr 1 | | -IP Addr 2 | |
+ | | -TCP Port 1 | | -TCP Port 2 | |
+ | +-----+ +-----+ +-----+ +-----+ |
+ | | | | | |
+ | +---------------+ +---------------------+ +---------------+ |
+ | +-------+ +----------------+ +-------------------+ +------+ |
+ | | | | | | | |
+ | +-------+ +-------+ +------+ +--------+ +--------+ +------+ |
+ | | | | | | | |
+ | | STORAGE NODE 1 | | STORAGE NODE 2 | | STORAGE NODE 3 | |
+ | | -iSCSI Name 1 | | -iSCSI Name 2 | | -iSCSI Name 3 | |
+ | | -Alias: "disk1"| | -Alias: "disk2"| | -Alias: "disk3"| |
+ | | -Type: target | | -Type: target | | -Type: target | |
+ | | | | | | | |
+ | +-----------------+ +-----------------+ +-----------------+ |
+ | |
+ | NETWORK ENTITY |
+ | -Entity ID (FQDN): "dev1.example.com" |
+ | -Protocol: iSCSI |
+ | |
+ | Portal Group Object Table |
+ | Storage-Node Portal Portal-Group-Tag |
+ | 1 1 10 |
+ | 1 2 NULL (no access permitted) |
+ | 2 1 20 |
+ | 2 2 20 |
+ | 3 1 30 |
+ | 3 2 10 |
+ | |
+ +---------------------------------------------------------------+
+
+ Storage Node 1 is accessible via Portal 1 with a PGT of 10. It does
+ not have a Portal Group Tag (PGT) assigned for Portal 2, so Storage
+ Node 1 cannot be accessed via Portal 2.
+
+ Storage Node 2 can be accessed via both Portal 1 and Portal 2. Since
+ Storage Node 2 has the same PGT value assigned to both Portal 1 and
+ Portal 2, in this case 20, coordinated access via the Portals is
+ available [iSCSI].
+
+
+
+
+
+Tseng, et al. Standards Track [Page 29]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Storage Node 3 can be accessed via Portal 1 or Portal 2. However,
+ since Storage Node 3 has different PGT values assigned to each
+ Portal, in this case 10 and 30, access is not coordinated [iSCSI].
+ Because PGTs are assigned within the context of a Storage Node, the
+ PGT value of 10 used for Storage Node 1 and Storage Node 3 are not
+ interrelated.
+
+4.1.3. Required Commands and Response Messages for Support of iSCSI
+
+ The following iSNSP messages and responses are available in support
+ of iSCSI. Messages indicated in the REQUIRED for Server column MUST
+ be implemented in iSNS servers used for iSCSI devices. Messages
+ indicated in the REQUIRED for Client column MUST be implemented in
+ iSCSI devices that elect to use the iSNS server.
+
+ REQUIRED for:
+ Message Description Abbreviation Func_ID Server Client
+ ------------------- ------------ ------- ------ ------
+ RESERVED 0x0000
+ Device Attr Reg Request DevAttrReg 0x0001 * *
+ Dev Attr Query Request DevAttrQry 0x0002 * *
+ Dev Get Next Request DevGetNext 0x0003 *
+ Deregister Dev Request DevDereg 0x0004 * *
+ SCN Register Request SCNReg 0x0005 *
+ SCN Deregister Request SCNDereg 0x0006 *
+ SCN Event SCNEvent 0x0007 *
+ State Change Notification SCN 0x0008 *
+ DD Register DDReg 0x0009 * *
+ DD Deregister DDDereg 0x000A * *
+ DDS Register DDSReg 0x000B * *
+ DDS Deregister DDSDereg 0x000C * *
+ Entity Status Inquiry ESI 0x000D *
+ Name Service Heartbeat Heartbeat 0x000E
+ RESERVED 0x000F-0x00FF
+ Vendor Specific 0x0100-0x01FF
+ RESERVED 0x0200-0x7FFF
+
+ The following are iSNSP response messages used in support of iSCSI:
+
+ REQUIRED for:
+ Response Message Desc Abbreviation Func_ID Server Client
+ --------------------- ------------ ------- ------ ------
+ RESERVED 0x8000
+ Device Attr Register Rsp DevAttrRegRsp 0x8001 * *
+ Device Attr Query Rsp DevAttrQryRsp 0x8002 * *
+ Device Get Next Rsp DevGetNextRsp 0x8003 *
+ Device Dereg Rsp DevDeregRsp 0x8004 * *
+ SCN Register Rsp SCNRegRsp 0x8005 *
+
+
+
+Tseng, et al. Standards Track [Page 30]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ SCN Deregister Rsp SCNDeregRsp 0x8006 *
+ SCN Event Rsp SCNEventRsp 0x8007 *
+ SCN Response SCNRsp 0x8008 *
+ DD Register Rsp DDRegRsp 0x8009 * *
+ DD Deregister Rsp DDDeregRsp 0x800A * *
+ DDS Register Rsp DDSRegRsp 0x800B * *
+ DDS Deregister Rsp DDSDeregRsp 0x800C * *
+ Entity Stat Inquiry Rsp ESIRsp 0x800D *
+ RESERVED 0x800E-0x80FF
+ Vendor Specific 0x8100-0x81FF
+ RESERVED 0x8200-0xFFFF
+
+4.2. iFCP Requirements
+
+ In iFCP, use of iSNS is REQUIRED. No alternatives exist for support
+ of iFCP Naming & Discovery functions.
+
+4.2.1. Required Attributes for Support of iFCP
+
+ The following table displays attributes that are used by iSNS to
+ support iFCP. Attributes indicated in the REQUIRED for Server column
+ MUST be implemented by the iSNS server that supports iFCP.
+ Attributes indicated in the REQUIRED for Client column MUST be
+ supported by iFCP gateways. Attributes indicated in the K (Key)
+ column uniquely identify the object type in the iSNS Server. A more
+ detailed description of each attribute is found in Section 6.
+
+ REQUIRED for:
+ Object Attribute K Server Client
+ ------ --------- - ------ ------
+ NETWORK ENTITY Entity Identifier * * *
+ Entity Protocol * *
+ Management IP Address *
+ Timestamp *
+ Protocol Version Range *
+ Registration period
+ Entity Index
+ Entity IKE Phase-1 Proposal
+ Entity Certificate
+
+ PORTAL IP Address * * *
+ TCP/UDP Port * * *
+ Symbolic Name *
+ ESI Interval *
+ ESI Port *
+ SCN Port *
+ Portal IKE Phase-1 Proposal
+ Portal IKE Phase-2 Proposal
+
+
+
+Tseng, et al. Standards Track [Page 31]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Portal Certificate
+ Security Bitmap *
+
+ STORAGE NODE FC Port Name (WWPN) * * *
+ (FC Port) Port_ID * *
+ FC Port Type * *
+ Port Symbolic Name *
+ Fabric Port Name (FWWN) *
+ Hard Address *
+ Port IP Address *
+ Class of Service *
+ FC FC-4 Types *
+ FC FC-4 Descriptors *
+ FC FC-4 Features *
+ SCN Bitmap *
+ iFCP Port Role *
+ Permanent Port Name *
+
+ FC DEVICE FC Node Name (WWNN) * * *
+ (FC Node) Node Symbolic Name *
+ Node IP Address *
+ Node IPA *
+ Proxy iSCSI Name
+
+ DISCOVERY DOMAIN DD ID * * *
+ DD Symbolic Name *
+ DD Member FC Port Name *
+ DD Member Portal Index *
+ DD Member Portal IP Addr *
+ DD Member Portal TCP/UDP *
+
+ DISCOVERY DOMAIN DDS ID * *
+ SET DDS Symbolic Name *
+ DDS Status *
+
+ OTHER Switch Name
+ Preferred_ID
+ Assigned_ID
+ Virtual_Fabric_ID
+
+ All iFCP user-specified and vendor-specified attributes are OPTIONAL
+ to implement and use.
+
+4.2.2. Example: iFCP Object Model Diagram
+
+ The iFCP protocol allows native Fibre Channel devices or Fibre
+ Channel fabrics connected to an iFCP gateway to be directly
+ internetworked using IP.
+
+
+
+Tseng, et al. Standards Track [Page 32]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ When supporting iFCP, the iSNS server stores Fibre Channel device
+ attributes, iFCP gateway attributes, and Fibre Channel fabric switch
+ attributes that might also be stored in an FC name server.
+
+ The following diagram shows a representation of a gateway supporting
+ multiple Fibre Channel devices behind it. The two Portal objects
+ represent IP interfaces on the iFCP gateway that can be used to
+ access any of the three Storage Node objects behind it. Note that
+ the FC Device object is not contained in the Network Entity object.
+ However, each FC Device has a relationship to one or more Storage
+ Node objects.
+
+ +--------------------------------------------------------+
+ | IP Network |
+ +--------+-----------------+-----------------------------+
+ | |
+ +-+------+------+---+------+------+----------------------+
+ | | PORTAL | | PORTAL | NETWORK ENTITY |
+ | | -IP Addr 1 | | -IP Addr 2 | -Entity ID (FQDN): |
+ | | -TCP Port 1 | | -TCP Port 2 | "gtwy1.example.com" |
+ | +-----+ +-----+ +-----+ +-----+ -Protocol: iFCP |
+ | | | | | |
+ | +-----+ +---------------+ +----------------------+ |
+ | +-----+ +---------------+ +-------------+ +------+ |
+ | | | | | | | |
+ | +-----+ +-----+ +----+ +------+ +----+ +------+ |
+ | |STORAGE NODE | |STORAGE NODE | |STORAGE NODE | |
+ | | -WWPN 1 | | -WWPN 2 | | -WWPN 3 | |
+ | | -Port ID 1 | | -Port ID 2 | | -Port ID 3 | |
+ | | -FWWN 1 | | -FWWN 2 | | -FWWN 3 | |
+ | | -FC COS | | -FC COS | | -FC COS | |
+ | +------+------+ +-------+-----+ +----+--------+ |
+ +--------|-------------------|------------|--------------+
+ | | |
+ +------+------+ +---+------------+---+
+ | FC DEVICE | | FC DEVICE |
+ | -WWNN 1 | | -WWNN 2 |
+ | | | |
+ +-------------+ +--------------------+
+
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 33]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+4.2.3. Required Commands and Response Messages for Support of iFCP
+
+ The iSNSP messages and responses displayed in the following tables
+ are available to support iFCP gateways. Messages indicated in the
+ REQUIRED TO IMPLEMENT column MUST be supported by the iSNS server
+ used by iFCP gateways. Messages indicated in the REQUIRED TO USE
+ column MUST be supported by the iFCP gateways themselves.
+
+ REQUIRED for:
+ Message Description Abbreviation Func ID Server Client
+ ------------------- ------------ ------- ------ ------
+ RESERVED 0x0000
+ Device Attr Reg Request DevAttrReg 0x0001 * *
+ Device Attr Query Request DevAttrQry 0x0002 * *
+ Device Get Next Request DevGetNext 0x0003 *
+ Device Dereg Request DevDereg 0x0004 * *
+ SCN Register Request SCNReg 0x0005 *
+ SCN Deregister Request SCNDereg 0x0006 *
+ SCN Event SCNEvent 0x0007 *
+ State Change Notification SCN 0x0008 *
+ DD Register DDReg 0x0009 * *
+ DD Deregister DDDereg 0x000A * *
+ DDS Register DDSReg 0x000B * *
+ DDS Deregister DDSDereg 0x000C * *
+ Entity Status Inquiry ESI 0x000D *
+ Name Service Heartbeat Heartbeat 0x000E *
+ Reserved Reserved 0x000F-0x0010
+ Request FC_DOMAIN_ID RqstDomId 0x0011
+ Release FC_DOMAIN_ID RlseDomId 0x0012
+ Get FC_DOMAIN_IDs GetDomId 0x0013
+ RESERVED 0x0014-0x00FF
+ Vendor Specific 0x0100-0x01FF
+ RESERVED 0x0200-0x7FFF
+
+ The following are iSNSP response messages in support of iFCP:
+
+ REQUIRED for:
+ Response Message Desc Abbreviation Func_ID Server Client
+ --------------------- ------------ ------- ------ ------
+ RESERVED 0x8000
+ Device Attr Reg Rsp DevAttrRegRsp 0x8001 * *
+ Device Attr Query Rsp DevAttrQryRsp 0x8002 * *
+ Device Get Next Rsp DevGetNextRsp 0x8003 *
+ Device Deregister Rsp DevDeregRsp 0x8004 * *
+ SCN Register Rsp SCNRegRsp 0x8005 *
+ SCN Deregister Rsp SCNDeregRsp 0x8006 *
+ SCN Event Rsp SCNEventRsp 0x8007 *
+ SCN Rsp SCNRsp 0x8008 *
+
+
+
+Tseng, et al. Standards Track [Page 34]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ DD Register Rsp DDRegRsp 0x8009 * *
+ DD Deregister Rsp DDDeregRsp 0x800A * *
+ DDS Register Rsp DDSRegRsp 0x800B * *
+ DDS Deregister Rsp DDSDeregRsp 0x800C * *
+ Entity Status Inquiry Rsp ESIRsp 0x800D *
+ NOT USED 0x800E
+ RESERVED 0x800F-0x8010
+ Request FC_DOMAIN_ID Rsp RqstDomIdRsp 0x8011
+ Release FC_DOMAIN_ID Rsp RlseDomIdRsp 0x8012
+ Get FC_DOMAIN_IDs GetDomIdRsp 0x0013
+ RESERVED 0x8014-0x80FF
+ Vendor Specific 0x8100-0x81FF
+ RESERVED 0x8200-0xFFFF
+
+5. iSNSP Message Format
+
+ The iSNSP message format is similar to the format of other common
+ protocols such as DHCP, DNS and BOOTP. An iSNSP message may be sent
+ in one or more iSNS Protocol Data Units (PDU). Each PDU is 4-byte
+ aligned. The following describes the format of the iSNSP PDU:
+
+ Byte MSb LSb
+ Offset 0 15 16 31
+ +---------------------+----------------------+
+ 0 | iSNSP VERSION | FUNCTION ID | 4 Bytes
+ +---------------------+----------------------+
+ 4 | PDU LENGTH | FLAGS | 4 Bytes
+ +---------------------+----------------------+
+ 8 | TRANSACTION ID | SEQUENCE ID | 4 Bytes
+ +---------------------+----------------------+
+ 12 | |
+ | PDU PAYLOAD | N Bytes
+ | ... |
+ +--------------------------------------------+
+ 12+N | AUTHENTICATION BLOCK (Multicast/Broadcast) | L Bytes
+ +--------------------------------------------+
+ Total Length = 12 + N + L
+
+5.1. iSNSP PDU Header
+
+ The iSNSP PDU header contains the iSNSP VERSION, FUNCTION ID, PDU
+ LENGTH, FLAGS, TRANSACTION ID, and SEQUENCE ID fields as defined
+ below.
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 35]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+5.1.1. iSNSP Version
+
+ The iSNSP version described in this document is 0x0001. All other
+ values are RESERVED. The iSNS server MAY reject messages for iSNSP
+ version numbers that it does not support.
+
+5.1.2. iSNSP Function ID
+
+ The FUNCTION ID defines the type of iSNS message and the operation to
+ be executed. FUNCTION_ID values with the leading bit cleared
+ indicate query, registration, and notification messages, whereas
+ FUNCTION_ID values with the leading bit set indicate response
+ messages.
+
+ See Section 4 under the appropriate protocol (i.e., iSCSI or iFCP)
+ for a mapping of the FUNCTION_ID value to the iSNSP Command or
+ Response message. All PDUs comprising an iSNSP message must have the
+ same FUNCTION_ID value.
+
+5.1.3. iSNSP PDU Length
+
+ The iSNS PDU Length specifies the length of the PDU PAYLOAD field in
+ bytes. The PDU Payload contains TLV attributes for the operation.
+
+ Additionally, response messages contain a success/failure code. The
+ PDU Length MUST be 4-byte aligned.
+
+5.1.4. iSNSP Flags
+
+ The FLAGS field indicates additional information about the message
+ and the type of Network Entity that generated the message. The
+ following table displays the valid flags:
+
+ Bit Position Enabled (1) means:
+ ------------ -----------------
+ 16 Sender is the iSNS client
+ 17 Sender is the iSNS server
+ 18 Authentication block is present
+ 19 Replace flag (for DevAttrReg)
+ 20 Last PDU of the iSNS message
+ 21 First PDU of the iSNS message
+ 22-31 RESERVED
+
+5.1.5. iSNSP Transaction ID
+
+ The TRANSACTION ID MUST be set to a unique value for each
+ concurrently outstanding request message. Replies MUST use the same
+ TRANSACTION ID value as the associated iSNS request message. If a
+
+
+
+Tseng, et al. Standards Track [Page 36]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ message is retransmitted, the original TRANSACTION ID value MUST be
+ used. All PDUs comprising an iSNSP message must have the same
+ TRANSACTION ID value.
+
+5.1.6. iSNSP Sequence ID
+
+ The SEQUENCE ID has a unique value for each PDU within a single
+ transaction. The SEQUENCE_ID value of the first PDU transmitted in a
+ given iSNS message MUST be zero (0), and each SEQUENCE_ID value in
+ each PDU MUST be numbered sequentially in the order in which the PDUs
+ are transmitted. Note that the two-byte SEQUENCE ID allows for up to
+ 65536 PDUs per iSNS message.
+
+5.2. iSNSP Message Segmentation and Reassembly
+
+ iSNS messages may be carried in one or more iSNS PDUs. If only one
+ iSNS PDU is used to carry the iSNS message, then bit 21 (First PDU)
+ and bit 20 in the FLAGS field (Last PDU) SHALL both be set. If
+ multiple PDUs are used to carry the iSNS message, then bit 21 SHALL
+ be set in the first PDU of the message, and bit 20 SHALL be set in
+ the last PDU.
+
+ All PDUs comprising the same iSNSP message SHALL have the same
+ FUNCTION_ID and TRANSACTION_ID values. Each PDU comprising an iSNSP
+ message SHALL have a unique SEQUENCE_ID value.
+
+5.3. iSNSP PDU Payload
+
+ The iSNSP PDU PAYLOAD is of variable length and contains attributes
+ used for registration and query operations. The attribute data items
+ use a format similar to that of other protocols, such as DHCP
+ [RFC2131] options. Each iSNS attribute is specified in the PDU
+ Payload using Tag-Length-Value (TLV) data format, as shown below:
+
+ Byte MSb LSb
+ Offset 0 31
+ +--------------------------------------------+
+ 0 | Attribute Tag | 4 Bytes
+ +--------------------------------------------+
+ 4 | Attribute Length (N) | 4 Bytes
+ +--------------------------------------------+
+ 8 | |
+ | Attribute Value | N Bytes
+ | |
+ +--------------------------------------------+
+ Total Length = 8 + N
+
+
+
+
+
+Tseng, et al. Standards Track [Page 37]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Attribute Tag: a 4-byte field that identifies the attribute as
+ defined in Section 6.1. This field contains the
+ tag value from the indicated table.
+
+ Attribute Length: a 4-byte field that indicates the length, in bytes,
+ of the value field to follow in the TLV. For
+ variable-length attributes, the value field MUST
+ contain padding bytes, if necessary, in order to
+ achieve 4-byte alignment. A "zero-length TLV"
+ contains only the attribute tag and length fields.
+
+ Attribute Value: a variable-length field containing the attribute
+ value and padding bytes (if necessary).
+
+ The above format is used to identify each attribute in the PDU
+ Payload. Note that TLV boundaries need not be aligned with PDU
+ boundaries; PDUs may carry one or more TLVs, or any fraction thereof.
+ The Response Status Code, contained in response message PDU Payloads
+ and described below, is not in TLV format. PDU Payloads for messages
+ that do not contain iSNS attributes, such as the Name Service
+ Heartbeat, do not use the TLV format.
+
+5.3.1. Attribute Value 4-Byte Alignment
+
+ All attribute values are aligned to 4-byte boundaries. For variable
+ length attributes, if necessary, the TLV length MUST be increased to
+ the next 4-byte boundary through padding with bytes containing zero
+ (0). If an attribute value is padded, a combination of the tag and
+ attribute value itself is used to determine the actual value length
+ and number of pad bytes. There is no explicit count of the number of
+ pad bytes provided in the TLV.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 38]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+5.4. iSNSP Response Status Codes
+
+ All iSNSP response messages contain a 4-byte Status Code field as the
+ first field in the iSNSP PDU PAYLOAD. If the original iSNSP request
+ message was processed normally by the iSNS server, or by the iSNS
+ client for ESI and SCN messages, then this field SHALL contain a
+ status code of 0 (Successful). A non-zero status code indicates
+ rejection of the entire iSNS client request message.
+
+ Status Code Status Description
+ ----------- -----------------
+ 0 Successful
+ 1 Unknown Error
+ 2 Message Format Error
+ 3 Invalid Registration
+ 4 RESERVED
+ 5 Invalid Query
+ 6 Source Unknown
+ 7 Source Absent
+ 8 Source Unauthorized
+ 9 No Such Entry
+ 10 Version Not Supported
+ 11 Internal Error
+ 12 Busy
+ 13 Option Not Understood
+ 14 Invalid Update
+ 15 Message (FUNCTION_ID) Not Supported
+ 16 SCN Event Rejected
+ 17 SCN Registration Rejected
+ 18 Attribute Not Implemented
+ 19 FC_DOMAIN_ID Not Available
+ 20 FC_DOMAIN_ID Not Allocated
+ 21 ESI Not Available
+ 22 Invalid Deregistration
+ 23 Registration Feature Not Supported
+ 24 and above RESERVED
+
+5.5. Authentication for iSNS Multicast and Broadcast Messages
+
+ For iSNS multicast and broadcast messages (see Section 2.9.3), the
+ iSNSP provides authentication capability. The following section
+ details the iSNS Authentication Block, which is identical in format
+ to the SLP authentication block [RFC2608]. iSNS unicast messages
+ SHOULD NOT include the authentication block, but rather should rely
+ upon IPSec security mechanisms.
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 39]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ If a message contains an authentication block, then the
+ "Authentication block present" bit in the iSNSP PDU header FLAGS
+ field SHALL be enabled.
+
+ If a PKI is available with an [X.509] Certificate Authority (CA),
+ then public key authentication of the iSNS server is possible. The
+ authentication block leverages the DSA with SHA-1 algorithm, which
+ can easily integrate into a public key infrastructure.
+
+ The authentication block contains a digital signature for the
+ multicast message. The digital signature is calculated on a per-PDU
+ basis. The authentication block contains the following information:
+
+ 1. A time stamp, to prevent replay attacks.
+ 2. A structured authenticator containing a signature calculated over
+ the time stamp and the message being secured.
+ 3. An indicator of the cryptographic algorithm that was used to
+ calculate the signature.
+ 4. An indicator of the keying material and algorithm parameters,
+ used to calculate the signature.
+
+ The authentication block is described in the following figure:
+
+ Byte MSb LSb
+ Offset 0 31
+ +----------------------------------+
+ 0 | BLOCK STRUCTURE DESCRIPTOR | 4 Bytes
+ +----------------------------------+
+ 4 | AUTHENTICATION BLOCK LENGTH | 4 Bytes
+ +----------------------------------+
+ 8 | TIMESTAMP | 8 Bytes
+ +----------------------------------+
+ 16 | SPI STRING LENGTH | 4 Bytes
+ +----------------------------------+
+ 20 | SPI STRING | N Bytes
+ +----------------------------------+
+ 20 + N | STRUCTURED AUTHENTICATOR | M Bytes
+ +----------------------------------+
+ Total Length = 20 + N + M
+
+ BLOCK STRUCTURE DESCRIPTOR (BSD): Defines the structure and algorithm
+ to use for the STRUCTURED AUTHENTICATOR. BSD values from
+ 0x00000000 to 0x00007FFF are assigned by IANA, while
+ values 0x00008000 to 0x00008FFF are for private use.
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 40]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ AUTHENTICATION BLOCK LENGTH: Defines the length of the authentication
+ block, beginning with the BSD field and running through
+ the last byte of the STRUCTURED AUTHENTICATOR.
+
+ TIMESTAMP: This is an 8-byte unsigned, fixed-point integer giving the
+ number of seconds since 00:00:00 GMT on January 1, 1970.
+
+ SPI STRING LENGTH: The length of the SPI STRING field.
+
+ SPI STRING (Security Parameters Index): Index to the key and
+ algorithm used by the message recipient to decode the
+ STRUCTURED AUTHENTICATOR field.
+
+ STRUCTURED AUTHENTICATOR: Contains the digital signature. For the
+ default BSD value of 0x0002, this field SHALL contain the
+ binary ASN.1 encoding of output values from the DSA with
+ SHA-1 signature calculation as specified in Section 2.2.2
+ of [RFC3279].
+
+5.6. Registration and Query Messages
+
+ The iSNSP registration and query message PDU Payloads contain a list
+ of attributes, and have the following format:
+
+ +----------------------------------------+
+ | Source Attribute (Requests Only) |
+ +----------------------------------------+
+ | Message Key Attribute[1] (if present) |
+ +----------------------------------------+
+ | Message Key Attribute[2] (if present) |
+ +----------------------------------------+
+ | . . . |
+ +----------------------------------------+
+ | - Delimiter Attribute - |
+ +----------------------------------------+
+ | Operating Attribute[1] (if present) |
+ +----------------------------------------+
+ | Operating Attribute[2] (if present) |
+ +----------------------------------------+
+ | Operating Attribute[3] (if present) |
+ +----------------------------------------+
+ | . . . |
+ +----------------------------------------+
+
+ Each Source, Message Key, Delimiter, and Operating attribute is
+ specified in the PDU Payload using the Tag-Length-Value (TLV) data
+ format. iSNS Registration and Query messages are sent by iSNS Clients
+
+
+
+
+Tseng, et al. Standards Track [Page 41]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ to the iSNS server IP Address and well-known TCP/UDP Port. The iSNS
+ Responses will be sent to the iSNS Client IP address and TCP/UDP port
+ number from the original request message.
+
+5.6.1. Source Attribute
+
+ The Source Attribute is used to identify the Storage Node to the iSNS
+ server for queries and other messages that require source
+ identification. The Source Attribute uniquely identifies the source
+ of the message. Valid Source Attribute types are shown below.
+
+ Valid Source Attributes
+ -----------------------
+ iSCSI Name
+ FC Port Name WWPN
+
+ For a query operation, the Source Attribute is used to limit the
+ scope of the specified operation to the Discovery Domains of which
+ the source is a member. Special Control Nodes, identified by the
+ Source Attribute, may be administratively configured to perform the
+ specified operation on all objects in the iSNS database without
+ scoping to Discovery Domains.
+
+ For messages that change the contents of the iSNS database, the iSNS
+ server MUST verify that the Source Attribute identifies either a
+ Control Node or a Storage Node that is a part of the Network Entity
+ containing the added, deleted, or modified objects.
+
+5.6.2. Message Key Attributes
+
+ Message Key attributes are used to identify matching objects in the
+ iSNS database for iSNS query and registration messages. If present,
+ the Message Key MUST be a Registration or Query Key for an object as
+ described in Sections 5.6.5 and 6.1. A Message Key is not required
+ when a query spans the entire set of objects available to the Source
+ or a registration is for a new Entity.
+
+ iSCSI Names used in the Message Key MUST be normalized according to
+ the stringprep template [STRINGPREP]. Entity Identifiers (EIDs) used
+ in the Message Key MUST be normalized according to the nameprep
+ template [NAMEPREP].
+
+5.6.3. Delimiter Attribute
+
+ The Delimiter Attribute separates the Message Key attributes from the
+ Operating Attributes in a PDU Payload. The Delimiter Attribute has a
+ tag value of 0 and a length value of 0. The Delimiter Attribute is
+ always 8 bytes long (a 4-byte tag field and a 4-byte length field,
+
+
+
+Tseng, et al. Standards Track [Page 42]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ all containing zeros). If a Message Key is not required for a
+ message, then the Delimiter Attribute immediately follows the Source
+ Attribute.
+
+5.6.4. Operating Attributes
+
+ The Operating Attributes are a list of one or more key and non-key
+ attributes related to the actual iSNS registration or query operation
+ being performed.
+
+ Operating Attributes include object key attributes and non-key
+ attributes. Object key attributes uniquely identify iSNS objects.
+ Key attributes MUST precede the non-key attributes of each object in
+ the Operating Attributes. The tag value distinguishes the attribute
+ as an object key attribute (i.e., tag=1, 16&17, 32, 64, and 96) or a
+ non-key attribute. iSCSI Names used in the Operating Attributes MUST
+ be normalized according to the stringprep template [STRINGPREP].
+ Entity Identifiers (EIDs) used in the Operating Attributes MUST be
+ normalized according to the nameprep template [NAMEPREP].
+
+ The ordering of Operating Attributes in the message is important for
+ determining the relationships among objects and their ownership of
+ non-key attributes. iSNS protocol messages that violate these
+ ordering rules SHALL be rejected with the Status Code of 2 (Message
+ Format Error). See the message descriptions for proper operating
+ attribute ordering requirements.
+
+ Some objects are keyed by more than one object key attribute value.
+ For example, the Portal object is keyed by attribute tags 16 and 17.
+ When describing an object keyed by more than one key attribute, every
+ object key attribute of that object MUST be listed sequentially by
+ tag value in the message before non-key attributes of that object and
+ key attributes of the next object. A group of key attributes of this
+ kind is treated as a single logical key attribute when identifying an
+ object.
+
+ Non-key attributes that immediately follow key attributes MUST be
+ attributes of the object referenced by the key attributes. All non-
+ key attributes of an object MUST be listed before the object key
+ attributes introducing the next object.
+
+ Objects MUST be listed in inheritance order, according to their
+ containment order. Storage Node and Portal objects and their
+ respective attributes MUST follow the Network Entity object to which
+ they have a relationship. Similarly, FC Device objects MUST follow
+ the Storage Node object to which they have a relationship.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 43]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Vendor-specific objects defined by tag values in the range 1537-2048
+ have the same requirements described above.
+
+5.6.4.1. Operating Attributes for Query and Get Next Requests
+
+ In Query and Get Next request messages, TLV attributes with length
+ value of 0 are used to indicate which Operating Attributes are to be
+ returned in the corresponding response. Operating Attribute values
+ that match the TLV attributes in the original message are returned in
+ the response message.
+
+5.6.5. Registration and Query Request Message Types
+
+ The following describes each query and message type.
+
+5.6.5.1. Device Attribute Registration Request (DevAttrReg)
+
+ The DevAttrReg message type is 0x0001. The DevAttrReg message
+ provides the means for iSNS clients to update existing objects or
+ register new objects. The value of the replace bit in the FLAGs
+ field determines whether the DevAttrReg message updates or replaces
+ an existing registration.
+
+ The Source Attribute identifies the Node initiating the registration
+ request.
+
+ The Message Key identifies the object the DevAttrReg message acts
+ upon. It MUST contain the key attribute(s) identifying an object.
+ This object MUST contain all attributes and related subordinate
+ object attributes that will be included in the Operating Attributes
+ of the DevAttrReg PDU Payload. The key attribute(s) identifying this
+ object MUST also be included among the Operating Attributes.
+
+ If the Message Key contains an EID and no pre-existing objects match
+ the Message Key, then the DevAttrReg message SHALL create a new
+ Entity with the specified EID and any new object(s) specified by the
+ Operating Attributes. The replace bit SHALL be ignored.
+
+ If the Message Key does not contain an EID, and no pre-existing
+ objects match the Message Key, then the DevAttrReg message SHALL be
+ rejected with a status code of 3 (Invalid Registration).
+
+ If the Message Key is not present, then the DevAttrReg message
+ implicitly registers a new Network Entity. In this case, the replace
+ bit SHALL be ignored; a new Network Entity SHALL be created.
+ Existing entities, their objects, and their relationships remain
+ unchanged.
+
+
+
+
+Tseng, et al. Standards Track [Page 44]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ The replace bit determines the kind of operation conducted on the
+ object identified in the DevAttrReg Message Key. The replace bit
+ only applies to the DevAttrReg message; it is ignored for all other
+ message types.
+
+ If the replace bit is set, then the objects, attributes, and
+ relationships specified in the Operating Attributes SHALL replace the
+ object identified by the Message Key. The object and all of its
+ subordinate objects SHALL be deregistered, and the appropriate SCNs
+ SHALL be sent by the iSNS server for the deregistered objects. The
+ objects listed in the Operating Attributes are then used to replace
+ the just-deregistered objects. Note that additional SCNs SHALL be
+ sent for the newly-registered objects, if appropriate. Existing
+ objects and relationships that are not identified or that are
+ subordinate to the object identified by the Message Key MUST NOT be
+ affected or changed.
+
+ If the replace bit is not set, then the message updates the
+ attributes of the object identified by the Message Key and its
+ subordinate objects. Existing object containment relationships MUST
+ NOT be changed. For existing objects, key attributes MUST NOT be
+ modified, but new subordinate objects MAY be added.
+
+ The Operating Attributes represent objects, attributes, and
+ relationships that are to be registered. Multiple related objects
+ and attributes MAY be registered in a single DevAttrReg message. The
+ ordering of the objects in this message indicates the structure of,
+ and associations among, the objects to be registered. At least one
+ object MUST be listed in the Operating Attributes. Additional
+ objects (if any) MUST be subordinate to the first object listed. Key
+ attributes MUST precede non-key attributes of each object. A given
+ object may only appear a maximum of once in the Operating Attributes
+ of a message. If the Node identified by the Source Attribute is not
+ a Control Node, then the objects in the operating attributes MUST be
+ members of the same Network Entity as the Source Node.
+
+ For example, to establish relationships between a Network Entity
+ object and its Portal and Storage Node objects, the Operating
+ Attributes list the key and non-key attributes of the Network Entity
+ object, followed by the key and non-key attributes of each Portal and
+ Storage Node object to be linked to that Network Entity. Similarly,
+ an FC Device object that follows a Storage Node object is considered
+ subordinate to that Storage Node.
+
+ New PG objects are registered when an associated Portal or iSCSI Node
+ object is registered. An explicit PG object registration MAY follow
+ a Portal or iSCSI Node object registration in a DevAttrReg message.
+
+
+
+
+Tseng, et al. Standards Track [Page 45]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ When a Portal is registered, the Portal attributes MAY immediately be
+ followed by a PGT attribute. The PGT attribute SHALL be followed by
+ the set of PG iSCSI Names representing nodes that will be associated
+ to the Portal using the indicated PGT value. Additional sets of PGTs
+ and PG iSCSI Names to be associated to the registered Portal MAY
+ follow. Indicated PGT values are assigned to the PG object
+ associated with the newly registered Portal and to the iSCSI Storage
+ Node(s) referenced immediately following the PGT attribute in the
+ operating attributes.
+
+ When an iSCSI Storage Node is registered, the Storage Node attributes
+ MAY immediately be followed by a PGT attribute. The PGT attribute
+ SHALL be followed by the set of PG Portal IP-Address, PG TCP/UDP Port
+ pairs representing Portal objects that will be associated with the
+ Storage Node using the indicated PGT value. Additional sets of PGTs
+ and PG Portal IP-Address PG TCP/UDP Port pairs to be associated with
+ the registered Storage Node MAY follow. Indicated PGT values are
+ assigned to the PG object associated with the newly registered iSCSI
+ Storage Node and Portal object(s) referenced immediately following
+ the PGT attribute in the operating attributes.
+
+ If the PGT value is not included in the Storage Node or Portal object
+ registration, and if a PGT value was not previously registered for
+ the relationship, then the PGT for the corresponding PG object SHALL
+ be registered with a value of 0x00000001. If the PGT attribute is
+ included in the registration message as a 0-length TLV, then the PGT
+ value for the corresponding PG object SHALL be registered as NULL. A
+ 0-length TLV for the PGT in an update registration message overwrites
+ the previous PGT value with NULL, indicating that there is no
+ relationship between the Storage Node and Portal.
+
+ A maximum of one Network Entity object can be created or updated with
+ a single DevAttrReg message. Consequently, the Operating Attributes
+ MUST NOT contain more than one Network Entity object. There is no
+ limit to the number of Portal, Storage Node, and FC Device objects
+ that can listed in the Operating Attributes, provided they are all
+ subordinate to the listed Network Entity object.
+
+ If the Message Key and Operating Attributes do not contain an EID
+ attribute, or if the EID attribute has a length of 0, then a new
+ Network Entity object SHALL be created and the iSNS server SHALL
+ supply a unique EID value for it. The assigned EID value SHALL be
+ included in the DevAttrReg Response message. If the Message Key and
+ Operating Attributes contain an EID that does not match the EID of an
+ existing Network Entity in the iSNS database, then a new Network
+ Entity SHALL be created and assigned the value contained in that EID
+ attribute. Finally, if the Message Key and Operating Attributes
+ contain an EID that matches the EID of an existing object in the iSNS
+
+
+
+Tseng, et al. Standards Track [Page 46]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ database, then the objects, attributes, and relationships specified
+ in the Operating Attributes SHALL be appended to the existing Network
+ Entity identified by the EID.
+
+ A registration message that creates a new Network Entity object MUST
+ contain at least one Portal or one Storage Node. If the message does
+ not, then it SHALL be considered invalid and result in a response
+ with Status Code of 3 (Invalid Registration).
+
+ If an iSNS Server does not support a registration feature, such as
+ explicit PG object registration, then the server SHALL return a
+ Status Code of 23 (Registration Feature Not Supported).
+
+ Note that the iSNS server may modify or reject the registration of
+ certain attributes, such as ESI Interval. In addition, the iSNS
+ server may assign values for additional Operating Attributes that are
+ not explicitly registered in the original DevAttrReg message, such as
+ the EID and WWNN Token.
+
+5.6.5.2. Device Attribute Query Request (DevAttrQry)
+
+ The DevAttrQry message type is 0x0002. The DevAttrQry message
+ provides an iSNS client with the means to query the iSNS server for
+ object attributes.
+
+ The Source Attribute identifies the Node initiating the request. For
+ non-Control Nodes initiating the DevAttrQry message, the query is
+ scoped to the Discovery Domains of which the initiating Node is a
+ member. The DevAttrQry message SHALL only return information on
+ Storage Nodes and their related parent and subordinate objects, where
+ the Storage Node has a common Discovery Domain with the Node
+ identified in the Source Attribute.
+
+ The Message Key may contain key or non-key attributes or no
+ attributes at all. If multiple attributes are used as the Message
+ Key, then they MUST all be from the same object type (e.g., IP
+ address and TCP/UDP Port are attributes of the Portal object type).
+ A Message Key with non-key attributes may match multiple instances of
+ the specific object type. A Message Key with zero-length TLV(s) is
+ scoped to every object of the type indicated by the zero-length
+ TLV(s). An empty Message Key field indicates the query is scoped to
+ the entire database accessible by the source Node.
+
+ The DevAttrQry response message returns attributes of objects listed
+ in the Operating Attributes that are related to the Message Key of
+ the original DevAttrQry message. The Operating Attributes of the
+ DevAttrQry message contain zero-length TLVs that specify the
+ attributes that are to be returned in the DevAttrQryRsp message. A
+
+
+
+Tseng, et al. Standards Track [Page 47]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Message Key containing zero-length TLVs indicates that the set of
+ attributes specified in the Operating Attributes are to be returned
+ for each object matching the type indicated by the Message Key.
+
+ If the Message Key contains non-zero length TLVs, then Operating
+ Attributes for the object matching the Message Key SHALL be returned
+ in the DevAttrQryRsp message. Each attribute type (i.e., zero-length
+ TLV) in the Operating Attributes indicates an attribute from the
+ object matching the Message Key, or from other objects in the same
+ Entity having a relationship to the object matching the Message Key,
+ is to be returned in the response. The ordering of the object keys
+ and associated attributes returned in the DevAttrQry response message
+ SHALL be the same as in the original query message. If no objects
+ match the Message Key, then the DevAttrQryRsp message SHALL NOT
+ return any operating attributes. Such a message and its
+ corresponding response SHALL NOT be considered an error.
+
+ The Portal Group object determines whether a relationship exists
+ between a given Storage Node and Portal object. If the PGT of the
+ Portal Group is not NULL, then a relationship exists between the
+ indicated Storage Node and Portal; if the PGT is NULL, then no
+ relationship exists. Therefore, the value (NULL or not NULL) of the
+ PGT attribute of each Portal Group object determines the structure
+ and ordering of the DevAttrQry response to a query for Storage Nodes
+ and Portals.
+
+ For example, an iSNS database contains a Network Entity having two
+ Portals and two Nodes. Each Storage Node has two Portal Groups, one
+ with a NULL PGT value for one Portal and another with a non-NULL PGT
+ value for the other Portal. The DevAttrQry message contains a
+ Message Key entry matching one of the Nodes, and Operating Attributes
+ with zero-length TLVs listing first the Node attributes, Portal
+ attributes, and then the PG attributes. The response message SHALL
+ therefore return first the matching Node object, then the requested
+ attributes of the one Portal object that can be used to access the
+ Storage Node (as indicated by the PGT), and finally the requested
+ attributes of the PG object used to access that Storage Node. The
+ order in which each object's attributes are listed is the same as the
+ ordering of the object's attributes in the Operating Attributes of
+ the original request message.
+
+ If the Message Key Attribute contains zero-length TLV(s), then the
+ query returns requested attributes for all objects matching the
+ Message Key type (DD restrictions SHALL apply for non-Control Nodes).
+ If multiple objects match the Message Key type, then the attributes
+ for each object matching the Message Key MUST be listed before the
+ attributes for the next matching object are listed in the query
+
+
+
+
+Tseng, et al. Standards Track [Page 48]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ response. In other words, the process described above must be
+ iterated in the message response for each object that matches the
+ Message Key type specified by the zero-length TLV(s).
+
+ For example, an iSNS database contains only one Network Entity having
+ two Portals and three Nodes. All PG objects in the Entity have a PGT
+ value of 0x00000001. In the DevAttrQry message, the Message Key
+ contains a zero-length TLV specifying a Node type, and Operating
+ Attributes listing first the Node attributes, and then the Portal
+ attributes. The response message will return, in the following
+ order, the attributes for the first, next, and last Node objects,
+ each followed by attributes for both Portals. If that same
+ DevAttrQry message had instead contained a zero-length TLV specifying
+ the Network Entity type, then the response message would have
+ returned attributes for all three Node objects, followed by
+ attributes for the two Portals.
+
+ If there is no Message Key Attribute, then the query returns all
+ attributes in the iSNS database (once again, DD restrictions SHALL
+ apply for non-Control Nodes). All attributes matching the type
+ specified by each zero-length TLV in the Operating Attributes SHALL
+ be listed. All attributes of each type SHALL be listed before the
+ attributes matching the next zero-length TLV are listed.
+
+ For example, an iSNS database contains two Entities, each having two
+ Nodes and two Portals. The DevAttrQry message contains no Message
+ Key attribute, and Operating Attributes list first the Portal
+ attributes, and then the Node attributes. The Operating Attributes
+ of the response message will return attributes from each of the four
+ Portals, followed by attributes from each of the four nodes.
+
+ If a DevAttrQry message requests an attribute for which the iSNS
+ server has no value, then the server SHALL NOT return the requested
+ attribute in the query response. Such query and response messages
+ SHALL NOT be considered errors.
+
+ Registration and query messages for iSNS server-specific attributes
+ (i.e., tags in the range 132 to 384) SHALL be formatted using the
+ identifying key attribute of the Storage Node originating the query
+ (i.e., iSCSI Name or FC Port Name WWPN) for both the Source Attribute
+ and Message Key attribute. Operating Attributes SHALL include the
+ TLV of the server-specific attribute being requested.
+
+ DD membership can be discovered through the DevAttrQry message by
+ including either DD member attributes (i.e., DD Member iSCSI Index,
+ DD Member iSCSI Node, DD Member iFCP Node, DD Member Portal Index, DD
+ Member Portal IP Addr, and DD Member Portal TCP/UDP) or the object
+ key of the Storage Node or Portal (i.e., iSCSI Name, iSCSI Index,
+
+
+
+Tseng, et al. Standards Track [Page 49]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Portal IP Addr, Portal TCP/UDP Port, and Portal Index) in the
+ Operating Attributes. Using DD member attributes SHALL return both
+ registered and unregistered member Storage Nodes and/or Portals of a
+ DD. DevAttrQry messages using the Storage Node and/or Portal object
+ key SHALL return only member Storage Nodes or Portals that are
+ currently registered in the iSNS database.
+
+ The DevAttrQry message SHALL support the following minimum set of
+ Message Key Attributes:
+
+ Valid Message Key Attributes for Queries
+ ----------------------------------------
+ Entity Identifier
+ Entity Protocol
+ Portal IP-Address & Portal TCP/UDP Port
+ Portal Index
+ iSCSI Node Type
+ iSCSI Name
+ iSCSI Index
+ PG Index
+ FC Port Name WWPN
+ FC Port Type
+ FC-4 Type
+ Discovery Domain ID
+ Discovery Domain Set ID
+ Source Attribute (for server-specific attributes)
+ Switch Name (FC Device WWNN--for Virtual_Fabric_ID queries)
+
+5.6.5.3. Device Get Next Request (DevGetNext)
+
+ The DevGetNext message type is 0x0003. This message provides the
+ iSNS client with the means to retrieve each and every instance of an
+ object type exactly once.
+
+ The Source Attribute identifies the Node initiating the DevGetNext
+ request, and is used to scope the retrieval process to the Discovery
+ Domains of which the initiating Node is a member.
+
+ The Message Key Attribute may be an Entity Identifier (EID), iSCSI
+ Name, iSCSI Index, Portal IP Address and TCP/UDP Port, Portal Index,
+ PG Index, FC Node Name WWNN, or FC Port Name WWPN. If the TLV length
+ of the Message Key Attribute(s) is zero, then the first object entry
+ in the iSNS database matching the Message Key type SHALL be returned
+ in the Message Key of the corresponding DevGetNextRsp message. If
+ non-zero-length TLV attributes are contained in the Message Key, then
+ the DevGetNext response message SHALL return the next object stored
+ after the object identified by the Message Key in the original
+ DevGetNext request message.
+
+
+
+Tseng, et al. Standards Track [Page 50]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ If the Message Key provided matches the last object instance in the
+ iSNS database, then the Status Code of 9 (No Such Entry) SHALL be
+ returned in the response.
+
+ The Operating Attributes can be used to specify the scope of the
+ DevGetNext request, and to specify the attributes of the next object,
+ which are to be returned in the DevGetNext response message. All
+ Operating Attributes MUST be attributes of the object type identified
+ by the Message Key. For example, if the Message Key is an Entity_ID
+ attribute, then the Operating Attributes MUST NOT contain attributes
+ of Portals.
+
+ Non-zero-length TLV attributes in the Operating Attributes are used
+ to scope the DevGetNext message. Only the next object with attribute
+ values that match the non-zero-length TLV attributes SHALL be
+ returned in the DevGetNext response message.
+
+ Zero-length TLV attributes MUST be listed after non-zero-length
+ attributes in the Operating Attributes of the DevGetNext request
+ message. Zero-length TLV attributes specify the attributes of the
+ next object which are to be returned in the DevGetNext response
+ message.
+
+ Note that there are no specific requirements concerning the order in
+ which object entries are retrieved from the iSNS database; the
+ retrieval order of object entries using the DevGetNext message is
+ implementation specific.
+
+ The iSNS client is responsible for ensuring that information acquired
+ through use of the DevGetNext message is accurate and up-to-date.
+ There is no assurance that the iSNS database will not change between
+ successive DevGetNext request messages. If the Message Key provided
+ does not match an existing database entry, then attributes for the
+ next object key following the provided Message Key SHALL be returned.
+ For example, an object entry may have been deleted between successive
+ DevGetNext messages. This may result in a DevGetNext request in
+ which the Message Key does not match an existing object entry. In
+ this case, attributes for the next object stored in the iSNS database
+ are returned.
+
+5.6.5.4. Device Deregister Request (DevDereg)
+
+ The DevDereg message type is 0x0004. This message is used to remove
+ object entries from the iSNS database. One or more objects may be
+ removed through a single DevDereg message. Note that deregistered
+ Storage Node objects will retain membership in their Discovery
+ Domain(s) until explicit deregistration of the membership(s) or
+ Discovery Domain(s).
+
+
+
+Tseng, et al. Standards Track [Page 51]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Upon receiving the DevDereg, the iSNS server removes all objects
+ identified by the Operating Attribute(s), and all subordinate objects
+ that are solely dependent on those identified objects. For example,
+ removal of a Network Entity also results in removal of all associated
+ Portal, Portal Group, Storage Node, and FC Device objects associated
+ with that Network Entity. FC Device objects SHALL not be
+ deregistered in this manner unless all Storage Nodes associated with
+ them have been deregistered.
+
+ The DevDereg request PDU Payload contains a Source Attribute and
+ Operating Attribute(s); there are no Message Key Attributes. If the
+ Node identified by the Source Attribute is not a Control Node, then
+ it MUST be from the same Network Entity as the object(s) identified
+ for removal by the Operating Attribute(s). Valid Operating
+ Attributes are shown below:
+
+ Valid Operating Attributes for DevDereg
+ ---------------------------------------
+ Entity Identifier
+ Portal IP-Address & Portal TCP/UDP Port
+ Portal Index
+ iSCSI Name
+ iSCSI Index
+ FC Port Name WWPN
+ FC Node Name WWNN
+
+ The removal of the object may result in SCN messages to the
+ appropriate iSNS clients.
+
+ Attempted deregistration of non-existing entries SHALL not be
+ considered an error.
+
+ If all Nodes and Portals associated with a Network Entity are
+ deregistered, then the Network Entity SHALL also be removed.
+
+ If both the Portal and iSCSI Storage Node objects associated with a
+ Portal Group object are removed, then that Portal Group object SHALL
+ also be removed. The Portal Group object SHALL remain registered as
+ long as either of its associated Portal or iSCSI Storage Node objects
+ remain registered. If a deleted Storage Node or Portal object is
+ subsequently re-registered, then a relationship between the re-
+ registered object and an existing Portal or Storage Node object
+ registration, indicated by the PG object, SHALL be restored.
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 52]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+5.6.5.5. SCN Register Request (SCNReg)
+
+ The SCNReg message type is 0x0005. The State Change Notification
+ Registration Request (SCNReg) message allows an iSNS client to
+ register a Storage Node to receive State Change Notification (SCN)
+ messages.
+
+ The SCN notifies the Storage Node of changes to any Storage Nodes
+ within any DD of which it is a member. If the Storage Node is a
+ Control Node, it SHALL receive SCN notifications for changes in the
+ entire network. Note that whereas SCNReg sets the SCN Bitmap field,
+ the DevAttrReg message registers the UDP or TCP Port used by each
+ Portal to receive SCN messages. If no SCN Port fields of any Portals
+ of the Storage Node are registered to receive SCN messages, then the
+ SCNReg message SHALL be rejected with Status Code 17 (SCN
+ Registration Rejected).
+
+ The SCNReg request PDU Payload contains a Source Attribute, a Message
+ Key Attribute, and an Operating Attribute. Valid Message Key
+ Attributes for a SCNReg are shown below:
+
+ Valid Message Key Attributes for SCNReg
+ ---------------------------------------
+ iSCSI Name
+ FC Port Name WWPN
+
+ The node with the iSCSI Name or FC Port Name WWPN attribute that
+ matches the Message Key in the SCNReg message is registered to
+ receive SCNs using the specified SCN bitmap. A maximum of one Node
+ SHALL be registered for each SCNReg message.
+
+ The SCN Bitmap is the only operating attribute of this message, and
+ it always overwrites the previous contents of this field in the iSNS
+ database. The bitmap indicates the SCN event types for which the
+ Node is registering.
+
+ Note that the settings of this bitmap determine whether the SCN
+ registration is for regular SCNs or management SCNs. Control Nodes
+ MAY conduct registrations for management SCNs; iSNS clients that are
+ not supporting Control Nodes MUST NOT conduct registrations for
+ management SCNs. Control Nodes that register for management SCNs
+ receive a copy of every SCN message generated by the iSNS server. It
+ is recommended that management registrations be used only when needed
+ in order to conserve iSNS server resources. In addition, a Control
+ Node that conducts such registrations should be prepared to receive
+ the anticipated volume of SCN message traffic.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 53]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+5.6.5.6. SCN Deregister Request (SCNDereg)
+
+ The SCNDereg message type is 0x0006. The SCNDereg message allows an
+ iSNS client to stop receiving State Change Notification (SCN)
+ messages.
+
+ The SCNDereg request message PDU Payload contains a Source Attribute
+ and Message Key Attribute(s). Valid Message Key Attributes for a
+ SCNDereg are shown below:
+
+ Valid Message Key Attributes for SCNDereg
+ -----------------------------------------
+ iSCSI Name
+ FC Port Name WWPN
+
+ The node with an iSCSI Name or FC Port Name WWPN attribute that
+ matches the Message Key Attributes in the SCNDereg message is
+ deregistered for SCNs. The SCN bitmap field of such Nodes are
+ cleared. A maximum of one Node SHALL be deregistered for each
+ SCNDereg message.
+
+ There are no Operating Attributes in the SCNDereg message.
+
+5.6.5.7. SCN Event (SCNEvent)
+
+ The SCNEvent message type is 0x0007. The SCNEvent is a message sent
+ by an iSNS client to request generation of a State Change
+ Notification (SCN) message by the iSNS server. The SCN, sent by the
+ iSNS server, then notifies iFCP, iSCSI, and Control Nodes within the
+ affected DD of the change indicated in the SCNEvent.
+
+ Most SCNs are automatically generated by the iSNS server when Nodes
+ are registered or deregistered from the directory database. SCNs are
+ also generated when a network management application or Control Node
+ makes changes to the DD membership in the iSNS server. However, an
+ iSNS client can trigger an SCN by using SCNEvent.
+
+ The SCNEvent message PDU Payload contains a Source Attribute, a
+ Message Key Attribute, and an Operating Attribute. Valid Key
+ Attributes for a SCNEvent are shown below:
+
+ Valid Message Key Attributes for SCNEvent
+ -----------------------------------------
+ iSCSI Name
+ FC Port Name WWPN
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 54]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ The Operating Attributes section SHALL contain the SCN Event Bitmap
+ attribute. The bitmap indicates the event that caused the SCNEvent
+ to be generated.
+
+5.6.5.8. State Change Notification (SCN)
+
+ The SCN message type is 0x0008. The SCN is a message generated by
+ the iSNS server, notifying a registered Storage Node of changes.
+ There are two types of SCN registrations: regular registrations and
+ management registrations. Regular SCNs notify iSNS clients of events
+ within the discovery domain. Management SCNs notify Control Nodes
+ that register for management SCNs of events occurring anywhere in the
+ network.
+
+ If no active TCP connection to the SCN recipient exists, then the SCN
+ message SHALL be sent to one Portal of the registered Storage Node
+ that has a registered TCP or UDP Port value in the SCN Port field.
+ If more than one Portal of the Storage Node has a registered SCN Port
+ value, then the SCN SHALL be delivered to any one of the indicated
+ Portals, provided that the selected Portal is not the subject of the
+ SCN.
+
+ The types of events that can trigger an SCN message, and the amount
+ of information contained in the SCN message, depend on the registered
+ SCN Event Bitmap for the Storage Node. The iSCSI Node SCN Bitmap is
+ described in Section 6.4.4. The iFCP SCN Bitmap is described in
+ Section 6.6.12.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 55]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ The format of the SCN PDU Payload is shown below:
+
+ +----------------------------------------+
+ | Destination Attribute |
+ +----------------------------------------+
+ | Timestamp |
+ +----------------------------------------+
+ | Source SCN Bitmap 1 |
+ +----------------------------------------+
+ | Source Attribute [1] |
+ +----------------------------------------+
+ | Source Attribute [2](if present) |
+ +----------------------------------------+
+ | Source Attribute [3](if present) |
+ +----------------------------------------+
+ | Source Attribute [n](if present) |
+ +----------------------------------------+
+ | Source SCN Bitmap 2 (if present) |
+ +----------------------------------------+
+ | . . . |
+ +----------------------------------------+
+
+ All PDU Payload attributes are in TLV format.
+
+ The Destination Attribute is the Node identifier that is receiving
+ the SCN. The Destination Attribute can be an iSCSI Name or FC Port
+ Name.
+
+ The Timestamp field, using the Timestamp TLV format, described in
+ Section 6.2.4, indicates the time the SCN was generated.
+
+ The Source SCN Bitmap field indicates the type of SCN notification
+ (i.e., regular or management SCN), and the type of event that caused
+ the SCN to be generated; it does not necessarily correlate with the
+ original SCN bitmap registered in the iSNS server.
+
+ Following the timestamp, the SCN message SHALL list the SCN bitmap,
+ followed by the key attribute (i.e., iSCSI Name or FC Port Name) of
+ the Storage Node affected by the SCN event. If the SCN is a
+ Management SCN, then the SCN message SHALL also list the DD_ID and/or
+ DDS_ID of the Discovery Domains and Discovery Domain Sets (if any)
+ that caused the change in state for that Storage Node. These
+ additional attributes (i.e., DD_ID and/or DDS_ID) shall immediately
+ follow the iSCSI Name or FC Port Name and precede the next SCN bitmap
+ for the next notification message (if any). The SCN bitmap is used
+ as a delineator for SCN messages providing multiple state change
+ notifications.
+
+
+
+
+Tseng, et al. Standards Track [Page 56]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ For example, a regular SCN for notifying an iSNS client of a new
+ Portal available for a particular iSCSI target would contain the SCN
+ bitmap followed by the iSCSI Name of the target device as the source
+ attribute. If the SCN were a management SCN, then the iSCSI Name
+ would be followed by the DD_ID(s) of the shared Discovery Domains
+ that allow the destination Storage Node to have visibility to the
+ affected Storage Node. If a Discovery Domain Set (DDS) was enabled
+ in order to provide this visibility, then the appropriate DDS_ID
+ would be included as well.
+
+ A management SCN is also generated to notify a Control Node of the
+ creation, deletion, or modification of a Discovery Domain or
+ Discovery Domain Set. In this case, the DD_ID and/or DDS_ID of the
+ affected Discovery Domain and/or Discovery Domain Set would follow
+ the SCN bitmap.
+
+ For example, a management SCN to notify a Control Node of a new DD
+ within a Discovery Domain Set would contain both the DD_ID and the
+ DDS_ID of the affected Discovery Domain and Discovery Domain Set
+ among the Source Attributes.
+
+ See Sections 6.4.4 and 6.6.12 for additional information on the SCN
+ Bitmap.
+
+5.6.5.9. DD Register (DDReg)
+
+ The DDReg message type is 0x0009. This message is used to create a
+ new Discovery Domain (DD), to update an existing DD Symbolic Name
+ and/or DD Features attribute, and to add DD members.
+
+ DDs are uniquely defined using DD_IDs. DD registration attributes
+ are described in Section 6.11.
+
+ The DDReg message PDU Payload contains the Source Attribute and
+ optional Message Key and Operating Attributes.
+
+ The Message Key, if used, contains the DD_ID of the Discovery Domain
+ to be registered. If the Message Key contains a DD_ID of an existing
+ DD entry in the iSNS database, then the DDReg message SHALL attempt
+ to update the existing entry. If the DD_ID in the Message Key (if
+ used) does not match an existing DD entry, then the iSNS server SHALL
+ reject the DDReg message with a status code of 3 (Invalid
+ Registration). If the DD_ID is included in both the Message Key and
+ Operating Attributes, then the DD_ID value in the Message Key MUST be
+ the same as the DD_ID value in the Operating Attributes.
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 57]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ A DDReg message with no Message Key SHALL result in the attempted
+ creation of a new Discovery Domain (DD). If the DD_ID attribute
+ (with non-zero length) is included among the Operating Attributes in
+ the DDReg message, then the new Discovery Domain SHALL be assigned
+ the value contained in that DD_ID attribute. Otherwise, if the DD_ID
+ attribute is not contained among the Operating Attributes of the
+ DDReg message, or if the DD_ID is an operating attribute with a TLV
+ length of 0, then the iSNS server SHALL assign a DD_ID value. The
+ assigned DD_ID value is then returned in the DDReg Response message.
+ The Operating Attributes can also contain the DD Member iSCSI Node
+ Index, DD Member iSCSI Name, DD Member FC Port Name, DD Member Portal
+ IP Address, DD Member Portal TCP/UDP Port Number, or DD Member Portal
+ Index of members to be added to the DD. It may also contain the
+ DD_Symbolic_Name and/or DD_Features of the DD.
+
+ This message SHALL add any DD members listed as Operating Attributes
+ to the Discovery Domain specified by the DD_ID. If the DD_Features
+ attribute is an Operating Attribute, then it SHALL be stored in the
+ iSNS server as the feature list for the specified DD. If the
+ DD_Symbolic_Name is an operating attribute and its value is unique
+ (i.e., it does not match the registered DD_Symbolic_Name for another
+ DD), then the value SHALL be stored in the iSNS database as the
+ DD_Symbolic_Name for the specified Discovery Domain. If the value
+ for the DD_Symbolic_Name is not unique, then the iSNS server SHALL
+ reject the attempted DD registration with a status code of 3 (Invalid
+ Registration).
+
+ When creating a new DD, if the DD_Symbolic_Name is not included in
+ the Operating Attributes, or if it is included with a zero-length
+ TLV, then the iSNS server SHALL provide a unique DD_Symbolic_Name
+ value for the created DD. The assigned DD_Symbolic_Name value SHALL
+ be returned in the DDRegRsp message.
+
+ When creating a new DD, if the DD_Features attribute is not included
+ in the Operating Attributes, then the iSNS server SHALL assign the
+ default value. The default value for DD_Features is 0.
+
+ DD Member iSCSI Name, DD Member iFCP Node, DD Member Portal IP
+ Address, and DD Member TCP/UDP Port Number attributes included in the
+ Operating Attributes need not match currently existing iSNS database
+ entries. This allows, for example, a Storage Node to be added to a
+ DD even if the Storage Node is not currently registered in the iSNS
+ database. A Storage Node or Portal can thereby be added to a DD at
+ the time of the DDs creation, even if the Storage Node or Portal is
+ not currently active in the storage network.
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 58]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ If the Operating Attributes contain a DD Member iSCSI Name value for
+ a Storage Node that is currently not registered in the iSNS database,
+ then the iSNS server MUST allocate an unused iSCSI Node Index for
+ that Storage Node. The assigned iSCSI Node Index SHALL be returned
+ in the DDRegRsp message as the DD Member iSCSI Node Index. The
+ allocated iSCSI Node Index value SHALL be assigned to the Storage
+ Node if and when it registers in the iSNS database.
+
+ If the Operating Attributes contain a DD Member Portal IP Addr and DD
+ Member Portal TCP/UDP value for a Portal that is not currently
+ registered in the iSNS database, then the iSNS server MUST allocate
+ an unused Portal Index value for that Portal. The assigned Portal
+ Index value SHALL be returned in the DDRegRsp message as the DD
+ Member Portal Index. The allocated Portal Index value SHALL be
+ assigned to the Portal if and when it registers in the iSNS database.
+
+ DD Member iSCSI Node Index and DD Member Portal Index attributes that
+ are provided in the Operating Attributes MUST match a corresponding
+ iSCSI Node Index or Portal Index of an existing Storage Node or
+ Portal entry in the iSNS database. Furthermore, the DD Member iSCSI
+ Node Index and DD Member Portal Index SHALL NOT be used to add
+ Storage Nodes or Portals to a DD unless those Storage Nodes or
+ Portals are actively registered in the iSNS database.
+
+5.6.5.10. DD Deregister (DDDereg)
+
+ The DDDereg message type is 0x000A. This message allows an iSNS
+ client to deregister an existing Discovery Domain (DD) and to remove
+ members from an existing DD.
+
+ DDs are uniquely identified using DD_IDs. DD registration attributes
+ are described in Section 6.11.
+
+ The DDDereg message PDU Payload contains a Source Attribute, Message
+ Key Attribute, and optional Operating Attributes.
+
+ The Message Key Attribute for a DDDereg message is the DD ID for the
+ Discovery Domain being removed or having members removed. If the DD
+ ID matches an existing DD and there are no Operating Attributes, then
+ the DD SHALL be removed and a success Status Code returned. Any
+ existing members of that DD SHALL remain in the iSNS database without
+ membership in the just-removed DD.
+
+ If the DD ID matches an existing DD and there are Operating
+ Attributes matching DD members, then the DD members identified by the
+ Operating Attributes SHALL be removed from the DD and a successful
+ Status Code returned.
+
+
+
+
+Tseng, et al. Standards Track [Page 59]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ If a DD Member iSCSI Name identified in the Operating Attributes
+ contains an iSCSI Name for a Storage Node that is not currently
+ registered in the iSNS database or contained in another DD, then the
+ association between that Storage Node and its pre-assigned iSCSI Node
+ Index SHALL be removed. The pre-assigned iSCSI Node Index value no
+ longer has an association to a specific iSCSI Name and can now be
+ re-assigned.
+
+ If a DD Member Portal IP Address and DD Member TCP/UDP Port
+ identified in the Operating Attributes reference a Portal that is not
+ currently registered in the iSNS database or contained in another DD,
+ then the association between that Portal and its pre-assigned Portal
+ Index SHALL be removed. The pre-assigned Portal Index value can now
+ be reassigned.
+
+ The attempted deregistration of non-existent DD entries SHALL not be
+ considered an error.
+
+5.6.5.11. DDS Register (DDSReg)
+
+ The DDSReg message type is 0x000B. This message allows an iSNS
+ client to create a new Discovery Domain Set (DDS), to update an
+ existing DDS Symbolic Name and/or DDS Status, or to add DDS members.
+
+ DDSs are uniquely defined using DDS_IDs. DDS registration attributes
+ are described in Section 6.11.1.
+
+ The DDSReg message PDU Payload contains the Source Attribute and,
+ optionally, Message Key and Operating Attributes.
+
+ The Message Key, if used, contains the DDS_ID of the Discover Domain
+ Set to be registered or modified. If the Message Key contains a
+ DDS_ID of an existing DDS entry in the iSNS database, then the DDSReg
+ message SHALL attempt to update the existing entry. If the DDS_ID in
+ the Message Key (if used) does not match an existing DDS entry, then
+ the iSNS server SHALL reject the DDSReg message with a status code of
+ 3 (Invalid Registration). If the DDS_ID is included in both the
+ Message Key and Operating Attributes, then the DDS_ID value in the
+ Message Key MUST be the same as the DDS_ID value in the Operating
+ Attributes.
+
+ A DDSReg message with no Message Key SHALL result in the attempted
+ creation of a new Discovery Domain Set (DDS). If the DDS_ID
+ attribute (with non-zero length) is included among the Operating
+ Attributes in the DDSReg message, then the new Discovery Domain Set
+ SHALL be assigned the value contained in that DDS_ID attribute.
+ Otherwise, if the DDS_ID attribute is not contained among the
+ Operating Attributes of the DDSReg message, or if the DDS_ID is an
+
+
+
+Tseng, et al. Standards Track [Page 60]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ operating attribute with a TLV length of 0, then the iSNS server
+ SHALL assign a DDS_ID value. The assigned DDS_ID value is then
+ returned in the DDSReg Response message. The Operating Attributes
+ can also contain the DDS_Symbolic_Name, the DDS Status, and the
+ DD_IDs of Discovery Domains to be added to the DDS.
+
+ When creating a new DDS, if the DDS Symbolic Name is included in the
+ Operating Attributes and its value is unique (i.e., it does not match
+ the registered DDS Symbolic Name for another DDS), then the value
+ SHALL be stored in the iSNS database as the DDS Symbolic Name for
+ that DDS. If the value for the DDS Symbolic Name is not unique, then
+ the iSNS server SHALL reject the attempted DDS registration with a
+ status code of 3 (Invalid Registration).
+
+ When creating a new DDS, if the DDS Symbolic Name is not included in
+ the Operating Attributes, or if it is included with a zero-length
+ TLV, then the iSNS server SHALL provide a unique DDS Symbolic Name
+ value for the created DDS. The assigned DDS Symbolic Name value
+ SHALL be returned in the DDSRegRsp message.
+
+ This message SHALL add any DD_IDs listed as Operating Attributes to
+ the Discovery Domain Set specified by the DDS_ID Message Key
+ Attribute. In addition, if the DDS_Symbolic_Name is an operating
+ attribute and the value is unique, then it SHALL be stored in the
+ iSNS database as the DDS_Symbolic_Name for the specified Discovery
+ Domain Set.
+
+ If a DD_ID listed in the Operating Attributes does not match an
+ existing DD, then a new DD using the DD_ID SHALL be created. In this
+ case for the new DD, the iSNS server SHALL assign a unique value for
+ the DD Symbolic Name and SHALL set the DD Features attribute to the
+ default value of 0. These assigned values SHALL be returned in the
+ DDSRegRsp message.
+
+5.6.5.12. DDS Deregister (DDSDereg)
+
+ The DDSDereg message type is 0x000C. This message allows an iSNS
+ client to deregister an existing Discovery Domain Set (DDS) or to
+ remove some DDs from an existing DDS.
+
+ The DDSDereg message PDU Payload contains a Source Attribute, a
+ Message Key Attribute, and optional Operating Attributes.
+
+ The Message Key Attribute for a DDSDereg message is the DDS ID for
+ the DDS being removed or having members removed. If the DDS ID
+ matches an existing DDS and there are no Operating Attributes, then
+
+
+
+
+
+Tseng, et al. Standards Track [Page 61]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ the DDS SHALL be removed and a success Status Code returned. Any
+ existing members of that DDS SHALL remain in the iSNS database
+ without membership in the just-removed DDS.
+
+ If the DDS ID matches an existing DDS, and there are Operating
+ Attributes matching DDS members, then the DDS members SHALL be
+ removed from the DDS and a success Status Code returned.
+
+ The attempted deregistration of non-existent DDS entries SHALL not be
+ considered an error.
+
+5.6.5.13. Entity Status Inquiry (ESI)
+
+ The ESI message type is 0x000D. This message is sent by the iSNS
+ server, and is used to verify that an iSNS client Portal is reachable
+ and available. The ESI message is sent to the ESI UDP port provided
+ during registration, or to the TCP connection used for ESI
+ registration, depending on which communication type that is being
+ used.
+
+ The ESI message PDU Payload contains the following attributes in TLV
+ format and in the order listed: the current iSNS timestamp, the EID,
+ the Portal IP Address, and the Portal TCP/UDP Port. The format of
+ this message is shown below:
+
+ +----------------------------------------+
+ | Timestamp |
+ +----------------------------------------+
+ | Entity_ID |
+ +----------------------------------------+
+ | Portal IP Address |
+ +----------------------------------------+
+ | Portal TCP/UDP Port |
+ +----------------------------------------+
+
+ The ESI response message PDU Payload contains a status code, followed
+ by the Attributes from the original ESI message.
+
+ If the Portal fails to respond to an administratively-determined
+ number of consecutive ESI messages, then the iSNS server SHALL remove
+ that Portal from the iSNS database. If there are no other remaining
+ ESI-monitored Portals for the associated Network Entity, then the
+ Network Entity SHALL also be removed. The appropriate State Change
+ Notifications, if any, SHALL be triggered.
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 62]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+5.6.5.14. Name Service Heartbeat (Heartbeat)
+
+ This message, if used, is only sent by the active iSNS server. It
+ allows iSNS clients and backup servers listening to a broadcast or
+ multicast address to discover the IP address of the primary and
+ backup iSNS servers. It also allows concerned parties to monitor the
+ health and status of the primary iSNS server.
+
+ This message is NOT in TLV format. There is no response message to
+ the Name Service Heartbeat.
+
+ MSb LSb
+ 0 31
+ +------------------------------------------------+
+ | Active Server IP-Address | 16 Bytes
+ +------------------------------------------------+
+ | iSNS TCP Port | iSNS UDP Port | 4 Bytes
+ +------------------------------------------------+
+ | Interval | 4 Bytes
+ +------------------------------------------------+
+ | Counter | 4 Bytes
+ +------------------------------------------------+
+ | RESERVED | Backup Servers | 4 Bytes
+ +------------------------------------------------+
+ | Primary Backup Server IP Address(if any) | 16 Bytes
+ +------------------------------------------------+
+ |Backup TCP Port(if any)|Backup UDP Port(if any) | 4 Bytes
+ +------------------------------------------------+
+ | 2nd Backup Server IP Address(if any) | 16 Bytes
+ +------------------------------------------------+
+ |Backup TCP Port(if any)|Backup UDP Port(if any) | 4 Bytes
+ +------------------------------------------------+
+ | . . . |
+ +------------------------------------------------+
+ | VENDOR SPECIFIC |
+ +------------------------------------------------+
+
+ The heartbeat PDU Payload contains the following:
+
+ Active Server IP Address: the IP Address of the active iSNS server in
+ IPv6 format. When this field contains an IPv4
+ value, it is stored as an IPv4-mapped IPv6 address.
+ That is, the most significant 10 bytes are set to
+ 0x00, with the next two bytes set to 0xFFFF
+ [RFC2373]. When this field contains an IPv6 value,
+ the entire 16-byte field is used.
+
+ Active TCP Port: the TCP Port of the server currently in use.
+
+
+
+Tseng, et al. Standards Track [Page 63]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Active UDP Port: the UDP Port of the server currently in use,
+ otherwise 0.
+
+ Interval: the interval, in seconds, of the heartbeat.
+
+ Counter: a count that begins at 0 when this server becomes
+ active. The count increments by one for each
+ heartbeat sent since this server became active.
+
+ Backup Servers: the number of iSNS backup servers. The IP address,
+ TCP Port, and UDP Port of each iSNS backup server
+ follow this field. Note that if backup servers are
+ used, then the active iSNS server SHOULD be among
+ the list of backup servers.
+
+ The content of the remainder of this message after the list of backup
+ servers is vendor-specific. Vendors may use additional fields to
+ coordinate between multiple iSNS servers, and/or to identify vendor-
+ specific features.
+
+5.6.5.15. Request FC_DOMAIN_ID (RqstDomId)
+
+ The RqstDomId message type is 0x0011. This message is used for iFCP
+ Transparent Mode to allocate non-overlapping FC_DOMAIN_ID values
+ between 1 and 239. The iSNS server becomes the address assignment
+ authority for the entire iFCP fabric. To obtain multiple
+ FC_DOMAIN_ID values, this request must be repeated to the iSNS server
+ multiple times. iSNS clients that acquire FC_DOMAIN_ID values from
+ an iSNS server MUST register for ESI monitoring from that iSNS
+ server.
+
+ The RqstDomId PDU Payload contains three TLV attributes in the
+ following order: the requesting Switch Name (WWN) as the Source
+ Attribute, the Virtual_Fabric_ID as the Message Key Attribute, and
+ Preferred ID as the operating attribute. The Virtual_Fabric_ID is a
+ string identifying the domain space for which the iSNS server SHALL
+ allocate non-overlapping integer FC_DOMAIN_ID values between 1 and
+ 239. The Preferred_ID is the nominal FC_DOMAIN_ID value requested by
+ the iSNS client. If the Preferred_ID value is available and has not
+ already been allocated for the Virtual_Fabric_ID specified in the
+ message, the iSNS server SHALL return the requested Preferred_ID
+ value as the Assigned_ID to the requesting client.
+
+ The RqstDomId response contains a Status Code, and the TLV attribute
+ Assigned ID, which contains the integer value in the space requested.
+ If no further unallocated values are available from this space, the
+ iSNS server SHALL respond with the Status Code 18 "FC_DOMAIN_ID Not
+ Available".
+
+
+
+Tseng, et al. Standards Track [Page 64]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Once a FC_DOMAIN_ID value has been allocated to an iSNS client by the
+ iSNS server for a given Virtual_Fabric_ID, that FC_DOMAIN_ID value
+ SHALL NOT be reused until it has been deallocated, or until ESI
+ monitoring detects that the iSNS client no longer exists on the
+ network and objects for that client are removed from the iSNS
+ database.
+
+ The iSNS server and client SHALL use TCP to transmit and receive
+ RqstDomId, RqstDomIdRsp, RlseDomId, and RlseDomIdRsp messages.
+
+5.6.5.16. Release FC_DOMAIN_ID (RlseDomId)
+
+ The RlseDomId message type is 0x0012. This message may be used by
+ iFCP Transparent Mode to release integer identifier values used to
+ assign 3-byte Fibre Channel PORT_ID values.
+
+ The RlseDomId message contains three TLV attributes in the following
+ order: the requesting EID as the Source Attribute, the
+ Virtual_Fabric_ID as the Message Key Attribute, and Assigned_ID as
+ the operating attribute. Upon receiving the RlseDomId message, the
+ iSNS server SHALL deallocate the FC_DOMAIN_ID value contained in the
+ Assigned_ID attribute for the Virtual_Fabric_ID attribute specified.
+ Upon deallocation, that FC_DOMAIN_ID value can then be requested by
+ and assigned to a different iSNS client.
+
+ The iSNS server and client SHALL use TCP to transmit and receive
+ RqstDomId, RqstDomIdRsp, RlseDomId, and RlseDomIdRsp messages.
+
+5.6.5.17. Get FC_DOMAIN_IDs (GetDomId)
+
+ The GetDomId message type is 0x0013. This message is used to learn
+ the currently-allocated FC_DOMAIN_ID values for a given
+ Virtual_Fabric_ID.
+
+ The GetDomId message PDU Payload contains a Source Attribute and
+ Message Key Attribute.
+
+ The Message Key Attribute for the GetDomId message is the
+ Virtual_Fabric_ID. The response to this message returns all the
+ FC_DOMAIN_ID values that have been allocated for the
+ Virtual_Fabric_ID specified.
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 65]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+5.7. Messages
+
+ The iSNSP response message PDU Payloads contain a Status Code,
+ followed by a list of attributes, and have the following format:
+
+ MSb LSb
+ 0 31
+ +----------------------------------------+
+ | 4-byte STATUS CODE |
+ +----------------------------------------+
+ | Message Key Attribute[1] (if present) |
+ +----------------------------------------+
+ | Message Key Attribute[2] (if present) |
+ +----------------------------------------+
+ | . . . |
+ +----------------------------------------+
+ | - Delimiter Attribute - (if present) |
+ +----------------------------------------+
+ | Operating Attribute[1] (if present) |
+ +----------------------------------------+
+ | Operating Attribute[2] (if present) |
+ +----------------------------------------+
+ | Operating Attribute[3] (if present) |
+ +----------------------------------------+
+ | . . . |
+ +----------------------------------------+
+
+ The iSNSP Response messages SHALL be sent to the iSNS Client IP
+ Address and the originating TCP/UDP Port that was used for the
+ associated registration and query message.
+
+5.7.1. Status Code
+
+ The first field in an iSNSP response message PDU Payload is the
+ Status Code for the operation that was performed. The Status Code
+ encoding is defined in Section 5.4.
+
+5.7.2. Message Key Attributes in Response
+
+ Depending on the specific iSNSP request, the response message MAY
+ contain Message Key Attributes. Message Key Attributes generally
+ contain the interesting key attributes that are affected by the
+ operation specified in the original iSNS registration or query
+ message.
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 66]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+5.7.3. Delimiter Attribute in Response
+
+ The Delimiter Attribute separates the key and Operating Attributes in
+ a response message, if they exist. The Delimiter Attribute has a tag
+ value of 0 and a length value of 0. The Delimiter Attribute is
+ effectively 8 bytes long: a 4-byte tag containing 0x00000000, and a 4
+ Byte length field containing 0x00000000.
+
+5.7.4. Operating Attributes in Response
+
+ The Operating Attributes in a response are the results related to the
+ iSNS registration or query operation being performed. Some response
+ messages will not have Operating Attributes.
+
+5.7.5. Registration and Query Response Message Types
+
+ The following sections describe each query and message type.
+
+5.7.5.1. Device Attribute Registration Response (DevAttrRegRsp)
+
+ The DevAttrRegRsp message type is 0x8001. The DevAttrRegRsp message
+ contains the results for the DevAttrReg message with the same
+ TRANSACTION ID.
+
+ The Message Key in the DevAttrRegRsp message SHALL return the Message
+ Key in the original registration message. If the iSNS server
+ assigned the Entity Identifier for a Network Entity, then the Message
+ Key Attribute field SHALL contain the assigned Entity Identifier.
+
+ The Operating Attributes of the DevAttrRegRsp message SHALL contain
+ the affected object's key and non-key attributes that have been
+ explicitly modified or created by the original DevAttrReg message.
+ Among the Operating Attributes, each modified or added non-key
+ attribute SHALL be listed after its key attribute(s) in the
+ DevAttrRegRsp message. Implicitly registered attributes MUST NOT be
+ returned in the DevAttrRegRsp message. Implicitly registered
+ attributes are those that are assigned a fixed default value or
+ secondary index value by the iSNS server.
+
+ Implicitly registered PG objects (i.e., PG objects that are not
+ explicitly included in the registration or replace message) MUST NOT
+ have their key or non-key attributes returned in the DevAttrRegRsp
+ message. However, explicitly registered PG objects (i.e., those with
+ PGT values that are explicitly included in the registration or
+ replace message) SHALL have their PGT values returned in the
+ DevAttrRegRsp message.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 67]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ For example, three Portals are registered in the original DevAttrReg
+ request message. Due to lack of resources, the iSNS server needs to
+ modify the registered ESI Interval value of one of those Portals. To
+ accomplish this, the iSNS server returns the key attributes
+ identifying the Portal, followed by the non-key modified ESI Interval
+ attribute value, as Operating Attributes of the corresponding
+ DevAttrRegRsp message.
+
+ If the iSNS server rejects a registration due to invalid attribute
+ values or types, then the indicated status code SHALL be 3 (Invalid
+ Registration). If this occurs, then the iSNS server MAY include the
+ list of invalid attributes in the Operating Attributes of the
+ DevAttrRsp message.
+
+ Some attributes values (e.g., ESI Interval, Registration Period) in
+ the original registration message MAY be modified by the iSNS server.
+ This can occur only for a limited set of attribute types, as
+ indicated in the table in Section 6.1. When this occurs, the
+ registration SHALL be considered a success (with status code 0), and
+ the changed value(s) indicated in the Operating Attributes of the
+ DevAttrRsp message.
+
+5.7.5.2. Device Attribute Query Response (DevAttrQryRsp)
+
+ The DevAttrQryRsp message type is 0x8002. The DevAttrQryRsp message
+ contains the results for the DevAttrQry message with the same
+ TRANSACTION ID.
+
+ The Message Key in the DevAttrQryRsp message SHALL return the Message
+ Key in the original query message.
+
+ If no Operating Attributes are included in the original query, then
+ all Operating Attributes SHALL be returned in the response.
+
+ For a successful query result, the DevAttrQryRsp Operating Attributes
+ SHALL contain the results of the original DevAttrQry message.
+
+5.7.5.3. Device Get Next Response (DevGetNextRsp)
+
+ The DevGetNextRsp message type is 0x8003. The DevGetNextRsp message
+ contains the results for the DevGetNext message with the same
+ TRANSACTION ID.
+
+ The Message Key Attribute field returns the object keys for the next
+ object after the Message Key Attribute in the original DevGetNext
+ message.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 68]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ The Operating Attribute field returns the Operating Attributes of the
+ next object as requested in the original DevGetNext message. The
+ values of the Operating Attributes are those associated with the
+ object identified by the Message Key Attribute field of the
+ DevGetNextRsp message.
+
+5.7.5.4. Deregister Device Response (DevDeregRsp)
+
+ The DevDeregRsp message type is 0x8004. This message is the response
+ to the DevDereg request message.
+
+ This message response does not contain a Message Key, but MAY contain
+ Operating Attributes.
+
+ In the event of an error, this response message contains the
+ appropriate status code as well as a list of objects from the
+ original DevDereg message that were not successfully deregistered
+ from the iSNS database. This list of objects is contained in the
+ Operating Attributes of the DevDeregRsp message. Note that an
+ attempted deregistration of a non-existent object does not constitute
+ an error, and non-existent entries SHALL not be returned in the
+ DevDeregRsp message.
+
+5.7.5.5. SCN Register Response (SCNRegRsp)
+
+ The SCNRegRsp message type is 0x8005. This message is the response
+ to the SCNReg request message.
+
+ The SCNRegRsp message does not contain any Message Key or Operating
+ Attributes.
+
+5.7.5.6. SCN Deregister Response (SCNDeregRsp)
+
+ The SCNDeregRsp message type is 0x8006. This message is the response
+ to the SCNDereg request message.
+
+ The SCNDeregRsp message does not contain any Message Key or Operating
+ Attributes.
+
+5.7.5.7. SCN Event Response (SCNEventRsp)
+
+ The SCNEventRsp message type is 0x8007. This message is the response
+ to the SCNEvent request message.
+
+ The SCNEventRsp message does not contain any Message Key or Operating
+ Attributes.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 69]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+5.7.5.8. SCN Response (SCNRsp)
+
+ The SCNRsp message type is 0x8008. This message is sent by an iSNS
+ client, and provides confirmation that the SCN message was received
+ and processed.
+
+ The SCNRsp response contains the SCN Destination Attribute
+ representing the Node identifier that received the SCN.
+
+5.7.5.9. DD Register Response (DDRegRsp)
+
+ The DDRegRsp message type is 0x8009. This message is the response to
+ the DDReg request message.
+
+ The Message Key in the DDRegRsp message SHALL return the Message Key
+ in the original query message. If the original DDReg message did not
+ have a Message Key, then the DDRegRsp message SHALL not have a
+ Message Key.
+
+ If the DDReg operation is successful, the DD ID of the DD created or
+ updated SHALL be returned as an operating attribute of the message.
+
+ If the DD Symbolic Name attribute or DD Features attribute was
+ assigned or updated during the DDReg operation, then any new values
+ SHALL be returned as an operating attribute of the DDRegRsp message.
+
+ If the iSNS server rejects a DDReg due to invalid attribute values or
+ types, then the indicated status code SHALL be 3 (Invalid
+ Registration). If this occurs, then the iSNS server MAY include the
+ list of invalid attributes in the Operating Attributes of the
+ DDRegRsp message.
+
+5.7.5.10. DD Deregister Response (DDDeregRsp)
+
+ The DDDeregRsp message type is 0x800A. This message is the response
+ to the DDDereg request message.
+
+ The DDDeregRsp message does not contain any Message Key or Operating
+ Attributes.
+
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 70]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+5.7.5.11. DDS Register Response (DDSRegRsp)
+
+ The DDSRegRsp message type is 0x800B. This message is the response
+ to the DDSReg request message.
+
+ The Message Key in the DDSRegRsp message SHALL contain the Message
+ Key of the original DDSReg message. If the original DDSReg message
+ did not have a Message Key, then the DDSRegRsp message SHALL NOT have
+ a Message Key.
+
+ If the DDSReg operation is successful, the DDS ID of the DDS created
+ or updated SHALL be returned as an operating attribute of the
+ message.
+
+ If the DDS Symbolic Name attribute or DDS Status attribute was
+ assigned or updated during the DDSRegRsp operation, then any new
+ values SHALL be returned as an operating attribute of the DDSRegRsp
+ message.
+
+ If the iSNS server rejects a DDSReg due to invalid attribute values
+ or types, then the indicated status code SHALL be 3 (Invalid
+ Registration). If this occurs, then the iSNS server MAY include the
+ list of invalid attributes in the Operating Attributes of the
+ DDSRegRsp message.
+
+5.7.5.12. DDS Deregister Response (DDSDeregRsp)
+
+ The DDSDeregRsp message type is 0x800C. This message is the response
+ to the DDSDereg request message.
+
+ The DDSDeregRsp message does not contain any Message Key or Operating
+ Attributes.
+
+5.7.5.13. Entity Status Inquiry Response (ESIRsp)
+
+ The ESIRsp message type is 0x800D. This message is sent by an iSNS
+ client and provides confirmation that the ESI message was received
+ and processed.
+
+ The ESIRsp response message PDU Payload contains the attributes from
+ the original ESI message. These attributes represent the Portal that
+ is responding to the ESI. The ESIRsp Attributes are in the order
+ they were provided in the original ESI message.
+
+ Upon receiving the ESIRsp from the iSNS client, the iSNS server SHALL
+ update the timestamp attribute for that Network Entity and Portal.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 71]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+5.7.5.14. Request FC_DOMAIN_ID Response (RqstDomIdRsp)
+
+ The RqstDomIdRsp message type is 0x8011. This message provides the
+ response for RqstDomId.
+
+ The RqstDomId response contains a Status Code and the TLV attribute
+ Assigned ID, which contains the integer value in the space requested.
+ If no further unallocated values are available from this space, the
+ iSNS server SHALL respond with the Status Code 19 "FC_DOMAIN_ID Not
+ Available".
+
+ Once a FC_DOMAIN_ID value is allocated by the iSNS server, it SHALL
+ NOT be reused until it has been deallocated by the iSNS client to
+ which the value was assigned, or until the ESI message detects that
+ the iSNS client no longer exists on the network.
+
+ The iSNS server and client SHALL use TCP to transmit and receive
+ RqstDomId, RqstDomIdRsp, RlseDomId, and RlseDomIdRsp messages.
+
+5.7.5.15. Release FC_DOMAIN_ID Response (RlseDomIdRsp)
+
+ The RlseDomIdRsp message type is 0x8012. This message provides the
+ response for RlseDomId. The response contains an Error indicating
+ whether the request was successful. If the Assigned_ID value in the
+ original RlseDomId message is not allocated, then the iSNS server
+ SHALL respond with this message using the Status Code 20
+ "FC_DOMAIN_ID Not Allocated".
+
+ The iSNS server and client SHALL use TCP to transmit and receive
+ RqstDomId, RqstDomIdRsp, RlseDomId, and RlseDomIdRsp messages.
+
+5.7.5.16. Get FC_DOMAIN_IDs Response (GetDomIdRsp)
+
+ The GetDomIdRsp message type is 0x8013. This message is used to
+ determine which FC_DOMAIN_ID values have been allocated for the
+ Virtual_Fabric_ID specified in the original GetDomId request message.
+
+ The GetDomId response message PDU Payload contains a Status Code
+ indicating whether the request was successful, and a list of the
+ Assigned IDs from the space requested. The Assigned_ID attributes
+ are listed in TLV format.
+
+5.8. Vendor-Specific Messages
+
+ Vendor-specific iSNSP messages have a functional ID of between 0x0100
+ and 0x01FF, whereas vendor-specific responses have a functional ID of
+ between 0x8100 and 0x81FF. The first Message Key Attribute in a
+
+
+
+
+Tseng, et al. Standards Track [Page 72]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ vendor-specific message SHALL be the company OUI (tag=256)
+ identifying the original creator of the proprietary iSNSP message.
+ The contents of the remainder of the message are vendor-specific.
+
+6. iSNS Attributes
+
+ Attributes can be stored in the iSNS server using iSNSP registration
+ messages, and they can be retrieved using iSNSP query messages.
+ Unless otherwise indicated, these attributes are supplied by iSNS
+ clients using iSNSP registration messages.
+
+6.1. iSNS Attribute Summary
+
+ The complete registry of iSNS attributes is maintained by IANA, and
+ the following table summarizes the initial set of iSNS attributes
+ available at the time of publication of this document.
+
+ Attributes Length Tag Reg Key Query Key
+ ---------- ------ --- ------- ---------
+ Delimiter 0 0 N/A N/A
+ Entity Identifier (EID) 4-256 1 1 1|2|16&17|32|64
+ Entity Protocol 4 2 1 1|2|16&17|32|64
+ Management IP Address 16 3 1 1|2|16&17|32|64
+ Timestamp 8 4 -- 1|2|16&17|32|64
+ Protocol Version Range 4 5 1 1|2|16&17|32|64
+ Registration Period 4 6 1 1|2|16&17|32|64
+ Entity Index 4 7 1 1|2|16&17|32|64
+ Entity Next Index 4 8 -- 1|2|16&17|32|64
+ Entity ISAKMP Phase-1 var 11 1 1|2|16&17|32|64
+ Entity Certificate var 12 1 1|2|16&17|32|64
+ Portal IP Address 16 16 1 1|16&17|32|64
+ Portal TCP/UDP Port 4 17 1 1|16&17|32|64
+ Portal Symbolic Name 4-256 18 16&17 1|16&17|32|64
+ ESI Interval 4 19 16&17 1|16&17|32|64
+ ESI Port 4 20 16&17 1|16&17|32|64
+ Portal Index 4 22 16&17 1|16&17|32|64
+ SCN Port 4 23 16&17 1|16&17|32|64
+ Portal Next Index 4 24 -- 1|16&17|32|64
+ Portal Security Bitmap 4 27 16&17 1|16&17|32|64
+ Portal ISAKMP Phase-1 var 28 16&17 1|16&17|32|64
+ Portal ISAKMP Phase-2 var 29 16&17 1|16&17|32|64
+ Portal Certificate var 31 16&17 1|16&17|32|64
+ iSCSI Name 4-224 32 1 1|16&17|32|33
+ iSCSI Node Type 4 33 32 1|16&17|32
+ iSCSI Alias 4-256 34 32 1|16&17|32
+ iSCSI SCN Bitmap 4 35 32 1|16&17|32
+ iSCSI Node Index 4 36 32 1|16&17|32
+ WWNN Token 8 37 32 1|16&17|32
+
+
+
+Tseng, et al. Standards Track [Page 73]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ iSCSI Node Next Index 4 38 -- 1|16&17|32
+ iSCSI AuthMethod var 42 32 1|16&17|32
+ PG iSCSI Name 4-224 48 32|16&17 1|16&17|32|52
+ PG Portal IP Addr 16 49 32|16&17 1|16&17|32|52
+ PG Portal TCP/UDP Port 4 50 32|16&17 1|16&17|32|52
+ PG Tag (PGT) 4 51 32|16&17 1|16&17|32|52
+ PG Index 4 52 32|16&17 1|16&17|32|52
+ PG Next Index 4 53 -- 1|16&17|32|52
+ FC Port Name WWPN 8 64 1 1|16&17|64|66|96|128
+ Port ID 4 65 64 1|16&17|64
+ FC Port Type 4 66 64 1|16&17|64
+ Symbolic Port Name 4-256 67 64 1|16&17|64
+ Fabric Port Name 8 68 64 1|16&17|64
+ Hard Address 4 69 64 1|16&17|64
+ Port IP-Address 16 70 64 1|16&17|64
+ Class of Service 4 71 64 1|16&17|64
+ FC-4 Types 32 72 64 1|16&17|64
+ FC-4 Descriptor 4-256 73 64 1|16&17|64
+ FC-4 Features 128 74 64 1|16&17|64
+ iFCP SCN bitmap 4 75 64 1|16&17|64
+ Port Role 4 76 64 1|16&17|64
+ Permanent Port Name 8 77 -- 1|16&17|64
+ FC-4 Type Code 4 95 -- 1|16&17|64
+ FC Node Name WWNN 8 96 64 1|16&17|64|96
+ Symbolic Node Name 4-256 97 96 64|96
+ Node IP-Address 16 98 96 64|96
+ Node IPA 8 99 96 64|96
+ Proxy iSCSI Name 4-256 101 96 64|96
+ Switch Name 8 128 128 128
+ Preferred ID 4 129 128 128
+ Assigned ID 4 130 128 128
+ Virtual_Fabric_ID 4-256 131 128 128
+ iSNS Server Vendor OUI 4 256 -- SOURCE Attribute
+ Vendor-Spec iSNS Srvr 257-384 -- SOURCE Attribute
+ Vendor-Spec Entity 385-512 1 1|2|16&17|32|64
+ Vendor-Spec Portal 513-640 16&17 1|16&17|32|64
+ Vendor-Spec iSCSI Node 641-768 32 16&17|32
+ Vendor-Spec FC Port Name 769-896 64 1|16&17|64
+ Vendor-Spec FC Node Name 897-1024 96 64|96
+ Vendor-Specific DDS 1025-1280 2049 2049
+ Vendor-Specific DD 1281-1536 2065 2065
+ Other Vendor-Specific 1537-2048
+ DD_Set ID 4 2049 2049 1|32|64|2049|2065
+ DD_Set Sym Name 4-256 2050 2049 2049
+ DD_Set Status 4 2051 2049 2049
+ DD_Set_Next_ID 4 2052 -- 2049
+ DD_ID 4 2065 2049 1|32|64|2049|2065
+ DD_Symbolic Name 4-256 2066 2065 2065
+
+
+
+Tseng, et al. Standards Track [Page 74]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ DD_Member iSCSI Index 4 2067 2065 2065
+ DD_Member iSCSI Name 4-224 2068 2065 2065
+ DD_Member FC Port Name 8 2069 2065 2065
+ DD_Member Portal Index 4 2070 2065 2065
+ DD_Member Portal IP Addr 16 2071 2065 2065
+ DD_Member Portal TCP/UDP 4 2072 2065 2065
+ DD_Features 4 2078 2065 2065
+ DD_ID Next ID 4 2079 -- 2065
+
+ The following are descriptions of the columns used in the above
+ table:
+
+ Length: indicates the attribute length in bytes used for the TLV
+ format. Variable-length identifiers are NULL-terminated
+ and 4-byte aligned (NULLs are included in the length).
+
+ Tag: the IANA-assigned integer tag value used to identify the
+ attribute. All undefined tag values are reserved.
+
+ Reg Key: indicates the tag values for the object key in DevAttrReg
+ messages for registering a new attribute value in the
+ database. These tags represent attributes defined as
+ object keys in Section 4.
+
+ Query Key: indicates the possible tag values for the Message Key and
+ object key that are used in the DevAttrQry messages for
+ retrieving a stored value from the iSNS database.
+
+ The following is a summary of iSNS attribute tag values available for
+ future allocation by IANA at the time of publication:
+
+ Tag Values Reg Key Query Key
+ ---------- ------- ---------
+ 9-10, 13-15 1 1|2|16&17|32|64
+ 21, 25-26, 30 16&17 1|16&17|32|64
+ 39-41, 44-47 32 1|16&17|32
+ 54-63 32|16&17 1|16&17|32|52
+ 78-82, 85-94 64 1|16&17|64
+ 102-127 96 64|96
+ 132-255 -- SOURCE Attribute
+ 2053-2064 2049 2049
+ 2073-2077 2065 2065
+ 2080-65535 To be assigned To be assigned
+
+ Registration and query keys for attributes with tags in the range
+ 2080 to 65535 are to be documented in the RFC introducing the new
+ iSNS attributes. IANA will maintain registration of these values as
+ required by the new RFC.
+
+
+
+Tseng, et al. Standards Track [Page 75]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ New iSNS attributes with any of the above tag values MAY also be
+ designated as "read-only" attributes. The new RFC introducing these
+ attributes as "read-only" SHALL document them as such, and IANA will
+ record their corresponding Registration Keys (Reg Keys) as "--".
+
+6.2. Entity Identifier-Keyed Attributes
+
+ The following attributes are stored in the iSNS server using the
+ Entity Identifier attribute as the key.
+
+6.2.1. Entity Identifier (EID)
+
+ The Entity Identifier (EID) is variable-length UTF-8 encoded NULL-
+ terminated text-based description for a Network Entity. This key
+ attribute uniquely identifies each Network Entity registered in the
+ iSNS server. The attribute length varies from 4 to 256 bytes
+ (including the NULL termination), and is a unique value within the
+ iSNS server.
+
+ If the iSNS client does not provide an EID during registration, the
+ iSNS server SHALL generate one that is unique within the iSNS
+ database. If an EID is to be generated, then the EID attribute value
+ in the registration message SHALL be empty (0 length). The generated
+ EID SHALL be returned in the registration response.
+
+ In environments where the iSNS server is integrated with a DNS
+ infrastructure, the Entity Identifier may be used to store the Fully
+ Qualified Domain Name (FQDN) of the iSCSI or iFCP device. FQDNs of
+ greater than 255 bytes MUST NOT be used.
+
+ If FQDNs are not used, the iSNS server can be used to generate EIDs.
+ EIDs generated by the iSNS server MUST begin with the string "isns:".
+ iSNS clients MUST NOT generate and register EIDs beginning with the
+ string "isns:".
+
+ This field MUST be normalized according to the nameprep template
+ [NAMEPREP] before it is stored in the iSNS database.
+
+6.2.2. Entity Protocol
+
+ The Entity Protocol is a required 4-byte integer attribute that
+ indicates the block storage protocol used by the registered NETWORK
+ ENTITY. Values used for this attribute are assigned and maintained
+ by IANA. The initial set of protocols supported by iSNS is as
+ follows:
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 76]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Value Entity Protocol Type
+ ----- --------------------
+ 1 No Protocol
+ 2 iSCSI
+ 3 iFCP
+ All others To be assigned by IANA
+
+ 'No Protocol' is used to indicate that the Network Entity does not
+ support an IP block storage protocol. A Control Node or monitoring
+ Node would likely (but not necessarily) use this value.
+
+ This attribute is required during initial registration of the Network
+ Entity.
+
+6.2.3. Management IP Address
+
+ This field contains the IP Address that may be used to manage the
+ Network Entity and all Storage Nodes contained therein via the iSNS
+ MIB [iSNSMIB]. Some implementations may also use this IP address to
+ support vendor-specific proprietary management protocols. The
+ Management IP Address is a 16-byte field that may contain an IPv4 or
+ IPv6 address. When this field contains an IPv4 value, it is stored
+ as an IPv4-mapped IPv6 address. That is, the most significant 10
+ bytes are set to 0x00, with the next two bytes set to 0xFFFF
+ [RFC2373]. When this field contains an IPv6 value, the entire 16-
+ byte field is used. If this field is not set, then in-band
+ management through the IP address of one of the Portals of the
+ Network Entity is assumed.
+
+6.2.4. Entity Registration Timestamp
+
+ This field indicates the most recent time when the Network Entity
+ registration occurred or when an associated object attribute was
+ updated or queried by the iSNS client registering the Network Entity.
+ The time format is, in seconds, the update period since the standard
+ base time of 00:00:00 GMT on January 1, 1970. This field cannot be
+ explicitly registered. This timestamp TLV format is also used in the
+ SCN and ESI messages.
+
+6.2.5. Protocol Version Range
+
+ This field contains the minimum and maximum version of the block
+ storage protocol supported by the Network Entity. The most
+ significant two bytes contain the maximum version supported, and the
+ least significant two bytes contain the minimum version supported.
+ If a range is not registered, then the Network Entity is assumed to
+
+
+
+
+
+Tseng, et al. Standards Track [Page 77]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ support all versions of the protocol. The value 0xffff is a wildcard
+ that indicates no minimum or maximum. If the Network Entity does not
+ support a protocol, then this field SHALL be set to 0.
+
+6.2.6. Registration Period
+
+ This 4-byte unsigned integer field indicates the maximum period, in
+ seconds, that the registration SHALL be maintained by the server
+ without receipt of an iSNS message from the iSNS client that
+ registered the Network Entity. Entities that are not registered for
+ ESI monitoring MUST have a non-zero Registration Period. If a
+ Registration Period is not requested by the iSNS client and Entity
+ Status Inquiry (ESI) messages are not enabled for that client, then
+ the Registration Period SHALL be set to a non-zero value by the iSNS
+ server. This implementation-specific value for the Registration
+ Period SHALL be returned in the registration response to the iSNS
+ client. The Registration Period may be set to zero, indicating its
+ non-use, only if ESI messages are enabled for that Network Entity.
+
+ The registration SHALL be removed from the iSNS database if an iSNS
+ Protocol message is not received from the iSNS client before the
+ registration period has expired. Receipt of any iSNS Protocol
+ message from the iSNS client automatically refreshes the Entity
+ Registration Period and Entity Registration Timestamp. To prevent a
+ registration from expiring, the iSNS client should send an iSNS
+ Protocol message to the iSNS server at intervals shorter than the
+ registration period. Such a message can be as simple as a query for
+ one of its own attributes, using its associated iSCSI Name or FC Port
+ Name WWPN as the Source attribute.
+
+ For an iSNS client that is supporting a Network Entity with multiple
+ Storage Node objects, receipt of an iSNS message from any Storage
+ Node of that Network Entity is sufficient to refresh the registration
+ for all Storage Node objects of the Network Entity.
+
+ If ESI support is requested as part of a Portal registration, the ESI
+ Response message received from the iSNS client by the iSNS server
+ SHALL refresh the registration.
+
+6.2.7. Entity Index
+
+ The Entity Index is an unsigned non-zero integer value that uniquely
+ identifies each Network Entity registered in the iSNS server. Upon
+ initial registration of a Network Entity, the iSNS server assigns an
+ unused value for the Entity Index. Each Network Entity in the iSNS
+ database MUST be assigned a value for the Entity Index that is not
+
+
+
+
+
+Tseng, et al. Standards Track [Page 78]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ assigned to any other Network Entity. Furthermore, Entity Index
+ values for recently deregistered Network Entities SHOULD NOT be
+ reused in the short term.
+
+ The Entity Index MAY be used to represent the Network Entity in
+ situations when the Entity Identifier is too long or otherwise
+ inappropriate. An example of this is when SNMP is used for
+ management, as described in Section 2.10.
+
+6.2.8. Entity Next Index
+
+ This is a virtual attribute containing a 4-byte integer value that
+ indicates the next available (i.e., unused) Entity Index value. This
+ attribute may only be queried; the iSNS server SHALL return an error
+ code of 3 (Invalid Registration) to any client that attempts to
+ register a value for this attribute. A Message Key is not required
+ when exclusively querying for this attribute.
+
+ The Entity Next Index MAY be used by an SNMP client to create an
+ entry in the iSNS server. SNMP requirements are described in Section
+ 2.10.
+
+6.2.9. Entity ISAKMP Phase-1 Proposals
+
+ This field contains the IKE Phase-1 proposal, listing in decreasing
+ order of preference the protection suites acceptable to protect all
+ IKE Phase-2 messages sent and received by the Network Entity. This
+ includes Phase-2 SAs from the iSNS client to the iSNS server as well
+ as to peer iFCP and/or iSCSI devices. This attribute contains the SA
+ payload, proposal payload(s), and transform payload(s) in the ISAKMP
+ format defined in [RFC2408].
+
+ This field should be used if the implementer wishes to define a
+ single phase-1 SA security configuration used to protect all phase-2
+ IKE traffic. If the implementer desires to have a different phase-1
+ SA security configuration to protect each Portal interface, then the
+ Portal Phase-1 Proposal (Section 6.3.10) should be used.
+
+6.2.10. Entity Certificate
+
+ This attribute contains one or more X.509 certificates that are bound
+ to the Network Entity. This certificate is uploaded and registered
+ to the iSNS server by clients wishing to allow other clients to
+ authenticate themselves and to access the services offered by that
+ Network Entity. The format of the X.509 certificate is found in
+ [RFC3280]. This certificate MUST contain a Subject Name with an
+ empty sequence and MUST contain a SubjectAltName extension encoded
+
+
+
+
+Tseng, et al. Standards Track [Page 79]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ with the dNSName type. The Entity Identifier (Section 6.2.1) of the
+ identified Entity MUST be stored in the SubjectAltName field of the
+ certificate.
+
+6.3. Portal-Keyed Attributes
+
+ The following Portal attributes are registered in the iSNS database
+ using the combined Portal IP-Address and Portal TCP/UDP Port as the
+ key. Each Portal is associated with one Entity Identifier object
+ key.
+
+6.3.1. Portal IP Address
+
+ This attribute is the IP address of the Portal through which a
+ Storage Node can transmit and receive storage data. The Portal IP
+ Address is a 16-byte field that may contain an IPv4 or IPv6 address.
+ When this field contains an IPv4 address, it is stored as an IPv4-
+ mapped IPv6 address. That is, the most significant 10 bytes are set
+ to 0x00, with the next 2 bytes set to 0xFFFF [RFC2373]. When this
+ field contains an IPv6 address, the entire 16-byte field is used.
+ The Portal IP Address and the Portal TCP/UDP Port number (see 6.3.2
+ below) are used as a key to identify a Portal uniquely. It is a
+ required attribute for registration of a Portal.
+
+6.3.2. Portal TCP/UDP Port
+
+ The TCP/UDP port of the Portal through which a Storage Node can
+ transmit and receive storage data. Bits 16 to 31 represents the
+ TCP/UDP port number. Bit 15 represents the port type. If bit 15 is
+ set, then the port type is UDP. Otherwise it is TCP. Bits 0 to 14
+ are reserved.
+
+ If the field value is 0, then the port number is the implied
+ canonical port number and type of the protocol indicated by the
+ associated Entity Type.
+
+ The Portal IP Address and the Portal TCP/UDP Port number are used as
+ a key to identify a Portal uniquely. It is a required attribute for
+ registration of a Portal.
+
+6.3.3. Portal Symbolic Name
+
+ A variable-length UTF-8 encoded NULL-terminated text-based
+ description of up to 256 bytes. The Portal Symbolic Name is a user-
+ readable description of the Portal entry in the iSNS server.
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 80]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.3.4. Entity Status Inquiry Interval
+
+ This field indicates the requested time, in seconds, between Entity
+ Status Inquiry (ESI) messages sent from the iSNS server to this
+ Network Entity. ESI messages can be used to verify that a Portal
+ registration continues to be valid. To request monitoring by the
+ iSNS server, an iSNS client registers a non-zero value for this
+ Portal attribute using a DevAttrReg message. The client MUST
+ register an ESI Port on at least one of its Portals to receive the
+ ESI monitoring.
+
+ If the iSNS server does not receive an expected response to an ESI
+ message, it SHALL attempt an administratively configured number of
+ re-transmissions of the ESI message. The ESI Interval period begins
+ with the iSNS server's receipt of the last ESI Response. All re-
+ transmissions MUST be sent before twice the ESI Interval period has
+ passed. If no response is received from any of the ESI messages,
+ then the Portal SHALL be deregistered. Note that only Portals that
+ have registered a value in their ESI Port field can be deregistered
+ in this way.
+
+ If all Portals associated with a Network Entity that have registered
+ for ESI messages are deregistered due to non-response, and if no
+ registrations have been received from the client for at least two ESI
+ Interval periods, then the Network Entity and all associated objects
+ (including Storage Nodes) SHALL be deregistered.
+
+ If the iSNS server is unable to support ESI messages or the ESI
+ Interval requested, it SHALL either reject the ESI request by
+ returning an "ESI Not Available" Status Code or modify the ESI
+ Interval attribute by selecting its own suitable value and returning
+ that value in the Operating Attributes of the registration response
+ message.
+
+ If at any time an iSNS client that is registered for ESI messages has
+ not received an ESI message to any of its Portals as expected, then
+ the client MAY attempt to query the iSNS server using a DevAttrQry
+ message using its Entity_ID as the key. If the query result is the
+ error "no such entry", then the client SHALL close all remaining TCP
+ connections to the iSNS server and assume that it is no longer
+ registered in the iSNS database. Such a client MAY attempt re-
+ registration.
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 81]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.3.5. ESI Port
+
+ This field contains the TCP or UDP port used for ESI monitoring by
+ the iSNS server at the Portal IP Address. Bits 16 to 31 represent
+ the port number. If bit 15 is set, then the port type is UDP.
+ Otherwise, the port is TCP. Bits 0 to 14 are reserved.
+
+ If the iSNS client registers a valid TCP or UDP port number in this
+ field, then the client SHALL allow ESI messages to be received at the
+ indicated TCP or UDP port. If a TCP port is registered and a pre-
+ existing TCP connection from that TCP port to the iSNS server does
+ not already exist, then the iSNS client SHALL accept new TCP
+ connections from the iSNS server at the indicated TCP port.
+
+ The iSNS server SHALL return an error if a Network Entity is
+ registered for ESI monitoring and none of the Portals of that Network
+ Entity has an entry for the ESI Port field. If multiple Portals have
+ a registered ESI port, then the ESI message may be delivered to any
+ one of the indicated Portals.
+
+6.3.6. Portal Index
+
+ The Portal Index is a 4-byte non-zero integer value that uniquely
+ identifies each Portal registered in the iSNS database. Upon initial
+ registration of a Portal, the iSNS server assigns an unused value for
+ the Portal Index of that Portal. Each Portal in the iSNS database
+ MUST be assigned a value for the Portal Index that is not assigned to
+ any other Portal. Furthermore, Portal Index values for recently
+ deregistered Portals SHOULD NOT be reused in the short term.
+
+ The Portal Index MAY be used to represent a registered Portal in
+ situations where the Portal IP-Address and Portal TCP/UDP Port is
+ unwieldy to use. An example of this is when SNMP is used for
+ management, as described in Section 2.10.
+
+6.3.7. SCN Port
+
+ This field contains the TCP or UDP port used by the iSNS client to
+ receive SCN messages from the iSNS server. When a value is
+ registered for this attribute, an SCN message may be received on the
+ indicated port for any of the Storage Nodes supported by the Portal.
+ Bits 16 to 31 contain the port number. If bit 15 is set, then the
+ port type is UDP. Otherwise, the port type is TCP. Bits 0 to 14 are
+ reserved.
+
+ If the iSNS client registers a valid TCP or UDP port number in this
+ field, then the client SHALL allow SCN messages to be received at the
+ indicated TCP or UDP port. If a TCP port is registered and a pre-
+
+
+
+Tseng, et al. Standards Track [Page 82]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ existing TCP connection from that TCP port to the iSNS server does
+ not already exist, then the iSNS client SHALL accept new TCP
+ connections from the iSNS server at the indicated TCP port.
+
+ The iSNS server SHALL return an error if an SCN registration message
+ is received and none of the Portals of the Network Entity has an
+ entry for the SCN Port. If multiple Portals have a registered SCN
+ Port, then the SCN SHALL be delivered to any one of the indicated
+ Portals of that Network Entity.
+
+6.3.8. Portal Next Index
+
+ This is a virtual attribute containing a 4-byte integer value that
+ indicates the next available (i.e., unused) Portal Index value. This
+ attribute may only be queried; the iSNS server SHALL return an error
+ code of 3 (Invalid Registration) to any client that attempts to
+ register a value for this attribute. A Message Key is not required
+ when exclusively querying for this attribute.
+
+ The Portal Next Index MAY be used by an SNMP client to create an
+ entry in the iSNS server. SNMP requirements are described in Section
+ 2.10.
+
+6.3.9. Portal Security Bitmap
+
+ This 4-byte field contains flags that indicate security attribute
+ settings for the Portal. Bit 31 (Lsb) of this field must be 1
+ (enabled) for this field to contain significant information. If Bit
+ 31 is enabled, this signifies that the iSNS server can be used to
+ store and distribute security policies and settings for iSNS clients
+ (i.e., iSCSI devices). Bit 30 must be 1 for bits 25-29 to contain
+ significant information. All other bits are reserved for non-
+ IKE/IPSec security mechanisms to be specified in the future.
+
+ Bit Position Flag Description
+ ------------ ----------------
+ 25 1 = Tunnel Mode Preferred; 0 = No Preference
+ 26 1 = Transport Mode Preferred; 0 = No Preference
+ 27 1 = Perfect Forward Secrecy (PFS) Enabled;
+ 0 = PFS Disabled
+ 28 1 = Aggressive Mode Enabled; 0 = Disabled
+ 29 1 = Main Mode Enabled; 0 = MM Disabled
+ 30 1 = IKE/IPSec Enabled; 0 = IKE/IPSec Disabled
+ 31 (Lsb) 1 = Bitmap VALID; 0 = INVALID
+ All others RESERVED
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 83]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.3.10. Portal ISAKMP Phase-1 Proposals
+
+ This field contains the IKE Phase-1 proposal listing in decreasing
+ order of preference of the protection suites acceptable to protect
+ all IKE Phase-2 messages sent and received by the Portal. This
+ includes Phase-2 SAs from the iSNS client to the iSNS server as well
+ as to peer iFCP and/or iSCSI devices. This attribute contains the SA
+ payload, proposal payload(s), and transform payload(s) in the ISAKMP
+ format defined in [RFC2408].
+
+ This field should be used if the implementer wishes to define phase-1
+ SA security configuration on a per-Portal basis, as opposed to on a
+ per-Network Entity basis. If the implementer desires to have a
+ single phase-1 SA security configuration to protect all phase-2
+ traffic regardless of the interface used, then the Entity Phase-1
+ Proposal (Section 6.2.9) should be used.
+
+6.3.11. Portal ISAKMP Phase-2 Proposals
+
+ This field contains the IKE Phase-2 proposal, in ISAKMP format
+ [RFC2408], listing in decreasing order of preference the security
+ proposals acceptable to protect traffic sent and received by the
+ Portal. This field is used only if bits 31, 30, and 29 of the
+
+ Security Bitmap (see 6.3.9) are enabled. This attribute contains the
+ SA payload, proposal payload(s), and associated transform payload(s)
+ in the ISAKMP format defined in [RFC2408].
+
+6.3.12. Portal Certificate
+
+ This attribute contains one or more X.509 certificates that are a
+ credential of the Portal. This certificate is used to identify and
+ authenticate communications to the IP address and TCP/UDP Port
+ supported by the Portal. The format of the X.509 certificate is
+ specified in [RFC3280]. This certificate MUST contain a Subject Name
+ with an empty sequence and MUST contain a SubjectAltName extension
+ encoded with the iPAddress type. The Portal IP Address (Section
+ 6.3.1) of the identified Portal SHALL be stored in the SubjectAltName
+ field of the certificate.
+
+6.4. iSCSI Node-Keyed Attributes
+
+ The following attributes are stored in the iSNS database using the
+ iSCSI Name attribute as the key. Each set of Node-Keyed attributes
+ is associated with one Entity Identifier object key.
+
+ Although the iSCSI Name key is associated with one Entity Identifier,
+ it is unique across the entire iSNS database.
+
+
+
+Tseng, et al. Standards Track [Page 84]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.4.1. iSCSI Name
+
+ This is a variable-length UTF-8 encoded NULL-terminated text-based
+ description of up to 224 bytes. This key attribute is required for
+ iSCSI Storage Nodes and is provided by the iSNS client. The
+ registered iSCSI Name MUST conform to the format described in [iSCSI]
+ for iSCSI Names. The maximum size for an iSCSI Name is 223 bytes.
+ Including the NULL character and 4-byte alignment (see Section
+ 5.3.1), the maximum iSCSI Name field size is 224 bytes.
+
+ If an iSCSI Name is registered without an EID key, then a Network
+ Entity SHALL be created and an EID assigned. The assigned EID SHALL
+ be returned in the registration response as an operating attribute.
+
+ This field MUST be normalized according to the stringprep template
+ [STRINGPREP] before it is stored in the iSNS database.
+
+6.4.2. iSCSI Node Type
+
+ This required 32-bit field is a bitmap indicating the type of iSCSI
+ Storage Node. The bit positions are defined below. A set bit (1)
+ indicates that the Node has the corresponding characteristics.
+
+ Bit Position Node Type
+ ------------ ---------
+ 29 Control
+ 30 Initiator
+ 31 (Lsb) Target
+ All others RESERVED
+
+ If the Target bit is set to 1, then the Node represents an iSCSI
+ target. The Target bit MAY be set by iSNS clients using the iSNSP.
+
+ If the Initiator bit is set to 1, then the Node represents an iSCSI
+ initiator. The Initiator bit MAY be set by iSNS clients using the
+ iSNSP.
+
+ If the control bit is set to 1, then the Node represents a gateway, a
+ management station, a backup iSNS server, or another device that is
+ not an initiator or target, but that requires the ability to send and
+ receive iSNSP messages, including state change notifications.
+ Setting the control bit is an administrative task that MUST be
+ performed on the iSNS server; iSNS clients SHALL NOT be allowed to
+ change this bit using the iSNSP.
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 85]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ This field MAY be used by the iSNS server to distinguish among
+ permissions by different iSCSI Node types for accessing various iSNS
+ functions. More than one Node Type bit may be simultaneously
+ enabled.
+
+6.4.3. iSCSI Node Alias
+
+ This is a variable-length UTF-8 encoded NULL-terminated text-based
+ description of up to 256 bytes. The Alias is a user-readable
+ description of the Node entry in the iSNS database.
+
+6.4.4. iSCSI Node SCN Bitmap
+
+ The iSCSI Node SCN Bitmap indicates events for which the registering
+ iSNS client wishes to receive a notification message. The following
+ table displays events that result in notifications, and the bit field
+ in the SCN Bitmap that, when enabled, results in the corresponding
+ notification.
+
+ Note that this field is of dual use: it is used in the SCN
+ registration process to define interested events that will trigger an
+ SCN message, and it is also contained in each SCN message itself, to
+ indicate the type of event that triggered the SCN message. A set bit
+ (1) indicates the corresponding type of SCN.
+
+ Bit Position Flag Description
+ ------------ ----------------
+ 24 INITIATOR AND SELF INFORMATION ONLY
+ 25 TARGET AND SELF INFORMATION ONLY
+ 26 MANAGEMENT REGISTRATION/SCN
+ 27 OBJECT REMOVED
+ 28 OBJECT ADDED
+ 29 OBJECT UPDATED
+ 30 DD/DDS MEMBER REMOVED (Mgmt Reg/SCN only)
+ 31 (Lsb) DD/DDS MEMBER ADDED (Mgmt Reg/SCN only)
+ All others RESERVED
+
+ DD/DDS MEMBER REMOVED indicates that an existing member of a
+ Discovery Domain and/or Discovery Domain Set has been removed.
+
+ DD/DDS MEMBER ADDED indicates that a new member was added to an
+ existing DD and/or DDS.
+
+ OBJECT REMOVED, OBJECT ADDED, and OBJECT UPDATED indicate a Network
+ Entity, Portal, Storage Node, FC Device, DD, and/or DDS object was
+ removed from, added to, or updated in the Discovery Domain or in the
+ iSNS database (Control Nodes only).
+
+
+
+
+Tseng, et al. Standards Track [Page 86]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Regular SCNs provide information about objects that are updated in,
+ added to or removed from Discovery Domains of which the Storage Node
+ is a member. An SCN or SCN registration is considered a regular SCN
+ or regular SCN registration if the MANAGEMENT REGISTRATION/SCN flag
+ is cleared. All iSNS clients may register for regular SCNs.
+
+ Management SCNs provide information about all changes to the network,
+ regardless of discovery domain membership. Registration for
+ management SCNs is indicated by setting bit 26 to 1. Only Control
+ Nodes may register for management SCNs. Bits 30 and 31 may only be
+ enabled if bit 26 is set to 1.
+
+ TARGET AND SELF INFORMATION ONLY SCNs (bit 25) provides information
+ only about changes to target devices, or if the iSCSI Storage Node
+ itself has undergone a change. Similarly, INITIATOR AND SELF
+ INFORMATION ONLY SCNs (bit 24) provides information only about
+ changes to initiator Nodes, or to the target itself.
+
+6.4.5. iSCSI Node Index
+
+ The iSCSI Node Index is a 4-byte non-zero integer value used as a key
+ that uniquely identifies each iSCSI Storage Node registered in the
+ iSNS database. Upon initial registration of the iSCSI Storage Node,
+ the iSNS server assigns an unused value for the iSCSI Node Index.
+ Each iSCSI Node MUST be assigned a value for the iSCSI Node Index
+ that is not assigned to any other iSCSI Storage Node. Furthermore,
+ iSCSI Node Index values for recently deregistered iSCSI Storage Nodes
+ SHOULD NOT be reused in the short term.
+
+ The iSCSI Node Index may be used as a key to represent a registered
+ Node in situations where the iSCSI Name is too long to be used as a
+ key. An example of this is when SNMP is used for management, as
+ described in Section 2.10.
+
+ The value assigned for the iSCSI Node Index SHALL persist as long as
+ the iSCSI Storage Node is registered in the iSNS database or a member
+ of a Discovery Domain. An iSCSI Node Index value that is assigned
+ for a Storage Node SHALL NOT be used for any other Storage Node as
+ long as the original node is registered in the iSNS database or a
+ member of a Discovery Domain.
+
+6.4.6. WWNN Token
+
+ This field contains a globally unique 64-bit integer value that can
+ be used to represent the World Wide Node Name of the iSCSI device in
+ a Fibre Channel fabric. This identifier is used during the device
+ registration process and MUST conform to the requirements in [FC-FS].
+
+
+
+
+Tseng, et al. Standards Track [Page 87]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ The FC-iSCSI gateway uses the value found in this field to register
+ the iSCSI device in the Fibre Channel name server. It is stored in
+ the iSNS server to prevent conflict when "proxy" WWNN values are
+ assigned to iSCSI initiators establishing storage sessions to devices
+ in the FC fabric.
+
+ If the iSNS client does not assign a value for WWNN Token, then the
+ iSNS server SHALL provide a value for this field upon initial
+ registration of the iSCSI Storage Node. The process by which the
+ WWNN Token is assigned by the iSNS server MUST conform to the
+ following requirements:
+
+ 1. The assigned WWNN Token value MUST be unique among all WWN
+ entries in the existing iSNS database, and among all devices that
+ can potentially be registered in the iSNS database.
+
+ 2. Once the value is assigned, the iSNS server MUST persistently
+ save the mapping between the WWNN Token value and registered
+ iSCSI Name. That is, successive re-registrations of the iSCSI
+ Storage Node keyed by the same iSCSI Name maintain the original
+ mapping to the associated WWNN Token value in the iSNS server.
+ Similarly, the mapping SHALL be persistent across iSNS server
+ reboots. Once assigned, the mapping can only be changed if a
+ DevAttrReg message from an authorized iSNS client explicitly
+ provides a different WWNN Token value.
+
+ 3. Once a WWNN Token value has been assigned and mapped to an iSCSI
+ name, that WWNN Token value SHALL NOT be reused or mapped to any
+ other iSCSI name.
+
+ 4. The assigned WWNN Token value MUST conform to the formatting
+ requirements of [FC-FS] for World Wide Names (WWNs).
+
+ An iSNS client, such as an FC-iSCSI gateway or the iSCSI initiator,
+ MAY register its own WWNN Token value or overwrite the iSNS Server-
+ supplied WWNN Token value, if it wishes to supply its own iSCSI-FC
+ name mapping. This is accomplished using the DevAttrReg message with
+ the WWNN Token (tag=37) as an operating attribute. Once overwritten,
+ the new WWNN Token value MUST be stored and saved by the iSNS server,
+ and all requirements specified above continue to apply. If an iSNS
+ client attempts to register a value for this field that is not unique
+ in the iSNS database or that is otherwise invalid, then the
+ registration SHALL be rejected with an Status Code of 3 (Invalid
+ Registration).
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 88]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ There MAY be matching records in the iSNS database for the Fibre
+ Channel device specified by the WWNN Token. These records may
+ contain device attributes for that FC device registered in the Fibre
+ Channel fabric name server.
+
+6.4.7. iSCSI Node Next Index
+
+ This is a virtual attribute containing a 4-byte integer value that
+ indicates the next available (i.e., unused) iSCSI Node Index value.
+ This attribute may only be queried; the iSNS server SHALL return an
+ error code of 3 (Invalid Registration) to any client that attempts to
+ register a value for this attribute. A Message Key is not required
+ when exclusively querying for this attribute.
+
+ The iSCSI Node Next Index MAY be used by an SNMP client to create an
+ entry in the iSNS server. SNMP requirements are described in Section
+ 2.10.
+
+6.4.8. iSCSI AuthMethod
+
+ This attribute contains a NULL-terminated string of UTF-8 text
+ listing the iSCSI authentication methods enabled for this iSCSI
+ Storage Node, in order of preference. The text values used to
+ identify iSCSI authentication methods are embedded in this string
+ attribute and delineated by a comma. The text values are identical
+ to those found in the main iSCSI document [iSCSI]; additional
+ vendor-specific text values are also possible.
+
+ Text Value Description Reference
+ ---------- ----------- ---------
+ KB5 Kerberos V5 [RFC1510]
+ SPKM1 Simple Public Key GSS-API [RFC2025]
+ SPKM2 Simple Public Key GSS-API [RFC2025]
+ SRP Secure Remote Password [RFC2945]
+ CHAP Challenge Handshake Protocol [RFC1994]
+ none No iSCSI Authentication
+
+6.5. Portal Group (PG) Object-Keyed Attributes
+
+ The following attributes are used to associate Portal and iSCSI
+ Storage Node objects. PG objects are stored in the iSNS database
+ using the PG iSCSI Name, the PG Portal IP Address, and the PG Portal
+ TCP/UDP Port as keys. New PG objects are implicitly or explicitly
+ created at the time that the corresponding Portal and/or iSCSI
+ Storage Node objects are registered. Section 3.4 has a general
+ discussion of PG usage. For further details on use of Portal Groups,
+ see [iSCSI].
+
+
+
+
+Tseng, et al. Standards Track [Page 89]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.5.1. Portal Group iSCSI Name
+
+ This is the iSCSI Name for the iSCSI Storage Node that is associated
+ with the PG object. This name MAY represent an iSCSI Storage Node
+ not currently registered in the server.
+
+6.5.2. PG Portal IP Addr
+
+ This is the Portal IP Address attribute for the Portal that is
+ associated with the PG object. This Portal IP Address MAY be that of
+ a Portal that is not currently registered in the server.
+
+6.5.3. PG Portal TCP/UDP Port
+
+ This is the Portal TCP/UDP Port attribute for the Portal that is
+ associated with the PG object. This Portal TCP/UDP Port MAY be that
+ of a Portal that is not currently registered in the server.
+
+6.5.4. Portal Group Tag (PGT)
+
+ This field is used to group Portals in order to coordinate
+ connections in a session across Portals to a specified iSCSI Node.
+ The PGT is a value in the range of 0-65535, or NULL. A NULL PGT
+ value is registered by using 0 for the length in the TLV during
+ registration. The two least significant bytes of the value contain
+ the PGT for the object. The two most significant bytes are reserved.
+ If a PGT value is not explicitly registered for an iSCSI Storage Node
+ and Portal pair, then the PGT value SHALL be implicitly registered as
+ 0x00000001.
+
+6.5.5. Portal Group Index
+
+ The PG Index is a 4-byte non-zero integer value used as a key that
+ uniquely identifies each PG object registered in the iSNS database.
+ Upon initial registration of a PG object, the iSNS server MUST assign
+ an unused value for the PG Index. Furthermore, PG Index values for
+ recently deregistered PG objects SHOULD NOT be reused in the short
+ term.
+
+ The PG Index MAY be used as the key to reference a registered PG in
+ situations where a unique index for each PG object is required. It
+ MAY also be used as the message key in an iSNS message to query or
+ update a pre-existing PG object. An example of this is when SNMP is
+ used for management, as described in Section 2.10. The value
+ assigned for the PG Index SHALL persist as long as the server is
+ active.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 90]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.5.6. Portal Group Next Index
+
+ The PG Next Index is a virtual attribute containing a 4-byte integer
+ value that indicates the next available (i.e., unused) PG Index
+ value. This attribute may only be queried; the iSNS server SHALL
+ return an error code of 3 (Invalid Registration) to any client that
+ attempts to register a value for this attribute. A Message Key is
+ not required when exclusively querying for this attribute.
+
+ The Portal Group Next Index MAY be used by an SNMP client to create
+ an entry in the iSNS server. SNMP requirements are described in
+ Section 2.10.
+
+6.6. FC Port Name-Keyed Attributes
+
+ The following attributes are registered in the iSNS database using
+ the FC Port World Wide Name (WWPN) attribute as the key. Each set of
+ FC Port-Keyed attributes is associated with one Entity Identifier
+ object key.
+
+ Although the FC Port World Wide Name is associated with one Entity
+ Identifier, it is also globally unique.
+
+6.6.1. FC Port Name (WWPN)
+
+ This 64-bit identifier uniquely defines the FC Port, and it is the
+ World Wide Port Name (WWPN) of the corresponding Fibre Channel
+ device. This attribute is the key for the iFCP Storage Node. This
+ globally unique identifier is used during the device registration
+ process, and it uses a value conforming to IEEE EUI-64 [EUI-64].
+
+6.6.2. Port ID (FC_ID)
+
+ The Port Identifier is a Fibre Channel address identifier assigned to
+ an N_Port or NL_Port during fabric login. The format of the Port
+ Identifier is defined in [FC-FS]. The least significant 3 bytes
+ contain this address identifier. The most significant byte is
+ RESERVED.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 91]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.6.3. FC Port Type
+
+ Indicates the type of FC port. Encoded values for this field are
+ listed in the following table:
+
+ Type Description
+ ---- -----------
+ 0x0000 Unidentified/Null Entry
+ 0x0001 Fibre Channel N_Port
+ 0x0002 Fibre Channel NL_Port
+ 0x0003 Fibre Channel F/NL_Port
+ 0x0004-0080 RESERVED
+ 0x0081 Fibre Channel F_Port
+ 0x0082 Fibre Channel FL_Port
+ 0x0083 RESERVED
+ 0x0084 Fibre Channel E_Port
+ 0x0085-00FF RESERVED
+ 0xFF11 RESERVED
+ 0xFF12 iFCP Port
+ 0xFF13-FFFF RESERVED
+
+6.6.4. Symbolic Port Name
+
+ This is a variable-length UTF-8 encoded NULL-terminated text-based
+ description of up to 256 bytes that is associated with the iSNS-
+ registered FC Port Name in the network.
+
+6.6.5. Fabric Port Name (FWWN)
+
+ This 64-bit identifier uniquely defines the fabric port. If the port
+ of the FC Device is attached to a Fibre Channel fabric port with a
+ registered Port Name, then that fabric Port Name SHALL be indicated
+ in this field.
+
+6.6.6. Hard Address
+
+ This field is the requested hard address 24-bit NL Port Identifier,
+ included in the iSNSP for compatibility with Fibre Channel Arbitrated
+ Loop devices and topologies. The least significant 3 bytes of this
+ field contain the address. The most significant byte is RESERVED.
+
+6.6.7. Port IP Address
+
+ The Fibre Channel IP address associated with the FC Port. When this
+ field contains an IPv4 value, it is stored as an IPv4-mapped IPv6
+ address. That is, the most significant 10 bytes are set to 0x00,
+ with the next two bytes set to 0xFFFF [RFC2373]. When an IPv6 value
+ is contained in this field, then the entire 16-byte field is used.
+
+
+
+Tseng, et al. Standards Track [Page 92]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.6.8. Class of Service (COS)
+
+ This 32-bit bit-map field indicates the Fibre Channel Class of
+ Service types that are supported by the registered port. In the
+ following table, a set bit (1) indicates a Class of Service
+ supported.
+
+ Bit Position Description
+ ------------ -----------
+ 29 Fibre Channel Class 2 Supported
+ 28 Fibre Channel Class 3 Supported
+
+6.6.9. FC-4 Types
+
+ This 32-byte field indicates the FC-4 protocol types supported by the
+ associated port. This field can be used to support Fibre Channel
+ devices and is consistent with FC-GS-4.
+
+6.6.10. FC-4 Descriptor
+
+ This is a variable-length UTF-8 encoded NULL-terminated text-based
+ description of up to 256 bytes that is associated with the iSNS-
+ registered device port in the network. This field can be used to
+ support Fibre Channel devices and is consistent with FC-GS-4.
+
+6.6.11. FC-4 Features
+
+ This is a 128-byte array, 4 bits per type, for the FC-4 protocol
+ types supported by the associated port. This field can be used to
+ support Fibre Channel devices and is consistent with FC-GS-4.
+
+6.6.12. iFCP SCN Bitmap
+
+ This field indicates the events the iSNS client is interested in.
+ These events can cause SCNs to be generated. SCNs provide
+ information about objects that are updated in, added to or removed
+ from Discovery Domains of which the source and destination are a
+ member. Management SCNs provide information about all changes to the
+ network. A set bit (1) indicates the type of SCN for the bitmap as
+ follows:
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 93]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Bit Position Flag Description
+ ------------ ----------------
+ 24 INITIATOR AND SELF INFORMATION ONLY
+ 25 TARGET AND SELF INFORMATION ONLY
+ 26 MANAGEMENT REGISTRATION/SCN
+ 27 OBJECT REMOVED
+ 28 OBJECT ADDED
+ 29 OBJECT UPDATED
+ 30 DD/DDS MEMBER REMOVED (Mgmt Reg/SCN only)
+ 31 (Lsb) DD/DDS MEMBER ADDED (Mgmt Reg/SCN only)
+ All others RESERVED
+
+ Further information on the use of the bit positions specified above
+ can be found in Section 6.4.4.
+
+6.6.13. Port Role
+
+ This required 32-bit field is a bitmap indicating the type of iFCP
+ Storage Node. The bit fields are defined below. A set bit indicates
+ the Node has the corresponding characteristics.
+
+ Bit Position Node Type
+ ------------ ---------
+ 29 Control
+ 30 FCP Initiator
+ 31 (Lsb) FCP Target
+ All Others RESERVED
+
+ If the 'Target' bit is set to 1, then the port represents an FC
+ target. Setting of the 'Target' bit MAY be performed by iSNS clients
+ using the iSNSP.
+
+ If the 'Initiator' bit is set to 1, then the port represents an FC
+ initiator. Setting of the 'Initiator' bit MAY be performed by iSNS
+ clients using the iSNSP.
+
+ If the 'Control' bit is set to 1, then the port represents a gateway,
+ a management station, an iSNS backup server, or another device.
+
+ This is usually a special device that is neither an initiator nor a
+ target, which requires the ability to send and receive iSNSP
+ messages, including state-change notifications. Setting the control
+ bit is an administrative task that MUST be administratively
+ configured on the iSNS server; iSNS clients SHALL NOT be allowed to
+ change this bit using the iSNSP.
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 94]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ This field MAY be used by the iSNS server to distinguish among
+ permissions by different iSNS clients. For example, an iSNS server
+ implementation may be administratively configured to allow only
+ targets to receive ESIs, or to permit only Control Nodes to add,
+ modify, or delete discovery domains.
+
+6.6.14. Permanent Port Name (PPN)
+
+ The Permanent Port Name can be used to support Fibre Channel devices
+ and is consistent with the PPN description in FC-GS-4 [FC-GS-4]. The
+ format of the PPN is identical to the FC Port Name WWPN attribute
+ format.
+
+6.7. Node-Keyed Attributes
+
+ The following attributes are registered in the iSNS database using
+ the FC Node Name (WWNN) attribute as the key. Each set of FC Node-
+ Keyed attributes represents a single device and can be associated
+ with many FC Ports.
+
+ The FC Node Name is unique across the entire iSNS database.
+
+6.7.1. FC Node Name (WWNN)
+
+ The FC Node Name is a 64-bit identifier that is the World Wide Node
+ Name (WWNN) of the corresponding Fibre Channel device. This
+ attribute is the key for the FC Device. This globally unique
+ identifier is used during the device registration process, and it
+ uses a value conforming to IEEE EUI-64 [EUI-64].
+
+6.7.2. Symbolic Node Name
+
+ This is a variable-length UTF-8 encoded NULL-terminated text-based
+ description of up to 256 bytes that is associated with the iSNS-
+ registered FC Device in the network.
+
+6.7.3. Node IP Address
+
+ This IP address is associated with the device Node in the network.
+ This field is included for compatibility with Fibre Channel. When
+ this field contains an IPv4 value, it is stored as an IPv4-mapped
+ IPv6 address. That is, the most significant 10 bytes are set to
+ 0x00, with the next two bytes set to 0xFFFF [RFC2373]. When an IPv6
+ value is contained in this field, the entire 16-byte field is used.
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 95]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.7.4. Node IPA
+
+ This field is the 8-byte Fibre Channel Initial Process Associator
+ (IPA) associated with the device Node in the network. The initial
+ process associator is used for communication between Fibre Channel
+ devices.
+
+6.7.5. Proxy iSCSI Name
+
+ This is a variable-length UTF-8 encoded NULL-terminated text-based
+ field that contains the iSCSI Name used to represent the FC Node in
+ the IP network. It is used as a pointer to the matching iSCSI Name
+ entry in the iSNS server. Its value is usually registered by an FC-
+ iSCSI gateway connecting the IP network to the fabric containing the
+ FC device.
+
+ Note that if this field is used, there SHOULD be a matching entry in
+ the iSNS database for the iSCSI device specified by the iSCSI name.
+ The database entry should include the full range of iSCSI attributes
+ needed for discovery and management of the "iSCSI proxy image" of the
+ FC device.
+
+6.8. Other Attributes
+
+ The following are not attributes of the previously-defined objects.
+
+6.8.1. FC-4 Type Code
+
+ This is a 4-byte field used to provide a FC-4 type during a FC-4 Type
+ query. The FC-4 types are consistent with the FC-4 Types as defined
+ in FC-FS. Byte 0 contains the FC-4 type. All other bytes are
+ reserved.
+
+6.8.2. iFCP Switch Name
+
+ The iFCP Switch Name is a 64-bit World Wide Name (WWN) identifier
+ that uniquely identifies a distinct iFCP gateway in the network.
+ This globally unique identifier is used during the switch
+ registration/FC_DOMAIN_ID assignment process. The iFCP Switch Name
+ value used MUST conform to the requirements stated in [FC-FS] for
+ World Wide Names. The iSNS server SHALL track the state of all
+ FC_DOMAIN_ID values that have been allocated to each iFCP Switch
+ Name. If a given iFCP Switch Name is deregistered from the iSNS
+ database, then all FC_DOMAIN_ID values allocated to that iFCP Switch
+ Name SHALL be returned to the unused pool of values.
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 96]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.8.3. iFCP Transparent Mode Commands
+
+6.8.3.1. Preferred ID
+
+ This is a 4-byte unsigned integer field, and it is the requested
+ value that the iSNS client wishes to use for the FC_DOMAIN_ID. The
+ iSNS server SHALL grant the iSNS client the use of the requested
+ value as the FC_DOMAIN_ID, if the requested value has not already
+ been allocated. If the requested value is not available, the iSNS
+ server SHALL return a different value that has not been allocated.
+
+6.8.3.2. Assigned ID
+
+ This is a 4-byte unsigned integer field that is used by an iFCP
+ gateway to reserve its own unique FC_DOMAIN_ID value from the range 1
+ to 239. When a FC_DOMAIN_ID is no longer required, it SHALL be
+ released by the iFCP gateway using the RlseDomId message. The iSNS
+ server MUST use the Entity Status Inquiry message to determine
+ whether an iFCP gateway is still present on the network.
+
+6.8.3.3. Virtual_Fabric_ID
+
+ This is a variable-length UTF-8 encoded NULL-terminated text-based
+ field of up to 256 bytes. The Virtual_Fabric_ID string is used as a
+ key attribute to identify a range of non-overlapping FC_DOMAIN_ID
+ values to be allocated using RqstDomId. Each Virtual_Fabric_ID
+ string submitted by an iSNS client SHALL have its own range of non-
+ overlapping FC_DOMAIN_ID values to be allocated to iSNS clients.
+
+
+6.9. iSNS Server-Specific Attributes
+
+ Access to the following attributes may be administratively
+ controlled. These attributes are specific to the iSNS server
+ instance; the same value is returned for all iSNS clients accessing
+ the iSNS server. Only query messages may be performed on these
+ attributes. Attempted registrations of values for these attributes
+ SHALL return a status code of 3 (Invalid Registration).
+
+ A query for an iSNS Server-Specific attribute MUST contain the
+ identifying key attribute (i.e., iSCSI Name or FC Port Name WWPN) of
+ the Node originating the registration or query message as the Source
+ and Message Key attributes. The Operating Attributes are the
+ server-specific attributes being registered or queried.
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 97]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.9.1. iSNS Server Vendor OUI
+
+ This attribute is the OUI (Organizationally Unique Identifier)
+ [802-1990] identifying the specific vendor implementing the iSNS
+ server. This attribute can only be queried; iSNS clients SHALL NOT be
+ allowed to register a value for the iSNS Server Vendor OUI.
+
+6.10. Vendor-Specific Attributes
+
+ iSNS server implementations MAY define vendor-specific attributes for
+ private use. These attributes MAY be used to store optional data
+ that is registered and/or queried by iSNS clients in order to gain
+ optional capabilities. Note that any implementation of vendor-
+ specific attributes in the iSNS server SHALL NOT impose any form of
+ mandatory behavior on the part of the iSNS client.
+
+ The tag values used for vendor-specific and user-specific use are
+ defined in Section 6.1. To avoid misinterpreting proprietary
+ attributes, the vendor's own OUI (Organizationally Unique Identifier)
+ MUST be placed in the upper three bytes of the attribute value field
+ itself.
+
+ The OUI is defined in IEEE Std 802-1990 and is the same constant used
+ to generate 48 bit Universal LAN MAC addresses. A vendor's own iSNS
+ implementation will then be able to recognize the OUI in the
+ attribute field and be able to execute vendor-specific handling of
+ the attribute.
+
+6.10.1. Vendor-Specific Server Attributes
+
+ Attributes with tags in the range 257 to 384 are vendor-specific or
+ site-specific attributes of the iSNS server. Values for these
+ attributes are administratively set by the specific vendor providing
+ the iSNS server implementation. Query access to these attributes may
+ be administratively controlled. These attributes are unique for each
+ logical iSNS server instance. Query messages for these attributes
+ SHALL use the key identifier (i.e., iSCSI Name or FC Port Name WWPN)
+ for both the Source attribute and Message Key attribute. These
+ attributes can only be queried; iSNS clients SHALL NOT be allowed to
+ register a value for server attributes.
+
+6.10.2. Vendor-Specific Entity Attributes
+
+ Attributes in the range 385 to 512 are vendor-specific or site-
+ specific attributes used to describe the Network Entity object.
+ These attributes are keyed by the Entity Identifier attribute
+ (tag=1).
+
+
+
+
+Tseng, et al. Standards Track [Page 98]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.10.3. Vendor-Specific Portal Attributes
+
+ Attributes in the range 513 to 640 are vendor-specific or site-
+ specific attributes used to describe the Portal object. These
+ attributes are keyed by the Portal IP-Address (tag=16) and Portal
+ TCP/UDP Port (tag=17).
+
+6.10.4. Vendor-Specific iSCSI Node Attributes
+
+ Attributes in the range 641 to 768 are vendor-specific or site-
+ specific attributes used to describe the iSCSI Node object. These
+ attributes are keyed by the iSCSI Name (tag=32).
+
+6.10.5. Vendor-Specific FC Port Name Attributes
+
+ Attributes in the range 769 to 896 are vendor-specific or site-
+ specific attributes used to describe the N_Port Port Name object.
+ These attributes are keyed by the FC Port Name WWPN (tag=64).
+
+6.10.6. Vendor-Specific FC Node Name Attributes
+
+ Attributes in the range 897 to 1024 are vendor-specific or site-
+ specific attributes used to describe the FC Node Name object. These
+ attributes are keyed by the FC Node Name WWNN (tag=96).
+
+6.10.7. Vendor-Specific Discovery Domain Attributes
+
+ Attributes in the range 1025 to 1280 are vendor-specific or site-
+ specific attributes used to describe the Discovery Domain object.
+ These attributes are keyed by the DD_ID (tag=104).
+
+6.10.8. Vendor-Specific Discovery Domain Set Attributes
+
+ Attributes in the range 1281 to 1536 are vendor-specific or site-
+ specific attributes used to describe the Discovery Domain Set object.
+ These attributes are keyed by the DD Set ID (tag=101)
+
+6.10.9. Other Vendor-Specific Attributes
+
+ Attributes in the range 1537 to 2048 can be used for key and non-key
+ attributes that describe new vendor-specific objects specific to the
+ vendor's iSNS server implementation.
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 99]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.11. Discovery Domain Registration Attributes
+
+6.11.1. DD Set ID Keyed Attributes
+
+6.11.1.1. Discovery Domain Set ID (DDS ID)
+
+ The DDS ID is an unsigned non-zero integer identifier used in the
+ iSNS directory database as a key to indicate a Discovery Domain Set
+ uniquely. A DDS is a collection of Discovery Domains that can be
+ enabled or disabled by a management station. This value is used as a
+ key for DDS attribute queries. When a Discovery Domain is
+ registered, it is initially not in any DDS.
+
+ If the iSNS client does not provide a DDS_ID in a DDS registration
+ request message, the iSNS server SHALL generate a DDS_ID value that
+ is unique within the iSNS database for that new DDS. The created DDS
+ ID SHALL be returned in the response message. The DDS ID value of 0
+ is reserved, and the DDS ID value of 1 is used for the default DDS
+ (see Section 2.2.2).
+
+6.11.1.2. Discovery Domain Set Symbolic Name
+
+ A variable-length UTF-8 encoded NULL-terminated text-based field of
+ up to 256 bytes. This is a user-readable field used to assist a
+ network administrator in tracking the DDS function. When a client
+ registers a DDS symbolic name, the iSNS server SHALL verify it is
+ unique. If the name is not unique, then the DDS registration SHALL
+ be rejected with an "Invalid Registration" Status Code. The invalid
+ attribute(s), in this case the DDS symbolic name, SHALL be included
+ in the response.
+
+6.11.1.3. Discovery Domain Set Status
+
+ The DDS_Status field is a 32-bit bitmap indicating the status of the
+ DDS. Bit 0 of the bitmap indicates whether the DDS is Enabled (1) or
+ Disabled (0). The default value for the DDS Enabled flag is Disabled
+ (0).
+
+ Bit Position DDS Status
+ ------------ ---------
+ 31 (Lsb) DDS Enabled (1) / DDS Disabled (0)
+ All others RESERVED
+
+6.11.1.4. Discovery Domain Set Next ID
+
+ This is a virtual attribute containing a 4-byte integer value that
+ indicates the next available (i.e., unused) Discovery Domain Set
+ Index value. This attribute may only be queried; the iSNS server
+
+
+
+Tseng, et al. Standards Track [Page 100]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ SHALL return an error code of 3 (Invalid Registration) to any client
+ that attempts to register a value for this attribute. A Message Key
+ is not required when exclusively querying for this attribute.
+
+ The Discovery Domain Set Next Index MAY be used by an SNMP client to
+ create an entry in the iSNS server. SNMP requirements are described
+ in Section 2.10.
+
+6.11.2. DD ID Keyed Attributes
+
+6.11.2.1. Discovery Domain ID (DD ID)
+
+ The DD ID is an unsigned non-zero integer identifier used in the iSNS
+ directory database as a key to identify a Discovery Domain uniquely.
+ This value is used as the key for any DD attribute query. If the
+ iSNS client does not provide a DD_ID in a DD registration request
+ message, the iSNS server SHALL generate a DD_ID value that is unique
+ within the iSNS database for that new DD (i.e., the iSNS client will
+ be registered in a new DD). The created DD ID SHALL be returned in
+ the response message. The DD ID value of 0 is reserved, and the DD
+ ID value of 1 is used for the default DD (see Section 2.2.2).
+
+6.11.2.2. Discovery Domain Symbolic Name
+
+ A variable-length UTF-8 encoded NULL-terminated text-based field of
+ up to 256 bytes. When a client registers a DD symbolic name, the
+ iSNS server SHALL verify it is unique. If the name is not unique,
+ then the DD registration SHALL be rejected with an "Invalid
+ Registration" Status Code. The invalid attribute(s), in this case
+ the DD symbolic name, SHALL be included in the response.
+
+6.11.2.3. Discovery Domain Member: iSCSI Node Index
+
+ This is the iSCSI Node Index of a Storage Node that is a member of
+ the DD. The DD may have a list of 0 to n members. The iSCSI Node
+ Index is one alternative representation of membership in a Discovery
+ Domain, the other alternative being the iSCSI Name. The Discovery
+ Domain iSCSI Node Index is a 4-byte non-zero integer value.
+
+ The iSCSI Node Index can be used to represent a DD member in
+ situations where the iSCSI Name is too long to be used. An example
+ of this is when SNMP is used for management, as described in Section
+ 2.10.
+
+ The iSCSI Node Index and the iSCSI Name stored as a member in a DD
+ SHALL be consistent with the iSCSI Node Index and iSCSI Name
+ attributes registered for the Storage Node object in the iSNS server.
+
+
+
+
+Tseng, et al. Standards Track [Page 101]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+6.11.2.4. Discovery Domain Member: iSCSI Name
+
+ A variable-length UTF-8 encoded NULL-terminated text-based field of
+ up to 224 bytes. It indicates membership for the specified iSCSI
+ Storage Node in the Discovery Domain. Note that the referenced
+ Storage Node does not need to be actively registered in the iSNS
+ database before the iSNS client uses this attribute. There is no
+ limit to the number of members that may be in a DD. Membership is
+ represented by the iSCSI Name of the iSCSI Storage Node.
+
+6.11.2.5. Discovery Domain Member: FC Port Name
+
+ This 64-bit identifier attribute indicates membership for an iFCP
+ Storage Node (FC Port) in the Discovery Domain. Note that the
+ referenced Storage Node does not need to be actively registered in
+ the iSNS database before the iSNS client uses this attribute. There
+ is no limit to the number of members that may be in a DD. Membership
+ is represented by the FC Port Name (WWPN) of the iFCP Storage Node.
+
+6.11.2.6. Discovery Domain Member: Portal Index
+
+ This attribute indicates membership in the Discovery Domain for a
+ Portal. It is an alternative representation for Portal membership to
+ the Portal IP Address and Portal TCP/UDP Port. The referenced Portal
+ MUST be actively registered in the iSNS database before the iSNS
+ client uses this attribute.
+
+6.11.2.7. Discovery Domain Member: Portal IP Address
+
+ This attribute and the Portal TCP/UDP Port attribute indicate
+ membership in the Discovery Domain for the specified Portal. Note
+ that the referenced Portal does not need to be actively registered in
+ the iSNS database before the iSNS client uses this attribute.
+
+6.11.2.8. Discovery Domain Member: Portal TCP/UDP Port
+
+ This attribute and the Portal IP Address attribute indicate
+ membership in the Discovery Domain for the specified Portal. Note
+ that the referenced Portal does not need to be actively registered in
+ the iSNS database before the iSNS client uses this attribute.
+
+6.11.2.9. Discovery Domain Features
+
+ The Discovery Domain Features is a bitmap indicating the features of
+ this DD. The bit positions are defined below. A bit set to 1
+ indicates the DD has the corresponding characteristics.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 102]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Bit Position DD Feature
+ ------------ ----------
+ 31 (Lsb) Boot List Enabled (1)/Boot List Disabled (0)
+ All others RESERVED
+
+ Boot List: this feature indicates that the target(s) in this DD
+ provides boot capabilities for the member initiators, as described in
+ [iSCSI-boot].
+
+6.11.2.10. Discovery Domain Next ID
+
+ This is a virtual attribute containing a 4-byte integer value that
+ indicates the next available (i.e., unused) Discovery Domain Index
+ value. This attribute may only be queried; the iSNS server SHALL
+ return an error code of 3 (Invalid Registration) to any client that
+ attempts to register a value for this attribute. A Message Key is
+ not required when exclusively querying for this attribute.
+
+7. Security Considerations
+
+7.1. iSNS Security Threat Analysis
+
+ When the iSNS protocol is deployed, the interaction between iSNS
+ server and iSNS clients is subject to the following security threats:
+
+ a) An attacker could alter iSNS protocol messages, such as to direct
+ iSCSI and iFCP devices to establish connections with rogue peer
+ devices, or to weaken/eliminate IPSec protection for iSCSI or
+ iFCP traffic.
+
+ b) An attacker could masquerade as the real iSNS server using false
+ iSNS heartbeat messages. This could cause iSCSI and iFCP devices
+ to use rogue iSNS servers.
+
+ c) An attacker could gain knowledge about iSCSI and iFCP devices by
+ snooping iSNS protocol messages. Such information could aid an
+ attacker in mounting a direct attack on iSCSI and iFCP devices,
+ such as a denial-of-service attack or outright physical theft.
+
+ To address these threats, the following capabilities are needed:
+
+ a) Unicast iSNS protocol messages may need to be authenticated. In
+ addition, to protect against threat c), confidentiality support
+ is desirable and is REQUIRED when certain functions of iSNS
+ server are utilized.
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 103]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ b) Multicast iSNS protocol messages such as the iSNS heartbeat
+ message may need to be authenticated. These messages need not be
+ confidential since they do not leak critical information.
+
+7.2. iSNS Security Implementation and Usage Requirements
+
+ If the iSNS server is used to distribute authorizations for
+ communications between iFCP and iSCSI peer devices, IPsec ESP with
+ null transform MUST be implemented, and non-null transform MAY be
+ implemented. If a non-null transform is implemented, then the DES
+ encryption algorithm SHOULD NOT be used.
+
+ If the iSNS server is used to distribute security policy for iFCP and
+ iSCSI devices, then authentication, data integrity, and
+ confidentiality MUST be supported and used. Where confidentiality is
+ desired or required, IPSec ESP with non-null transform SHOULD be
+ used, and the DES encryption algorithm SHOULD NOT be used.
+
+ If the iSNS server is used to provide the boot list for clients, as
+ described in Section 6.11.2.9, then the iSCSI boot client SHOULD
+ implement a secure iSNS connection.
+
+ In order to protect against an attacker masquerading as an iSNS
+ server, client devices MUST support the ability to authenticate
+ broadcast or multicast messages such as the iSNS heartbeat. The iSNS
+ authentication block (which is identical in format to the SLP
+ authentication block) SHALL be used for this purpose. iSNS clients
+ MUST implement the iSNS authentication block and MUST support BSD
+ value 0x002. If the iSNS server supports broadcast or multicast iSNS
+ messages (i.e., the heartbeat), then the server MUST implement the
+ iSNS authentication block and MUST support BSD value 0x002. Note
+ that the authentication block is used only for iSNS broadcast or
+ multicast messages and MUST NOT be used in unicast iSNS messages.
+
+ There is no requirement that the communicating identities in iSNS
+ protocol messages be kept confidential. Specifically, the identity
+ and location of the iSNS server is not considered confidential.
+
+ For protecting unicast iSNS protocol messages, iSNS servers
+ supporting security MUST implement ESP in tunnel mode and MAY
+ implement transport mode.
+
+ All iSNS implementations supporting security MUST support the replay
+ protection mechanisms of IPsec.
+
+ iSNS security implementations MUST support both IKE Main Mode and
+ Aggressive Mode for authentication, negotiation of security
+ associations, and key management, using the IPSec DOI [RFC2407].
+
+
+
+Tseng, et al. Standards Track [Page 104]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ Manual keying SHOULD NOT be used since it does not provide the
+ necessary rekeying support. Conforming iSNS security implementations
+ MUST support authentication using a pre-shared key, and MAY support
+ certificate-based peer authentication using digital signatures. Peer
+ authentication using the public key encryption methods outlined in
+ IKEs Sections 5.2 and 5.3 [RFC2409] SHOULD NOT be supported.
+
+ Conforming iSNS implementations MUST support both IKE Main Mode and
+ Aggressive Mode. IKE Main Mode with pre-shared key authentication
+ SHOULD NOT be used when either of the peers use dynamically assigned
+ IP addresses. Although Main Mode with pre-shared key authentication
+ offers good security in many cases, situations where dynamically
+ assigned addresses are used force the use of a group pre-shared key,
+ which is vulnerable to man-in-the-middle attack. IKE Identity
+ Payload ID_KEY_ID MUST NOT be used.
+
+ When digital signatures are used for authentication, either IKE Main
+ Mode or IKE Aggressive Mode MAY be used. In all cases, access to
+ locally stored secret information (pre-shared key or private key for
+ digital signing) MUST be suitably restricted, since compromise of the
+ secret information nullifies the security properties of the IKE/IPsec
+ protocols.
+
+ When digital signatures are used to achieve authentication, an IKE
+ negotiator SHOULD use IKE Certificate Request Payload(s) to specify
+ the certificate authority (or authorities) that are trusted in
+ accordance with its local policy. IKE negotiators SHOULD check the
+ pertinent Certificate Revocation List (CRL) before accepting a PKI
+ certificate for use in IKE's authentication procedures.
+
+ When the iSNS server is used without security, IP block storage
+ protocol implementations MUST support a negative cache for
+ authentication failures. This allows implementations to avoid
+ continually contacting discovered endpoints that fail authentication
+ within IPsec or at the application layer (in the case of iSCSI
+ Login). The negative cache need not be maintained within the IPsec
+ implementation, but rather within the IP block storage protocol
+ implementation.
+
+7.3. Discovering Security Requirements of Peer Devices
+
+ Once communication between iSNS clients and the iSNS server has been
+ secured through use of IPSec, the iSNS client devices have the
+ capability to discover the security settings that they need to use
+ for their peer-to-peer communications using the iSCSI and/or iFCP
+ protocols. This provides a potential scaling advantage over device-
+ by-device configuration of individual security policies for each
+ iSCSI and iFCP device.
+
+
+
+Tseng, et al. Standards Track [Page 105]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ The iSNS server stores security settings for each iSCSI and iFCP
+ device interface. These security settings, which can be retrieved by
+ authorized hosts, include use or non-use of IPSec, IKE, Main Mode,
+ and Aggressive Mode. For example, IKE may not be enabled for a
+ particular interface of a peer device. If a peer device can learn of
+ this in advance by consulting the iSNS server, it will not need to
+ waste time and resources attempting to initiate an IKE phase 1
+ session with that peer device interface.
+
+ If iSNS is used for this purpose, then the minimum information that
+ should be learned from the iSNS server is the use or non-use of IKE
+ and IPSec by each iFCP or iSCSI peer device interface. This
+ information is encoded in the Security Bitmap field of each Portal of
+ the peer device, and is applicable on a per-interface basis for the
+ peer device. iSNS queries for acquiring security configuration data
+ about peer devices MUST be protected by IPSec/ESP authentication.
+
+7.4. Configuring Security Policies of iFCP/iSCSI Devices
+
+ Use of iSNS for distribution of security policies offers the
+ potential to reduce the burden of manual device configuration, and to
+ decrease the probability of communications failures due to
+ incompatible security policies. If iSNS is used to distribute
+ security policies, then IPSec authentication, data integrity, and
+ confidentiality MUST be used to protect all iSNS protocol messages.
+
+ The complete IKE/IPSec configuration of each iFCP and/or iSCSI device
+ can be stored in the iSNS server, including policies that are used
+ for IKE Phase 1 and Phase 2 negotiations between client devices. The
+ IKE payload format includes a series of one or more proposals that
+ the iSCSI or iFCP device will use when negotiating the appropriate
+ IPsec policy to use to protect iSCSI or iFCP traffic.
+
+ In addition, the iSCSI Authentication Methods used by each iSCSI
+ device can also be stored in the iSNS server. The iSCSI AuthMethod
+ field (tag=42) contains a null-terminated string embedded with the
+ text values indicating iSCSI authentication methods to be used by
+ that iSCSI device.
+
+ Note that iSNS distribution of security policy is not necessary if
+ the security settings can be determined by other means, such as
+ manual configuration or IPsec security policy distribution. If a
+ network entity has already obtained its security configuration via
+ other mechanisms, then it MUST NOT request security policy via iSNS.
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 106]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+7.5. Resource Issues
+
+ The iSNS protocol is lightweight and will not generate a significant
+ amount of traffic. iSNS traffic is characterized by occasional
+ registration, notification, and update messages that do not consume
+ significant amounts of bandwidth. Even software-based IPSec
+ implementations should not have a problem handling the traffic loads
+ generated by the iSNS protocol.
+
+ To fulfill iSNS security requirements, the only additional resources
+ needed beyond what is already required for iSCSI and iFCP involve the
+ iSNS server. Because iSCSI and iFCP end nodes are already required
+ to implement IKE and IPSec, these existing requirements can also be
+ used to fulfill IKE and IPSec requirements for iSNS clients.
+
+7.6. iSNS Interaction with IKE and IPSec
+
+ When IPSec security is enabled, each iSNS client with at least one
+ Storage Node that is registered in the iSNS database SHALL maintain
+ at least one phase-1 security association with the iSNS server. All
+ iSNS protocol messages between iSNS clients and the iSNS server SHALL
+ be protected by a phase-2 security association.
+
+ When a Network Entity is removed from the iSNS database, the iSNS
+ server SHALL send a phase-1 delete message to the associated iSNS
+ client IKE peer, and tear down all phase-1 and phase-2 SAs associated
+ with that iSNS client.
+
+8. IANA Considerations
+
+ The well-known TCP and UDP port number for iSNS is 3205.
+
+ The standards action of this RFC creates two registries to be
+ maintained by IANA in support of iSNSP and assigns initial values for
+ both registries. The first registry is of Block Storage Protocols
+ supported by iSNS. The second registry is a detailed registry of
+ standard iSNS attributes that can be registered to and queried from
+ the iSNS server. Note that this RFC uses the registry created for
+ Block Structure Descriptor (BSD) in Section 15 of Service Location
+ Protocol, Version 2 [RFC2608].
+
+8.1. Registry of Block Storage Protocols
+
+ In order to maintain a registry of block storage protocols supported
+ by iSNSP, IANA will assign a 32-bit unsigned integer number for each
+ block storage protocol supported by iSNS. This number is stored in
+ the iSNS database as the Entity Protocol. The initial set of values
+ to be maintained by IANA for Entity Protocol is indicated in the
+
+
+
+Tseng, et al. Standards Track [Page 107]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ table in Section 6.2.2. Additional values for new block storage
+ protocols to be supported by iSNS SHALL be assigned by the IPS WG
+ Chairperson, or by a Designated Expert [RFC2434] appointed by the
+ IETF Transport Area Director.
+
+8.2. Registry of Standard iSNS Attributes
+
+ IANA is responsible for creating and maintaining the Registry of
+ Standard iSNS Attributes. The initial list of iSNS attributes is
+ described in Section 6. For each iSNS attribute this information
+ MUST include, its tag value, the attribute length, and the tag values
+ for the set of permissible registration and query keys that can be
+ used for that attribute. The initial list of iSNS attributes to be
+ maintained by IANA is indicated in Section 6.1.
+
+ Additions of new standard attributes to the Registry of Standard iSNS
+ Attributes SHALL require IETF Consensus [RFC2434]. The RFC required
+ for this process SHALL specify use of tag values reserved for IANA
+ allocation in Section 6.1. The RFC SHALL specify as a minimum, the
+ new attribute tag value, attribute length, and the set of permissible
+ registration and query keys that can be used for the new attribute.
+ The RFC SHALL also include a discussion of the reasons for the new
+ attribute(s) and how the new attribute(s) are to be used.
+
+ As part of the process of obtaining IETF Consensus, the proposed RFC
+ and its supporting documentation SHALL be made available to the IPS
+ WG mailing list or, if the IPS WG is disbanded at the time, to a
+ mailing list designated by the IETF Transport Area Director. The
+ review and comment period SHALL last at least three months before the
+ IPS WG Chair or a person designated by the IETF Transport Area
+ Director decides either to reject the proposal or to forward the
+ draft to the IESG for publication as an RFC. When the specification
+ is published as an RFC, then IANA will register the new iSNS
+ attribute(s) and make the registration available to the community.
+
+8.3. Block Structure Descriptor (BSD) Registry
+
+ Note that IANA is already responsible for assigning and maintaining
+ values used for the Block Structure Descriptor for the iSNS
+ Authentication Block (see Section 5.5). Section 15 of [RFC2608]
+ describes the process for allocation of new BSD values.
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 108]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+9. Normative References
+
+ [iSCSI] Satran, J., Meth, K., Sapuntzakis, C., Chadalapaka, M.,
+ and E. Zeidner, "Internet Small Computer Systems
+ Interface (iSCSI)", RFC 3720, April 2004.
+
+ [iFCP] Monia, C., Mullendore, R., Travostino, F., Jeong, W.,
+ and M. Edwards, "iFCP - A Protocol for Internet Fibre
+ Channel Storage Networking", RFC 4172, September 2005.
+
+ [iSNSOption] Monia, C., Tseng, J., and K. Gibbons, The IPv4 Dynamic
+ Host Configuration Protocol (DHCP) Option for the
+ Internet Storage Name Service, RFC 4174, September 2005.
+
+ [RFC2608] Guttman, E., Perkins, C., Veizades, J., and M. Day,
+ "Service Location Protocol, Version 2 ", RFC 2608, June
+ 1999.
+
+ [iSCSI-SLP] Bakke, M., Hufferd, J., Voruganti, K., Krueger, M., and
+ T. Sperry, "Finding Internet Small Computer Systems
+ Interface (iSCSI) Targets and Name Servers by Using
+ Service Location Protocol version 2 (SLP), RFC 4018,
+ April 2005.
+
+ [iSCSI-boot] Sarkar, P., Missimer, D., and C. Sapuntzakis,
+ "Bootstrapping Clients using the Internet Samll Computer
+ System Interface (iSCSI) Protocol", RFC 4173, September
+ 2005.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [STRINGPREP] Bakke, M., "String Profile for Internet Small Computer
+ Systems Interface (iSCSI) Names", RFC 3722, April 2004.
+
+ [NAMEPREP] Hoffman, P. Nameprep: A Stringprep Profile for
+ Internationalized Domain Names, July 2002.
+
+ [RFC2407] Piper, D., "The Internet IP Security Domain of
+ Interpretation for ISAKMP", RFC 2407, November 1998.
+
+ [RFC2408] Maughan, D., Schertler, M., Schneider, M., and J.
+ Turner, "Internet Security Association and Key
+ Management Protocol (ISAKMP)", RFC 2408, November 1998.
+
+ [RFC2409] Harkins, D. and D. Carrel, "The Internet Key Exchange
+ (IKE)", RFC 2409, November 1998.
+
+
+
+
+Tseng, et al. Standards Track [Page 109]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ [EUI-64] Guidelines for 64-bit Global Identifier (EUI-64)
+ Registration Authority, May 2001, IEEE
+
+ [RFC3279] Bassham, L., Polk, W., and R. Housley, "Algorithms and
+ Identifiers for the Internet X.509 Public Key
+ Infrastructure Certificate and Certificate Revocation
+ List (CRL) Profile", RFC 3279, April 2002.
+
+ [RFC3280] Housley, R., Polk, W., Ford, W., and D. Solo, "Internet
+ X.509 Public Key Infrastructure Certificate and
+ Certificate Revocation List (CRL) Profile", RFC 3280,
+ April 2002.
+
+ [802-1990] IEEE Standards for Local and Metropolitan Area Networks:
+ Overview and Architecture, Technical Committee on
+ Computer Communications of the IEEE Computer Society,
+ May 31, 1990
+
+ [FC-FS] Fibre Channel Framing and Signaling Interface, NCITS
+ Working Draft Project 1331-D
+
+10. Informative References
+
+ [iSNSMIB] Gibbons, K., et al., "Definitions of Managed Objects for
+ iSNS (Internet Storage name Service)", Work in Progress,
+ July 2003.
+
+ [X.509] ITU-T Recommendation X.509 (1997 E): Information
+ Technology - Open Systems Interconnection - The
+ Directory: Authentication Framework, June 1997
+
+ [FC-GS-4] Fibre Channel Generic Services-4 (work in progress),
+ NCITS Working Draft Project 1505-D
+
+ [RFC1510] Kohl, J. and C. Neuman, "The Kerberos Network
+ Authentication Service (V5)", RFC 1510, September 1993.
+
+ [RFC2025] Adams, C., "The Simple Public-Key GSS-API Mechanism
+ (SPKM)", RFC 2025, October 1996.
+
+ [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing an
+ IANA Considerations Section in RFCs", BCP 26, RFC 2434,
+ October 1998.
+
+ [RFC2945] Wu, T., "The SRP Authentication and Key Exchange
+ System", RFC 2945, September 2000.
+
+
+
+
+
+Tseng, et al. Standards Track [Page 110]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ [RFC1994] Simpson, W., "PPP Challenge Handshake Authentication
+ Protocol (CHAP)", RFC 1994, August 1996.
+
+ [RFC2131] Droms, R., "Dynamic Host Configuration Protocol", RFC
+ 2131, March 1997.
+
+ [RFC3410] Case, J., Mundy, R., Partain, D., and B. Stewart,
+ "Introduction and Applicability Statements for
+ Internet-Standard Management Framework", RFC 3410,
+ December 2002.
+
+ [RFC3411] Harrington, D., Presuhn, R., and B. Wijnen, "An
+ Architecture for Describing Simple Network Management
+ Protocol (SNMP) Management Frameworks", STD 62, RFC
+ 3411, December 2002.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 111]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+Appendix A: iSNS Examples
+
+A.1. iSCSI Initialization Example
+
+ This example assumes an SLP Service Agent (SA) has been implemented
+ on the iSNS host, and an SLP User Agent (UA) has been implemented on
+ the iSNS initiator. See [RFC2608] for further details on SAs and
+ UAs. This example also assumes that the target is configured to use
+ the iSNS server, and have its access control policy subordinated to
+ the iSNS server.
+
+A.1.1. Simple iSCSI Target Registration
+
+ In this example, a simple target with a single iSCSI name registers
+ with the iSNS server. The target is represented in the iSNS by an
+ Entity containing one Storage Node, one Portal, and an implicitly
+ registered Portal Group that provides a relationship between the
+ Storage Node and Portal. The target has not been assigned a Fully
+ Qualified Domain Name (FQDN) by the administrator. In this example,
+ because a PG object is not explicitly registered, a Portal Group with
+ a PGT of 1 is implicitly registered. In this example SLP is used to
+ discover the location of the iSNS Server. An alternative is to use
+ the iSNS DHCP option [iSNSOption] to discover the iSNS server.
+
+ +--------------------------+------------------+-------------------+
+ | iSCSI Target Device | iSNS Server |Management Station |
+ +--------------------------+------------------+-------------------+
+ |Discover iSNS--SLP------->| |/*mgmt station is |
+ | |<--SLP--iSNS Here:| administratively |
+ | | 192.0.2.100 | authorized to view|
+ | | | all DDs. Device |
+ | DevAttrReg--------->| | NAMEabcd was |
+ |Src:(tag=32) "NAMEabcd" | | previously placed |
+ |Key: <none present> | | into DDabcd along |
+ |Oper Attrs: | | with devpdq and |
+ |tag=1: NULL | | devrst. |
+ |tag=2: "iSCSI" | | |
+ |tag=16: 192.0.2.5 | | |
+ |tag=17: 5001 | | |
+ |tag=32: "NAMEabcd" | | |
+ |tag=33: target | | |
+ |tag=34: "disk 1" | | |
+ | |<---DevAttrRegRsp | |
+ | |SUCCESS | |
+ | |Key:(tag=1) "isns:0001" |
+ | |Oper Attrs: | |
+ | |tag=1: "isns:0001"| |
+ | |tag=2: "iSCSI" | |
+
+
+
+Tseng, et al. Standards Track [Page 112]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ | |tag=16: 192.0.2.5 | |
+ | |tag=17: 5001 | |
+ | |tag=32: "NAMEabcd"|/* previously |
+ | |tag=33: target | placed in a DD */ |
+ | |tag=34: "disk 1" | |
+ | | | |
+ | | SCN-------->| |
+ | |(or SNMP notification) |
+ | |dest:(tag=32):"MGMTname1" |
+ | |time:(tag=4): <current time> |
+ | |tag=35: "MGT-SCN, OBJ-ADD" |
+ | |tag=32: "NAMEabcd"| |
+ | | |<-------SCNRsp |
+ | DevAttrQry--------->| | |
+ |Src:(tag=32) "NAMEabcd" | | |
+ |Key:(tag=33) "initiator" | | |
+ |Oper Attrs: | | |
+ |tag=16: NULL | | |
+ |tag=17: NULL | | |
+ |tag=32: NULL | | |
+ |/*Query asks for all initr| | |
+ |devices' IP address, port |<---DevAttrQryRsp | |
+ |number, and Name*/ |SUCCESS | |
+ | |tag=16:192.0.2.1 | |
+ | |tag=17:50000 | |
+ | |tag=32:"devpdq" | |
+ | |tag=16:192.0.2.2 | |
+ | |tag=17:50000 | |
+ | |tag=32:"devrst" | |
+ |/*************************| |<-----DevAttrQry |
+ |Our target "NAMEabcd" | |src: "MGMTname1" |
+ |discovers two initiators | key:(tag=32)"NAMEabcd"
+ |in shared DDs. It will | |Op Attrs: |
+ |accept iSCSI logins from | |tag=16: NULL |
+ |these two identified | |tag=17: NULL |
+ |initiators presented by | |tag=32: NULL |
+ |iSNS | | |
+ |*************************/| DevAttrQryRsp--->| |
+ | |SUCCESS | |
+ | |tag=16: 192.0.2.5 | |
+ | |tag=17: 5001 | |
+ | |tag=32: "NAMEabcd"| |
+ +--------------------------+------------------+-------------------+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 113]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+A.1.2. Target Registration and DD Configuration
+
+ In this example, a more complex target, with two Storage Nodes and
+ two Portals using ESI monitoring, registers with the iSNS. This
+ target has been configured with a Fully Qualified Domain Name (FQDN)
+ in the DNS servers, and the user wishes to use this identifier for
+ the device. The target explicitly registers Portal Groups to
+ describe how each Portal provides access to each Storage Node. One
+ target Storage Node allows coordinated access through both Portals.
+ The other Storage Node allows access, but not coordinated access,
+ through both Portals.
+
+ +--------------------------+------------------+-------------------+
+ | iSCSI Target Device | iSNS Server |Management Station |
+ +--------------------------+------------------+-------------------+
+ |Discover iSNS--SLP--> | |/*mgmt station is |
+ | |<--SLP--iSNS Here:| administratively |
+ | | 192.0.2.100 | authorized to view|
+ | DevAttrReg--> | | all DDs */ |
+ |Src: | | |
+ |tag=32: "NAMEabcd" | | |
+ |Msg Key: | | |
+ |tag=1: "jbod1.example.com"| | |
+ |Oper Attrs: | | |
+ |tag=1: "jbod1.example.com"| | |
+ |tag=2: "iSCSI" | | |
+ |tag=16: 192.0.2.4 | | |
+ |tag=17: 5001 | | |
+ |tag=19: 5 | | |
+ |tag=20: 5002 | | |
+ |tag=16: 192.0.2.5 | | |
+ |tag=17: 5001 | | |
+ |tag=19: 5 | | |
+ |tag=20: 5002 | | |
+ |tag=32: "NAMEabcd" | | |
+ |tag=33: "Target" | | |
+ |tag=34: "Storage Array 1" | | |
+ |tag=51: 10 | | |
+ |tag=49: 192.0.2.4 | | |
+ |tag=50: 5001 | | |
+ |tag=49: 192.0.2.5 | | |
+ |tag=50: 5001 | | |
+ |tag=32: "NAMEefgh" | | |
+ |tag=33: "Target" | | |
+ |tag=34: "Storage Array 2" |/*****************| |
+ |tag=51: 20 |jbod1.example.com is |
+ |tag=49: 192.0.2.4 |now registered in | |
+ |tag=50: 5001 |iSNS, but is not | |
+
+
+
+Tseng, et al. Standards Track [Page 114]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ |tag=51: 30 |in any DD. Therefore, |
+ |tag=49: 192.0.2.5 |no other devices | |
+ |tag=50: 5001 |can "see" it. | |
+ | |*****************/| |
+ | |<--DevAttrRegRsp | |
+ | |SUCCESS | |
+ | |Msg Key: | |
+ | |tag=1: "jbod1.example.com" |
+ | |Oper Attrs: | |
+ | |tag=1: "jbod1.example.com" |
+ | |tag=2: "iSCSI" | |
+ | |tag=16: 192.0.2.4 | |
+ | |tag=17: 5001 | |
+ | |tag=19: 5 | |
+ | |tag=20: 5002 | |
+ | |tag=16: 192.0.2.5 | |
+ | |tag=17: 5001 | |
+ | |tag=19: 5 | |
+ | |tag=20: 5002 | |
+ | |tag=32: "NAMEabcd"| |
+ | |tag=33: "Target" | |
+ | |tag=34: "Storage Array 1" |
+ | |tag=48: "NAMEabcd"| |
+ | |tag=49: 192.0.2.4 | |
+ | |tag=50: 5001 | |
+ | |tag=51: 10 | |
+ | |tag=48: "NAMEabcd"| |
+ | |tag=49: 192.0.2.5 | |
+ | |tag=50: 5001 | |
+ | |tag=51: 10 | |
+ | |tag=32: "NAMEefgh"| |
+ | |tag=33: "Target" | |
+ | |tag=34: "Storage Array 2" |
+ | |tag=43: X.509 cert| |
+ | |tag=48: "NAMEefgh"| |
+ | |tag=49: 192.0.2.4 | |
+ | |tag=50: 5001 | |
+ | |tag=51: 20 | |
+ | |tag=48: "NAMEefgh"| |
+ | |tag=49: 192.0.2.5 | |
+ | |tag=50: 5001 | |
+ | |tag=51: 30 | |
+ | | | |
+ | | SCN------> | |
+ | | (or SNMP notification) |
+ | |dest:(tag=32)"mgmt.example.com" |
+ | |time:(tag=4): <current time> |
+ | |tag=35: "MGT-SCN, OBJ-ADD" |
+
+
+
+Tseng, et al. Standards Track [Page 115]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ | |tag=32: "NAMEabcd"| |
+ | |tag=35: "MGT-SCN, OBJ-ADD" |
+ | |tag=32: "NAMEefgh"| |
+ | | |<--SCNRsp |
+ | | |SUCCESS |
+ | | tag=32:"mgmt.example.com"|
+ | | | |
+ | | |<--DevAttrQry |
+ | | |Src: |
+ | | tag=32:"mgmt.example.com"
+ | | |Msg Key: |
+ | | |tag=32: "NAMEabcd" |
+ | | |Oper Attrs: |
+ | | |tag=16: <0-length> |
+ | | |tag=17: <0-length> |
+ | | |tag=32: <0-length> |
+ | | | |
+ | | DevAttrQryRsp--> | |
+ | |SUCCESS | |
+ | |Msg Key: | |
+ | |tag=32: "NAMEabcd"| |
+ | |Oper Attrs: | |
+ | |tag=16: 192.0.2.4 | |
+ | |tag=17: 5001 | |
+ | |tag=32:"NAMEabcd" | |
+ | |tag=16: 192.0.2.5 | |
+ | |tag=17: 5001 | |
+ | |tag=32:"NAMEabcd" | |
+ | | |Src: |
+ | | tag=32:"mgmt.example.com"
+ | | |Msg Key: |
+ | | |tag=32: "NAMEefgh" |
+ | | |Oper Attrs: |
+ | | |tag=16: <0-length> |
+ | | |tag=17: <0-length> |
+ | | |tag=32: <0-length> |
+ | | | |
+ | | DevAttrQryRsp--> | |
+ | |SUCCESS | |
+ | |Msg Key: | |
+ | |tag=32: "NAMEefgh"| |
+ | |Oper Attrs: | |
+ | |tag=16: 192.0.2.4 | |
+ | |tag=17: 5001 | |
+ | |tag=32:"NAMEefgh" | |
+ | |tag=16: 192.0.2.5 |/**Mgmt Station ***|
+ | |tag=17: 5001 |displays device, |
+ | |tag=32:"NAMEefgh" |the operator decides
+
+
+
+Tseng, et al. Standards Track [Page 116]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ | | |to place "NAMEabcd"|
+ | | |into Domain "DDxyz"|
+ |/*************************| |******************/|
+ |Target is now registered | | |
+ |in iSNS. It is then placed| |<--DDReg |
+ |in a pre-existing DD with | |Src: |
+ |DD_ID 123 by a management | tag=32:"mgmt.example.com"
+ |station. | |Msg Key: |
+ |*************************/| |tag=2065: 123 |
+ | | |Oper Attrs: |
+ | | |tag=2068: "NAMEabcd"
+ | | DDRegRsp-----> | |
+ | |SUCCESS | |
+ | |Msg Key: | |
+ | |tag=2065: 123 | |
+ | |Oper Attrs: | |
+ | |tag=2065: 123 | |
+ +--------------------------+------------------+-------------------+
+
+A.1.3. Initiator Registration and Target Discovery
+
+ The following example illustrates a new initiator registering with
+ the iSNS, and discovering the target NAMEabcd from the example in
+ A.1.2.
+
+ +--------------------------+------------------+-------------------+
+ | iSCSI Initiator | iSNS |Management Station |
+ +--------------------------+------------------+-------------------+
+ |Discover iSNS--SLP--> | |/*mgmt station is |
+ | |<--SLP--iSNS Here:| administratively |
+ | | 192.36.53.1 | authorized to view|
+ |DevAttrReg--> | | all DDs ********/ |
+ |Src: | | |
+ |tag=32: "NAMEijkl" | | |
+ |Msg Key: | | |
+ |tag=1: "svr1.example.com" | | |
+ |Oper Attrs: | | |
+ |tag=1: "svr1.example.com" | | |
+ |tag=2: "iSCSI" | | |
+ |tag=16: 192.20.3.1 |/*****************| |
+ |tag=17: 5001 |Device not in any | |
+ |tag=19: 5 |DD, so it is | |
+ |tag=20: 5002 |inaccessible by | |
+ |tag=32: "NAMEijkl" |other devices | |
+ |tag=33: "Initiator" |*****************/| |
+ |tag=34: "Server1" | | |
+ |tag=51: 11 | | |
+ |tag=49: 192.20.3.1 | | |
+
+
+
+Tseng, et al. Standards Track [Page 117]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ |tag=50: 5001 | | |
+ | |<--DevAttrRegRsp | |
+ | |SUCCESS | |
+ | |Msg Key: | |
+ | |tag=1: "svr1.example.com" |
+ | |Oper Attrs: | |
+ | |tag=1: "svr1.example.com" |
+ | |tag=2: "iSCSI" | |
+ | |tag=16: 192.20.3.1| |
+ | |tag=17: 5001 | |
+ | |tag=19: 5 | |
+ | |tag=20: 5002 | |
+ | |tag=32: "NAMEijkl"| |
+ | |tag=33: "Initiator" |
+ | |tag=34: "Server1" | |
+ | |tag=48: "NAMEijkl"| |
+ | |tag=49: 192.20.3.1| |
+ | |tag=50: 5001 | |
+ | |tag=51: 11 | |
+ | | | |
+ | | SCN------> | |
+ | | (or SNMP notification) |
+ | |dest:(tag=32)"mgmt.example.com" |
+ | |time:(tag=4): <current time> |
+ | |tag=35: "MGT-SCN, OBJ-ADD" |
+ | |tag=32: "NAMEijkl"| |
+ | | | |
+ | | |<------SCNRsp |
+ | | |SUCCESS |
+ | | tag=32:"mgmt.example.com"
+ | | | |
+ |SCNReg--> | | |
+ |Src: | | |
+ |tag=32: "NAMEijkl" | | |
+ |Msg Key: | | |
+ |tag=32: "NAMEijkl" | | |
+ |Oper Attrs: | | |
+ |tag=35: <TARG&SELF, OBJ-RMV/ADD/UPD> | |
+ | |<--SCNRegRsp | |
+ | |SUCCESS | |
+ | | | |
+ | | |<----DevAttrQry |
+ | | |Src: |
+ | | tag=32:"mgmt.example.com"
+ | | |Msg Key: |
+ | | |tag=32: "NAMEijkl" |
+ | | |Oper Attrs: |
+ | | |tag=16: <0-length> |
+
+
+
+Tseng, et al. Standards Track [Page 118]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ | | |tag=17: <0-length> |
+ | | |tag=32: <0-length> |
+ | | DevAttrQryRsp--->| |
+ | |SUCCESS | |
+ | |Msg Key: | |
+ | |tag=32: "NAMEijkl"| |
+ | |Oper Attrs: | |
+ | |tag=16:192.20.3.1 | |
+ | |tag=17: 5001 | |
+ | |tag=32:"NAMEijkl" | |
+ | | |/**Mgmt Station ***|
+ | | |displays device, the
+ | | |operator decides to|
+ | | |place "NAMEijkl" into
+ | | |pre-existing Disc |
+ | | |Domain "DDxyz" with|
+ | | |device NAMEabcd |
+ | | |******************/|
+ | | |<--DDReg |
+ | | |Src: |
+ | | tag=32:"mgmt.example.com"
+ | | |Msg Key: |
+ | | |tag=2065: 123 |
+ | | |Oper Attrs: |
+ | | |tag=2068: "NAMEijkl"
+ | | | |
+ | | DDRegRsp---->| |
+ | |SUCCESS | |
+ | |Msg Key: | |
+ | |tag=2065: 123 | |
+ | |Oper Attrs: | |
+ | |tag=2065: 123 |/******************|
+ | | |"NAMEijkl" has been|
+ | | |moved to "DDxyz" |
+ | | |******************/|
+ | | SCN------>| |
+ | |dest:(tag=32)"mgmt.example.com" |
+ | |time:(tag=4): <current time> |
+ | |tag=35: <MGT-SCN, DD/DDS-MBR-ADD> |
+ | |tag=2065: 123 | |
+ | |tag=2068: "NAMEijkl" |
+ | | | |
+ | | |<------SCNRsp |
+ | | |SUCCESS |
+ | | tag=32:"mgmt.example.com"
+ | |<-----SCN | |
+ | |dest:(tag=32)"NAMEijkl" |
+ | |time:(tag=4): <current time> |
+
+
+
+Tseng, et al. Standards Track [Page 119]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ | |tag=35: <TARG&SELF, OBJ-ADD> |
+ | |tag=32: "NAMEijkl"| |
+ | SCNRsp------> | | |
+ |SUCCESS | | |
+ |tag=32:"NAMEijkl" | | |
+ | | | |
+ | |/*****************| |
+ | |Note that NAMEabcd| |
+ | |also receives an | |
+ | |SCN that NAMEijkl | |
+ | |is in the same DD | |
+ | |*****************/| |
+ | (to "NAMEabcd")|<-----SCN | |
+ | |dest:(tag=32)"NAMEabcd" |
+ | |time:(tag=4): <current time> |
+ | |tag=35: <INIT&SELF, OBJ-ADD> |
+ | |tag=32: "NAMEijkl"| |
+ | SCNRsp------> | | |
+ |SUCCESS | | |
+ |tag=32:"NAMEabcd" | | |
+ | | | |
+ | DevAttrQry----------->| | |
+ |Src: | | |
+ |tag=32: "NAMEijkl" | | |
+ |Msg Key: | | |
+ |tag=33: "Target" | | |
+ |Oper Attrs: | | |
+ |tag=16: <0-length> | | |
+ |tag=17: <0-length> | | |
+ |tag=32: <0-length> | | |
+ |tag=34: <0-length> | | |
+ |tag=43: <0-length> | | |
+ |tag=48: <0-length> | | |
+ |tag=49: <0-length> | | |
+ |tag=50: <0-length> | | |
+ |tag=51: <0-length> | | |
+ | |<--DevAttrQryRsp | |
+ | |SUCCESS | |
+ | |Msg Key: | |
+ | |tag=33:"Target" | |
+ | |Oper Attrs: | |
+ | |tag=16: 192.0.2.4 | |
+ | |tag=17: 5001 | |
+ | |tag=32: "NAMEabcd"| |
+ | |tag=34: "Storage Array 1" |
+ | |tag=16: 192.0.2.5 | |
+ | |tag=17: 5001 | |
+ | |tag=32: "NAMEabcd"| |
+
+
+
+Tseng, et al. Standards Track [Page 120]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+ | |tag=34: "Storage Array 1" |
+ | |tag=43: X.509 cert| |
+ | |tag=48: "NAMEabcd"| |
+ | |tag=49: 192.0.2.4 | |
+ | |tag=50: 5001 | |
+ | |tag=51: 10 | |
+ | |tag=48: "NAMEabcd"| |
+ | |tag=49: 192.0.2.5 | |
+ | |tag=50: 5001 | |
+ | |tag=51: 10 | |
+ | | | |
+ |/***The initiator has discovered | |
+ |the target, and has everything | |
+ |needed to complete iSCSI login | |
+ |The same process occurs on the | |
+ |target side; the SCN prompts the | |
+ |target to download the list of | |
+ |authorized initiators from the | |
+ |iSNS (i.e., those initiators in the | |
+ |same DD as the target.************/ | |
+ +--------------------------+------------------+-------------------+
+
+Acknowledgements
+
+ Numerous individuals contributed to the creation of this document
+ through their careful review and submissions of comments and
+ recommendations. We acknowledge the following persons for their
+ technical contributions to this document: Mark Bakke (Cisco), John
+ Hufferd (IBM), Julian Satran (IBM), Kaladhar Voruganti(IBM), Joe Czap
+ (IBM), John Dowdy (IBM), Tom McSweeney (IBM), Jim Hafner (IBM), Chad
+ Gregory (Intel), Yaron Klein (Sanrad), Larry Lamers (Adaptec), Jack
+ Harwood (EMC), David Black (EMC), David Robinson (Sun), Alan Warwick
+ (Microsoft), Bob Snead (Microsoft), Fa Yoeu (Intransa), Joe White
+ (McDATA), Charles Monia (McDATA), Larry Hofer (McDATA), Ken Hirata
+ (Vixel), Howard Hall (Pirus), Malikarjun Chadalapaka (HP), Marjorie
+ Krueger (HP), Siva Vaddepuri (McDATA), and Vinai Singh (American
+ Megatrends).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 121]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+Authors' Addresses
+
+ Josh Tseng
+ Riverbed Technology
+ 501 2nd Street, Suite 410
+ San Francisco, CA 94107
+
+ Phone: (650)274-2109
+ EMail: joshtseng@yahoo.com
+
+
+ Kevin Gibbons
+ McDATA Corporation
+ 4555 Great America Parkway
+ Santa Clara, CA 95054-1208
+
+ Phone: (408) 567-5765
+ EMail: kevin.gibbons@mcdata.com
+
+
+ Franco Travostino
+ Nortel
+ 600 Technology Park Drive
+ Billerica, MA 01821 USA
+
+ Phone: (978) 288-7708
+ EMail: travos@nortel.com
+
+
+ Curt du Laney
+ Rincon Research Corporation
+ 101 North Wilmot Road, Suite 101
+ Tucson AZ 85711
+
+ Phone: (520) 519-4409
+ EMail: cdl@rincon.com
+
+
+ Joe Souza
+ Microsoft Corporation
+ One Microsoft Way
+ Redmond, WA 98052-6399
+
+ Phone: (425) 706-3135
+ EMail: joes@exmsft.com
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 122]
+
+RFC 4171 Internet Storage Name Service (iSNS) September 2005
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2005).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at ietf-
+ ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+Tseng, et al. Standards Track [Page 123]
+
diff --git a/utils/open-isns/domain.c b/utils/open-isns/domain.c
new file mode 100644
index 0000000..3b848ac
--- /dev/null
+++ b/utils/open-isns/domain.c
@@ -0,0 +1,208 @@
+/*
+ * iSNS object model - discovery domain specific code
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "isns.h"
+#include "objects.h"
+#include "util.h"
+
+static int
+__isns_default_dd_rebuild(isns_object_t *obj, isns_db_t *db)
+{
+ isns_object_list_t list = ISNS_OBJECT_LIST_INIT;
+ unsigned int i;
+
+ isns_object_prune_attrs(obj);
+
+ isns_db_get_domainless(db, &isns_iscsi_node_template, &list);
+ for (i = 0; i < list.iol_count; ++i) {
+ isns_object_t *node = list.iol_data[i];
+ const char *name;
+ uint32_t type;
+
+ if (!isns_object_get_uint32(node,
+ ISNS_TAG_ISCSI_NODE_TYPE,
+ &type))
+ continue;
+ if (type & ISNS_ISCSI_CONTROL_MASK)
+ continue;
+ if (!isns_object_get_string(node,
+ ISNS_TAG_ISCSI_NAME,
+ &name))
+ continue;
+ isns_object_set_string(obj,
+ ISNS_TAG_DD_MEMBER_ISCSI_NAME,
+ name);
+ }
+
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Create the default domain
+ */
+isns_object_t *
+isns_create_default_domain(void)
+{
+ isns_object_t *obj;
+
+ obj = isns_create_object(&isns_dd_template, NULL, NULL);
+ if (!obj)
+ return NULL;
+
+ isns_object_set_uint32(obj, ISNS_TAG_DD_ID, 0);
+ obj->ie_rebuild = __isns_default_dd_rebuild;
+ return obj;
+}
+
+/*
+ * Check object type
+ */
+int
+isns_object_is_dd(const isns_object_t *obj)
+{
+ return ISNS_IS_DD(obj);
+}
+
+int
+isns_object_is_ddset(const isns_object_t *obj)
+{
+ return ISNS_IS_DDSET(obj);
+}
+
+/*
+ * Keep track of DD membership through a bit vector
+ */
+int
+isns_object_mark_membership(isns_object_t *obj, uint32_t id)
+{
+ if (!obj->ie_membership)
+ obj->ie_membership = isns_bitvector_alloc();
+
+ return isns_bitvector_set_bit(obj->ie_membership, id);
+}
+
+int
+isns_object_test_membership(const isns_object_t *obj, uint32_t id)
+{
+ if (!obj->ie_membership)
+ return 0;
+
+ return isns_bitvector_test_bit(obj->ie_membership, id);
+}
+
+int
+isns_object_clear_membership(isns_object_t *obj, uint32_t id)
+{
+ if (!obj->ie_membership)
+ return 0;
+
+ return isns_bitvector_clear_bit(obj->ie_membership, id);
+}
+
+/*
+ * Check whether the two objects share a discovery domain,
+ * and if so, return the DD_ID.
+ * Returns -1 otherwise.
+ */
+int
+isns_object_test_visibility(const isns_object_t *a, const isns_object_t *b)
+{
+ /* The admin can tell isnsd to put all nodes which are *not*
+ * in any discovery domain, into the so-called default domain */
+ if (isns_config.ic_use_default_domain
+ && a->ie_template == b->ie_template
+ && isns_bitvector_is_empty(a->ie_membership)
+ && isns_bitvector_is_empty(b->ie_membership))
+ return 1;
+
+ return isns_bitvector_intersect(a->ie_membership, b->ie_membership, NULL) >= 0;
+}
+
+/*
+ * Return all visible nodes and portals
+ */
+static int
+__isns_object_vis_callback(uint32_t dd_id, void *ptr)
+{
+ isns_object_list_t *list = ptr;
+
+ /* Get all active members */
+ isns_dd_get_members(dd_id, list, 1);
+ return 0;
+}
+
+void
+isns_object_get_visible(const isns_object_t *obj,
+ isns_db_t *db,
+ isns_object_list_t *result)
+{
+ if (isns_bitvector_is_empty(obj->ie_membership)) {
+ /* Get all other nodes not in any DD */
+ if (isns_config.ic_use_default_domain)
+ isns_db_get_domainless(db,
+ obj->ie_template,
+ result);
+ return;
+ }
+
+ isns_bitvector_foreach(obj->ie_membership,
+ __isns_object_vis_callback,
+ result);
+}
+
+/*
+ * Object templates
+ */
+static uint32_t discovery_domain_attrs[] = {
+ ISNS_TAG_DD_ID,
+ ISNS_TAG_DD_SYMBOLIC_NAME,
+ ISNS_TAG_DD_MEMBER_ISCSI_INDEX,
+ ISNS_TAG_DD_MEMBER_ISCSI_NAME,
+ ISNS_TAG_DD_MEMBER_FC_PORT_NAME,
+ ISNS_TAG_DD_MEMBER_PORTAL_INDEX,
+ ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR,
+ ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT,
+ ISNS_TAG_DD_FEATURES,
+};
+
+static uint32_t discovery_domain_key_attrs[] = {
+ ISNS_TAG_DD_ID,
+};
+
+isns_object_template_t isns_dd_template = {
+ .iot_name = "Discovery Domain",
+ .iot_handle = ISNS_OBJECT_TYPE_DD,
+ .iot_attrs = discovery_domain_attrs,
+ .iot_num_attrs = array_num_elements(discovery_domain_attrs),
+ .iot_keys = discovery_domain_key_attrs,
+ .iot_num_keys = array_num_elements(discovery_domain_key_attrs),
+ .iot_index = ISNS_TAG_DD_ID,
+ .iot_next_index = ISNS_TAG_DD_NEXT_ID,
+};
+
+static uint32_t dd_set_attrs[] = {
+ ISNS_TAG_DD_SET_ID,
+ ISNS_TAG_DD_SET_SYMBOLIC_NAME,
+ ISNS_TAG_DD_SET_STATUS,
+};
+
+static uint32_t dd_set_key_attrs[] = {
+ ISNS_TAG_DD_SET_ID,
+};
+
+isns_object_template_t isns_ddset_template = {
+ .iot_name = "Discovery Domain Set",
+ .iot_handle = ISNS_OBJECT_TYPE_DDSET,
+ .iot_attrs = dd_set_attrs,
+ .iot_num_attrs = array_num_elements(dd_set_attrs),
+ .iot_keys = dd_set_key_attrs,
+ .iot_num_keys = array_num_elements(dd_set_key_attrs),
+ .iot_next_index = ISNS_TAG_DD_SET_NEXT_ID,
+};
+
diff --git a/utils/open-isns/entity.c b/utils/open-isns/entity.c
new file mode 100644
index 0000000..cd45e1f
--- /dev/null
+++ b/utils/open-isns/entity.c
@@ -0,0 +1,127 @@
+/*
+ * iSNS object model - network entity specific code
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "isns.h"
+#include "objects.h"
+#include "util.h"
+
+/*
+ * Create a network entity
+ */
+isns_object_t *
+isns_create_entity(int protocol, const char *name)
+{
+ isns_object_t *obj;
+
+ obj = isns_create_object(&isns_entity_template, NULL, NULL);
+ isns_object_set_string(obj,
+ ISNS_TAG_ENTITY_IDENTIFIER,
+ name);
+ isns_object_set_uint32(obj,
+ ISNS_TAG_ENTITY_PROTOCOL,
+ protocol);
+
+ return obj;
+}
+
+isns_object_t *
+isns_create_entity_for_source(const isns_source_t *source,
+ const char *eid)
+{
+ switch (isns_source_type(source)) {
+ case ISNS_TAG_ISCSI_NAME:
+ return isns_create_entity(ISNS_ENTITY_PROTOCOL_ISCSI, eid);
+
+ case ISNS_TAG_FC_PORT_NAME_WWPN:
+ return isns_create_entity(ISNS_ENTITY_PROTOCOL_IFCP, eid);
+ }
+
+ return NULL;
+}
+
+const char *
+isns_entity_name(const isns_object_t *node)
+{
+ const isns_attr_t *attr;
+
+ if (node->ie_attrs.ial_count == 0)
+ return NULL;
+ attr = node->ie_attrs.ial_data[0];
+ if (attr->ia_value.iv_type != &isns_attr_type_string
+ || attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER)
+ return NULL;
+
+ return attr->ia_value.iv_string;
+
+}
+
+int
+isns_object_is_entity(const isns_object_t *obj)
+{
+ return ISNS_IS_ENTITY(obj);
+}
+
+/*
+ * 6.2.4. Entity Registration Timestamp
+ *
+ * This field indicates the most recent time when the Network Entity
+ * registration occurred or when an associated object attribute was
+ * updated or queried by the iSNS client registering the Network Entity.
+ * The time format is, in seconds, the update period since the standard
+ * base time of 00:00:00 GMT on January 1, 1970. This field cannot be
+ * explicitly registered. This timestamp TLV format is also used in
+ * the SCN and ESI messages.
+ *
+ * Implementer's note: we consider any kind of activity from
+ * the client an indication that it is still alive.
+ * Only exception is the pseudo-entity that holds the access control
+ * information; we never assign it a timestamp so it is never subject
+ * to expiry.
+ */
+void
+isns_entity_touch(isns_object_t *obj)
+{
+ /* Do not add a timestamp to entity CONTROL */
+ if (obj == NULL
+ || (obj->ie_flags & ISNS_OBJECT_PRIVATE)
+ || obj->ie_template != &isns_entity_template)
+ return;
+ isns_object_set_uint64(obj, ISNS_TAG_TIMESTAMP, time(NULL));
+}
+
+/*
+ * Object template
+ */
+static uint32_t entity_attrs[] = {
+ ISNS_TAG_ENTITY_IDENTIFIER,
+ ISNS_TAG_ENTITY_PROTOCOL,
+ ISNS_TAG_MGMT_IP_ADDRESS,
+ ISNS_TAG_TIMESTAMP,
+ ISNS_TAG_PROTOCOL_VERSION_RANGE,
+ ISNS_TAG_REGISTRATION_PERIOD,
+ ISNS_TAG_ENTITY_INDEX,
+ ISNS_TAG_ENTITY_ISAKMP_PHASE_1,
+ ISNS_TAG_ENTITY_CERTIFICATE,
+};
+
+static uint32_t entity_key_attrs[] = {
+ ISNS_TAG_ENTITY_IDENTIFIER,
+};
+
+isns_object_template_t isns_entity_template = {
+ .iot_name = "Network Entity",
+ .iot_handle = ISNS_OBJECT_TYPE_ENTITY,
+ .iot_attrs = entity_attrs,
+ .iot_num_attrs = array_num_elements(entity_attrs),
+ .iot_keys = entity_key_attrs,
+ .iot_num_keys = array_num_elements(entity_key_attrs),
+ .iot_index = ISNS_TAG_ENTITY_INDEX,
+ .iot_next_index = ISNS_TAG_ENTITY_NEXT_INDEX,
+};
+
diff --git a/utils/open-isns/error.c b/utils/open-isns/error.c
new file mode 100644
index 0000000..0d365e8
--- /dev/null
+++ b/utils/open-isns/error.c
@@ -0,0 +1,65 @@
+/*
+ * iSNS error strings etc.
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include "isns.h"
+
+const char *
+isns_strerror(enum isns_status status)
+{
+ switch (status) {
+ case ISNS_SUCCESS:
+ return "Success";
+ case ISNS_UNKNOWN_ERROR:
+ return "Unknown error";
+ case ISNS_MESSAGE_FORMAT_ERROR:
+ return "Message format error";
+ case ISNS_INVALID_REGISTRATION:
+ return "Invalid registration";
+ case ISNS_INVALID_QUERY:
+ return "Invalid query";
+ case ISNS_SOURCE_UNKNOWN:
+ return "Source unknown";
+ case ISNS_SOURCE_ABSENT:
+ return "Source absent";
+ case ISNS_SOURCE_UNAUTHORIZED:
+ return "Source unauthorized";
+ case ISNS_NO_SUCH_ENTRY:
+ return "No such entry";
+ case ISNS_VERSION_NOT_SUPPORTED:
+ return "Version not supported";
+ case ISNS_INTERNAL_ERROR:
+ return "Internal error";
+ case ISNS_BUSY:
+ return "Busy";
+ case ISNS_OPTION_NOT_UNDERSTOOD:
+ return "Option not understood";
+ case ISNS_INVALID_UPDATE:
+ return "Invalid update";
+ case ISNS_MESSAGE_NOT_SUPPORTED:
+ return "Message not supported";
+ case ISNS_SCN_EVENT_REJECTED:
+ return "SCN event rejected";
+ case ISNS_SCN_REGISTRATION_REJECTED:
+ return "SCN registration rejected";
+ case ISNS_ATTRIBUTE_NOT_IMPLEMENTED:
+ return "Attribute not implemented";
+ case ISNS_FC_DOMAIN_ID_NOT_AVAILABLE:
+ return "FC domain id not available";
+ case ISNS_FC_DOMAIN_ID_NOT_ALLOCATED:
+ return "FC domain id not allocated";
+ case ISNS_ESI_NOT_AVAILABLE:
+ return "ESI not available";
+ case ISNS_INVALID_DEREGISTRATION:
+ return "Invalid deregistration";
+ case ISNS_REGISTRATION_FEATURE_NOT_SUPPORTED:
+ return "Registration feature not supported";
+ default:
+ break;
+ }
+
+ return "Unknown iSNS status code";
+}
+
diff --git a/utils/open-isns/esi.c b/utils/open-isns/esi.c
new file mode 100644
index 0000000..3fe62ac
--- /dev/null
+++ b/utils/open-isns/esi.c
@@ -0,0 +1,575 @@
+/*
+ * Handle ESI events
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "isns.h"
+#include "attrs.h"
+#include "objects.h"
+#include "message.h"
+#include "security.h"
+#include "util.h"
+#include "db.h"
+
+#define ESI_RETRANS_TIMEOUT 60
+
+typedef struct isns_esi isns_esi_t;
+typedef struct isns_esi_portal isns_esi_portal_t;
+
+struct isns_esi {
+ isns_list_t esi_list;
+ isns_object_t * esi_object;
+ isns_list_t esi_portals;
+
+ unsigned int esi_update : 1;
+};
+
+struct isns_esi_portal {
+ isns_list_t esp_list;
+ isns_object_t * esp_object;
+ isns_portal_info_t esp_portal;
+ unsigned int esp_interval;
+ isns_portal_info_t esp_dest;
+
+ isns_socket_t * esp_socket;
+ unsigned int esp_retries;
+ unsigned int esp_timeout;
+ time_t esp_start;
+ time_t esp_next_xmit;
+ uint32_t esp_xid;
+};
+
+int isns_esi_enabled = 0;
+static isns_server_t * isns_esi_server = NULL;
+static ISNS_LIST_DECLARE(isns_esi_list);
+
+static void isns_esi_transmit(void *);
+static void isns_esi_sendto(isns_esi_t *, isns_esi_portal_t *);
+static void isns_process_esi_response(uint32_t, isns_simple_t *);
+static void isns_esi_disconnect(isns_esi_portal_t *);
+static void isns_esi_restart(isns_esi_portal_t *);
+static void isns_esi_drop_portal(isns_esi_portal_t *, isns_db_t *, int);
+static void isns_esi_drop_entity(isns_esi_t *, isns_db_t *, int);
+static int isns_esi_update(isns_esi_t *);
+static void isns_esi_schedule(int);
+static void isns_esi_callback(const isns_db_event_t *, void *);
+
+void
+isns_esi_init(isns_server_t *srv)
+{
+ if (isns_config.ic_esi_retries == 0) {
+ isns_debug_esi("ESI disabled by administrator\n");
+ } else {
+ unsigned int max_interval;
+
+ isns_register_callback(isns_esi_callback, NULL);
+ isns_esi_schedule(0);
+
+ max_interval = isns_config.ic_registration_period / 2;
+ if (isns_config.ic_esi_max_interval > max_interval) {
+ isns_warning("Max ESI interval adjusted to %u sec "
+ "to match registration period\n",
+ max_interval);
+ isns_config.ic_esi_max_interval = max_interval;
+ if (isns_config.ic_esi_min_interval > max_interval)
+ isns_config.ic_esi_min_interval = max_interval;
+ }
+ isns_esi_server = srv;
+ isns_esi_enabled = 1;
+ }
+}
+
+/*
+ * Timer callback to send out ESI messages.
+ */
+void
+isns_esi_transmit(void *ptr)
+{
+ isns_db_t *db = isns_esi_server->is_db;
+ isns_list_t *esi_pos, *esi_next;
+ time_t now;
+ isns_object_t *obj;
+ time_t next_timeout;
+
+ now = time(NULL);
+ next_timeout = now + 3600;
+
+ isns_list_foreach(&isns_esi_list, esi_pos, esi_next) {
+ isns_list_t *esp_pos, *esp_next;
+ isns_esi_t *esi = isns_list_item(isns_esi_t, esi_list, esi_pos);
+
+ if (esi->esi_update) {
+ esi->esi_update = 0;
+ if (!isns_esi_update(esi))
+ continue;
+ }
+
+ isns_list_foreach(&esi->esi_portals, esp_pos, esp_next) {
+ isns_esi_portal_t *esp = isns_list_item(isns_esi_portal_t,
+ esp_list, esp_pos);
+
+ /* Check whether the portal object still exist */
+ obj = esp->esp_object;
+ if (obj->ie_state != ISNS_OBJECT_STATE_MATURE) {
+ isns_esi_drop_portal(esp, db, 0);
+ continue;
+ }
+
+ if (esp->esp_next_xmit <= now) {
+ if (esp->esp_retries == 0) {
+ isns_debug_esi("No ESI response from %s - dropping\n",
+ isns_portal_string(&esp->esp_dest));
+ isns_esi_drop_portal(esp, db, 1);
+ continue;
+ }
+
+ esp->esp_retries -= 1;
+ esp->esp_next_xmit = now + esp->esp_timeout;
+ isns_esi_sendto(esi, esp);
+ }
+ if (esp->esp_next_xmit < next_timeout)
+ next_timeout = esp->esp_next_xmit;
+ }
+
+ if (isns_list_empty(&esi->esi_portals))
+ isns_esi_drop_entity(esi, db, 1);
+ }
+
+ isns_debug_esi("Next ESI message in %d seconds\n", next_timeout - now);
+ isns_esi_schedule(next_timeout - now);
+}
+
+/*
+ * Send an ESI message
+ */
+void
+isns_esi_sendto(isns_esi_t *esi, isns_esi_portal_t *esp)
+{
+ isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
+ isns_socket_t *sock;
+ isns_simple_t *msg;
+
+ /* For TCP portals, kill the TCP socket every time. */
+ if (esp->esp_dest.proto == IPPROTO_TCP)
+ isns_esi_disconnect(esp);
+
+ if (esp->esp_socket == NULL) {
+ sock = isns_connect_to_portal(&esp->esp_dest);
+ if (sock == NULL)
+ return;
+
+ isns_socket_set_security_ctx(sock,
+ isns_default_security_context(0));
+ /* sock->is_disconnect_fatal = 1; */
+ esp->esp_socket = sock;
+ }
+
+ isns_attr_list_append_uint64(&attrs,
+ ISNS_TAG_TIMESTAMP,
+ time(NULL));
+ /* The following will extract the ENTITY IDENTIFIER */
+ isns_object_extract_keys(esi->esi_object, &attrs);
+ isns_portal_to_attr_list(&esp->esp_portal,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ &attrs);
+
+ msg = isns_simple_create(ISNS_ENTITY_STATUS_INQUIRY,
+ NULL, &attrs);
+ if (msg == NULL)
+ return;
+
+ isns_debug_esi("*** Sending ESI message to %s (xid=0x%x); %u retries left\n",
+ isns_portal_string(&esp->esp_dest),
+ msg->is_xid, esp->esp_retries);
+ isns_simple_transmit(esp->esp_socket, msg,
+ NULL, esp->esp_timeout - 1,
+ isns_process_esi_response);
+ esp->esp_xid = msg->is_xid;
+ isns_simple_free(msg);
+}
+
+/*
+ * A new entity was added. See if it uses ESI, and create
+ * portals and such.
+ */
+static void
+isns_esi_add_entity(isns_object_t *obj)
+{
+ isns_esi_t *esi;
+
+ isns_debug_esi("Enable ESI monitoring for entity %u\n", obj->ie_index);
+ esi = isns_calloc(1, sizeof(*esi));
+ esi->esi_object = isns_object_get(obj);
+ esi->esi_update = 1;
+ isns_list_init(&esi->esi_list);
+ isns_list_init(&esi->esi_portals);
+
+ isns_list_append(&isns_esi_list, &esi->esi_list);
+}
+
+/*
+ * Given an entity, see if we can find ESI state for it.
+ */
+static isns_esi_t *
+isns_esi_find(isns_object_t *obj)
+{
+ isns_list_t *pos, *next;
+
+ isns_list_foreach(&isns_esi_list, pos, next) {
+ isns_esi_t *esi = isns_list_item(isns_esi_t, esi_list, pos);
+
+ if (esi->esi_object == obj)
+ return esi;
+ }
+ return NULL;
+}
+
+/*
+ * Update the ESI state after an entity has changed
+ */
+static int
+isns_esi_update(isns_esi_t *esi)
+{
+ isns_object_t *entity = esi->esi_object;
+ ISNS_LIST_DECLARE(hold);
+ isns_esi_portal_t *esp;
+ unsigned int i;
+
+ isns_debug_esi("Updating ESI state for entity %u\n", entity->ie_index);
+
+ isns_list_move(&hold, &esi->esi_portals);
+ for (i = 0; i < entity->ie_children.iol_count; ++i) {
+ isns_object_t *child = entity->ie_children.iol_data[i];
+ isns_portal_info_t esi_portal, portal_info;
+ uint32_t esi_interval;
+ isns_list_t *pos, *next;
+ int changed = 0;
+
+ if (!ISNS_IS_PORTAL(child))
+ continue;
+
+ if (!isns_portal_from_object(&portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ child)
+ || !isns_portal_from_object(&esi_portal,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_ESI_PORT,
+ child)
+ || !isns_object_get_uint32(child,
+ ISNS_TAG_ESI_INTERVAL,
+ &esi_interval))
+ continue;
+
+ isns_list_foreach(&hold, pos, next) {
+ esp = isns_list_item(isns_esi_portal_t, esp_list, pos);
+
+ if (esp->esp_object == child) {
+ isns_debug_esi("Updating ESI state for %s\n",
+ isns_portal_string(&portal_info));
+ isns_list_del(&esp->esp_list);
+ goto update;
+ }
+ }
+
+ isns_debug_esi("Creating ESI state for %s\n",
+ isns_portal_string(&portal_info));
+ esp = isns_calloc(1, sizeof(*esp));
+ esp->esp_object = isns_object_get(child);
+ isns_list_init(&esp->esp_list);
+ changed = 1;
+
+update:
+ if (!isns_portal_equal(&esp->esp_portal, &portal_info)) {
+ esp->esp_portal = portal_info;
+ changed++;
+ }
+ if (!isns_portal_equal(&esp->esp_dest, &esi_portal)) {
+ isns_esi_disconnect(esp);
+ esp->esp_dest = esi_portal;
+ changed++;
+ }
+ if (esp->esp_interval != esi_interval) {
+ esp->esp_interval = esi_interval;
+ changed++;
+ }
+
+ isns_esi_restart(esp);
+
+ isns_list_append(&esi->esi_portals, &esp->esp_list);
+ }
+
+ /* Destroy any old ESI portals */
+ while (!isns_list_empty(&hold)) {
+ esp = isns_list_item(isns_esi_portal_t, esp_list, hold.next);
+
+ isns_esi_drop_portal(esp, NULL, 0);
+ }
+
+ /* If the client explicitly unregistered all ESI portals,
+ * stop monitoring it but *without* destroying the entity. */
+ if (isns_list_empty(&esi->esi_portals)) {
+ isns_esi_drop_entity(esi, NULL, 0);
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+isns_esi_restart(isns_esi_portal_t *esp)
+{
+ unsigned int timeo;
+
+ isns_esi_disconnect(esp);
+
+ esp->esp_start = time(NULL);
+ esp->esp_retries = isns_config.ic_esi_retries;
+ esp->esp_next_xmit = esp->esp_start + esp->esp_interval;
+ esp->esp_xid = 0;
+
+ timeo = esp->esp_interval / esp->esp_retries;
+ if (timeo == 0)
+ timeo = 1;
+ else if (timeo > ESI_RETRANS_TIMEOUT)
+ timeo = ESI_RETRANS_TIMEOUT;
+ esp->esp_timeout = timeo;
+}
+
+void
+isns_esi_disconnect(isns_esi_portal_t *esp)
+{
+ if (esp->esp_socket)
+ isns_socket_free(esp->esp_socket);
+ esp->esp_socket = NULL;
+}
+
+/*
+ * Generic wrapper to dropping an object
+ */
+static inline void
+__isns_esi_drop_object(isns_db_t *db, isns_object_t *obj, unsigned int dead)
+{
+ if (db && obj && obj->ie_state == ISNS_OBJECT_STATE_MATURE && dead)
+ isns_db_remove(db, obj);
+ isns_object_release(obj);
+}
+
+/*
+ * Portal did not respond in time. Drop it
+ */
+void
+isns_esi_drop_portal(isns_esi_portal_t *esp, isns_db_t *db, int dead)
+{
+ isns_debug_esi("ESI: dropping portal %s\n",
+ isns_portal_string(&esp->esp_portal));
+
+ isns_list_del(&esp->esp_list);
+ isns_esi_disconnect(esp);
+ __isns_esi_drop_object(db, esp->esp_object, dead);
+ isns_free(esp);
+}
+
+/*
+ * We ran out of ESI portals for this entity.
+ */
+void
+isns_esi_drop_entity(isns_esi_t *esi, isns_db_t *db, int dead)
+{
+ isns_debug_esi("ESI: dropping entity %u\n",
+ esi->esi_object->ie_index);
+
+ isns_list_del(&esi->esi_list);
+ __isns_esi_drop_object(db, esi->esi_object, dead);
+
+ while (!isns_list_empty(&esi->esi_portals)) {
+ isns_esi_portal_t *esp;
+
+ esp = isns_list_item(isns_esi_portal_t, esp_list,
+ esi->esi_portals.next);
+ isns_esi_drop_portal(esp, db, dead);
+ }
+ isns_free(esi);
+}
+
+/*
+ * When receiving an ESI response, find the portal we sent the
+ * original message to.
+ */
+static isns_esi_portal_t *
+isns_esi_get_msg_portal(uint32_t xid, isns_esi_t **esip)
+{
+ isns_list_t *esi_pos, *esi_next;
+
+ isns_list_foreach(&isns_esi_list, esi_pos, esi_next) {
+ isns_esi_t *esi = isns_list_item(isns_esi_t, esi_list, esi_pos);
+ isns_list_t *esp_pos, *esp_next;
+
+ isns_list_foreach(&esi->esi_portals, esp_pos, esp_next) {
+ isns_esi_portal_t *esp = isns_list_item(isns_esi_portal_t,
+ esp_list, esp_pos);
+
+ if (esp->esp_xid == xid) {
+ *esip = esi;
+ return esp;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Handle incoming ESI request
+ */
+int
+isns_process_esi(isns_server_t *srv, isns_simple_t *call, isns_simple_t **reply)
+{
+ const isns_attr_list_t *attrs = &call->is_message_attrs;
+ isns_object_t *portal = NULL;
+
+ /* We just echo back the attributes sent to us by the server,
+ * without further checking. */
+ *reply = isns_simple_create(ISNS_ENTITY_STATUS_INQUIRY,
+ srv->is_source, attrs);
+
+ /* Look up the portal and update its mtime.
+ * This can help the application find out if a portal has
+ * seen ESIs recently, and react.
+ */
+ if (srv->is_db && attrs->ial_count == 4) {
+ const isns_attr_t *addr_attr, *port_attr;
+
+ addr_attr = attrs->ial_data[2];
+ port_attr = attrs->ial_data[3];
+ if (addr_attr->ia_tag_id == ISNS_TAG_PORTAL_IP_ADDRESS
+ && port_attr->ia_tag_id == ISNS_TAG_PORTAL_TCP_UDP_PORT) {
+ isns_attr_list_t key;
+
+ key.ial_count = 2;
+ key.ial_data = attrs->ial_data + 2;
+ portal = isns_db_lookup(srv->is_db,
+ &isns_portal_template,
+ &key);
+ }
+
+ if (portal)
+ portal->ie_mtime = time(NULL);
+ }
+ return ISNS_SUCCESS;
+}
+
+void
+isns_process_esi_response(uint32_t xid, isns_simple_t *msg)
+{
+ isns_portal_info_t portal_info;
+ isns_esi_portal_t *esp;
+ isns_esi_t *esi;
+
+ if (msg == NULL) {
+ isns_debug_esi("ESI call 0x%x timed out\n", xid);
+ return;
+ }
+
+ /* FIXME: As a matter of security, we should probably
+ * verify that the ESI response originated from the
+ * portal we sent it to; or at least that it was authenticated
+ * by the client we think we're talking to. */
+
+ /* Get the portal */
+ if (!isns_portal_from_attr_list(&portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ &msg->is_message_attrs)) {
+ isns_debug_esi("Ignoring unintelligible ESI response\n");
+ return;
+ }
+
+ if (!(esp = isns_esi_get_msg_portal(xid, &esi))) {
+ isns_debug_esi("Ignoring unmatched ESI reply\n");
+ return;
+ }
+
+ if (!isns_portal_equal(&esp->esp_portal, &portal_info)) {
+ isns_warning("Faked ESI response for portal %s\n",
+ isns_portal_string(&portal_info));
+ return;
+ }
+
+ isns_debug_esi("Good ESI response from %s\n",
+ isns_portal_string(&portal_info));
+ isns_esi_restart(esp);
+
+ /* Refresh the entity's registration timestamp */
+ isns_object_set_uint64(esi->esi_object,
+ ISNS_TAG_TIMESTAMP,
+ time(NULL));
+ isns_db_sync(isns_esi_server->is_db);
+}
+
+/*
+ * Helper function to schedule the next timeout
+ */
+static void
+isns_esi_schedule(int timeout)
+{
+ isns_cancel_timer(isns_esi_transmit, NULL);
+ isns_add_oneshot_timer(timeout, isns_esi_transmit, NULL);
+}
+
+/*
+ * Register an entity for ESI monitoring.
+ * This is called when reloading the database.
+ */
+void
+isns_esi_register(isns_object_t *obj)
+{
+ if (!isns_esi_find(obj))
+ isns_esi_add_entity(obj);
+ /* We do not call esi_schedule(0) here; that happens in
+ * isns_esi_init already. */
+}
+
+/*
+ * This callback is invoked whenever an object is added/removed/modified.
+ * We use this to keep track of ESI portals and such.
+ */
+void
+isns_esi_callback(const isns_db_event_t *ev, void *ptr)
+{
+ isns_object_t *obj, *entity;
+ isns_esi_t *esi;
+ uint32_t event;
+
+ obj = ev->ie_object;
+ event = ev->ie_bits;
+
+ if (obj->ie_flags & ISNS_OBJECT_PRIVATE)
+ return;
+
+ isns_debug_esi("isns_esi_callback(%p, 0x%x)\n", obj, event);
+
+ if (ISNS_IS_ENTITY(obj)
+ && (event & ISNS_SCN_OBJECT_ADDED_MASK)) {
+ if (!isns_esi_find(obj))
+ isns_esi_add_entity(obj);
+ /* Schedule an immediate ESI timer run */
+ isns_esi_schedule(0);
+ return;
+ }
+
+ if (!(entity = isns_object_get_entity(obj)))
+ return;
+
+ esi = isns_esi_find(entity);
+ if (esi != NULL)
+ esi->esi_update = 1;
+
+ /* Schedule an immediate ESI timer run */
+ isns_esi_schedule(0);
+}
diff --git a/utils/open-isns/etc/isnsadm.conf b/utils/open-isns/etc/isnsadm.conf
new file mode 100644
index 0000000..e7ee681
--- /dev/null
+++ b/utils/open-isns/etc/isnsadm.conf
@@ -0,0 +1,73 @@
+#
+# Sample iSNS client configuration file
+#
+
+# The source name. This is an iSCSI qualified name,
+# and identifies the client uniquely.
+#
+# If left empty, the source name is derived from
+# the client's hostname.
+#
+#SourceName = iqn.2006-01.com.example.host1
+
+# Name and port of the iSNS server.
+# Possible formats:
+# foo.example.com
+# foo.example.com:3205
+# 192.168.1.7:isns
+# [2001:4e5f::1]:isns
+# SLP:
+# If the special string "SLP:" is given, Open-iSNS will
+# query the SLP directory service to find the iSNS server.
+#ServerAddress = isns.example.com
+
+
+# Authentication enable/disable.
+# When set to 1, the client will sign
+# all messages, and expect all server messages
+# to be signed.
+#
+# Authentication requires a valid private DSA
+# key in AuthKeyFile, and the server's DSA public
+# key in ServerKeyFile.
+#
+# The default is to use authentication if the
+# requires keys are installed, and use unauthenticated
+# iSNS otherwise.
+#Security = 1
+
+# Location of the client's private key.
+# The file must contain a PEM encoded DSA key.
+# The default is /etc/isns/auth_key
+#AuthKeyFile = /etc/isns/auth_key
+
+# Location of the servers's public key.
+# The file must contain a PEM encoded DSA key.
+# The default is /etc/isns/server_key.pub
+#ServerKeyFile = /etc/isns/server_key.pub
+
+# In order to prevent replay attacks, the
+# authentication blocks carried by iSNS
+# include a time stamp. The following two
+# parameters control how we verify the
+# time stamp
+Auth.ReplayWindow = 2m
+Auth.TimeStampJitter = 1s
+
+# Maximum number of incoming connections
+# accepted. This usually applies to server
+# side only, but is relevant if you create
+# a passive TCP socket for ESI or SCN.
+# Network.MaxSockets = 1024
+
+# Time to wait for a TCP connection to be
+# established.
+# Network.ConnectTimeout = 60
+
+# When a connection attempt failed, we wait
+# before we try connecting again.
+# Network.ReonnectTimeout = 10
+
+# Total amount of time to wait before timing
+# out a call to the iSNS server.
+# Network.CallTimeout = 60
diff --git a/utils/open-isns/etc/isnsd.conf b/utils/open-isns/etc/isnsd.conf
new file mode 100644
index 0000000..bc90f40
--- /dev/null
+++ b/utils/open-isns/etc/isnsd.conf
@@ -0,0 +1,129 @@
+#
+# Sample iSNS Server configuration file
+#
+
+# The source name. This is an iSCSI qualified name,
+# and identifies the client uniquely.
+#
+# If left empty, the source name is derived from
+# the client's hostname.
+#
+#SourceName = iqn.2006-01.com.example.host1
+
+# Where to store the database.
+# If you leave this empty, isnsd will keep its
+# database in memory.
+# Setting this to an absolute path name will
+# make isnsd keep its database in a directory
+# hierarchy below that directory.
+Database = /var/lib/isns
+
+# The iSNS server can purge registered entities
+# after a certain period of inactivity. This is
+# called the registration period.
+# Clients who register objects are supposed to
+# refresh their registration within this period.
+#
+# The default value is 0, which disables this
+# feature.
+RegistrationPeriod = 10m
+
+# iSNS scopes visibility of other nodes using so-called
+# Discovery Domains. A storage node A will only "see"
+# storage node B, if both are members of the same
+# discovery domain.
+#
+# So if a storage node is registered which is not part of
+# any discovery domain, it will not see any other nodes.
+#
+# By setting DefaultDiscoveryDomain=1, you can tell isnsd to
+# create a virtual "default discovery domain", which
+# holds all nodes that are not part of any administratively
+# configured discovery domain.
+DefaultDiscoveryDomain = 1
+
+# Make the iSNS server register itself with SLP.
+# Clients will be able to discover the server by
+# querying for service type "iscsi:sms", and a query
+# of "(protocols=isns)"
+SLPRegister = 1
+
+# Authentication enable/disable.
+# When set to 1, the client will sign
+# all messages, and expect all server messages
+# to be signed.
+#
+# Authentication requires a valid private DSA
+# key in AuthKeyFile, and the server's DSA public
+# key in ServerKeyFile.
+#
+# The default is to use authentication if the
+# requires keys are installed, and use unauthenticated
+# iSNS otherwise.
+#Security = 1
+
+# Location of the client's private key.
+# The file must contain a PEM encoded DSA key.
+# The default is /etc/isns/auth_key
+#AuthKeyFile = /etc/isns/auth_key
+
+# Location of the servers's public key.
+# The file must contain a PEM encoded DSA key.
+# The default is /etc/isns/server_key.pub
+#ServerKeyFile = /etc/isns/server_key.pub
+
+# This describes where the iSNS server stores
+# authentication keys and policy information.
+# Two options are currently supported: a
+# simple key store (flat directory with public
+# keys in PEM encoded files), and the iSNS
+# database itself
+#ClientKeyStore = /etc/isns/keystores
+ClientKeyStore = DB:
+
+# When transmitting State Change Notification,
+# we expect the client to ack them. If the
+# ACK doesn't arrive in due time, we retransmit
+# for a limited number of attempts, cycling
+# through the available portals.
+SCNTimeout = 60
+SCNRetries = 3
+
+# Configuration of ESI.
+# Defaults are
+# ESIMaxInterval = 1h
+# ESIMinInterval = 60s
+# ESIRetries = 3
+# Setting ESIRetries to 0 disables ESI support, and makes
+# the server reject any portal registrations that specify
+# an ESI portal.
+ESIMinInterval = 1m
+ESIMaxInterval = 2m
+ESIRetries = 3
+
+# In order to prevent replay attacks, the
+# authentication blocks carried by iSNS
+# include a time stamp. The following two
+# parameters control how we verify the
+# time stamp
+Auth.ReplayWindow = 2m
+Auth.TimeStampJitter = 1s
+
+# Maximum number of incoming connections
+# accepted.
+# Network.MaxSockets = 1024
+
+# Time to wait for a TCP connection to be
+# established.
+# (Client only)
+# Network.ConnectTimeout = 60
+
+# When a connection attempt failed, we wait
+# before we try connecting again.
+# (Client only)
+# Network.ReonnectTimeout = 10
+
+# Total amount of time to wait before timing
+# out a call to the iSNS server.
+# (Client only)
+# Network.CallTimeout = 60
diff --git a/utils/open-isns/etc/isnsdd.conf b/utils/open-isns/etc/isnsdd.conf
new file mode 100644
index 0000000..d751c3d
--- /dev/null
+++ b/utils/open-isns/etc/isnsdd.conf
@@ -0,0 +1,72 @@
+#
+# Sample iSNS Discovery Daemon configuration file
+#
+
+# The source name. This is an iSCSI qualified name,
+# and identifies the client uniquely.
+#
+# If left empty, the source name is derived from
+# the client's hostname.
+#
+#SourceName = iqn.2006-01.com.example.host1:monitor
+
+# Name and port of the iSNS server.
+# Possible formats:
+# foo.example.com
+# foo.example.com:3205
+# 192.168.1.7:isns
+# [2001:4e5f::1]:isns
+# SLP:
+# If the special string "SLP:" is given, Open-iSNS will
+# query the SLP directory service to find the iSNS server.
+#ServerAddress = isns.example.com
+
+# Authentication enable/disable.
+# When set to 1, the client will sign
+# all messages, and expect all server messages
+# to be signed.
+#
+# Authentication requires a valid private DSA
+# key in AuthKeyFile, and the server's DSA public
+# key in ServerKeyFile.
+#
+# The default is to use authentication if the
+# required keys are installed, and use unauthenticated
+# iSNS otherwise.
+#Security = 1
+
+# Location of the client's private key.
+# The file must contain a PEM encoded DSA key.
+# The default is /etc/isns/auth_key
+#AuthKeyFile = /etc/isns/auth_key
+
+# Location of the servers's public key.
+# The file must contain a PEM encoded DSA key.
+# The default is /etc/isns/server_key.pub
+#ServerKeyFile = /etc/isns/server_key.pub
+
+# In order to prevent replay attacks, the
+# authentication blocks carried by iSNS
+# include a time stamp. The following two
+# parameters control how we verify the
+# time stamp
+Auth.ReplayWindow = 2m
+Auth.TimeStampJitter = 1s
+
+# Maximum number of incoming connections
+# accepted. This usually applies to server
+# side only, but is relevant if you create
+# a passive TCP socket for ESI or SCN.
+# Network.MaxSockets = 1024
+
+# Time to wait for a TCP connection to be
+# established.
+# Network.ConnectTimeout = 60
+
+# When a connection attempt failed, we wait
+# before we try connecting again.
+# Network.ReonnectTimeout = 10
+
+# Total amount of time to wait before timing
+# out a call to the iSNS server.
+# Network.CallTimeout = 60
diff --git a/utils/open-isns/etc/openisns.init b/utils/open-isns/etc/openisns.init
new file mode 100644
index 0000000..7c03778
--- /dev/null
+++ b/utils/open-isns/etc/openisns.init
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# Init script for Open-iSNS.
+#
+# Copyright (C) 2007 Albert Pauw
+#
+# chkconfig: 345 13 89
+# description: Starts and stops the iSCSI isns server
+#
+# processname: isnsd
+# pidfile: /var/run/isnsd.pid
+# config: /etc/isns/isnsd.conf
+
+# Source function library.
+. /etc/init.d/functions
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+#OPTIONS="-4 -d all"
+CONFIG="-c /etc/isns/isnsd.conf"
+RETVAL=0
+
+start()
+{
+ echo -n "Starting iSCSI isns service: "
+ daemon isnsd $OPTIONS $CONFIG
+ RETVAL=$?
+ success
+ echo
+ [ $RETVAL -eq 0 ] || return
+ touch /var/lock/subsys/open-isns
+}
+
+stop()
+{
+ echo -n "Stopping iSCSI isns service: "
+ killproc isnsd
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/open-isns
+ success
+ echo
+
+}
+
+restart()
+{
+ stop
+ start
+}
+
+case "$1" in
+start)
+ start
+ ;;
+stop)
+ stop
+ ;;
+restart)
+ restart
+ ;;
+status)
+ status isnsd
+ RETVAL=$?
+ ;;
+condrestart)
+ [ -f /var/lock/subsys/open-isns ] && restart
+ ;;
+*)
+ echo $"Usage: $0 {start|stop|restart|status|condrestart}"
+ exit 1
+esac
+
+exit $RETVAL
diff --git a/utils/open-isns/export.c b/utils/open-isns/export.c
new file mode 100644
index 0000000..fa4c278
--- /dev/null
+++ b/utils/open-isns/export.c
@@ -0,0 +1,547 @@
+/*
+ * Helper functions to represent iSNS objects as text,
+ * and/or to parse objects represented in textual form.
+ * These functions can be used by command line utilities
+ * such as isnsadm, as well as applications like iscsid
+ * or stgtd when talking to the iSNS discovery daemon.
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "isns.h"
+#include "util.h"
+#include "vendor.h"
+#include "attrs.h"
+#include "security.h"
+#include "objects.h"
+#include "paths.h"
+
+#define MAX_ALIASES 4
+
+struct isns_tag_prefix {
+ const char * name;
+ unsigned int name_len;
+ isns_object_template_t *context;
+};
+
+struct tag_name {
+ const char * name;
+ uint32_t tag;
+ struct isns_tag_prefix *prefix;
+ const char * alias[MAX_ALIASES];
+};
+
+static struct isns_tag_prefix all_prefixes[__ISNS_OBJECT_TYPE_MAX] = {
+[ISNS_OBJECT_TYPE_ENTITY] = { "entity-", 7, &isns_entity_template },
+[ISNS_OBJECT_TYPE_NODE] = { "iscsi-", 6, &isns_iscsi_node_template },
+[ISNS_OBJECT_TYPE_PORTAL] = { "portal-", 7, &isns_portal_template },
+[ISNS_OBJECT_TYPE_PG] = { "pg-", 3, &isns_iscsi_pg_template },
+[ISNS_OBJECT_TYPE_DD] = { "dd-", 3, &isns_dd_template },
+[ISNS_OBJECT_TYPE_POLICY] = { "policy-", 7, &isns_policy_template },
+};
+
+static struct tag_name all_attrs[] = {
+{ "id", ISNS_TAG_ENTITY_IDENTIFIER,
+ .alias = { "eid", },
+},
+{ "prot", ISNS_TAG_ENTITY_PROTOCOL },
+{ "idx", ISNS_TAG_ENTITY_INDEX },
+
+{ "name", ISNS_TAG_ISCSI_NAME },
+{ "node-type", ISNS_TAG_ISCSI_NODE_TYPE },
+{ "alias", ISNS_TAG_ISCSI_ALIAS },
+{ "authmethod", ISNS_TAG_ISCSI_AUTHMETHOD },
+{ "idx", ISNS_TAG_ISCSI_NODE_INDEX },
+
+{ "addr", ISNS_TAG_PORTAL_IP_ADDRESS },
+{ "port", ISNS_TAG_PORTAL_TCP_UDP_PORT },
+{ "name", ISNS_TAG_PORTAL_SYMBOLIC_NAME },
+{ "esi-port", ISNS_TAG_ESI_PORT },
+{ "esi-interval", ISNS_TAG_ESI_INTERVAL },
+{ "scn-port", ISNS_TAG_SCN_PORT },
+{ "idx", ISNS_TAG_PORTAL_INDEX },
+
+{ "name", ISNS_TAG_PG_ISCSI_NAME },
+{ "addr", ISNS_TAG_PG_PORTAL_IP_ADDR },
+{ "port", ISNS_TAG_PG_PORTAL_TCP_UDP_PORT },
+{ "tag", ISNS_TAG_PG_TAG },
+{ "pgt", ISNS_TAG_PG_TAG },
+{ "idx", ISNS_TAG_PG_INDEX },
+
+{ "id", ISNS_TAG_DD_ID },
+{ "name", ISNS_TAG_DD_SYMBOLIC_NAME },
+{ "member-name", ISNS_TAG_DD_MEMBER_ISCSI_NAME },
+{ "member-iscsi-idx", ISNS_TAG_DD_MEMBER_ISCSI_INDEX },
+{ "member-fc-name", ISNS_TAG_DD_MEMBER_FC_PORT_NAME },
+{ "member-portal-idx", ISNS_TAG_DD_MEMBER_PORTAL_INDEX },
+{ "member-addr", ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR },
+{ "member-port", ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT },
+{ "features", ISNS_TAG_DD_FEATURES },
+
+{ "name", OPENISNS_TAG_POLICY_SPI,
+ .alias = { "spi" },
+},
+{ "key", OPENISNS_TAG_POLICY_KEY },
+{ "entity", OPENISNS_TAG_POLICY_ENTITY },
+{ "object-type", OPENISNS_TAG_POLICY_OBJECT_TYPE },
+{ "node-type", OPENISNS_TAG_POLICY_NODE_TYPE },
+{ "node-name", OPENISNS_TAG_POLICY_NODE_NAME },
+{ "functions", OPENISNS_TAG_POLICY_FUNCTIONS },
+
+{ NULL }
+};
+
+/*
+ * Initialize tag array
+ */
+static void
+init_tags(void)
+{
+ struct tag_name *t;
+
+ for (t = all_attrs; t->name; ++t) {
+ isns_object_template_t *tmpl;
+
+ tmpl = isns_object_template_for_tag(t->tag);
+ if (tmpl == NULL)
+ isns_fatal("Bug: cannot find object type for tag %s\n",
+ t->name);
+ t->prefix = &all_prefixes[tmpl->iot_handle];
+ }
+}
+
+/*
+ * Match prefix
+ */
+static struct isns_tag_prefix *
+find_prefix(const char *name)
+{
+ struct isns_tag_prefix *p;
+ unsigned int i;
+
+ for (i = 0, p = all_prefixes; i < __ISNS_OBJECT_TYPE_MAX; ++i, ++p) {
+ if (p->name && !strncmp(name, p->name, p->name_len))
+ return p;
+ }
+ return NULL;
+}
+
+/*
+ * Look up the tag for a given attribute name.
+ * By default, attr names come with a disambiguating
+ * prefix that defines the object type the attribute applies
+ * to, such as "entity-" or "portal-". Once a context has
+ * been established (ie we know the object type subsequent
+ * attributes apply to), specifying the prefix is optional.
+ *
+ * For instance, in a portal context, "addr=10.1.1.1 port=616 name=foo"
+ * specifies three portal related attributes. Whereas in a portal
+ * group context, the same string would specify three portal group
+ * related attributes. To disambiguate, the first attribute in
+ * this list should be prefixed by "portal-" or "pg-", respectively.
+ */
+static uint32_t
+tag_by_name(const char *name, struct isns_attr_list_parser *st)
+{
+ const char *orig_name = name;
+ unsigned int nmatch = 0, i;
+ struct tag_name *t, *match[8];
+ struct isns_tag_prefix *specific = NULL;
+
+ if (all_attrs[0].prefix == NULL)
+ init_tags();
+
+ specific = find_prefix(name);
+ if (specific != NULL) {
+ if (st->prefix
+ && st->prefix != specific
+ && !st->multi_type_permitted) {
+ isns_error("Cannot mix attributes of different types\n");
+ return 0;
+ }
+ name += specific->name_len;
+ st->prefix = specific;
+ }
+
+ for (t = all_attrs; t->name; ++t) {
+ if (specific && t->prefix != specific)
+ continue;
+ if (!st->multi_type_permitted
+ && st->prefix && t->prefix != st->prefix)
+ continue;
+ if (!strcmp(name, t->name))
+ goto match;
+ for (i = 0; i < MAX_ALIASES && t->alias[i]; ++i) {
+ if (!strcmp(name, t->alias[i]))
+ goto match;
+ }
+ continue;
+
+match:
+ if (nmatch < 8)
+ match[nmatch++] = t;
+ }
+
+ if (nmatch > 1) {
+ char conflict[128];
+ unsigned int i;
+
+ conflict[0] = '\0';
+ for (i = 0; i < nmatch; ++i) {
+ if (i)
+ strcat(conflict, ", ");
+ t = match[i];
+ strcat(conflict, t->prefix->name);
+ strcat(conflict, t->name);
+ }
+ isns_error("tag name \"%s\" not unique in this context "
+ "(could be one of %s)\n",
+ orig_name, conflict);
+ return 0;
+ }
+
+ if (nmatch == 0) {
+ isns_error("tag name \"%s\" not known in this context\n",
+ orig_name);
+ return 0;
+ }
+
+ st->prefix = match[0]->prefix;
+ return match[0]->tag;
+}
+
+static const char *
+name_by_tag(uint32_t tag, struct isns_attr_list_parser *st)
+{
+ struct tag_name *t;
+
+ for (t = all_attrs; t->name; ++t) {
+ if (st->prefix && t->prefix != st->prefix)
+ continue;
+ if (t->tag == tag)
+ return t->name;
+ }
+ return NULL;
+}
+
+static int
+parse_one_attr(const char *name, const char *value,
+ isns_attr_list_t *attrs,
+ struct isns_attr_list_parser *st)
+{
+ isns_attr_t *attr;
+ uint32_t tag;
+
+ /* Special case: "portal=<address:port>" is translated to
+ * addr=<address> port=<port>
+ * If no context has been set, assume portal context.
+ */
+ if (!strcasecmp(name, "portal")) {
+ isns_portal_info_t portal_info;
+ uint32_t addr_tag, port_tag;
+
+ if (st->prefix == NULL) {
+ addr_tag = tag_by_name("portal-addr", st);
+ port_tag = tag_by_name("portal-port", st);
+ } else {
+ addr_tag = tag_by_name("addr", st);
+ port_tag = tag_by_name("port", st);
+ }
+
+ if (!addr_tag || !port_tag) {
+ isns_error("portal=... not supported in this context\n");
+ return 0;
+ }
+ if (value == NULL) {
+ isns_attr_list_append_nil(attrs, addr_tag);
+ isns_attr_list_append_nil(attrs, port_tag);
+ return 1;
+ }
+ if (!isns_portal_parse(&portal_info, value, st->default_port))
+ return 0;
+ isns_portal_to_attr_list(&portal_info, addr_tag, port_tag, attrs);
+ return 1;
+ }
+
+ if (!(tag = tag_by_name(name, st)))
+ return 0;
+
+ /* Special handling for key objects */
+ if (tag == OPENISNS_TAG_POLICY_KEY) {
+ if (!value || !strcasecmp(value, "gen")) {
+ if (st->generate_key == NULL) {
+ isns_error("Key generation not supported in this context\n");
+ return 0;
+ }
+ attr = st->generate_key();
+ } else {
+ if (st->load_key == NULL) {
+ isns_error("Policy-key attribute not supported in this context\n");
+ return 0;
+ }
+ attr = st->load_key(value);
+ }
+ goto append_attr;
+ }
+
+ if (value == NULL) {
+ isns_attr_list_append_nil(attrs, tag);
+ return 1;
+ }
+
+ attr = isns_attr_from_string(tag, value);
+ if (!attr)
+ return 0;
+
+append_attr:
+ isns_attr_list_append_attr(attrs, attr);
+ return 1;
+}
+
+void
+isns_attr_list_parser_init(struct isns_attr_list_parser *st,
+ isns_object_template_t *tmpl)
+{
+ if (all_attrs[0].prefix == NULL)
+ init_tags();
+
+ memset(st, 0, sizeof(*st));
+ if (tmpl)
+ st->prefix = &all_prefixes[tmpl->iot_handle];
+}
+
+int
+isns_attr_list_split(char *line, char **argv, unsigned int argc_max)
+{
+ char *src = line;
+ unsigned int argc = 0, quoted = 0;
+
+ if (!line)
+ return 0;
+
+ while (1) {
+ char *dst;
+
+ while (isspace(*src))
+ ++src;
+ if (!*src)
+ break;
+
+ argv[argc] = dst = src;
+ while (*src) {
+ char cc = *src++;
+
+ if (cc == '"') {
+ quoted = !quoted;
+ continue;
+ }
+ if (!quoted && isspace(cc)) {
+ *dst = '\0';
+ break;
+ }
+ *dst++ = cc;
+ }
+
+ if (quoted) {
+ isns_error("%s: Unterminated quoted string: \"%s\"\n",
+ __FUNCTION__, argv[argc]);
+ return -1;
+ }
+ argc++;
+ }
+
+ return argc;
+}
+
+int
+isns_parse_attrs(unsigned int argc, char **argv,
+ isns_attr_list_t *attrs,
+ struct isns_attr_list_parser *st)
+{
+ unsigned int i;
+
+ for (i = 0; i < argc; ++i) {
+ char *name, *value;
+
+ name = argv[i];
+ if ((value = strchr(name, '=')) != NULL)
+ *value++ = '\0';
+
+ if (!value && !st->nil_permitted) {
+ isns_error("Missing value for atribute %s\n", name);
+ return 0;
+ }
+
+ if (!parse_one_attr(name, value, attrs, st)) {
+ isns_error("Unable to parse %s=%s\n", name, value);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Query strings may contain a mix of query keys (foo=bar),
+ * and requested attributes (?foo). The former are used by
+ * the server in its object search, whereas the latter instruct
+ * it which attributes to return.
+ */
+int
+isns_parse_query_attrs(unsigned int argc, char **argv,
+ isns_attr_list_t *keys,
+ isns_attr_list_t *requested_attrs,
+ struct isns_attr_list_parser *st)
+{
+ struct isns_attr_list_parser query_state;
+ unsigned int i;
+
+ query_state = *st;
+ query_state.multi_type_permitted = 1;
+
+ for (i = 0; i < argc; ++i) {
+ char *name, *value;
+
+ name = argv[i];
+ if ((value = strchr(name, '=')) != NULL)
+ *value++ = '\0';
+
+ if (name[0] == '?') {
+ uint32_t tag;
+
+ if (value) {
+ isns_error("No value allowed for query attribute %s\n",
+ name);
+ return 0;
+ }
+
+ if ((tag = tag_by_name(name + 1, &query_state)) != 0) {
+ isns_attr_list_append_nil(requested_attrs, tag);
+ continue;
+ }
+ } else {
+ if (!value && !st->nil_permitted) {
+ isns_error("Missing value for atribute %s\n", name);
+ return 0;
+ }
+
+ if (parse_one_attr(name, value, keys, st))
+ continue;
+ }
+
+ isns_error("Unable to parse %s=%s\n", name, value);
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+isns_attr_list_parser_help(struct isns_attr_list_parser *st)
+{
+ isns_object_template_t *tmpl, *current = NULL;
+ struct tag_name *t;
+
+ if (all_attrs[0].prefix == NULL)
+ init_tags();
+
+ for (t = all_attrs; t->name; ++t) {
+ const isns_tag_type_t *tag_type;
+ char namebuf[64];
+ const char *help;
+ unsigned int i;
+
+ if (st && !st->multi_type_permitted
+ && st->prefix && t->prefix != st->prefix)
+ continue;
+
+ tmpl = t->prefix->context;
+ if (tmpl != current) {
+ printf("\nAttributes for object type %s; using prefix %s\n",
+ tmpl->iot_name, t->prefix->name);
+ current = tmpl;
+ }
+
+ snprintf(namebuf, sizeof(namebuf), "%s%s", t->prefix->name, t->name);
+ printf(" %-20s ", namebuf);
+
+ tag_type = isns_tag_type_by_id(t->tag);
+ if (tag_type == NULL) {
+ printf("Unknown\n");
+ continue;
+ }
+ printf("%s (%s", tag_type->it_name,
+ tag_type->it_type->it_name);
+
+ if (tag_type->it_readonly)
+ printf("; readonly");
+ if (tag_type->it_multiple)
+ printf("; multiple instances");
+ printf(")");
+
+ help = NULL;
+ if (t->tag == OPENISNS_TAG_POLICY_KEY) {
+ help = "name of key file, or \"gen\" for key generation";
+ } else
+ if (tag_type->it_help)
+ help = tag_type->it_help();
+
+ if (help) {
+ if (strlen(help) < 20)
+ printf(" [%s]", help);
+ else
+ printf("\n%25s[%s]", "", help);
+ }
+ printf("\n");
+
+ if (t->alias[0]) {
+ printf("%25sAliases:", "");
+ for (i = 0; i < MAX_ALIASES && t->alias[i]; ++i)
+ printf(" %s", t->alias[i]);
+ printf("\n");
+ }
+ }
+}
+
+isns_object_template_t *
+isns_attr_list_parser_context(const struct isns_attr_list_parser *st)
+{
+ if (st->prefix)
+ return st->prefix->context;
+ return NULL;
+}
+
+int
+isns_print_attrs(isns_object_t *obj, char **argv, unsigned int argsmax)
+{
+ struct isns_attr_list_parser st;
+ unsigned int i, argc = 0;
+
+ isns_attr_list_parser_init(&st, obj->ie_template);
+
+ for (i = 0; i < obj->ie_attrs.ial_count; ++i) {
+ isns_attr_t *attr = obj->ie_attrs.ial_data[i];
+ char argbuf[512], value[512];
+ const char *name;
+
+ name = name_by_tag(attr->ia_tag_id, &st);
+ if (name == NULL)
+ continue;
+ if (argc + 1 >= argsmax)
+ break;
+
+ snprintf(argbuf, sizeof(argbuf), "%s%s=%s",
+ st.prefix->name, name,
+ isns_attr_print_value(attr, value, sizeof(value)));
+ argv[argc++] = isns_strdup(argbuf);
+ }
+
+ argv[argc] = NULL;
+ return argc;
+}
diff --git a/utils/open-isns/getnext.c b/utils/open-isns/getnext.c
new file mode 100644
index 0000000..916ee80
--- /dev/null
+++ b/utils/open-isns/getnext.c
@@ -0,0 +1,257 @@
+/*
+ * Handle iSNS DevGetNext
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "attrs.h"
+#include "message.h"
+#include "security.h"
+#include "objects.h"
+#include "db.h"
+#include "util.h"
+
+/*
+ * Create a GetNext query, and set the source name
+ */
+static isns_simple_t *
+__isns_create_getnext(isns_source_t *source,
+ const isns_attr_list_t *key,
+ const isns_attr_list_t *scope)
+{
+ isns_simple_t *simp;
+
+ simp = isns_simple_create(ISNS_DEVICE_GET_NEXT, source, key);
+ if (simp && scope)
+ isns_attr_list_copy(&simp->is_operating_attrs,
+ scope);
+ return simp;
+}
+
+isns_simple_t *
+isns_create_getnext(isns_client_t *clnt,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *scope)
+{
+ isns_simple_t *simp;
+ unsigned int i;
+
+ simp = __isns_create_getnext(clnt->ic_source, NULL, scope);
+ if (simp == NULL)
+ return NULL;
+
+ for (i = 0; i < tmpl->iot_num_keys; ++i) {
+ isns_attr_list_append_nil(&simp->is_message_attrs,
+ tmpl->iot_keys[i]);
+ }
+ return simp;
+}
+
+isns_simple_t *
+isns_create_getnext_followup(isns_client_t *clnt,
+ const isns_simple_t *resp,
+ const isns_attr_list_t *scope)
+{
+ return __isns_create_getnext(clnt->ic_source,
+ &resp->is_message_attrs, scope);
+}
+
+/*
+ * Get the list of objects matching this query
+ */
+static int
+isns_getnext_get_object(isns_simple_t *qry, isns_db_t *db,
+ isns_object_t **result)
+{
+ isns_scope_t *scope;
+ isns_attr_list_t *keys = &qry->is_message_attrs, match;
+ isns_object_template_t *tmpl;
+ unsigned int i;
+
+ /*
+ * 5.6.5.3.
+ * The Message Key Attribute may be an Entity Identifier (EID),
+ * iSCSI Name, iSCSI Index, Portal IP Address and TCP/UDP Port,
+ * Portal Index, PG Index, FC Node Name WWNN, or FC Port Name
+ * WWPN.
+ *
+ * Implementer's comment: In other words, it must be the
+ * key attr(s) of a specific object type, or an index attribute.
+ */
+ if ((tmpl = isns_object_template_for_key_attrs(keys)) != NULL) {
+ if (keys->ial_count != tmpl->iot_num_keys)
+ return ISNS_INVALID_QUERY;
+ } else if (keys->ial_count == 1) {
+ isns_attr_t *attr = keys->ial_data[0];
+
+ tmpl = isns_object_template_for_index_tag(attr->ia_tag_id);
+ }
+ if (tmpl == NULL)
+ return ISNS_INVALID_QUERY;
+
+ /* Verify whether the client is permitted to retrieve
+ * objects of the given type. */
+ if (!isns_policy_validate_object_type(qry->is_policy, tmpl,
+ qry->is_function))
+ return ISNS_SOURCE_UNAUTHORIZED;
+
+ /*
+ * 5.6.5.3.
+ * The Operating Attributes can be used to specify the scope
+ * of the DevGetNext request, and to specify the attributes of
+ * the next object, which are to be returned in the DevGetNext
+ * response message. All Operating Attributes MUST be attributes
+ * of the object type identified by the Message Key.
+ */
+ match = qry->is_operating_attrs;
+ for (i = 0; i < match.ial_count; ++i) {
+ isns_attr_t *attr = match.ial_data[i];
+
+ if (tmpl != isns_object_template_for_tag(attr->ia_tag_id))
+ return ISNS_INVALID_QUERY;
+ }
+
+ /*
+ * 5.6.5.3.
+ * Non-zero-length TLV attributes in the Operating Attributes
+ * are used to scope the DevGetNext message.
+ * [...]
+ * Zero-length TLV attributes MUST be listed after non-zero-length
+ * attributes in the Operating Attributes of the DevGetNext
+ * request message.
+ */
+ for (i = 0; i < match.ial_count; ++i) {
+ if (ISNS_ATTR_IS_NIL(match.ial_data[i])) {
+ match.ial_count = i;
+ break;
+ }
+ }
+
+ /* Get the scope for the originating node. */
+ scope = isns_scope_for_call(db, qry);
+
+ *result = isns_scope_get_next(scope, tmpl, keys, &match);
+
+ isns_scope_release(scope);
+
+ if (*result == NULL)
+ return ISNS_NO_SUCH_ENTRY;
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Create a Query Response
+ */
+static isns_simple_t *
+isns_create_getnext_response(isns_source_t *source,
+ const isns_simple_t *qry, isns_object_t *obj)
+{
+ const isns_attr_list_t *req_attrs = NULL;
+ isns_attr_list_t requested;
+ isns_simple_t *resp;
+ unsigned int i;
+
+ resp = __isns_create_getnext(source, NULL, NULL);
+
+ /*
+ * 5.7.5.3. Device Get Next Response (DevGetNextRsp)
+ * The Message Key Attribute field returns the object keys
+ * for the next object after the Message Key Attribute in the
+ * original DevGetNext message.
+ *
+ * Implementer's note: slightly convoluted English here.
+ * I *think* this means the key attributes of the object
+ * we matched.
+ */
+ if (!isns_object_get_key_attrs(obj, &resp->is_message_attrs))
+ return NULL;
+
+ /*
+ * 5.7.5.3.
+ * The Operating Attribute field returns the Operating Attributes
+ * of the next object as requested in the original DevGetNext
+ * message. The values of the Operating Attributes are those
+ * associated with the object identified by the Message Key
+ * Attribute field of the DevGetNextRsp message.
+ *
+ * Implementer's note: the RFC doesn't say clearly what to
+ * do when the list of operating attributes does not
+ * contain any NIL TLVs. Let's default to the same
+ * behavior as elsewhere, and return all attributes
+ * in this case.
+ */
+ req_attrs = &qry->is_operating_attrs;
+ for (i = 0; i < req_attrs->ial_count; ++i) {
+ if (ISNS_ATTR_IS_NIL(req_attrs->ial_data[i]))
+ break;
+ }
+ requested.ial_count = req_attrs->ial_count - i;
+ requested.ial_data = req_attrs->ial_data + i;
+ if (requested.ial_count)
+ req_attrs = &requested;
+ else
+ req_attrs = NULL;
+
+ isns_object_get_attrlist(obj,
+ &resp->is_operating_attrs,
+ req_attrs);
+ return resp;
+}
+
+/*
+ * Process a GetNext request
+ */
+int
+isns_process_getnext(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
+{
+ isns_simple_t *reply = NULL;
+ isns_object_t *obj = NULL;
+ isns_db_t *db = srv->is_db;
+ int status;
+
+ /* Get the next object */
+ status = isns_getnext_get_object(call, db, &obj);
+ if (status != ISNS_SUCCESS)
+ goto done;
+
+ /* If it's a virtual object, rebuild it */
+ if (obj->ie_rebuild)
+ obj->ie_rebuild(obj, srv->is_db);
+
+ /* Success: create a new simple message, and
+ * send it in our reply. */
+ reply = isns_create_getnext_response(srv->is_source, call, obj);
+ if (reply == NULL)
+ status = ISNS_INTERNAL_ERROR;
+
+done:
+ if (obj)
+ isns_object_release(obj);
+ *result = reply;
+ return status;
+}
+
+/*
+ * Parse the object in a getnext response
+ */
+int
+isns_getnext_response_get_object(isns_simple_t *qry,
+ isns_object_t **result)
+{
+ isns_object_template_t *tmpl;
+
+ tmpl = isns_object_template_for_key_attrs(&qry->is_operating_attrs);
+ if (tmpl == NULL) {
+ isns_error("Cannot determine object type in GetNext response\n");
+ return ISNS_ATTRIBUTE_NOT_IMPLEMENTED;
+ }
+
+ *result = isns_create_object(tmpl,
+ &qry->is_operating_attrs,
+ NULL);
+ return ISNS_SUCCESS;
+}
+
diff --git a/utils/open-isns/internal.h b/utils/open-isns/internal.h
new file mode 100644
index 0000000..fb80b48
--- /dev/null
+++ b/utils/open-isns/internal.h
@@ -0,0 +1,16 @@
+/*
+ * iSNS implementation - internal functions and types
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_INTERNAL_H
+#define ISNS_INTERNAL_H
+
+extern char * isns_slp_build_url(uint16_t);
+extern int isns_slp_register(const char *);
+extern int isns_slp_unregister(const char *);
+extern char * isns_slp_find(void);
+
+#endif /* ISNS_INTERNAL_H */
+
diff --git a/utils/open-isns/isns-proto.h b/utils/open-isns/isns-proto.h
new file mode 100644
index 0000000..1e2f682
--- /dev/null
+++ b/utils/open-isns/isns-proto.h
@@ -0,0 +1,258 @@
+/*
+ * iSNS protocol definitions
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_PROTO_H
+#define ISNS_PROTO_H
+
+#include <stdint.h>
+
+struct isns_hdr {
+ uint16_t i_version;
+ uint16_t i_function;
+ uint16_t i_length;
+ uint16_t i_flags;
+ uint16_t i_xid;
+ uint16_t i_seq;
+};
+
+#define ISNS_VERSION 0x0001
+#define ISNS_MAX_PDU_SIZE 65535
+
+/*
+ * Values for the i_flags field:
+ */
+#define ISNS_F_CLIENT 0x8000
+#define ISNS_F_SERVER 0x4000
+#define ISNS_F_AUTHBLK_PRESENT 0x2000
+#define ISNS_F_REPLACE 0x1000
+#define ISNS_F_LAST_PDU 0x0800
+#define ISNS_F_FIRST_PDU 0x0400
+
+/*
+ * Function values
+ */
+enum isns_function {
+ ISNS_DEVICE_ATTRIBUTE_REGISTER = 1,
+ ISNS_DEVICE_ATTRIBUTE_QUERY = 2,
+ ISNS_DEVICE_GET_NEXT = 3,
+ ISNS_DEVICE_DEREGISTER = 4,
+ ISNS_SCN_REGISTER = 5,
+ ISNS_SCN_DEREGISTER = 6,
+ ISNS_SCN_EVENT = 7,
+ ISNS_STATE_CHANGE_NOTIFICATION = 8,
+ ISNS_DD_REGISTER = 9,
+ ISNS_DD_DEREGISTER = 10,
+ ISNS_DDS_REGISTER = 11,
+ ISNS_DDS_DEREGISTER = 12,
+ ISNS_ENTITY_STATUS_INQUIRY = 13,
+ ISNS_HEARTBEAT = 14,
+};
+
+/*
+ * iSNS status codes:
+ */
+enum isns_status {
+ ISNS_SUCCESS = 0,
+ ISNS_UNKNOWN_ERROR,
+ ISNS_MESSAGE_FORMAT_ERROR,
+ ISNS_INVALID_REGISTRATION,
+ __ISNS_RESERVED_STATUS,
+ ISNS_INVALID_QUERY,
+ ISNS_SOURCE_UNKNOWN,
+ ISNS_SOURCE_ABSENT,
+ ISNS_SOURCE_UNAUTHORIZED,
+ ISNS_NO_SUCH_ENTRY,
+ ISNS_VERSION_NOT_SUPPORTED,
+ ISNS_INTERNAL_ERROR,
+ ISNS_BUSY,
+ ISNS_OPTION_NOT_UNDERSTOOD,
+ ISNS_INVALID_UPDATE,
+ ISNS_MESSAGE_NOT_SUPPORTED,
+ ISNS_SCN_EVENT_REJECTED,
+ ISNS_SCN_REGISTRATION_REJECTED,
+ ISNS_ATTRIBUTE_NOT_IMPLEMENTED,
+ ISNS_FC_DOMAIN_ID_NOT_AVAILABLE,
+ ISNS_FC_DOMAIN_ID_NOT_ALLOCATED,
+ ISNS_ESI_NOT_AVAILABLE,
+ ISNS_INVALID_DEREGISTRATION,
+ ISNS_REGISTRATION_FEATURE_NOT_SUPPORTED,
+};
+
+enum isns_tag {
+ ISNS_TAG_DELIMITER = 0,
+ ISNS_TAG_ENTITY_IDENTIFIER = 1,
+ ISNS_TAG_ENTITY_PROTOCOL = 2,
+ ISNS_TAG_MGMT_IP_ADDRESS = 3,
+ ISNS_TAG_TIMESTAMP = 4,
+ ISNS_TAG_PROTOCOL_VERSION_RANGE = 5,
+ ISNS_TAG_REGISTRATION_PERIOD = 6,
+ ISNS_TAG_ENTITY_INDEX = 7,
+ ISNS_TAG_ENTITY_NEXT_INDEX = 8,
+ ISNS_TAG_ENTITY_ISAKMP_PHASE_1 = 11,
+ ISNS_TAG_ENTITY_CERTIFICATE = 12,
+ ISNS_TAG_PORTAL_IP_ADDRESS = 16,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT = 17,
+ ISNS_TAG_PORTAL_SYMBOLIC_NAME = 18,
+ ISNS_TAG_ESI_INTERVAL = 19,
+ ISNS_TAG_ESI_PORT = 20,
+ ISNS_TAG_PORTAL_INDEX = 22,
+ ISNS_TAG_SCN_PORT = 23,
+ ISNS_TAG_PORTAL_NEXT_INDEX = 24,
+ ISNS_TAG_PORTAL_SECURITY_BITMAP = 27,
+ ISNS_TAG_PORTAL_ISAKMP_PHASE_1 = 28,
+ ISNS_TAG_PORTAL_ISAKMP_PHASE_2 = 29,
+ ISNS_TAG_PORTAL_CERTIFICATE = 31,
+ ISNS_TAG_ISCSI_NAME = 32,
+ ISNS_TAG_ISCSI_NODE_TYPE = 33,
+ ISNS_TAG_ISCSI_ALIAS = 34,
+ ISNS_TAG_ISCSI_SCN_BITMAP = 35,
+ ISNS_TAG_ISCSI_NODE_INDEX = 36,
+ ISNS_TAG_WWNN_TOKEN = 37,
+ ISNS_TAG_ISCSI_NODE_NEXT_INDEX = 38,
+ ISNS_TAG_ISCSI_AUTHMETHOD = 42,
+ ISNS_TAG_PG_ISCSI_NAME = 48,
+ ISNS_TAG_PG_PORTAL_IP_ADDR = 49,
+ ISNS_TAG_PG_PORTAL_TCP_UDP_PORT = 50,
+ ISNS_TAG_PG_TAG = 51,
+ ISNS_TAG_PG_INDEX = 52,
+ ISNS_TAG_PG_NEXT_INDEX = 53,
+ ISNS_TAG_FC_PORT_NAME_WWPN = 64,
+ ISNS_TAG_PORT_ID = 65,
+ ISNS_TAG_FC_PORT_TYPE = 66,
+ ISNS_TAG_SYMBOLIC_PORT_NAME = 67,
+ ISNS_TAG_FABRIC_PORT_NAME = 68,
+ ISNS_TAG_HARD_ADDRESS = 69,
+ ISNS_TAG_PORT_IP_ADDRESS = 70,
+ ISNS_TAG_CLASS_OF_SERVICE = 71,
+ ISNS_TAG_FC4_TYPES = 72,
+ ISNS_TAG_FC4_DESCRIPTOR = 73,
+ ISNS_TAG_FC4_FEATURES = 74,
+ ISNS_TAG_IFCP_SCN_BITMAP = 75,
+ ISNS_TAG_PORT_ROLE = 76,
+ ISNS_TAG_PERMANENT_PORT_NAME = 77,
+ ISNS_TAG_FC4_TYPE_CODE = 95,
+ ISNS_TAG_FC_NODE_NAME_WWNN = 96,
+ ISNS_TAG_SYMBOLIC_NODE_NAME = 97,
+ ISNS_TAG_NODE_IP_ADDRESS = 98,
+ ISNS_TAG_NODE_IPA = 99,
+ ISNS_TAG_PROXY_ISCSI_NAME = 101,
+ ISNS_TAG_SWITCH_NAME = 128,
+ ISNS_TAG_PREFERRED_ID = 129,
+ ISNS_TAG_ASSIGNED_ID = 130,
+ ISNS_TAG_VIRTUAL_FABRIC_ID = 131,
+ ISNS_TAG_SERVER_VENDOR_OUI = 256,
+ ISNS_TAG_DD_SET_ID = 2049,
+ ISNS_TAG_DD_SET_SYMBOLIC_NAME = 2050,
+ ISNS_TAG_DD_SET_STATUS = 2051,
+ ISNS_TAG_DD_SET_NEXT_ID = 2052,
+ ISNS_TAG_DD_ID = 2065,
+ ISNS_TAG_DD_SYMBOLIC_NAME = 2066,
+ ISNS_TAG_DD_MEMBER_ISCSI_INDEX = 2067,
+ ISNS_TAG_DD_MEMBER_ISCSI_NAME = 2068,
+ ISNS_TAG_DD_MEMBER_FC_PORT_NAME = 2069,
+ ISNS_TAG_DD_MEMBER_PORTAL_INDEX = 2070,
+ ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR = 2071,
+ ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT = 2072,
+ ISNS_TAG_DD_FEATURES = 2078,
+ ISNS_TAG_DD_NEXT_ID = 2079,
+
+ __ISNS_TAG_MAX,
+
+ ISNS_VENDOR_SPECIFIC_SERVER_BASE = 257, /* end 384 */
+ ISNS_VENDOR_SPECIFIC_ENTITY_BASE = 385, /* end 512 */
+ ISNS_VENDOR_SPECIFIC_PORTAL_BASE = 513, /* end 640 */
+ ISNS_VENDOR_SPECIFIC_NODE_BASE = 641, /* end 768 */
+ ISNS_VENDOR_SPECIFIC_DD_BASE = 1024, /* end 1280 */
+ ISNS_VENDOR_SPECIFIC_DDSET_BASE = 1281, /* end 1536 */
+ ISNS_VENDOR_SPECIFIC_OTHER_BASE = 1537, /* end 2048 */
+};
+
+typedef enum isns_entity_protocol {
+ ISNS_ENTITY_PROTOCOL_NONE = 1,
+ ISNS_ENTITY_PROTOCOL_ISCSI = 2,
+ ISNS_ENTITY_PROTOCOL_IFCP = 3,
+} isns_entity_protocol_t;
+
+enum isns_iscsi_node_type_bits {
+ ISNS_ISCSI_NODE_TYPE_TARGET = 0,
+ ISNS_ISCSI_NODE_TYPE_INITIATOR = 1,
+ ISNS_ISCSI_NODE_TYPE_CONTROL = 2,
+};
+#define ISNS_ISCSI_INITIATOR_MASK (1 << ISNS_ISCSI_NODE_TYPE_INITIATOR)
+#define ISNS_ISCSI_TARGET_MASK (1 << ISNS_ISCSI_NODE_TYPE_TARGET)
+#define ISNS_ISCSI_CONTROL_MASK (1 << ISNS_ISCSI_NODE_TYPE_CONTROL)
+
+enum isns_portal_port_bits {
+ ISNS_PORTAL_PORT_UDP = 16,
+};
+#define ISNS_PORTAL_PORT_UDP_MASK (1 << ISNS_PORTAL_PORT_UDP)
+
+enum isns_portal_security_bits {
+ ISNS_PORTAL_SEC_BITMAP_VALID = 0,
+ ISNS_PORTAL_SEC_IPSEC_ENABLED = 1,
+ ISNS_PORTAL_SEC_MAIN_MODE_ENABLED = 2,
+ ISNS_PORTAL_SEC_AGGR_MODE_ENABLED = 3,
+ ISNS_PORTAL_SEC_PFS_ENABLED = 4,
+ ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED = 5,
+ ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED = 6,
+};
+#define ISNS_PORTAL_SEC_BITMAP_VALID_MASK (1 << ISNS_PORTAL_SEC_BITMAP_VALID)
+#define ISNS_PORTAL_SEC_IPSEC_ENABLED_MASK (1 << ISNS_PORTAL_SEC_IPSEC_ENABLED)
+#define ISNS_PORTAL_SEC_MAIN_MODE_ENABLED_MASK (1 << ISNS_PORTAL_SEC_MAIN_MODE_ENABLED)
+#define ISNS_PORTAL_SEC_AGGR_MODE_ENABLED_MASK (1 << ISNS_PORTAL_SEC_AGGR_MODE_ENABLED)
+#define ISNS_PORTAL_SEC_PFS_ENABLED_MASK (1 << ISNS_PORTAL_SEC_PFS_ENABLED)
+#define ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED_MASK (1 << ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED)
+#define ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED_MASK (1 << ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED)
+
+enum isns_scn_bits {
+ ISNS_SCN_DD_MEMBER_ADDED = 0,
+ ISNS_SCN_DD_MEMBER_REMOVED = 1,
+ ISNS_SCN_OBJECT_UPDATED = 2,
+ ISNS_SCN_OBJECT_ADDED = 3,
+ ISNS_SCN_OBJECT_REMOVED = 4,
+ ISNS_SCN_MANAGEMENT_REGISTRATION = 5,
+ ISNS_SCN_TARGET_AND_SELF_ONLY = 6,
+ ISNS_SCN_INITIATOR_AND_SELF_ONLY = 7,
+};
+#define ISNS_SCN_DD_MEMBER_ADDED_MASK (1 << ISNS_SCN_DD_MEMBER_ADDED)
+#define ISNS_SCN_DD_MEMBER_REMOVED_MASK (1 << ISNS_SCN_DD_MEMBER_REMOVED)
+#define ISNS_SCN_OBJECT_UPDATED_MASK (1 << ISNS_SCN_OBJECT_UPDATED)
+#define ISNS_SCN_OBJECT_ADDED_MASK (1 << ISNS_SCN_OBJECT_ADDED)
+#define ISNS_SCN_OBJECT_REMOVED_MASK (1 << ISNS_SCN_OBJECT_REMOVED)
+#define ISNS_SCN_MANAGEMENT_REGISTRATION_MASK (1 << ISNS_SCN_MANAGEMENT_REGISTRATION)
+#define ISNS_SCN_TARGET_AND_SELF_ONLY_MASK (1 << ISNS_SCN_TARGET_AND_SELF_ONLY)
+#define ISNS_SCN_INITIATOR_AND_SELF_ONLY_MASK (1 << ISNS_SCN_INITIATOR_AND_SELF_ONLY)
+
+enum isns_dds_status_bits {
+ ISNS_DDS_ENABLED = 0,
+};
+#define ISNS_DDS_ENABLED_MASK (1 << ISNS_DDS_ENABLED)
+
+enum isns_dd_feature_bits {
+ ISNS_DD_BOOT_LIST_ENABLED = 0,
+};
+#define ISNS_DD_BOOT_LIST_ENABLED_MASK (1 << ISN_BOOT_LIST_DDS_ENABLED)
+
+#define ISNS_PAD(len) (((len) + 3) & ~3UL)
+
+/*
+ * iSNS auth block
+ */
+#define ISNS_AUTHBLK_SIZE 20
+struct isns_authblk {
+ uint32_t iab_bsd; /* 16bit in SLP */
+ uint32_t iab_length; /* 16bit in SLP */
+ uint64_t iab_timestamp; /* 32bit in SLP */
+ uint32_t iab_spi_len; /* 16bit in SLP */
+
+ char * iab_spi;
+ void * iab_sig;
+ uint32_t iab_sig_len;
+} __attribute__((packed));
+
+#define ISNS_AUTH_TYPE_SHA1_DSA 0x0002
+
+#endif /* ISNS_PROTO_H */
diff --git a/utils/open-isns/isns.h b/utils/open-isns/isns.h
new file mode 100644
index 0000000..73d4a45
--- /dev/null
+++ b/utils/open-isns/isns.h
@@ -0,0 +1,670 @@
+/*
+ * iSNS implementation - library header file.
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ *
+ * This file contains all declarations and definitions
+ * commonly required by users of libisns.
+ */
+
+#ifndef ISNS_H
+#define ISNS_H
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#include <isns-proto.h>
+#include "types.h"
+
+#define ISNS_MAX_BUFFER 8192
+#define ISNS_MAX_MESSAGE 8192
+
+
+/*
+ * Client handle
+ */
+typedef struct isns_client isns_client_t;
+struct isns_client {
+ isns_source_t * ic_source;
+ isns_socket_t * ic_socket;
+};
+
+/*
+ * Server operations
+ */
+typedef int isns_service_fn_t(isns_server_t *, isns_simple_t *, isns_simple_t **);
+typedef void isns_scn_callback_fn_t(isns_db_t *, uint32_t scn_bits,
+ isns_object_template_t *node_type,
+ const char *node_name,
+ const char *recipient);
+struct isns_service_ops {
+ isns_service_fn_t * process_registration;
+ isns_service_fn_t * process_query;
+ isns_service_fn_t * process_getnext;
+ isns_service_fn_t * process_deregistration;
+ isns_service_fn_t * process_scn_registration;
+ isns_service_fn_t * process_scn_deregistration;
+ isns_service_fn_t * process_scn_event;
+ isns_service_fn_t * process_scn;
+ isns_service_fn_t * process_dd_registration;
+ isns_service_fn_t * process_dd_deregistration;
+ isns_service_fn_t * process_esi;
+ isns_service_fn_t * process_heartbeat;
+};
+
+extern struct isns_service_ops isns_default_service_ops;
+extern struct isns_service_ops isns_callback_service_ops;
+
+/*
+ * Output function
+ */
+void isns_print_stdout(const char *, ...);
+
+/*
+ * Database events
+ */
+struct isns_db_event {
+ isns_object_t * ie_recipient; /* Recipient node or NULL */
+ isns_object_t * ie_object; /* Affected object */
+ isns_object_t * ie_trigger; /* Triggering object */
+ unsigned int ie_bits; /* SCN bitmask */
+};
+typedef void isns_db_callback_t(const isns_db_event_t *,
+ void *user_data);
+
+/*
+ * Handling of client objects
+ */
+extern isns_client_t * isns_create_default_client(isns_security_t *);
+extern isns_client_t * isns_create_client(isns_security_t *,
+ const char *source_name);
+extern isns_client_t * isns_create_local_client(isns_security_t *,
+ const char *source_name);
+extern int isns_client_call(isns_client_t *,
+ isns_simple_t **inout);
+extern void isns_client_destroy(isns_client_t *);
+extern int isns_client_get_local_address(const isns_client_t *,
+ isns_portal_info_t *);
+
+/*
+ * Handling of server objects
+ */
+extern isns_server_t * isns_create_server(isns_source_t *,
+ isns_db_t *,
+ struct isns_service_ops *);
+extern void isns_server_set_scn_callback(isns_server_t *,
+ isns_scn_callback_fn_t *);
+
+
+/*
+ * Handling of source names
+ */
+extern int isns_init_names(void);
+extern const char * isns_default_source_name(void);
+extern isns_source_t * isns_source_create(isns_attr_t *);
+extern isns_source_t * isns_source_create_iscsi(const char *name);
+extern isns_source_t * isns_source_create_ifcp(const char *name);
+extern uint32_t isns_source_type(const isns_source_t *);
+extern const char * isns_source_name(const isns_source_t *);
+extern isns_attr_t * isns_source_attr(const isns_source_t *);
+extern isns_source_t * isns_source_get(isns_source_t *);
+extern isns_source_t * isns_source_from_object(const isns_object_t *);
+extern void isns_source_release(isns_source_t *);
+extern int isns_source_match(const isns_source_t *,
+ const isns_source_t *);
+
+extern void isns_server_set_source(isns_source_t *);
+extern isns_message_t * isns_process_message(isns_server_t *, isns_message_t *);
+
+extern void isns_simple_print(isns_simple_t *,
+ isns_print_fn_t *);
+extern int isns_simple_call(isns_socket_t *,
+ isns_simple_t **);
+extern int isns_simple_transmit(isns_socket_t *,
+ isns_simple_t *,
+ const isns_portal_info_t *,
+ unsigned int,
+ void (*callback)(uint32_t, isns_simple_t *));
+extern void isns_simple_free(isns_simple_t *);
+extern const isns_attr_list_t *isns_simple_get_attrs(isns_simple_t *);
+
+extern isns_simple_t * isns_create_query(isns_client_t *clnt,
+ const isns_attr_list_t *query_key);
+extern isns_simple_t * isns_create_query2(isns_client_t *clnt,
+ const isns_attr_list_t *query_key,
+ isns_source_t *source);
+extern int isns_query_request_attr_tag(isns_simple_t *,
+ uint32_t);
+extern int isns_query_request_attr(isns_simple_t *,
+ isns_attr_t *);
+extern int isns_query_response_get_objects(isns_simple_t *qry,
+ isns_object_list_t *result);
+
+extern isns_simple_t * isns_create_registration(isns_client_t *clnt,
+ isns_object_t *key_object);
+extern isns_simple_t * isns_create_registration2(isns_client_t *clnt,
+ isns_object_t *key_object,
+ isns_source_t *source);
+extern void isns_registration_set_replace(isns_simple_t *, int);
+extern void isns_registration_add_object(isns_simple_t *,
+ isns_object_t *object);
+extern void isns_registration_add_object_list(isns_simple_t *,
+ isns_object_list_t *);
+extern int isns_registration_response_get_objects(isns_simple_t *,
+ isns_object_list_t *);
+
+extern isns_simple_t * isns_create_getnext(isns_client_t *,
+ isns_object_template_t *,
+ const isns_attr_list_t *);
+extern int isns_getnext_response_get_object(isns_simple_t *,
+ isns_object_t **);
+extern isns_simple_t * isns_create_getnext_followup(isns_client_t *,
+ const isns_simple_t *,
+ const isns_attr_list_t *);
+
+extern isns_simple_t * isns_create_deregistration(isns_client_t *clnt,
+ const isns_attr_list_t *);
+
+extern isns_simple_t * isns_create_scn_registration(isns_client_t *clnt,
+ unsigned int);
+extern isns_simple_t * isns_create_scn_registration2(isns_client_t *clnt,
+ unsigned int,
+ isns_source_t *);
+
+extern int isns_dd_load_all(isns_db_t *);
+extern void isns_dd_get_members(uint32_t, isns_object_list_t *, int);
+extern isns_simple_t * isns_create_dd_registration(isns_client_t *,
+ const isns_attr_list_t *);
+extern isns_simple_t * isns_create_dd_deregistration(isns_client_t *,
+ uint32_t, const isns_attr_list_t *);
+
+extern isns_object_t * isns_create_object(isns_object_template_t *,
+ const isns_attr_list_t *,
+ isns_object_t *);
+extern isns_object_t * isns_create_entity(int, const char *);
+extern isns_object_t * isns_create_entity_for_source(const isns_source_t *,
+ const char *);
+extern const char * isns_entity_name(const isns_object_t *);
+extern isns_object_t * isns_create_portal(const isns_portal_info_t *,
+ isns_object_t *parent);
+extern isns_object_t * isns_create_storage_node(const char *name,
+ uint32_t type_mask,
+ isns_object_t *parent);
+extern isns_object_t * isns_create_storage_node2(const isns_source_t *,
+ uint32_t type_mask,
+ isns_object_t *parent);
+extern isns_object_t * isns_create_iscsi_initiator(const char *name,
+ isns_object_t *parent);
+extern isns_object_t * isns_create_iscsi_target(const char *name,
+ isns_object_t *parent);
+extern const char * isns_storage_node_name(const isns_object_t *);
+extern isns_attr_t * isns_storage_node_key_attr(const isns_object_t *);
+extern isns_object_t * isns_create_portal_group(isns_object_t *portal,
+ isns_object_t *iscsi_node, uint32_t pg_tag);
+extern isns_object_t * isns_create_default_portal_group(isns_db_t *,
+ isns_object_t *portal,
+ isns_object_t *node);
+extern void isns_get_portal_groups(isns_object_t *portal,
+ isns_object_t *node,
+ isns_object_list_t *result);
+
+extern const char * isns_object_template_name(isns_object_template_t *);
+extern int isns_object_set_attr(isns_object_t *, isns_attr_t *);
+extern int isns_object_set_attrlist(isns_object_t *, const isns_attr_list_t *);
+extern isns_object_t * isns_object_get(isns_object_t *);
+extern int isns_object_get_attrlist(isns_object_t *obj,
+ isns_attr_list_t *result,
+ const isns_attr_list_t *requested_attrs);
+extern int isns_object_get_key_attrs(isns_object_t *,
+ isns_attr_list_t *);
+extern int isns_object_get_attr(const isns_object_t *, uint32_t,
+ isns_attr_t **);
+extern void isns_object_get_related(isns_db_t *,
+ isns_object_t *, isns_object_list_t *);
+extern void isns_object_get_descendants(const isns_object_t *,
+ isns_object_template_t *,
+ isns_object_list_t *);
+extern void isns_object_release(isns_object_t *);
+extern int isns_object_match(const isns_object_t *,
+ const isns_attr_list_t *);
+extern isns_object_t * isns_object_get_entity(isns_object_t *);
+extern int isns_object_attr_valid(isns_object_template_t *, uint32_t);
+extern int isns_object_contains(const isns_object_t *, const isns_object_t *);
+extern int isns_object_delete_attr(isns_object_t *, uint32_t);
+extern int isns_object_is(const isns_object_t *,
+ isns_object_template_t *);
+extern int isns_object_is_entity(const isns_object_t *);
+extern int isns_object_is_iscsi_node(const isns_object_t *);
+extern int isns_object_is_fc_port(const isns_object_t *);
+extern int isns_object_is_fc_node(const isns_object_t *);
+extern int isns_object_is_portal(const isns_object_t *);
+extern int isns_object_is_pg(const isns_object_t *);
+extern int isns_object_is_policy(const isns_object_t *);
+extern int isns_object_is_dd(const isns_object_t *);
+extern int isns_object_is_ddset(const isns_object_t *);
+extern void isns_object_print(isns_object_t *,
+ isns_print_fn_t *);
+extern time_t isns_object_last_modified(const isns_object_t *);
+extern int isns_object_mark_membership(isns_object_t *, uint32_t);
+extern int isns_object_clear_membership(isns_object_t *, uint32_t);
+extern int isns_object_test_membership(const isns_object_t *, uint32_t);
+extern int isns_object_test_visibility(const isns_object_t *,
+ const isns_object_t *);
+extern void isns_object_get_visible(const isns_object_t *,
+ isns_db_t *, isns_object_list_t *);
+extern void isns_entity_touch(isns_object_t *);
+extern int isns_object_extract_keys(const isns_object_t *,
+ isns_attr_list_t *);
+extern int isns_object_extract_all(const isns_object_t *,
+ isns_attr_list_t *);
+extern int isns_object_extract_writable(const isns_object_t *,
+ isns_attr_list_t *);
+
+
+extern int isns_object_set_nil(isns_object_t *obj,
+ uint32_t tag);
+extern int isns_object_set_string(isns_object_t *obj,
+ uint32_t tag,
+ const char *value);
+extern int isns_object_set_uint32(isns_object_t *obj,
+ uint32_t tag,
+ uint32_t value);
+extern int isns_object_set_uint64(isns_object_t *obj,
+ uint32_t tag,
+ uint64_t value);
+extern int isns_object_set_ipaddr(isns_object_t *obj,
+ uint32_t tag,
+ const struct in6_addr *value);
+
+extern int isns_object_get_string(const isns_object_t *,
+ uint32_t,
+ const char **);
+extern int isns_object_get_ipaddr(const isns_object_t *,
+ uint32_t,
+ struct in6_addr *);
+extern int isns_object_get_uint32(const isns_object_t *,
+ uint32_t,
+ uint32_t *);
+extern int isns_object_get_uint64(const isns_object_t *,
+ uint32_t,
+ uint64_t *);
+extern int isns_object_get_opaque(const isns_object_t *,
+ uint32_t,
+ const void **, size_t *);
+
+
+extern int isns_object_find_descendants(isns_object_t *obj,
+ isns_object_template_t *,
+ const isns_attr_list_t *keys,
+ isns_object_list_t *result);
+extern isns_object_t * isns_object_find_descendant(isns_object_t *obj,
+ const isns_attr_list_t *keys);
+extern int isns_object_detach(isns_object_t *);
+extern int isns_object_attach(isns_object_t *, isns_object_t *);
+extern void isns_object_prune_attrs(isns_object_t *);
+extern void isns_mark_object(isns_object_t *, unsigned int);
+
+extern int isns_get_entity_identifier(isns_object_t *, const char **);
+extern int isns_get_entity_protocol(isns_object_t *, isns_entity_protocol_t *);
+extern int isns_get_entity_index(isns_object_t *, uint32_t *);
+
+extern int isns_get_portal_ipaddr(isns_object_t *, struct in6_addr *);
+extern int isns_get_portal_tcpudp_port(isns_object_t *,
+ int *ipprotocol, uint16_t *port);
+extern int isns_get_portal_index(isns_object_t *, uint32_t *);
+
+extern int isns_get_address(struct sockaddr_storage *,
+ const char *, const char *, int, int, int);
+extern char * isns_get_canon_name(const char *);
+
+extern isns_db_t * isns_db_open(const char *location);
+extern isns_db_t * isns_db_open_shadow(isns_object_list_t *);
+extern isns_object_t * isns_db_lookup(isns_db_t *,
+ isns_object_template_t *,
+ const isns_attr_list_t *);
+extern isns_object_t * isns_db_vlookup(isns_db_t *,
+ isns_object_template_t *,
+ ...);
+extern int isns_db_gang_lookup(isns_db_t *,
+ isns_object_template_t *,
+ const isns_attr_list_t *,
+ isns_object_list_t *);
+extern isns_object_t * isns_db_get_next(isns_db_t *,
+ isns_object_template_t *,
+ const isns_attr_list_t *current,
+ const isns_attr_list_t *scope,
+ const isns_source_t *source);
+extern isns_object_t * isns_db_lookup_source_node(isns_db_t *,
+ const isns_source_t *);
+extern void isns_db_get_domainless(isns_db_t *,
+ isns_object_template_t *,
+ isns_object_list_t *);
+extern uint32_t isns_db_allocate_index(isns_db_t *);
+extern void isns_db_insert(isns_db_t *, isns_object_t *);
+extern void isns_db_insert_limbo(isns_db_t *, isns_object_t *);
+extern int isns_db_remove(isns_db_t *, isns_object_t *);
+extern time_t isns_db_expire(isns_db_t *);
+extern void isns_db_purge(isns_db_t *);
+extern void isns_db_sync(isns_db_t *);
+extern const char * isns_db_generate_eid(isns_db_t *, char *, size_t);
+extern isns_object_t * isns_db_get_control(isns_db_t *);
+extern void isns_db_print(isns_db_t *,
+ isns_print_fn_t *);
+
+extern void isns_db_begin_transaction(isns_db_t *);
+extern void isns_db_commit(isns_db_t *);
+extern void isns_db_rollback(isns_db_t *);
+
+extern void isns_object_event(isns_object_t *obj,
+ unsigned int bits,
+ isns_object_t *trigger);
+extern void isns_unicast_event(isns_object_t *dst,
+ isns_object_t *obj,
+ unsigned int bits,
+ isns_object_t *trigger);
+extern void isns_register_callback(isns_db_callback_t *,
+ void *);
+extern void isns_flush_events(void);
+extern const char * isns_event_string(unsigned int);
+
+extern void isns_add_timer(unsigned int,
+ isns_timer_callback_t *, void *);
+extern void isns_add_oneshot_timer(unsigned int,
+ isns_timer_callback_t *, void *);
+extern void isns_cancel_timer(isns_timer_callback_t *, void *);
+extern time_t isns_run_timers(void);
+
+extern void isns_object_list_init(isns_object_list_t *);
+extern void isns_object_list_destroy(isns_object_list_t *);
+extern int isns_object_list_contains(const isns_object_list_t *,
+ isns_object_t *);
+extern void isns_object_list_append(isns_object_list_t *,
+ isns_object_t *);
+extern void isns_object_list_append_list(isns_object_list_t *,
+ const isns_object_list_t *);
+extern isns_object_t * isns_object_list_lookup(const isns_object_list_t *,
+ isns_object_template_t *,
+ const isns_attr_list_t *);
+extern int isns_object_list_gang_lookup(const isns_object_list_t *,
+ isns_object_template_t *,
+ const isns_attr_list_t *,
+ isns_object_list_t *);
+extern int isns_object_list_remove(isns_object_list_t *,
+ isns_object_t *);
+extern void isns_object_list_uniq(isns_object_list_t *);
+extern void isns_object_list_print(const isns_object_list_t *,
+ isns_print_fn_t *);
+
+isns_object_template_t *isns_object_template_for_key_attrs(const isns_attr_list_t *);
+isns_object_template_t *isns_object_template_for_tag(uint32_t);
+isns_object_template_t *isns_object_template_for_index_tag(uint32_t);
+isns_object_template_t *isns_object_template_find(uint32_t);
+
+extern int isns_attr_set(isns_attr_t *, const void *);
+extern isns_attr_t * isns_attr_get(isns_attr_t *);
+extern void isns_attr_release(isns_attr_t *);
+extern void isns_attr_print(const isns_attr_t *,
+ isns_print_fn_t *);
+extern char * isns_attr_print_value(const isns_attr_t *,
+ char *, size_t);
+extern int isns_attr_match(const isns_attr_t *,
+ const isns_attr_t *);
+extern int isns_attr_compare(const isns_attr_t *,
+ const isns_attr_t *);
+extern isns_attr_t * isns_attr_from_string(uint32_t, const char *);
+
+extern void isns_attr_list_print(const isns_attr_list_t *,
+ isns_print_fn_t *);
+
+extern void isns_attr_list_init(isns_attr_list_t *);
+extern void isns_attr_list_copy(isns_attr_list_t *,
+ const isns_attr_list_t *);
+extern void isns_attr_list_destroy(isns_attr_list_t *);
+extern int isns_attr_list_remove_tag(isns_attr_list_t *,
+ uint32_t);
+
+extern void isns_attr_list_append_attr(isns_attr_list_t *,
+ isns_attr_t *);
+extern void isns_attr_list_append_list(isns_attr_list_t *,
+ const isns_attr_list_t *);
+extern int isns_attr_list_replace_attr(isns_attr_list_t *,
+ isns_attr_t *);
+/* Warning: this does *NOT* return a reference to the attribute */
+extern int isns_attr_list_get_attr(const isns_attr_list_t *,
+ uint32_t tag,
+ isns_attr_t **);
+
+extern void isns_attr_list_append_nil(isns_attr_list_t *,
+ uint32_t tag);
+extern void isns_attr_list_append_string(isns_attr_list_t *,
+ uint32_t tag, const char *value);
+extern void isns_attr_list_append_uint32(isns_attr_list_t *,
+ uint32_t tag, uint32_t value);
+extern void isns_attr_list_append_uint64(isns_attr_list_t *,
+ uint32_t, int64_t);
+extern void isns_attr_list_append_int32(isns_attr_list_t *,
+ uint32_t tag, int32_t value);
+extern void isns_attr_list_append_opaque(isns_attr_list_t *,
+ uint32_t tag, const void *ptr, size_t len);
+extern void isns_attr_list_append_ipaddr(isns_attr_list_t *,
+ uint32_t tag, const struct in6_addr *);
+
+extern int isns_attr_list_append(isns_attr_list_t *,
+ uint32_t tag, const void *);
+extern int isns_attr_list_update(isns_attr_list_t *,
+ uint32_t tag, const void *);
+
+extern int isns_attr_list_contains(const isns_attr_list_t *,
+ uint32_t tag);
+extern int isns_attr_list_compare(const isns_attr_list_t *,
+ const isns_attr_list_t *);
+
+/*
+ * Helper macros
+ */
+#define ISNS_ATTR_TYPE_CHECK(attr, type) \
+ ((attr)->ia_value.iv_type == &isns_attr_type_##type)
+#define ISNS_ATTR_IS_NIL(attr) \
+ ISNS_ATTR_TYPE_CHECK(attr, nil)
+#define ISNS_ATTR_IS_STRING(attr) \
+ ISNS_ATTR_TYPE_CHECK(attr, string)
+#define ISNS_ATTR_IS_IPADDR(attr) \
+ ISNS_ATTR_TYPE_CHECK(attr, ipaddr)
+#define ISNS_ATTR_IS_UINT32(attr) \
+ ISNS_ATTR_TYPE_CHECK(attr, uint32)
+#define ISNS_ATTR_IS_UINT64(attr) \
+ ISNS_ATTR_TYPE_CHECK(attr, uint64)
+#define ISNS_ATTR_IS_OPAQUE(attr) \
+ ISNS_ATTR_TYPE_CHECK(attr, opaque)
+
+
+
+extern isns_socket_t * isns_create_server_socket(const char *hostname, const char *portname,
+ int af_hint, int sock_type);
+extern isns_socket_t * isns_create_client_socket(const char *hostname, const char *portname,
+ int af_hint, int sock_type);
+extern isns_socket_t * isns_create_bound_client_socket(const char *myaddr,
+ const char *hostname, const char *portname,
+ int af_hint, int sock_type);
+extern isns_socket_t * isns_connect_to_portal(const isns_portal_info_t *);
+extern void isns_socket_set_disconnect_fatal(isns_socket_t *);
+extern int isns_socket_get_local_addr(const isns_socket_t *,
+ struct sockaddr_storage *);
+extern int isns_socket_get_portal_info(const isns_socket_t *,
+ isns_portal_info_t *);
+extern void isns_socket_set_security_ctx(isns_socket_t *,
+ isns_security_t *);
+extern isns_message_t * isns_recv_message(struct timeval *timeout);
+extern isns_message_t * isns_socket_call(isns_socket_t *, isns_message_t *, long);
+extern int isns_socket_send(isns_socket_t *, isns_message_t *);
+extern void isns_socket_free(isns_socket_t *);
+extern int isns_addr_get_port(const struct sockaddr *);
+extern void isns_addr_set_port(struct sockaddr *, unsigned int);
+extern isns_socket_t * isns_socket_find_server(const isns_portal_info_t *);
+
+extern isns_message_t * isns_create_message(uint16_t function, uint16_t flags);
+extern isns_message_t * isns_create_reply(const isns_message_t *);
+extern int isns_message_init(isns_message_t *,
+ uint16_t, uint16_t, size_t);
+extern int isns_message_status(isns_message_t *);
+extern void isns_message_release(isns_message_t *);
+extern unsigned int isns_message_function(const isns_message_t *);
+extern isns_socket_t * isns_message_socket(const isns_message_t *);
+extern void isns_message_set_error(isns_message_t *, uint32_t);
+
+extern const char * isns_strerror(enum isns_status);
+extern const char * isns_function_name(unsigned int);
+
+/*
+ * Security related functions
+ */
+extern int isns_security_init(void);
+extern isns_principal_t *isns_security_load_privkey(isns_security_t *,
+ const char *filename);
+extern isns_principal_t *isns_security_load_pubkey(isns_security_t *,
+ const char *filename);
+extern isns_security_t *isns_default_security_context(int server_only);
+extern isns_security_t *isns_control_security_context(int server_only);
+extern isns_security_t *isns_create_dsa_context(void);
+extern void isns_security_set_identity(isns_security_t *, isns_principal_t *);
+extern void isns_principal_free(isns_principal_t *);
+extern void isns_add_principal(isns_security_t *, isns_principal_t *);
+extern isns_keystore_t *isns_create_keystore(const char *);
+extern void isns_security_set_keystore(isns_security_t *,
+ isns_keystore_t *);
+extern void isns_principal_set_name(isns_principal_t *, const char *);
+extern const char * isns_principal_name(const isns_principal_t *);
+
+extern isns_object_template_t isns_entity_template;
+extern isns_object_template_t isns_portal_template;
+extern isns_object_template_t isns_iscsi_node_template;
+extern isns_object_template_t isns_fc_port_template;
+extern isns_object_template_t isns_fc_node_template;
+extern isns_object_template_t isns_iscsi_pg_template;
+extern isns_object_template_t isns_dd_template;
+extern isns_object_template_t isns_ddset_template;
+
+/*
+ * Config file parser
+ */
+struct isns_config {
+ char * ic_host_name;
+ char * ic_auth_name;
+ char * ic_source_name;
+ char * ic_source_suffix;
+ char * ic_entity_name;
+
+ char * ic_server_name;
+ char * ic_bind_address;
+ char * ic_database;
+ char * ic_auth_key_file;
+ char * ic_server_key_file;
+ char * ic_client_keystore;
+ char * ic_control_socket;
+ char * ic_pidfile;
+ char * ic_local_registry_file;
+ int ic_security;
+ int ic_slp_register;
+
+ char * ic_control_name;
+ char * ic_control_key_file;
+
+ unsigned int ic_registration_period;
+ unsigned int ic_scn_timeout;
+ unsigned int ic_scn_retries;
+ char * ic_scn_callout;
+
+ unsigned int ic_esi_max_interval;
+ unsigned int ic_esi_min_interval;
+ unsigned int ic_esi_retries;
+
+ unsigned int ic_use_default_domain;
+
+ struct {
+ unsigned int policy;
+ unsigned int replay_window;
+ unsigned int timestamp_jitter;
+ int allow_unknown_peers;
+ } ic_auth;
+ struct {
+ unsigned int max_sockets;
+ unsigned int connect_timeout;
+ unsigned int reconnect_timeout;
+ unsigned int call_timeout;
+ unsigned int udp_retrans_timeout;
+ unsigned int tcp_retrans_timeout;
+ unsigned int idle_timeout;
+ } ic_network;
+ struct {
+ char * param_file;
+ unsigned int key_bits;
+ } ic_dsa;
+
+};
+
+extern struct isns_config isns_config;
+extern int isns_read_config(const char *);
+extern int isns_config_set(const char *, char *);
+
+/*
+ * Reserved entity name for Policy information
+ */
+#define ISNS_ENTITY_CONTROL "CONTROL"
+
+
+/*
+ * Helpers to deal with portal information
+ */
+struct isns_portal_info {
+ struct sockaddr_in6 addr;
+ int proto;
+};
+
+extern void isns_portal_init(isns_portal_info_t *,
+ const struct sockaddr *, int);
+extern int isns_portal_parse(isns_portal_info_t *portal,
+ const char *addr_spec,
+ const char *default_port);
+extern int isns_portal_from_attr_list(isns_portal_info_t *,
+ uint32_t addr_tag, uint32_t port_tag,
+ const isns_attr_list_t *);
+extern int isns_portal_from_attr_pair(isns_portal_info_t *,
+ const isns_attr_t *,
+ const isns_attr_t *);
+extern int isns_portal_from_object(isns_portal_info_t *,
+ uint32_t addr_tag, uint32_t port_tag,
+ const isns_object_t *);
+extern int isns_portal_from_sockaddr(isns_portal_info_t *,
+ const struct sockaddr_storage *);
+extern int isns_portal_to_sockaddr(const isns_portal_info_t *,
+ struct sockaddr_storage *);
+extern int isns_portal_to_attr_list(const isns_portal_info_t *,
+ uint32_t addr_tag, uint32_t port_tag,
+ isns_attr_list_t *);
+extern int isns_portal_to_object(const isns_portal_info_t *,
+ uint32_t addr_tag, uint32_t port_tag,
+ isns_object_t *);
+extern int isns_portal_is_wildcard(const isns_portal_info_t *);
+extern uint32_t isns_portal_tcpudp_port(const isns_portal_info_t *);
+extern const char * isns_portal_string(const isns_portal_info_t *);
+extern int isns_portal_equal(const isns_portal_info_t *,
+ const isns_portal_info_t *);
+extern int isns_enumerate_portals(isns_portal_info_t *,
+ unsigned int);
+
+/* Local registry stuff */
+extern int isns_local_registry_load(const char *, pid_t, isns_object_list_t *);
+extern int isns_local_registry_store(const char *, pid_t, const isns_object_list_t *);
+extern int isns_local_registry_purge(const char *, pid_t);
+
+/* Should go somwhere else .*/
+extern int isns_esi_enabled;
+
+extern void isns_esi_init(isns_server_t *);
+extern void isns_esi_register(isns_object_t *);
+
+extern void isns_scn_init(isns_server_t *);
+extern time_t isns_scn_transmit_all(void);
+
+#endif /* ISNS_H */
diff --git a/utils/open-isns/isnsadm.c b/utils/open-isns/isnsadm.c
new file mode 100644
index 0000000..fadd87d
--- /dev/null
+++ b/utils/open-isns/isnsadm.c
@@ -0,0 +1,1149 @@
+/*
+ * isnsadm - helper utility
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "isns.h"
+#include "util.h"
+#include "vendor.h"
+#include "attrs.h"
+#include "security.h"
+#include "objects.h"
+#include "paths.h"
+#include "config.h"
+
+#define ISNS_DEFAULT_PORT_INITIATOR 860
+#define ISNS_DEFAULT_PORT_TARGET 3260
+
+
+enum {
+ DO_REGISTER = 1024,
+ DO_QUERY,
+ DO_QUERY_EID,
+ DO_LIST,
+ DO_DEREGISTER,
+ DO_DD_REGISTER,
+ DO_DD_DEREGISTER,
+ DO_ENROLL,
+ DO_EDIT_POLICY,
+ DO_DELETE_POLICY,
+};
+
+static struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "config", required_argument, NULL, 'c' },
+ { "debug", required_argument, NULL, 'd' },
+ { "keyfile", required_argument, NULL, 'K', },
+ { "key", required_argument, NULL, 'k', },
+ { "local", no_argument, NULL, 'l' },
+ { "control", no_argument, NULL, 'C' },
+ { "replace", no_argument, NULL, 'r' },
+ { "query", no_argument, NULL, DO_QUERY },
+ { "query-eid", no_argument, NULL, DO_QUERY_EID },
+ { "list", no_argument, NULL, DO_LIST },
+ { "register", no_argument, NULL, DO_REGISTER },
+ { "deregister", no_argument, NULL, DO_DEREGISTER },
+ { "dd-register", no_argument, NULL, DO_DD_REGISTER },
+ { "dd-deregister", no_argument, NULL, DO_DD_DEREGISTER},
+
+ { "enroll", no_argument, NULL, DO_ENROLL },
+ { "edit-policy", no_argument, NULL, DO_EDIT_POLICY },
+ { "delete-policy", no_argument, NULL, DO_DELETE_POLICY },
+
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+};
+
+
+static const char * opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG;
+static int opt_af = AF_UNSPEC;
+static int opt_action = 0;
+static int opt_local = 0;
+static int opt_control = 0;
+static int opt_replace = 0;
+static const char * opt_keyfile = NULL;
+static char * opt_key = NULL;
+static struct sockaddr_storage opt_myaddr;
+
+static void usage(int, const char *);
+
+static int register_objects(isns_client_t *, int, char **);
+static int query_objects(isns_client_t *, int, char **);
+static int query_entity_id(isns_client_t *, int, char **);
+static int list_objects(isns_client_t *, int, char **);
+static int deregister_objects(isns_client_t *, int, char **);
+static int register_domain(isns_client_t *, int, char **);
+static int deregister_domain(isns_client_t *, int, char **);
+static int enroll_client(isns_client_t *, int, char **);
+static int edit_policy(isns_client_t *, int, char **);
+
+static isns_attr_t * load_key_callback(const char *);
+static isns_attr_t * generate_key_callback(void);
+
+int
+main(int argc, char **argv)
+{
+ isns_client_t *clnt;
+ isns_security_t *security = NULL;
+ int c, status;
+
+ while ((c = getopt_long(argc, argv, "46Cc:d:hK:k:l", options, NULL)) != -1) {
+ switch (c) {
+ case '4':
+ opt_af = AF_INET;
+ break;
+
+ case '6':
+ opt_af = AF_INET6;
+ break;
+
+ case 'C':
+ opt_control = 1;
+ break;
+
+ case 'c':
+ opt_configfile = optarg;
+ break;
+
+ case 'd':
+ isns_enable_debugging(optarg);
+ break;
+
+ case 'h':
+ usage(0, NULL);
+ break;
+
+ case 'K':
+ opt_keyfile = optarg;
+ break;
+
+ case 'k':
+ opt_key = optarg;
+ break;
+
+ case 'l':
+ opt_local = 1;
+ break;
+
+ case 'r':
+ opt_replace = 1;
+ break;
+
+ case 'V':
+ printf("Open-iSNS version %s\n"
+ "Copyright (C) 2007, Olaf Kirch <olaf.kirch@oracle.com>\n",
+ OPENISNS_VERSION_STRING);
+ return 0;
+
+ case DO_REGISTER:
+ case DO_QUERY:
+ case DO_QUERY_EID:
+ case DO_LIST:
+ case DO_DEREGISTER:
+ case DO_DD_REGISTER:
+ case DO_DD_DEREGISTER:
+ case DO_ENROLL:
+ case DO_EDIT_POLICY:
+ case DO_DELETE_POLICY:
+ if (opt_action)
+ usage(1, "You cannot specify more than one mode\n");
+ opt_action = c;
+ break;
+
+ default:
+ usage(1, "Unknown option");
+ }
+ }
+
+ isns_read_config(opt_configfile);
+
+ if (!isns_config.ic_source_name)
+ usage(1, "Please specify an iSNS source name");
+ if (!isns_config.ic_server_name)
+ usage(1, "Please specify an iSNS server name");
+ if (!opt_action)
+ usage(1, "Please specify an operating mode");
+
+ if (opt_control) {
+ if (!isns_config.ic_security)
+ isns_fatal("Cannot use control mode, security disabled\n");
+ security = isns_control_security_context(0);
+ if (!security)
+ isns_fatal("Unable to create control security context\n");
+
+ /* Create a networked client, using isns.control as
+ * the source name */
+ clnt = isns_create_client(security, isns_config.ic_control_name);
+ } else if (opt_local) {
+ /* Create a local client, using isns.control as
+ * the source name */
+ clnt = isns_create_local_client(security,
+ isns_config.ic_control_name);
+ } else {
+ /* Create a networked client, using the configured
+ * source name */
+ clnt = isns_create_default_client(security);
+ }
+
+ if (clnt == NULL)
+ return 1;
+
+ /* We're an interactive app, and don't want to retry
+ * forever if the server refuses us. */
+ isns_socket_set_disconnect_fatal(clnt->ic_socket);
+
+ /* Get the IP address we use to talk to the iSNS server */
+ if (opt_myaddr.ss_family == AF_UNSPEC && !opt_local) {
+ if (!isns_socket_get_local_addr(clnt->ic_socket, &opt_myaddr))
+ isns_fatal("Unable to obtain my IP address\n");
+ isns_addr_set_port((struct sockaddr *) &opt_myaddr, 860);
+ }
+
+ argv += optind; argc -= optind;
+ switch (opt_action) {
+ case DO_REGISTER:
+ status = register_objects(clnt, argc, argv);
+ break;
+
+ case DO_QUERY:
+ status = query_objects(clnt, argc, argv);
+ break;
+
+ case DO_QUERY_EID:
+ status = query_entity_id(clnt, argc, argv);
+ break;
+
+ case DO_LIST:
+ status = list_objects(clnt, argc, argv);
+ break;
+
+ case DO_DEREGISTER:
+ status = deregister_objects(clnt, argc, argv);
+ break;
+
+ case DO_DD_REGISTER:
+ status = register_domain(clnt, argc, argv);
+ break;
+
+ case DO_DD_DEREGISTER:
+ status = deregister_domain(clnt, argc, argv);
+ break;
+
+
+ case DO_ENROLL:
+ status = enroll_client(clnt, argc, argv);
+ break;
+
+ case DO_EDIT_POLICY:
+ status = edit_policy(clnt, argc, argv);
+ break;
+
+ // case DO_DELETE_POLICY:
+
+ default:
+ isns_fatal("Not yet implemented\n");
+ status = 1; /* compiler food */
+ }
+
+ return status != ISNS_SUCCESS;
+}
+
+void
+usage(int exval, const char *msg)
+{
+ if (msg)
+ fprintf(stderr, "Error: %s\n", msg);
+ fprintf(stderr,
+ "Usage: isnsadm [options] --action ...\n"
+ " --config Specify alternative config fille\n"
+ " --debug Enable debugging (list of debug flags)\n"
+ " --keyfile Where to store newly generated private key\n"
+ " --local Use local Unix socket to talk to isnsd\n"
+ " --control Assume control node identity for authentication\n"
+ " --replace Use replace mode (--register only)\n"
+ "\nThe following actions are supported:\n"
+ " --register Register one or more objects\n"
+ " --deregister Deregister an object (and children)\n"
+ " --query Query iSNS server for objects\n"
+ " --list List all objects of a given type\n"
+ " --enroll Create a new policy object for a client\n"
+ " --edit-policy Edit a policy object\n"
+ " --delete-policy Edit a policy object\n"
+ " --help Display this message\n"
+ "\nUse \"--query help\" to get help on e.g. the query action\n"
+ );
+ exit(exval);
+}
+
+int
+parse_registration(char **argv, int argc, isns_object_list_t *objs, isns_object_t *key_obj)
+{
+ struct sockaddr_storage def_addr;
+ isns_object_t *entity = NULL, *last_portal = NULL, *last_node = NULL;
+ const char *def_port = NULL;
+ int i;
+
+ if (argc == 1 && !strcmp(argv[0], "help")) {
+ printf("Object registration:\n"
+ " isnsadm [-key attr=value] --register type,attr=value,... type,attr=value,...\n"
+ "Where type can be one of:\n"
+ " entity create/update network entity\n"
+ " initiator create iSCSI initiator storage node\n"
+ " target create iSCSI target storage node\n"
+ " control create control node\n"
+ " portal create portal\n"
+ " pg create portal group\n"
+ "\nThe following attributes are recognized:\n");
+
+ isns_attr_list_parser_help(NULL);
+ exit(0);
+ }
+
+ if (argc == 0)
+ usage(1, "Missing object list\n");
+
+ if (key_obj) {
+ //isns_object_list_append(objs, key_obj);
+ if (isns_object_is_entity(key_obj))
+ entity = key_obj;
+ }
+
+ def_addr = opt_myaddr;
+
+ for (i = 0; i < argc; ++i) {
+ isns_attr_list_t attrlist = ISNS_ATTR_LIST_INIT;
+ struct isns_attr_list_parser state;
+ isns_object_t *obj;
+ char *type, *name, *value, *next_attr;
+ char *attrs[128];
+ unsigned int nattrs = 0;
+
+ name = argv[i];
+
+ if ((next_attr = strchr(name, ',')) != NULL)
+ *next_attr++ = '\0';
+
+ while (next_attr && *next_attr) {
+ if (nattrs > 128)
+ isns_fatal("Too many attributes\n");
+
+ /* Show mercy with fat fingered
+ * people,,,,who,cannot,,,type,properly */
+ if (next_attr[0] != ',')
+ attrs[nattrs++] = next_attr;
+ if ((next_attr = strchr(next_attr, ',')) != NULL)
+ *next_attr++ = '\0';
+ }
+
+ if ((value = strchr(name, '=')) != NULL)
+ *value++ = '\0';
+
+ type = name;
+ if (!strcmp(name, "entity")) {
+ if (entity == NULL) {
+ isns_error("Cannot create entity object "
+ "within this key object\n");
+ return 0;
+ }
+
+ if (value != NULL)
+ isns_object_set_string(entity,
+ ISNS_TAG_ENTITY_IDENTIFIER,
+ value);
+ obj = isns_object_get(entity);
+ goto handle_attributes;
+ } else
+ if (!strcmp(name, "node")
+ || !strcmp(name, "initiator")) {
+ const char *node_name;
+
+ node_name = isns_config.ic_source_name;
+ if (value)
+ node_name = value;
+
+ obj = isns_create_storage_node(node_name,
+ ISNS_ISCSI_INITIATOR_MASK,
+ entity);
+ last_node = obj;
+
+ isns_addr_set_port((struct sockaddr *) &def_addr,
+ ISNS_DEFAULT_PORT_INITIATOR);
+ def_port = "iscsi";
+ } else
+ if (!strcmp(name, "target")) {
+ const char *node_name;
+
+ node_name = isns_config.ic_source_name;
+ if (value)
+ node_name = value;
+ obj = isns_create_storage_node(node_name,
+ ISNS_ISCSI_TARGET_MASK,
+ entity);
+ last_node = obj;
+
+ isns_addr_set_port((struct sockaddr *) &def_addr,
+ ISNS_DEFAULT_PORT_TARGET);
+ def_port = "iscsi-target";
+ } else
+ if (!strcmp(name, "control")) {
+ const char *node_name;
+
+ node_name = isns_config.ic_control_name;
+ if (value)
+ node_name = value;
+ obj = isns_create_storage_node(node_name,
+ ISNS_ISCSI_CONTROL_MASK,
+ entity);
+ last_node = obj;
+
+ def_port = NULL;
+ } else
+ if (!strcmp(name, "portal")) {
+ isns_portal_info_t portal_info;
+
+ if (value == NULL) {
+ if (def_port == NULL)
+ isns_fatal("portal must follow initiator or target\n");
+ isns_portal_init(&portal_info,
+ (struct sockaddr *) &def_addr,
+ IPPROTO_TCP);
+ } else
+ if (!isns_portal_parse(&portal_info, value, def_port))
+ isns_fatal("Unable to parse portal=%s\n", value);
+ obj = isns_create_portal(&portal_info, entity);
+ last_portal = obj;
+ } else
+ if (!strcmp(name, "pg")) {
+ if (value)
+ isns_fatal("Unexpected value for portal group\n");
+ if (!last_portal || !last_node)
+ isns_fatal("Portal group registration must follow portal and node\n");
+ obj = isns_create_portal_group(last_portal, last_node, 10);
+ } else {
+ isns_error("Unknown object type \"%s\"\n", name);
+ return 0;
+ }
+
+ if (obj == NULL) {
+ isns_error("Failure to create %s object\n", name);
+ return 0;
+ }
+ isns_object_list_append(objs, obj);
+
+handle_attributes:
+ isns_attr_list_parser_init(&state, obj->ie_template);
+ state.default_port = def_port;
+
+ if (!isns_parse_attrs(nattrs, attrs, &attrlist, &state)
+ || !isns_object_set_attrlist(obj, &attrlist)) {
+ isns_error("Failure to set all %s attributes\n", name);
+ isns_attr_list_destroy(&attrlist);
+ return 0;
+ }
+
+ isns_attr_list_destroy(&attrlist);
+ isns_object_release(obj);
+ }
+
+ return 1;
+}
+
+static int
+__register_objects(isns_client_t *clnt,
+ isns_object_t *key_obj,
+ const isns_object_list_t *objects)
+{
+ isns_source_t *source = NULL;
+ isns_simple_t *reg;
+ uint32_t status;
+ unsigned int i;
+
+ for (i = 0; i < objects->iol_count && !source; ++i) {
+ isns_object_t *obj = objects->iol_data[i];
+
+ if (!isns_object_is_iscsi_node(obj))
+ continue;
+ source = isns_source_from_object(obj);
+ }
+
+ reg = isns_create_registration2(clnt, key_obj, source);
+ isns_registration_set_replace(reg, opt_replace);
+
+ /* Add all objects to be registered */
+ for (i = 0; i < objects->iol_count; ++i)
+ isns_registration_add_object(reg, objects->iol_data[i]);
+
+ status = isns_client_call(clnt, &reg);
+ isns_simple_free(reg);
+
+ if (status == ISNS_SUCCESS)
+ printf("Successfully registered object(s)\n");
+ else
+ isns_error("Failed to register object(s): %s\n",
+ isns_strerror(status));
+
+ if (source)
+ isns_source_release(source);
+ return status;
+}
+
+int
+register_objects(isns_client_t *clnt,
+ int argc, char **argv)
+{
+ isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
+ isns_object_t *key_obj = NULL;
+ uint32_t status;
+
+ if (opt_key != NULL) {
+ isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
+ struct isns_attr_list_parser state;
+
+ isns_attr_list_parser_init(&state, NULL);
+
+ if (!isns_parse_attrs(1, &opt_key, &key_attrs, &state)) {
+ isns_error("Cannot parse registration key \"%s\"\n",
+ opt_key);
+ return 0;
+ }
+
+ key_obj = isns_create_object(isns_attr_list_parser_context(&state),
+ &key_attrs, NULL);
+ isns_attr_list_destroy(&key_attrs);
+
+ if (!key_obj) {
+ isns_error("Cannot create registration key object\n");
+ return 0;
+ }
+ } else {
+ /* If the user does not provide a key object,
+ * create/update an entity.
+ */
+ key_obj = isns_create_entity(ISNS_ENTITY_PROTOCOL_ISCSI, NULL);
+ }
+
+ if (!parse_registration(argv, argc, &objects, key_obj))
+ isns_fatal("Unable to parse registration\n");
+
+ status = __register_objects(clnt, key_obj, &objects);
+ isns_object_list_destroy(&objects);
+
+ isns_object_release(key_obj);
+ return status;
+}
+
+/*
+ * Parse the query string given by the user
+ *
+ * 5.6.5.2
+ * The Message Key may contain key or non-key attributes or no
+ * attributes at all. If multiple attributes are used as the
+ * Message Key, then they MUST all be from the same object type
+ * (e.g., IP address and TCP/UDP Port are attributes of the
+ * Portal object type).
+ */
+int
+parse_query(char **argv, int argc, isns_attr_list_t *keys, isns_attr_list_t *query)
+{
+ struct isns_attr_list_parser state;
+
+ isns_attr_list_parser_init(&state, NULL);
+ state.nil_permitted = 1;
+
+ if (argc == 1 && !strcmp(argv[0], "help")) {
+ printf("Object query:\n"
+ " isnsadm --query attr=value attr=value ... ?query-attr ?query-attr ...\n"
+ "All key attributes must refer to a common object type.\n"
+ "Query attributes specify the attributes the server should return,"
+ "and can refer to any object type.\n"
+ "The following attributes are recognized:\n");
+ isns_attr_list_parser_help(&state);
+ exit(0);
+ }
+
+ if (argc == 0)
+ isns_fatal("Missing query attributes\n");
+
+ return isns_parse_query_attrs(argc, argv, keys, query, &state);
+}
+
+int
+query_objects(isns_client_t *clnt, int argc, char **argv)
+{
+ isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT;
+ isns_attr_list_t oper_attrs = ISNS_ATTR_LIST_INIT;
+ isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
+ uint32_t status;
+ isns_simple_t *qry;
+ unsigned int i;
+
+ if (!parse_query(argv, argc, &query_key, &oper_attrs))
+ isns_fatal("Unable to parse query\n");
+
+ qry = isns_create_query(clnt, &query_key);
+ isns_attr_list_destroy(&query_key);
+
+ /* Add the list of attributes we request */
+ for (i = 0; i < oper_attrs.ial_count; ++i)
+ isns_query_request_attr(qry, oper_attrs.ial_data[i]);
+ isns_attr_list_destroy(&oper_attrs);
+
+ status = isns_client_call(clnt, &qry);
+ if (status != ISNS_SUCCESS) {
+ isns_error("Query failed: %s\n", isns_strerror(status));
+ return status;
+ }
+
+ status = isns_query_response_get_objects(qry, &objects);
+ if (status) {
+ isns_error("Unable to extract object list from query response: %s\n",
+ isns_strerror(status), status);
+ return status;
+ }
+
+ isns_object_list_print(&objects, isns_print_stdout);
+ isns_object_list_destroy(&objects);
+ isns_simple_free(qry);
+
+ return status;
+}
+
+int
+query_entity_id(isns_client_t *clnt, int argc, char **argv)
+{
+ isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT;
+ isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
+ uint32_t status;
+ isns_simple_t *qry;
+ const char *eid;
+
+ if (argc == 1 && !strcmp(argv[0], "help")) {
+ printf("Query iSNS for own entity ID.\n"
+ "No arguments allowed\n");
+ exit(0);
+ }
+ if (argc != 0)
+ isns_fatal("EID query - no arguments accepted\n");
+
+ isns_attr_list_append_string(&query_key,
+ ISNS_TAG_ISCSI_NAME,
+ isns_config.ic_source_name);
+ qry = isns_create_query(clnt, &query_key);
+ isns_attr_list_destroy(&query_key);
+
+ isns_query_request_attr_tag(qry, ISNS_TAG_ENTITY_IDENTIFIER);
+
+ status = isns_client_call(clnt, &qry);
+ if (status != ISNS_SUCCESS) {
+ isns_error("Query failed: %s\n", isns_strerror(status));
+ return status;
+ }
+
+ status = isns_query_response_get_objects(qry, &objects);
+ if (status) {
+ isns_error("Unable to extract object list from query response: %s\n",
+ isns_strerror(status), status);
+ return status;
+ }
+
+ status = ISNS_NO_SUCH_ENTRY;
+ if (objects.iol_count == 0) {
+ isns_error("Node %s not registered with iSNS\n",
+ isns_config.ic_source_name);
+ } else
+ if (!isns_object_get_string(objects.iol_data[0],
+ ISNS_TAG_ENTITY_IDENTIFIER, &eid)) {
+ isns_error("Query for %s returned an object without EID\n",
+ isns_config.ic_source_name);
+ } else {
+ printf("%s\n", eid);
+ status = ISNS_SUCCESS;
+ }
+
+ isns_object_list_destroy(&objects);
+ isns_simple_free(qry);
+
+ return status;
+}
+
+/*
+ * Parse the list query string given by the user
+ */
+int
+parse_list(int argc, char **argv, isns_object_template_t **type_p, isns_attr_list_t *keys)
+{
+ struct isns_attr_list_parser state;
+ isns_object_template_t *query_type = NULL;
+ char *type_name;
+
+ if (argc == 0)
+ usage(1, "Missing object type");
+
+ if (argc == 1 && !strcmp(argv[0], "help")) {
+ printf("Object query:\n"
+ " isnsadm --list type attr=value attr=value ...\n"
+ "Possible value for <type>:\n"
+ " entities - list all network entites\n"
+ " nodes - list all storage nodes\n"
+ " portals - list all portals\n"
+ " portal-groups - list all portal groups\n"
+ " dds - list all discovery domains\n"
+ " ddsets - list all discovery domains sets\n"
+ " policies - list all policies (privileged)\n"
+ "Additional attributes can be specified to scope the\n"
+ "search. They must match the specified object type.\n"
+ "\nThe following attributes are recognized:\n");
+ isns_attr_list_parser_help(NULL);
+ exit(0);
+ }
+
+ type_name = *argv++; --argc;
+ if (!strcasecmp(type_name, "entities"))
+ query_type = &isns_entity_template;
+ else
+ if (!strcasecmp(type_name, "nodes"))
+ query_type = &isns_iscsi_node_template;
+ else
+ if (!strcasecmp(type_name, "portals"))
+ query_type = &isns_portal_template;
+ else
+ if (!strcasecmp(type_name, "portal-groups"))
+ query_type = &isns_iscsi_pg_template;
+ else
+ if (!strcasecmp(type_name, "dds"))
+ query_type = &isns_dd_template;
+ else
+ if (!strcasecmp(type_name, "ddsets"))
+ query_type = &isns_ddset_template;
+ else
+ if (!strcasecmp(type_name, "policies"))
+ query_type = &isns_policy_template;
+ else {
+ isns_error("Unknown object type \"%s\"\n",
+ type_name);
+ return 0;
+ }
+
+ *type_p = query_type;
+
+ isns_attr_list_parser_init(&state, query_type);
+ state.nil_permitted = 1;
+
+ return isns_parse_attrs(argc, argv, keys, &state);
+}
+
+int
+list_objects(isns_client_t *clnt, int argc, char **argv)
+{
+ isns_attr_list_t query_keys = ISNS_ATTR_LIST_INIT;
+ isns_object_template_t *query_type = NULL;
+ isns_simple_t *simp;
+ int status, count = 0;
+
+ if (!parse_list(argc, argv, &query_type, &query_keys))
+ isns_fatal("Unable to parse parameters\n");
+
+ simp = isns_create_getnext(clnt, query_type, &query_keys);
+ while (1) {
+ isns_object_t *obj = NULL;
+ isns_simple_t *followup;
+
+ status = isns_client_call(clnt, &simp);
+ if (status)
+ break;
+
+ status = isns_getnext_response_get_object(simp, &obj);
+ if (status)
+ break;
+
+ printf("Object %u:\n", count++);
+ isns_object_print(obj, isns_print_stdout);
+ isns_object_release(obj);
+
+ followup = isns_create_getnext_followup(clnt,
+ simp, &query_keys);
+ isns_simple_free(simp);
+ simp = followup;
+ }
+
+ if (status == ISNS_SOURCE_UNAUTHORIZED
+ && query_type == &isns_policy_template
+ && !opt_local)
+ isns_warning("Please use --local trying to list policies\n");
+
+ if (status != ISNS_NO_SUCH_ENTRY) {
+ isns_error("GetNext call failed: %s\n",
+ isns_strerror(status));
+ return status;
+ }
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Parse the deregistration string given by the user
+ *
+ * 5.6.5.2
+ * The Message Key may contain key or non-key attributes or no
+ * attributes at all. If multiple attributes are used as the
+ * Message Key, then they MUST all be from the same object type
+ * (e.g., IP address and TCP/UDP Port are attributes of the
+ * Portal object type).
+ */
+int
+parse_deregistration(char **argv, int argc, isns_attr_list_t *keys)
+{
+ struct isns_attr_list_parser state;
+
+ isns_attr_list_parser_init(&state, NULL);
+ state.multi_type_permitted = 1;
+ state.nil_permitted = 1;
+
+ if (argc == 1 && !strcmp(argv[0], "help")) {
+ printf("Object deregistration:\n"
+ " isnsadm --deregister attr=value attr=value ...\n"
+ "All attributes must refer to a common object type.\n"
+ "\nThe following attributes are recognized:\n");
+ isns_attr_list_parser_help(&state);
+ exit(0);
+ }
+
+ return isns_parse_attrs(argc, argv, keys, &state);
+}
+
+int
+deregister_objects(isns_client_t *clnt, int argc, char **argv)
+{
+ isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT;
+ isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
+ isns_simple_t *dereg;
+ uint32_t status;
+
+ if (!parse_deregistration(argv, argc, &query_key))
+ isns_fatal("Unable to parse unregistration\n");
+
+ dereg = isns_create_deregistration(clnt, &query_key);
+ isns_attr_list_destroy(&query_key);
+
+ status = isns_client_call(clnt, &dereg);
+ if (status != ISNS_SUCCESS) {
+ isns_error("Deregistration failed: %s\n",
+ isns_strerror(status));
+ return status;
+ }
+
+#if 0
+ status = isns_dereg_msg_response_get_objects(dereg, &objects);
+ if (status) {
+ isns_error("Unable to extract object list from deregistration response: %s\n",
+ isns_strerror(status), status);
+ goto done;
+ }
+ isns_object_list_print(&objects, isns_print_stdout);
+#endif
+
+ isns_object_list_destroy(&objects);
+ isns_simple_free(dereg);
+
+ return status;
+}
+
+/*
+ * Handle discovery domain registration/deregistration
+ */
+int
+parse_dd_registration(char **argv, int argc, isns_attr_list_t *keys)
+{
+ struct isns_attr_list_parser state;
+
+ isns_attr_list_parser_init(&state, &isns_dd_template);
+ if (argc == 1 && !strcmp(argv[0], "help")) {
+ printf("Object query:\n"
+ " isnsadm --dd-register attr=value attr=value ...\n"
+ "You cannot specify more than one domain.\n"
+ "If you want to modify an existing domain, you must specify its ID.\n"
+ "The following attributes are recognized:\n");
+ isns_attr_list_parser_help(&state);
+ exit(0);
+ }
+
+ return isns_parse_attrs(argc, argv, keys, &state);
+}
+
+int
+register_domain(isns_client_t *clnt, int argc, char **argv)
+{
+ isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
+ isns_simple_t *msg;
+ uint32_t status;
+
+ if (!parse_dd_registration(argv, argc, &attrs))
+ isns_fatal("Unable to parse DD registration\n");
+
+ msg = isns_create_dd_registration(clnt, &attrs);
+ isns_attr_list_destroy(&attrs);
+
+ if (msg == NULL) {
+ isns_error("Cannot create message\n");
+ return ISNS_INTERNAL_ERROR;
+ }
+
+ status = isns_client_call(clnt, &msg);
+ if (status != ISNS_SUCCESS) {
+ isns_error("Registration failed: %s\n",
+ isns_strerror(status));
+ return status;
+ }
+
+ if (status == ISNS_SUCCESS) {
+ printf("Registered DD:\n");
+ isns_attr_list_print(
+ isns_simple_get_attrs(msg),
+ isns_print_stdout);
+ }
+ isns_simple_free(msg);
+
+ return status;
+}
+
+int
+parse_dd_deregistration(char **argv, int argc,
+ uint32_t *dd_id, isns_attr_list_t *keys)
+{
+ struct isns_attr_list_parser state;
+
+ isns_attr_list_parser_init(&state, &isns_dd_template);
+ if (argc == 0 || (argc == 1 && !strcmp(argv[0], "help"))) {
+ printf("DD deregistration:\n"
+ " isnsadm --dd-deregister dd-id attr=value attr=value ...\n"
+ "You cannot specify more than one domain.\n"
+ "The following attributes are recognized:\n");
+ isns_attr_list_parser_help(&state);
+ exit(0);
+ }
+
+ *dd_id = parse_count(argv[0]);
+
+ return isns_parse_attrs(argc - 1, argv + 1, keys, &state);
+}
+
+int
+deregister_domain(isns_client_t *clnt, int argc, char **argv)
+{
+ isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
+ isns_simple_t *msg;
+ uint32_t dd_id, status;
+
+ if (!parse_dd_deregistration(argv, argc, &dd_id, &attrs))
+ isns_fatal("Unable to parse DD registration\n");
+
+ msg = isns_create_dd_deregistration(clnt, dd_id, &attrs);
+ isns_attr_list_destroy(&attrs);
+
+ if (msg == NULL) {
+ isns_error("Cannot create message\n");
+ return ISNS_INTERNAL_ERROR;
+ }
+
+ status = isns_client_call(clnt, &msg);
+ if (status != ISNS_SUCCESS) {
+ isns_error("Deregistration failed: %s\n",
+ isns_strerror(status));
+ return status;
+ }
+
+ isns_simple_free(msg);
+ return status;
+}
+
+/*
+ * Parse a policy
+ */
+int
+parse_policy(int argc, char **argv, isns_attr_list_t *attrs,
+ const char *help_title, const char *help_action)
+{
+ struct isns_attr_list_parser state;
+
+ isns_attr_list_parser_init(&state, &isns_policy_template);
+ state.nil_permitted = 0;
+ state.load_key = load_key_callback;
+ state.generate_key = generate_key_callback;
+
+ if (argc == 1 && !strcmp(argv[0], "help")) {
+ printf("%s:\n"
+ " isnsadm %s attr=value attr=value ...\n"
+ "Specifying a Security Policy Index is mandatory.\n"
+ "\nThe following attributes are recognized:\n",
+ help_title, help_action);
+ isns_attr_list_parser_help(&state);
+ exit(0);
+ }
+
+ return isns_parse_attrs(argc, argv, attrs, &state);
+}
+
+static int
+__create_policy(isns_client_t *clnt, const isns_attr_list_t *attrs)
+{
+ isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
+ isns_object_t *obj;
+ int status;
+
+ obj = isns_create_object(&isns_policy_template, attrs, NULL);
+ if (!obj)
+ isns_fatal("Cannot create policy object\n");
+ isns_object_list_append(&objects, obj);
+
+ status = __register_objects(clnt, NULL, &objects);
+ isns_object_list_destroy(&objects);
+ return status;
+}
+
+/*
+ * Enroll a new client
+ */
+int
+enroll_client(isns_client_t *clnt, int argc, char **argv)
+{
+ isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
+ const char *client_name;
+ int status;
+
+ if (argc == 0)
+ usage(1, "Missing client name");
+
+ client_name = *argv++; --argc;
+
+ isns_attr_list_append_string(&attrs,
+ OPENISNS_TAG_POLICY_SPI,
+ client_name);
+#if 0
+ isns_attr_list_append_string(&attrs,
+ OPENISNS_TAG_POLICY_SOURCE_NAME,
+ client_name);
+#endif
+
+ if (!opt_keyfile) {
+ static char namebuf[PATH_MAX];
+
+ snprintf(namebuf, sizeof(namebuf), "%s.key", client_name);
+ opt_keyfile = namebuf;
+ }
+
+ if (argc && !parse_policy(argc, argv, &attrs,
+ "Enroll an iSNS client",
+ "--enroll hostname"))
+ isns_fatal("Cannot parse policy\n");
+
+ /* If no key is given, generate one */
+ if (!isns_attr_list_contains(&attrs, OPENISNS_TAG_POLICY_KEY)) {
+ printf("No key given, generating one\n");
+ isns_attr_list_append_attr(&attrs,
+ generate_key_callback());
+ }
+
+ status = __create_policy(clnt, &attrs);
+ isns_attr_list_destroy(&attrs);
+ return status;
+}
+
+
+/*
+ * Create a new policy
+ */
+int
+edit_policy(isns_client_t *clnt, int argc, char **argv)
+{
+ isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
+ int status;
+
+ if (!parse_policy(argc, argv, &attrs,
+ "Edit an existing policy",
+ "--edit-policy"))
+ isns_fatal("Cannot parse policy\n");
+
+ status = __create_policy(clnt, &attrs);
+ isns_attr_list_destroy(&attrs);
+
+ return status;
+}
+
+#ifdef WITH_SECURITY
+static isns_attr_t *
+__key_to_attr(EVP_PKEY *pkey)
+{
+ struct __isns_opaque key;
+ isns_value_t value;
+ isns_attr_t *attr = NULL;
+
+ if (!isns_dsa_encode_public(pkey, &key.ptr, &key.len))
+ goto out;
+
+ /* Must pad key. This means we may end up encoding a few
+ * bytes of trash. Oh well. */
+ key.len = ISNS_PAD(key.len);
+
+ value = ISNS_VALUE_INIT(opaque, key);
+ attr = isns_attr_alloc(OPENISNS_TAG_POLICY_KEY, NULL, &value);
+
+ isns_free(key.ptr);
+
+out:
+ EVP_PKEY_free(pkey);
+ return attr;
+}
+
+isns_attr_t *
+generate_key_callback(void)
+{
+ EVP_PKEY *pkey;
+
+ if (opt_keyfile == NULL)
+ isns_fatal("Key generation requires --keyfile option\n");
+
+ if (!(pkey = isns_dsa_generate_key()))
+ isns_fatal("Key generation failed\n");
+
+ if (!isns_dsa_store_private(opt_keyfile, pkey))
+ isns_fatal("Unable to write private key to %s\n",
+ opt_keyfile);
+
+ printf("Stored DSA private key in %s\n", opt_keyfile);
+ return __key_to_attr(pkey);
+}
+
+isns_attr_t *
+load_key_callback(const char *pathname)
+{
+ EVP_PKEY *pkey;
+
+ if (!(pkey = isns_dsa_load_public(pathname)))
+ isns_fatal("Unable to load public key from file %s\n", pathname);
+
+ return __key_to_attr(pkey);
+}
+
+#else /* WITH_SECURITY */
+isns_attr_t *
+generate_key_callback(void)
+{
+ isns_fatal("Authentication disabled in this build\n");
+ return NULL;
+}
+
+isns_attr_t *
+load_key_callback(const char *pathname)
+{
+ isns_fatal("Authentication disabled in this build\n");
+ return NULL;
+}
+
+#endif
diff --git a/utils/open-isns/isnsd.c b/utils/open-isns/isnsd.c
new file mode 100644
index 0000000..3f983d6
--- /dev/null
+++ b/utils/open-isns/isnsd.c
@@ -0,0 +1,299 @@
+/*
+ * isnsd - the iSNS Daemon
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#ifdef MTRACE
+# include <mcheck.h>
+#endif
+
+#include <isns.h>
+#include "security.h"
+#include "util.h"
+#include "paths.h"
+#include "internal.h"
+
+enum {
+ MODE_NORMAL,
+ MODE_DUMP_DB,
+ MODE_INIT,
+};
+
+static const char * opt_configfile = ISNS_DEFAULT_ISNSD_CONFIG;
+static int opt_af = AF_UNSPEC;
+static int opt_mode = MODE_NORMAL;
+static int opt_foreground = 0;
+
+static char * slp_url;
+
+static int init_server(void);
+static void run_server(isns_server_t *, isns_db_t *);
+static void usage(int, const char *);
+static void cleanup(int);
+
+static struct option options[] = {
+ { "config", required_argument, NULL, 'c' },
+ { "debug", required_argument, NULL, 'd' },
+ { "foreground", no_argument, NULL, 'f' },
+ { "init", no_argument, NULL, MODE_INIT },
+ { "dump-db", no_argument, NULL, MODE_DUMP_DB },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+};
+
+int
+main(int argc, char **argv)
+{
+ isns_server_t *server;
+ isns_source_t *source;
+ isns_db_t *db;
+ int c;
+
+#ifdef MTRACE
+ mtrace();
+#endif
+
+ while ((c = getopt_long(argc, argv, "46c:d:fh", options, NULL)) != -1) {
+ switch (c) {
+ case '4':
+ opt_af = AF_INET;
+ break;
+
+ case '6':
+ opt_af = AF_INET6;
+ break;
+
+ case 'c':
+ opt_configfile = optarg;
+ break;
+
+ case 'd':
+ isns_enable_debugging(optarg);
+ break;
+
+ case 'f':
+ opt_foreground = 1;
+ break;
+
+ case MODE_DUMP_DB:
+ case MODE_INIT:
+ opt_mode = c;
+ break;
+
+ case 'h':
+ usage(0, NULL);
+
+ case 'V':
+ printf("Open-iSNS version %s\n"
+ "Copyright (C) 2007, Olaf Kirch <olaf.kirch@oracle.com>\n",
+ OPENISNS_VERSION_STRING);
+ return 0;
+
+ default:
+ usage(1, "Unknown option");
+ }
+ }
+
+ if (optind != argc)
+ usage(1, NULL);
+
+ isns_read_config(opt_configfile);
+
+ if (!isns_config.ic_source_name)
+ usage(1, "Please specify an iSNS source name");
+ source = isns_source_create_iscsi(isns_config.ic_source_name);
+
+ if (opt_mode == MODE_INIT)
+ return !init_server();
+
+ if (opt_mode == MODE_NORMAL)
+ isns_write_pidfile(isns_config.ic_pidfile);
+
+ db = isns_db_open(isns_config.ic_database);
+ if (db == NULL)
+ isns_fatal("Unable to open database\n");
+
+ if (opt_mode == MODE_DUMP_DB) {
+ isns_db_print(db, isns_print_stdout);
+ exit(0);
+ }
+
+ if (!opt_foreground) {
+ if (daemon(0, 0) < 0)
+ isns_fatal("Unable to background server process\n");
+ isns_log_background();
+ isns_update_pidfile(isns_config.ic_pidfile);
+ }
+
+ signal(SIGTERM, cleanup);
+ signal(SIGINT, cleanup);
+
+ server = isns_create_server(source, db, &isns_default_service_ops);
+
+ run_server(server, db);
+ return 0;
+}
+
+void
+usage(int exval, const char *msg)
+{
+ if (msg)
+ fprintf(stderr, "Error: %s\n", msg);
+ fprintf(stderr,
+ "Usage: isnsd [options]\n\n"
+ " --config Specify alternative config fille\n"
+ " --foreground Do not put daemon in the background\n"
+ " --debug Enable debugging (list of debug flags)\n"
+ " --init Initialize the server (key generation etc)\n"
+ " --dump-db Display the database contents and exit\n"
+ " --help Print this message\n"
+ );
+ exit(exval);
+}
+
+void
+cleanup(int sig)
+{
+ isns_remove_pidfile(isns_config.ic_pidfile);
+ exit(1);
+}
+
+static void
+slp_cleanup(void)
+{
+ char *url = slp_url;
+
+ slp_url = NULL;
+ if (url) {
+ isns_slp_unregister(url);
+ isns_free(url);
+ }
+}
+
+/*
+ * Initialize server
+ */
+int
+init_server(void)
+{
+ if (!isns_security_init())
+ return 0;
+
+ /* Anything else? */
+
+ return 1;
+}
+
+/*
+ * Server main loop
+ */
+void
+run_server(isns_server_t *server, isns_db_t *db)
+{
+ isns_socket_t *sock;
+ isns_security_t *ctx = NULL;
+ isns_message_t *msg, *resp;
+ int status;
+
+ if (isns_config.ic_security) {
+ const char *ksname;
+ isns_keystore_t *ks;
+
+ ctx = isns_default_security_context(1);
+ if (!(ksname = isns_config.ic_client_keystore))
+ isns_fatal("config problem: no key store specified\n");
+ if (!strcasecmp(ksname, "db:"))
+ ks = isns_create_db_keystore(db);
+ else
+ ks = isns_create_keystore(ksname);
+ if (ks == NULL)
+ isns_fatal("Unable to create keystore %s\n", ksname);
+ isns_security_set_keystore(ctx, ks);
+ }
+
+ status = isns_dd_load_all(db);
+ if (status != ISNS_SUCCESS)
+ isns_fatal("Problem loading Discovery Domains from database\n");
+
+ if (isns_config.ic_control_socket) {
+ sock = isns_create_server_socket(isns_config.ic_control_socket,
+ NULL, AF_UNSPEC, SOCK_STREAM);
+ if (sock == NULL)
+ isns_fatal("Unable to create control socket\n");
+ /*
+ isns_socket_set_security_ctx(sock, ctx);
+ */
+ }
+
+ sock = isns_create_server_socket(isns_config.ic_bind_address,
+ "isns", opt_af, SOCK_STREAM);
+ if (sock == NULL)
+ isns_fatal("Unable to create server socket\n");
+ isns_socket_set_security_ctx(sock, ctx);
+
+ if (isns_config.ic_slp_register) {
+ slp_url = isns_slp_build_url(0);
+ isns_slp_register(slp_url);
+
+ atexit(slp_cleanup);
+ }
+
+ isns_esi_init(server);
+ isns_scn_init(server);
+
+ while (1) {
+ struct timeval timeout = { 0, 0 };
+ time_t now, then, next_timeout = time(NULL) + 3600;
+
+ /* Expire entities that haven't seen any activity
+ * for a while. */
+ if (isns_config.ic_registration_period) {
+ then = isns_db_expire(db);
+ if (then && then < next_timeout)
+ next_timeout = then;
+ }
+
+ /* Run any timers (eg for ESI) */
+ then = isns_run_timers();
+ if (then && then < next_timeout)
+ next_timeout = then;
+
+ /* There may be pending SCNs, push them out now */
+ then = isns_scn_transmit_all();
+ if (then && then < next_timeout)
+ next_timeout = then;
+
+ /* Purge any objects that have been marked for removal
+ * from the DB (deleting them, or moving them to limbo
+ * state). */
+ isns_db_purge(db);
+
+ /* Determine how long we can sleep before working
+ * the ESI queues and DB expiry again. */
+ now = time(NULL);
+ if (next_timeout <= now)
+ continue;
+ timeout.tv_sec = next_timeout - now;
+
+ if ((msg = isns_recv_message(&timeout)) == NULL)
+ continue;
+
+ if ((resp = isns_process_message(server, msg)) != NULL) {
+ isns_socket_t *sock = isns_message_socket(msg);
+
+ isns_socket_send(sock, resp);
+ isns_message_release(resp);
+ }
+
+ isns_message_release(msg);
+ }
+}
diff --git a/utils/open-isns/isnsdd.c b/utils/open-isns/isnsdd.c
new file mode 100644
index 0000000..e4e212d
--- /dev/null
+++ b/utils/open-isns/isnsdd.c
@@ -0,0 +1,1153 @@
+/*
+ * isnsdd - the iSNS Discovery Daemon
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ *
+ * The way isnsdd communicates with local services (initiator,
+ * target) is via a set of files and signals. That sounds rather
+ * awkward, but it's a lot simpler to add to these services
+ * than another socket based communication mechanism I guess.
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#ifdef MTRACE
+# include <mcheck.h>
+#endif
+
+#include <isns.h>
+#include "security.h"
+#include "util.h"
+#include "isns-proto.h"
+#include "paths.h"
+#include "attrs.h"
+
+enum {
+ ROLE_INITIATOR = 1,
+ ROLE_MONITOR = 2,
+};
+
+#define ISNSDD_REG_NAME "isns"
+#define ISNSDD_PGT_OFFSET 10000
+#define MAX_RETRY_TIMEOUT 300
+
+typedef struct isns_proxy isns_proxy_t;
+struct isns_proxy {
+ isns_list_t ip_list;
+ char * ip_eid;
+ isns_object_t * ip_entity;
+ isns_client_t * ip_client;
+ isns_object_list_t ip_objects;
+ time_t ip_last_registration;
+};
+
+static const char * opt_configfile = ISNS_DEFAULT_ISNSDD_CONFIG;
+static int opt_af = AF_INET6;
+static int opt_foreground = 0;
+static int opt_role = ROLE_INITIATOR;
+static int opt_scn_bits = ISNS_SCN_OBJECT_UPDATED_MASK |
+ ISNS_SCN_OBJECT_ADDED_MASK |
+ ISNS_SCN_OBJECT_REMOVED_MASK |
+ ISNS_SCN_TARGET_AND_SELF_ONLY_MASK;
+static unsigned int opt_retry_timeout = 10;
+static int opt_esi = 1;
+
+static isns_socket_t * server_socket;
+static ISNS_LIST_DECLARE(proxies);
+static isns_object_list_t local_registry = ISNS_OBJECT_LIST_INIT;
+static isns_object_list_t local_portals = ISNS_OBJECT_LIST_INIT;
+static isns_object_list_t visible_nodes = ISNS_OBJECT_LIST_INIT;
+static unsigned int esi_interval;
+static int should_reexport;
+
+static void run_discovery(isns_server_t *srv);
+static void scn_callback(isns_db_t *, uint32_t,
+ isns_object_template_t *,
+ const char *, const char *);
+static void refresh_registration(void *);
+static void retry_registration(void *);
+static void load_exported_objects(void);
+static void store_imported_objects(void);
+static void usage(int, const char *);
+
+static void install_sighandler(int, void (*func)(int));
+static void sig_cleanup(int);
+static void sig_reread(int);
+
+static struct option options[] = {
+ { "config", required_argument, NULL, 'c' },
+ { "debug", required_argument, NULL, 'd' },
+ { "foreground", no_argument, NULL, 'f' },
+ { "role", required_argument, NULL, 'r' },
+ { "no-esi", no_argument, NULL, 'E' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+};
+
+int
+main(int argc, char **argv)
+{
+ isns_server_t *server;
+ isns_source_t *source;
+ isns_db_t *db;
+ int c;
+
+#ifdef MTRACE
+ mtrace();
+#endif
+
+ while ((c = getopt_long(argc, argv, "46c:d:Efhr:", options, NULL)) != -1) {
+ switch (c) {
+ case '4':
+ opt_af = AF_INET;
+ break;
+
+ case '6':
+ opt_af = AF_INET6;
+ break;
+
+ case 'c':
+ opt_configfile = optarg;
+ break;
+
+ case 'd':
+ isns_enable_debugging(optarg);
+ break;
+
+ case 'E':
+ opt_esi = 0;
+ break;
+
+ case 'f':
+ opt_foreground = 1;
+ break;
+
+ case 'h':
+ usage(0, NULL);
+
+ case 'r':
+ if (!strcasecmp(optarg, "initiator"))
+ opt_role = ROLE_INITIATOR;
+ else
+ if (!strcasecmp(optarg, "control")
+ || !strcasecmp(optarg, "monitor"))
+ opt_role = ROLE_MONITOR;
+ else {
+ isns_error("Unknown role \"%s\"\n", optarg);
+ usage(1, NULL);
+ }
+ break;
+
+ case 'V':
+ printf("Open-iSNS version %s\n"
+ "Copyright (C) 2007, Olaf Kirch <olaf.kirch@oracle.com>\n",
+ OPENISNS_VERSION_STRING);
+ return 0;
+
+ default:
+ usage(1, "Unknown option");
+ }
+ }
+
+ if (optind != argc)
+ usage(1, NULL);
+
+ /* If the config code derives the source name
+ * automatically, we want it to be distinct from
+ * any other source name (chosen by eg the iSCSI
+ * initiator). Adding a suffix of ":isns" is a
+ * somewhat lame attempt.
+ */
+ isns_config.ic_source_suffix = "isns";
+
+ isns_read_config(opt_configfile);
+
+ if (!isns_config.ic_source_name)
+ usage(1, "Please specify an iSNS source name");
+ source = isns_source_create_iscsi(isns_config.ic_source_name);
+
+ isns_write_pidfile(isns_config.ic_pidfile);
+
+ if (!opt_foreground) {
+ if (daemon(0, 0) < 0)
+ isns_fatal("Unable to background server process\n");
+ isns_log_background();
+ isns_update_pidfile(isns_config.ic_pidfile);
+ }
+
+ install_sighandler(SIGTERM, sig_cleanup);
+ install_sighandler(SIGINT, sig_cleanup);
+ install_sighandler(SIGUSR2, sig_reread);
+
+ /* Create a DB object that shadows our portal list. This is for ESI -
+ * when an ESI comes in, the library will look up the portal in this
+ * database, and update its mtime. By checking the mtime at regular
+ * intervals, we can verify whether the server's ESIs actually
+ * reach us.
+ */
+ db = isns_db_open_shadow(&local_portals);
+
+ server = isns_create_server(source, db, &isns_callback_service_ops);
+ isns_server_set_scn_callback(server, scn_callback);
+
+ run_discovery(server);
+ return 0;
+}
+
+void
+usage(int exval, const char *msg)
+{
+ if (msg)
+ fprintf(stderr, "Error: %s\n", msg);
+ fprintf(stderr,
+ "Usage: isnsdd [options]\n\n"
+ " --role <role> Specify role (one of initiator, control)\n"
+ " --config Specify alternative config fille\n"
+ " --foreground Do not put daemon in the background\n"
+ " --no-esi Do not try to register an portals for ESI status inquiries\n"
+ " --debug Enable debugging (list of debug flags)\n"
+ " --help Print this message\n"
+ );
+ exit(exval);
+}
+
+void
+install_sighandler(int signo, void (*func)(int))
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = func;
+ sigaction(signo, &act, NULL);
+}
+
+void
+sig_reread(int sig)
+{
+ should_reexport = 1;
+}
+
+void
+sig_cleanup(int sig)
+{
+ isns_remove_pidfile(isns_config.ic_pidfile);
+ exit(1);
+}
+
+/*
+ * Proxy handling functions
+ */
+static isns_proxy_t *
+isns_create_proxy(const char *eid)
+{
+ isns_proxy_t *proxy;
+
+ proxy = calloc(1, sizeof(*proxy));
+ isns_list_init(&proxy->ip_list);
+ proxy->ip_eid = strdup(eid);
+ return proxy;
+}
+
+static isns_proxy_t *
+__isns_proxy_find(isns_list_t *head, const char *eid)
+{
+ isns_list_t *pos, *next;
+
+ isns_list_foreach(head, pos, next) {
+ isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, pos);
+
+ if (!strcmp(proxy->ip_eid, eid))
+ return proxy;
+ }
+ return NULL;
+}
+
+static isns_proxy_t *
+isns_proxy_by_entity(const isns_object_t *entity)
+{
+ isns_list_t *pos, *next;
+
+ isns_list_foreach(&proxies, pos, next) {
+ isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, pos);
+
+ if (proxy->ip_entity == entity)
+ return proxy;
+ }
+ return NULL;
+}
+
+static void
+isns_proxy_erase(isns_proxy_t *proxy)
+{
+ isns_object_list_destroy(&proxy->ip_objects);
+ if (proxy->ip_client) {
+ isns_client_destroy(proxy->ip_client);
+ proxy->ip_client = NULL;
+ }
+ if (proxy->ip_entity) {
+ isns_object_release(proxy->ip_entity);
+ proxy->ip_entity = NULL;
+ }
+ isns_cancel_timer(refresh_registration, proxy);
+}
+
+static void
+isns_proxy_free(isns_proxy_t *proxy)
+{
+ isns_proxy_erase(proxy);
+ isns_list_del(&proxy->ip_list);
+ free(&proxy->ip_eid);
+ free(proxy);
+}
+
+/*
+ * Force a re-registration of the whole object set.
+ */
+static void
+force_reregistration(isns_proxy_t *proxy)
+{
+ isns_cancel_timer(refresh_registration, proxy);
+ isns_add_oneshot_timer(0, retry_registration, proxy);
+}
+
+/*
+ * Refresh the registration by calling DevAttrQry
+ */
+static void
+refresh_registration(void *ptr)
+{
+ isns_proxy_t *proxy = ptr;
+ isns_client_t *clnt = proxy->ip_client;
+ isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
+ isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT;
+ isns_simple_t *qry = NULL;
+ uint32_t status;
+
+ isns_debug_state("Refreshing registration for %s\n", proxy->ip_eid);
+ isns_attr_list_append_string(&query_key,
+ ISNS_TAG_ENTITY_IDENTIFIER,
+ proxy->ip_eid);
+
+ qry = isns_create_query(clnt, &query_key);
+ isns_attr_list_destroy(&query_key);
+
+ /* We should have an async call mechanism. If the server
+ * is wedged, we'll block here, unable to service any other
+ * functions.
+ */
+ status = isns_simple_call(clnt->ic_socket, &qry);
+ if (status != ISNS_SUCCESS) {
+ isns_error("Query failed: %s\n", isns_strerror(status));
+ goto re_register;
+ }
+
+ status = isns_query_response_get_objects(qry, &objects);
+ isns_simple_free(qry);
+
+ if (status == ISNS_SUCCESS) {
+ if (objects.iol_count != 0)
+ return;
+ } else {
+ isns_error("Unable to parse query response\n");
+ }
+
+re_register:
+ isns_warning("Lost registration, trying to re-register\n");
+ force_reregistration(proxy);
+}
+
+/*
+ * Check if all portals have seen ESI messages from the server
+ */
+static void
+check_portal_registration(void *ptr)
+{
+ isns_object_list_t bad_portals = ISNS_OBJECT_LIST_INIT;
+ unsigned int i, need_reregister = 0, good_portals = 0;
+ time_t now;
+
+ isns_debug_state("%s()\n", __FUNCTION__);
+ now = time(NULL);
+ for (i = 0; i < local_portals.iol_count; ++i) {
+ isns_object_t *obj = local_portals.iol_data[i];
+ isns_portal_info_t portal_info;
+ isns_proxy_t *proxy;
+ time_t last_modified;
+ uint32_t interval;
+
+ if (!isns_object_get_uint32(obj, ISNS_TAG_ESI_INTERVAL, &interval))
+ continue;
+
+ last_modified = isns_object_last_modified(obj);
+ if (last_modified + 2 * interval > now) {
+ good_portals++;
+ continue;
+ }
+
+ isns_portal_from_object(&portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ obj);
+
+ isns_notice("Portal %s did not receive ESIs within %u seconds - "
+ "server may have lost us.\n",
+ isns_portal_string(&portal_info),
+ now - last_modified);
+
+ proxy = isns_proxy_by_entity(isns_object_get_entity(obj));
+ if (!proxy)
+ continue;
+
+ /* If we haven't received ANY ESIs, ever, the portal
+ * may be using a non-routable IP */
+ if (last_modified <= proxy->ip_last_registration)
+ isns_object_list_append(&bad_portals, obj);
+
+ force_reregistration(proxy);
+ need_reregister++;
+ }
+
+ for (i = 0; i < bad_portals.iol_count; ++i)
+ isns_object_list_remove(&local_portals, bad_portals.iol_data[i]);
+ isns_object_list_destroy(&bad_portals);
+
+ if (need_reregister && local_portals.iol_count == 0) {
+ /* Force a re-registration from scratch.
+ * This time without ESI.
+ */
+ isns_notice("Suspiciously little ESI traffic - server may be broken\n");
+ isns_notice("Disabling ESI\n");
+ opt_esi = 0;
+ }
+}
+
+static void
+setup_esi_watchdog(void)
+{
+ unsigned int i;
+
+ isns_cancel_timer(check_portal_registration, NULL);
+ esi_interval = 0;
+
+ for (i = 0; i < local_portals.iol_count; ++i) {
+ isns_object_t *obj = local_portals.iol_data[i];
+ uint32_t interval;
+
+ /* should always succeed */
+ if (isns_object_get_uint32(obj, ISNS_TAG_ESI_INTERVAL, &interval))
+ continue;
+
+ if (!esi_interval || interval < esi_interval)
+ esi_interval = interval;
+ }
+
+ if (esi_interval) {
+ isns_debug_state("Setting up timer to check for ESI reachability\n");
+ isns_add_timer(esi_interval * 4 / 5,
+ check_portal_registration,
+ NULL);
+ }
+}
+
+static void
+load_exported_objects(void)
+{
+ isns_debug_state("Reading list of exported objects\n");
+ isns_object_list_destroy(&local_registry);
+ if (!isns_local_registry_load("!" ISNSDD_REG_NAME, 0, &local_registry)) {
+ isns_warning("Unable to obtain locally registered objects\n");
+ return;
+ }
+}
+
+static void
+store_imported_objects(void)
+{
+ if (!isns_local_registry_store(ISNSDD_REG_NAME, 0, &visible_nodes))
+ isns_warning("Unable to store discovered objects\n");
+}
+
+/*
+ * Given the DevAttrReg response, extract the entity ID we
+ * have been assigned.
+ */
+static int
+extract_entity_id(isns_proxy_t *proxy, isns_simple_t *resp)
+{
+ isns_object_list_t resp_objects = ISNS_OBJECT_LIST_INIT;
+ isns_object_t *entity = NULL;
+ int status;
+ unsigned int i;
+
+ status = isns_query_response_get_objects(resp, &resp_objects);
+ if (status) {
+ isns_error("Unable to extract object list from "
+ "registration response: %s\n",
+ isns_strerror(status), status);
+ goto out;
+ }
+
+ for (i = 0; i < resp_objects.iol_count; ++i) {
+ isns_object_t *obj = resp_objects.iol_data[i];
+ uint32_t interval;
+
+ if (!isns_object_is_entity(obj))
+ continue;
+
+ if (entity) {
+ isns_error("Server returns more than one entity "
+ "in registration response. What a weirdo.\n");
+ continue;
+ }
+ entity = obj;
+
+ if (!isns_object_get_uint32(obj,
+ ISNS_TAG_REGISTRATION_PERIOD,
+ &interval))
+ continue;
+
+ if (interval == 0) {
+ isns_error("Server returns a registration period of 0\n");
+ continue;
+ }
+
+ isns_debug_state("Setting up timer for registration refresh\n");
+ isns_add_timer(interval / 2,
+ refresh_registration,
+ proxy);
+ }
+
+ for (i = 0; i < resp_objects.iol_count; ++i) {
+ isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
+ isns_object_t *obj = resp_objects.iol_data[i];
+ uint32_t interval;
+
+ if (!isns_object_is_portal(obj)
+ || !isns_object_get_uint32(obj, ISNS_TAG_ESI_INTERVAL, &interval))
+ continue;
+
+ if (interval == 0) {
+ isns_error("Server returns an ESI interval of 0\n");
+ continue;
+ }
+
+ isns_object_get_key_attrs(obj, &key_attrs);
+ if (!(obj = isns_object_list_lookup(&proxy->ip_objects, NULL, &key_attrs))) {
+ isns_error("Server response includes a portal we never registered\n");
+ continue;
+ }
+
+ isns_object_set_uint32(obj, ISNS_TAG_ESI_INTERVAL, interval);
+
+ /* Server enabled ESI for this portal, so add it to
+ * the list of local portals we regularly check for
+ * incoming ESI messages. */
+ isns_object_list_append(&local_portals, obj);
+ }
+
+ proxy->ip_last_registration = time(NULL);
+out:
+ isns_object_list_destroy(&resp_objects);
+ return status;
+}
+
+static inline void
+__add_release_object(isns_object_list_t *objects, isns_object_t *cur)
+{
+ if (cur == NULL)
+ return;
+ isns_object_list_append(objects, cur);
+ isns_object_release(cur);
+}
+
+/*
+ * Rebuild the list of proxies given the set of entities
+ */
+void
+rebuild_proxy_list(isns_object_list_t *entities, isns_list_t *old_list)
+{
+ isns_proxy_t *proxy;
+ unsigned int i;
+
+ isns_list_move(old_list, &proxies);
+
+ for (i = 0; i < entities->iol_count; ++i) {
+ isns_object_t *entity = entities->iol_data[i];
+ isns_object_t *node;
+ const char *eid;
+
+ eid = isns_entity_name(entity);
+ if (eid == NULL) {
+ isns_error("Whoopee, entity without name\n");
+ continue;
+ }
+
+ proxy = __isns_proxy_find(old_list, eid);
+ if (proxy == NULL) {
+ proxy = isns_create_proxy(eid);
+ } else {
+ isns_proxy_erase(proxy);
+ }
+
+ isns_object_list_append(&proxy->ip_objects, entity);
+ isns_object_get_descendants(entity, NULL, &proxy->ip_objects);
+
+ node = isns_object_list_lookup(&proxy->ip_objects,
+ &isns_iscsi_node_template,
+ NULL);
+ if (node == NULL) {
+ isns_warning("Service %s did not register any "
+ "storage nodes - skipped\n", eid);
+ continue;
+ }
+
+ proxy->ip_client = isns_create_client(NULL,
+ isns_storage_node_name(node));
+ proxy->ip_entity = isns_object_get(entity);
+
+ isns_list_del(&proxy->ip_list);
+ isns_list_append(&proxies, &proxy->ip_list);
+ }
+}
+
+/*
+ * Unregister old proxies
+ */
+static void
+unregister_entities(isns_list_t *list)
+{
+ while (!isns_list_empty(list)) {
+ isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, list->next);
+
+ /* XXX send a DevDereg */
+ isns_proxy_free(proxy);
+ }
+}
+
+/*
+ * The local registry creates fake entities to group objects
+ * registered by the same service. We use this to perform
+ * several registration calls, each with a different EID
+ */
+static int
+register_entity(isns_proxy_t *proxy)
+{
+ isns_client_t *clnt = proxy->ip_client;
+ isns_simple_t *call = NULL;
+ int status;
+
+ call = isns_create_registration(clnt, proxy->ip_entity);
+ isns_registration_set_replace(call, 1);
+ isns_registration_add_object_list(call, &proxy->ip_objects);
+
+ status = isns_simple_call(clnt->ic_socket, &call);
+ if (status == ISNS_SUCCESS) {
+ /* Extract the EID and registration period */
+ extract_entity_id(proxy, call);
+ }
+
+ isns_simple_free(call);
+ return status;
+}
+
+static int
+register_exported_entities(void)
+{
+ int status = ISNS_SUCCESS;
+ isns_list_t *pos, *next;
+
+ isns_list_foreach(&proxies, pos, next) {
+ isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, pos);
+
+ status = register_entity(proxy);
+ if (status != ISNS_SUCCESS)
+ break;
+ }
+
+ setup_esi_watchdog();
+ return status;
+}
+
+static void
+all_objects_set(isns_object_list_t *list, uint32_t tag, uint32_t value)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ isns_object_set_uint32(obj, tag, value);
+ }
+}
+
+static void
+all_objects_unset(isns_object_list_t *list, uint32_t tag)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ isns_object_delete_attr(obj, tag);
+ }
+}
+
+static int
+register_exported_objects(isns_client_t *clnt)
+{
+ isns_portal_info_t portal_info;
+ isns_object_list_t entities = ISNS_OBJECT_LIST_INIT;
+ isns_object_list_t portals = ISNS_OBJECT_LIST_INIT;
+ isns_simple_t *call = NULL;
+ int status, with_esi;
+ unsigned int i, my_port;
+ isns_list_t old_proxies;
+
+ if (!isns_socket_get_portal_info(server_socket, &portal_info))
+ isns_fatal("Unable to get portal info\n");
+ my_port = isns_portal_tcpudp_port(&portal_info);
+
+ /* Look up all entites and portals */
+ isns_object_list_gang_lookup(&local_registry,
+ &isns_entity_template, NULL,
+ &entities);
+ isns_object_list_gang_lookup(&local_registry,
+ &isns_portal_template, NULL,
+ &portals);
+
+ isns_list_init(&old_proxies);
+ rebuild_proxy_list(&entities, &old_proxies);
+ unregister_entities(&old_proxies);
+
+ /* Enable SCN on all portals we're about to register */
+ all_objects_set(&portals, ISNS_TAG_SCN_PORT, my_port);
+
+ /* Try ESI first. If the server doesn't support it, or doesn't
+ * have the resources to serve us, fall back to normal
+ * registration refresh. */
+ if (opt_esi) {
+ all_objects_set(&portals,
+ ISNS_TAG_ESI_INTERVAL,
+ isns_config.ic_esi_min_interval);
+ all_objects_set(&portals,
+ ISNS_TAG_ESI_PORT,
+ my_port);
+ }
+
+ for (with_esi = opt_esi; 1; with_esi--) {
+ status = register_exported_entities();
+
+ /* At some point, we need to add these portals
+ * to the local_portals list so that ESI works
+ * properly.
+ * Right now, we extract the portals from the response
+ * and add those. The down side of this is that we no
+ * longer use the same object (pointer) to refer to the
+ * same thing. The up side is that the information returned
+ * by the server reflects the correct ESI interval.
+ */
+ if (status == ISNS_SUCCESS)
+ break;
+
+ if (status != ISNS_ESI_NOT_AVAILABLE || with_esi == 0) {
+ isns_error("Failed to register object(s): %s\n",
+ isns_strerror(status));
+ goto out;
+ }
+
+ /* Continue and retry without ESI */
+ all_objects_unset(&portals, ISNS_TAG_ESI_INTERVAL);
+ all_objects_unset(&portals, ISNS_TAG_ESI_PORT);
+ }
+
+ for (i = 0; i < local_registry.iol_count; ++i) {
+ isns_object_t *obj = local_registry.iol_data[i];
+ isns_source_t *source;
+ int status;
+
+ if (!isns_object_is_iscsi_node(obj)
+ && !isns_object_is_fc_port(obj))
+ continue;
+
+ if (!(source = isns_source_from_object(obj)))
+ continue;
+ call = isns_create_scn_registration2(clnt, opt_scn_bits, source);
+ status = isns_simple_call(clnt->ic_socket, &call);
+ if (status != ISNS_SUCCESS) {
+ isns_error("SCN registration for %s failed: %s\n",
+ isns_storage_node_name(obj),
+ isns_strerror(status));
+ }
+ isns_source_release(source);
+ }
+
+out:
+ if (call)
+ isns_simple_free(call);
+ isns_object_list_destroy(&entities);
+ isns_object_list_destroy(&portals);
+ return status;
+}
+
+static void
+retry_registration(void *ptr)
+{
+ isns_proxy_t *proxy = ptr;
+ static unsigned int timeout = 0;
+ int status;
+
+ status = register_exported_objects(proxy->ip_client);
+ if (status) {
+ if (timeout == 0)
+ timeout = opt_retry_timeout;
+ else if (timeout >= MAX_RETRY_TIMEOUT)
+ timeout = MAX_RETRY_TIMEOUT;
+
+ isns_debug_state("Retrying to register in %u seconds\n", timeout);
+ isns_add_oneshot_timer(timeout, retry_registration, proxy);
+
+ /* Exponential backoff */
+ timeout <<= 1;
+ }
+}
+
+/*
+ * Get a list of all visible storage nodes
+ */
+static int
+get_objects_from_query(isns_simple_t *resp)
+{
+ isns_object_list_t resp_objects = ISNS_OBJECT_LIST_INIT;
+ unsigned int i;
+ int status;
+
+ status = isns_query_response_get_objects(resp, &resp_objects);
+ if (status) {
+ isns_error("Unable to extract object list from "
+ "query response: %s\n",
+ isns_strerror(status));
+ return status;
+ }
+
+ isns_debug_state("Initial query returned %u object(s)\n", resp_objects.iol_count);
+ for (i = 0; i < resp_objects.iol_count; ++i) {
+ isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
+ isns_object_t *obj = resp_objects.iol_data[i];
+ isns_object_t *found;
+
+ if (!isns_object_extract_keys(obj, &key_attrs))
+ continue;
+
+ /* Don't add an object twice, and don't add objects
+ * that *we* registered.
+ * This still leaves any default PGs created by the server,
+ * but we cannot help that (for now).
+ */
+ found = isns_object_list_lookup(&visible_nodes, NULL, &key_attrs);
+ if (!found)
+ found = isns_object_list_lookup(&local_registry, NULL, &key_attrs);
+ if (found) {
+ isns_object_release(found);
+ } else {
+ isns_object_list_append(&visible_nodes, obj);
+ }
+ isns_attr_list_destroy(&key_attrs);
+ }
+
+ isns_object_list_destroy(&resp_objects);
+ return status;
+}
+
+static int
+query_storage_node(isns_source_t *source, const char *name)
+{
+ isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
+ isns_simple_t *call;
+ uint32_t tag;
+ int status;
+ isns_client_t *clnt;
+
+ if (isns_source_type(source) != ISNS_TAG_ISCSI_NAME) {
+ isns_error("FC source node - doesn't work yet\n");
+ return ISNS_SUCCESS;
+ }
+ clnt = isns_create_client(NULL, isns_source_name(source));
+
+ tag = isns_source_type(source);
+ if (name) {
+ isns_attr_list_append_string(&key_attrs, tag, name);
+ } else {
+ /* Query for visible nodes */
+ isns_attr_list_append_nil(&key_attrs, tag);
+ }
+
+ call = isns_create_query2(clnt, &key_attrs, source);
+ isns_attr_list_destroy(&key_attrs);
+
+ isns_query_request_attr_tag(call, tag);
+ switch (tag) {
+ case ISNS_TAG_ISCSI_NAME:
+ isns_query_request_attr_tag(call, ISNS_TAG_ISCSI_NODE_TYPE);
+ isns_query_request_attr_tag(call, ISNS_TAG_ISCSI_ALIAS);
+ isns_query_request_attr_tag(call, ISNS_TAG_ISCSI_NODE_INDEX);
+
+ isns_query_request_attr_tag(call, ISNS_TAG_PORTAL_IP_ADDRESS);
+ isns_query_request_attr_tag(call, ISNS_TAG_PORTAL_TCP_UDP_PORT);
+ isns_query_request_attr_tag(call, ISNS_TAG_PORTAL_INDEX);
+
+ isns_query_request_attr_tag(call, ISNS_TAG_PG_ISCSI_NAME);
+ isns_query_request_attr_tag(call, ISNS_TAG_PG_PORTAL_IP_ADDR);
+ isns_query_request_attr_tag(call, ISNS_TAG_PG_PORTAL_TCP_UDP_PORT);
+ isns_query_request_attr_tag(call, ISNS_TAG_PG_TAG);
+ isns_query_request_attr_tag(call, ISNS_TAG_PG_INDEX);
+ break;
+
+ default: ;
+ }
+
+ status = isns_simple_call(clnt->ic_socket, &call);
+ if (status == ISNS_SUCCESS)
+ status = get_objects_from_query(call);
+
+ isns_simple_free(call);
+ isns_client_destroy(clnt);
+ return status;
+}
+
+/*
+ * Query for visible iscsi nodes
+ */
+static int
+query_visible(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < local_registry.iol_count; ++i) {
+ isns_object_t *obj = local_registry.iol_data[i];
+ isns_source_t *source;
+ int status;
+
+ if (!isns_object_is_iscsi_node(obj)
+ && !isns_object_is_fc_port(obj))
+ continue;
+
+ if (isns_object_is_fc_port(obj)) {
+ isns_error("FC source node - sorry, won't work yet\n");
+ continue;
+ }
+
+ if (!(source = isns_source_from_object(obj)))
+ continue;
+ status = query_storage_node(source, NULL);
+ if (status != ISNS_SUCCESS) {
+ isns_warning("Unable to run query on behalf of %s: %s\n",
+ isns_storage_node_name(obj),
+ isns_strerror(status));
+ }
+ isns_source_release(source);
+ }
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Invoke the registered callout program
+ */
+static void
+callout(const char *how, isns_object_t *obj, unsigned int bitmap)
+{
+ char *argv[128];
+ int fargc, argc = 0;
+ pid_t pid;
+
+ if (!isns_config.ic_scn_callout)
+ return;
+
+ argv[argc++] = isns_config.ic_scn_callout;
+ argv[argc++] = (char *) how;
+ fargc = argc;
+
+ argc += isns_print_attrs(obj, argv + argc, 128 - argc);
+
+ pid = fork();
+ if (pid == 0) {
+ execv(argv[0], argv);
+ isns_fatal("Cannot execute %s: %m\n", argv[0]);
+ }
+
+ while (fargc < argc)
+ isns_free(argv[fargc++]);
+
+ if (pid < 0) {
+ isns_error("fork: %m\n");
+ return;
+ }
+
+ while (waitpid(pid, NULL, 0) < 0)
+ ;
+}
+
+/*
+ * This is called when we receive a State Change Notification
+ */
+static void
+scn_callback(isns_db_t *db, uint32_t bitmap,
+ isns_object_template_t *node_type,
+ const char *node_name,
+ const char *dst_name)
+{
+ isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
+ uint32_t key_tag;
+ isns_object_t *node = NULL, *recipient = NULL;
+
+ isns_notice("%s \"%s\" %s\n",
+ isns_object_template_name(node_type),
+ node_name, isns_event_string(bitmap));
+
+ /* This is either an iSCSI node or a FC node - in
+ both cases the storage node name is the key attr */
+ if (node_type == &isns_iscsi_node_template) {
+ key_tag = ISNS_TAG_ISCSI_NAME;
+ } else if (node_type == &isns_fc_node_template) {
+ key_tag = ISNS_TAG_FC_PORT_NAME_WWPN;
+ } else
+ return;
+
+ isns_attr_list_append_string(&key_attrs, key_tag, dst_name);
+ recipient = isns_object_list_lookup(&local_registry, node_type, &key_attrs);
+ if (recipient == NULL) {
+ isns_error("Received SCN for unknown recipient \"%s\"\n",
+ dst_name);
+ goto out;
+ }
+ isns_attr_list_destroy(&key_attrs);
+
+ isns_attr_list_append_string(&key_attrs, key_tag, node_name);
+ node = isns_object_list_lookup(&visible_nodes, node_type, &key_attrs);
+
+ if (bitmap & (ISNS_SCN_OBJECT_REMOVED_MASK|ISNS_SCN_DD_MEMBER_REMOVED_MASK)) {
+ if (node) {
+ isns_object_list_remove(&visible_nodes, node);
+ /* FIXME: We also want to remove any PGs associated with
+ * this node. */
+ }
+ store_imported_objects();
+ callout("remove", node, bitmap);
+ } else
+ if (bitmap & (ISNS_SCN_OBJECT_ADDED_MASK|ISNS_SCN_OBJECT_UPDATED_MASK|ISNS_SCN_DD_MEMBER_ADDED_MASK)) {
+ const char *how = "add";
+ isns_source_t *source;
+
+ if (bitmap & ISNS_SCN_OBJECT_UPDATED_MASK)
+ how = "update";
+ if (!node) {
+ node = isns_create_object(node_type, &key_attrs, NULL);
+ if (!node)
+ goto out;
+ isns_object_list_append(&visible_nodes, node);
+ }
+
+ /* Query the server for information on this node */
+ source = isns_source_from_object(recipient);
+ query_storage_node(source, node_name);
+ isns_source_release(source);
+
+ store_imported_objects();
+ callout(how, node, bitmap);
+
+ }
+
+out:
+ if (node)
+ isns_object_release(node);
+ if (recipient)
+ isns_object_release(recipient);
+ isns_attr_list_destroy(&key_attrs);
+}
+
+/*
+ * Server main loop
+ */
+void
+run_discovery(isns_server_t *server)
+{
+ isns_client_t *clnt;
+ isns_security_t *ctx = NULL;
+ isns_message_t *msg, *resp;
+
+ /* Create the server socket */
+ ctx = isns_default_security_context(0);
+ server_socket = isns_create_server_socket(isns_config.ic_bind_address,
+ NULL, opt_af, SOCK_DGRAM);
+ if (server_socket == NULL)
+ isns_fatal("Unable to create server socket\n");
+ isns_socket_set_security_ctx(server_socket, ctx);
+
+ /* Create the client socket */
+ clnt = isns_create_default_client(NULL);
+ if (clnt == NULL)
+ isns_fatal("Cannot connect to server\n");
+
+ /* Load all objects registered by local services */
+ should_reexport = 1;
+
+ while (1) {
+ struct timeval timeout = { 0, 0 };
+ time_t now, then, next_timeout;
+ unsigned int function;
+
+ next_timeout = time(NULL) + 3600;
+
+ /* Run timers */
+ then = isns_run_timers();
+ if (then && then < next_timeout)
+ next_timeout = then;
+
+ /* Determine how long we can sleep */
+ now = time(NULL);
+ if (next_timeout <= now)
+ continue;
+ timeout.tv_sec = next_timeout - now;
+
+ if (should_reexport) {
+ load_exported_objects();
+
+ if (register_exported_objects(clnt))
+ isns_error("Failed to register exported objects.\n");
+
+ /* Prime the list of visible storage nodes */
+ if (query_visible())
+ isns_error("Unable to query list of visible nodes.\n");
+ store_imported_objects();
+
+ should_reexport = 0;
+ }
+
+ if ((msg = isns_recv_message(&timeout)) == NULL)
+ continue;
+
+ function = isns_message_function(msg);
+ if (function != ISNS_STATE_CHANGE_NOTIFICATION
+ && function != ISNS_ENTITY_STATUS_INQUIRY) {
+ isns_warning("Discarding unexpected %s message\n",
+ isns_function_name(function));
+ isns_message_release(msg);
+ continue;
+ }
+
+ if ((resp = isns_process_message(server, msg)) != NULL) {
+ isns_socket_t *sock = isns_message_socket(msg);
+
+ isns_socket_send(sock, resp);
+ isns_message_release(resp);
+ }
+
+ isns_message_release(msg);
+ }
+}
diff --git a/utils/open-isns/isnssetup b/utils/open-isns/isnssetup
new file mode 100644
index 0000000..df0bd00
--- /dev/null
+++ b/utils/open-isns/isnssetup
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# isnssetup - bootstrap open-isns server
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This is a very simple script to bootstrap an iSNS server.
+# It creates the necessary keys, enrolls a control node,
+# and enrolls the local host as target and initiator.
+
+hostname=`hostname -f`
+
+if [ -f isnsd -a -d isnsadm ]; then
+ PATH=.:$PATH
+fi
+
+# Massage the configuration file
+for f in isnsadm.conf isnsdd.conf; do
+ etcfile=/etc/isns/$f
+ sed -e 's/^#*\(ServerAddress[[:space:]]*=\).*/\1 localhost/' \
+ -e 's/^#*\(Security[[:space:]]*=\).*/\1 1/' \
+ $etcfile > $etcfile.tmp
+ mv $etcfile.tmp $etcfile
+done
+
+echo "*** Initializing server security ***"
+isnsd --init
+cp /etc/isns/auth_key.pub /etc/isns/server_key.pub
+
+if ps ax|grep isnsd | grep -qv grep; then
+ killall -TERM isnsd
+ sleep 1
+fi
+isnsd
+sleep 1
+
+echo "*** Registering control node policy ***"
+rm -f /etc/isns/control.key
+isnsadm --local \
+ --keyfile=/etc/isns/control.key \
+ --enroll isns.control \
+ node-type=ALL functions=ALL object-type=ALL
+
+echo "*** Registering control node ***"
+isnsadm --local \
+ --register control
+
+echo "*** Registering policy for server ***"
+isnsadm --control \
+ --enroll $hostname \
+ key=/etc/isns/auth_key.pub \
+ node-type=target+initiator
diff --git a/utils/open-isns/local.c b/utils/open-isns/local.c
new file mode 100644
index 0000000..4bc1cb1
--- /dev/null
+++ b/utils/open-isns/local.c
@@ -0,0 +1,353 @@
+/*
+ * Local iSNS registration
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ *
+ * The way isnsdd communicates with local services (initiator,
+ * target) is via a file and signals. That sounds rather
+ * awkward, but it's a lot simpler to add to these services
+ * than another socket based communication mechanism I guess.
+ *
+ * The file format is simple:
+ * <object> owner=<owner>
+ * <object> owner=<owner>
+ * ...
+ *
+ * <owner> identifies the service owning these entries.
+ * This is a service name, such as iscsid, tgtd, isnsdd,
+ * optionally followed by a colon and a PID. This allows
+ * removal of all entries created by one service in one go.
+ *
+ * <object> is the description of one iSNS object, using the
+ * syntax used by all other open-isns apps.
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <isns.h>
+#include "security.h"
+#include "util.h"
+#include "isns-proto.h"
+#include "paths.h"
+#include "attrs.h"
+#include "util.h"
+
+typedef int __isns_local_registry_cb_fn_t(const char *line,
+ int argc, char **argv,
+ void *user_data);
+
+/*
+ * Build the owner=<svcname>:<pid> tag
+ */
+static const char *
+__isns_local_registry_make_owner(const char *svcname, pid_t pid)
+{
+ static char owner[128];
+
+ if (pid == 0) {
+ return svcname;
+ }
+ snprintf(owner, sizeof(owner), "%s:%u", svcname, pid);
+ return owner;
+}
+
+/*
+ * Read the registry file, match each entry against the given owner=<foo> tag,
+ * and invoke the callback function.
+ * This is used for both reading the registry, and rewriting it.
+ */
+static int
+__isns_local_registry_read(const char *match_owner,
+ __isns_local_registry_cb_fn_t handle_matching,
+ __isns_local_registry_cb_fn_t handle_nonmatching,
+ void *user_data)
+{
+ const char *filename = isns_config.ic_local_registry_file;
+ char *line, *copy = NULL;
+ FILE *fp;
+ int rv = 0, owner_len;
+
+ if (!(fp = fopen(filename, "r"))) {
+ if (errno == ENOENT) {
+ isns_debug_state("Unable to open %s: %m\n", filename);
+ return 1;
+ }
+ isns_error("Unable to open %s: %m\n", filename);
+ return 0;
+ }
+
+ owner_len = match_owner? strlen(match_owner) : 0;
+ while ((line = parser_get_next_line(fp)) != NULL) {
+ __isns_local_registry_cb_fn_t *cb;
+ char *argv[256], *owner;
+ int argc = 0;
+
+ isns_assign_string(&copy, line);
+
+ argc = isns_attr_list_split(line, argv, 255);
+ if (argc <= 0)
+ continue;
+
+ /* Last attr should be owner */
+ if (strncasecmp(argv[argc-1], "owner=", 6)) {
+ isns_error("%s: syntax error (missing owner field)\n",
+ filename);
+ goto out;
+ }
+ owner = argv[argc-1] + 6;
+
+ if (!strncasecmp(owner, match_owner, owner_len)
+ && (owner[owner_len] == '\0' || owner[owner_len] == ':'))
+ cb = handle_matching;
+ else
+ cb = handle_nonmatching;
+
+ if (cb && !cb(copy, argc, argv, user_data))
+ goto out;
+
+ }
+ rv = 1;
+
+out:
+ free(copy);
+ fclose(fp);
+ return rv;
+}
+
+/*
+ * Open and lock the registry file for writing. Returns an
+ * open stream and the name of the lock file.
+ * Follow up with _finish_write when done.
+ */
+static FILE *
+__isns_local_registry_open_write(char **lock_name)
+{
+ char lock_path[PATH_MAX];
+ FILE *fp;
+ int fd, retry;
+
+ snprintf(lock_path, sizeof(lock_path), "%s.lock",
+ isns_config.ic_local_registry_file);
+
+ for (retry = 0; retry < 5; ++retry) {
+ fd = open(lock_path, O_RDWR|O_CREAT|O_EXCL, 0644);
+ if (fd >= 0)
+ break;
+ if (errno != EEXIST) {
+ isns_error("Unable to create %s: %m\n",
+ lock_path);
+ return NULL;
+ }
+ isns_error("Cannot lock %s - retry in 1 sec\n",
+ isns_config.ic_local_registry_file);
+ sleep(1);
+ }
+
+ if (!(fp = fdopen(fd, "w"))) {
+ isns_error("fdopen failed: %m\n");
+ close(fd);
+ return NULL;
+ }
+ isns_assign_string(lock_name, lock_path);
+ return fp;
+}
+
+/*
+ * We're done with (re)writing the registry. Commit the changes,
+ * or discard them.
+ * Also frees the lock_name returned by registry_open_write.
+ */
+static int
+__isns_local_registry_finish_write(FILE *fp, char *lock_name, int commit)
+{
+ int rv = 1;
+
+ fclose(fp);
+ if (!commit) {
+ if (unlink(lock_name))
+ isns_error("Failed to unlink %s: %m\n", lock_name);
+ } else
+ if (rename(lock_name, isns_config.ic_local_registry_file)) {
+ isns_error("Failed to rename %s to %s: %m\n",
+ lock_name, isns_config.ic_local_registry_file);
+ rv = 0;
+ }
+
+ free(lock_name);
+ return rv;
+}
+
+/*
+ * Get the entity name for this service
+ */
+static char *
+__isns_local_registry_entity_name(const char *owner)
+{
+ static char namebuf[1024];
+
+ snprintf(namebuf, sizeof(namebuf), "%s:%s",
+ isns_config.ic_entity_name,
+ owner);
+ return namebuf;
+}
+
+/*
+ * Callback function which builds an iSNS object from the
+ * list of attr=tag values.
+ */
+static int
+__isns_local_registry_load_object(const char *line,
+ int argc, char **argv, void *user_data)
+{
+ isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
+ struct isns_attr_list_parser state;
+ isns_object_list_t *list = user_data;
+ isns_object_t *obj, *entity = NULL;
+
+ for (; argc > 0; --argc) {
+ char *attr = argv[argc-1];
+
+ if (!strncasecmp(attr, "owner=", 6)) {
+ char *eid = __isns_local_registry_entity_name(attr + 6);
+ ISNS_QUICK_ATTR_LIST_DECLARE(key_attrs,
+ ISNS_TAG_ENTITY_IDENTIFIER,
+ string, eid);
+
+ if (entity) {
+ isns_error("Duplicate owner entry in registry\n");
+ continue;
+ }
+ isns_attr_print(&key_attrs.iqa_attr, isns_print_stdout);
+ entity = isns_object_list_lookup(list,
+ &isns_entity_template,
+ &key_attrs.iqa_list);
+ if (entity != NULL)
+ continue;
+
+ isns_debug_state("Creating fake entity %s\n", eid);
+ entity = isns_create_entity(ISNS_ENTITY_PROTOCOL_ISCSI, eid);
+ isns_object_list_append(list, entity);
+ } else {
+ break;
+ }
+ }
+
+ isns_attr_list_parser_init(&state, NULL);
+ if (!isns_parse_attrs(argc, argv, &attrs, &state)) {
+ isns_error("Unable to parse attrs\n");
+ isns_attr_list_destroy(&attrs);
+ return 0;
+ }
+
+ obj = isns_create_object(isns_attr_list_parser_context(&state),
+ &attrs, entity);
+ isns_attr_list_destroy(&attrs);
+
+ if (obj == NULL) {
+ isns_error("Unable to create object\n");
+ return 0;
+ }
+
+ isns_object_list_append(list, obj);
+ return 1;
+}
+
+/*
+ * Callback function that simply writes out the line as-is
+ */
+static int
+__isns_local_registry_rewrite_object(const char *line,
+ int argc, char **argv, void *user_data)
+{
+ FILE *ofp = user_data;
+
+ fprintf(ofp, "%s\n", line);
+ return 1;
+}
+
+/*
+ * Load all objects owner by a specific service from the local registry.
+ * If the svcname starts with "!", all entries except those matching this
+ * particular service are returned.
+ */
+int
+isns_local_registry_load(const char *svcname, pid_t pid, isns_object_list_t *objs)
+{
+ __isns_local_registry_cb_fn_t *if_matching = NULL, *if_nonmatching = NULL;
+
+ if (svcname == NULL) {
+ isns_error("%s: no svcname given\n", __FUNCTION__);
+ return 0;
+ }
+ if (*svcname == '!') {
+ if_nonmatching = __isns_local_registry_load_object;
+ svcname++;
+ } else {
+ if_matching = __isns_local_registry_load_object;
+ }
+
+ return __isns_local_registry_read(
+ __isns_local_registry_make_owner(svcname, pid),
+ if_matching, if_nonmatching, objs);
+}
+
+/*
+ * Store the given list of objects in the registry.
+ * This replaces all objects previously registered by this service.
+ */
+int
+isns_local_registry_store(const char *svcname, pid_t pid, const isns_object_list_t *objs)
+{
+ const char *owner = __isns_local_registry_make_owner(svcname, pid);
+ char *lock_name = NULL;
+ FILE *ofp;
+
+ if (!(ofp = __isns_local_registry_open_write(&lock_name))) {
+ isns_error("%s: could not open registry for writing\n", __FUNCTION__);
+ return 0;
+ }
+
+ /* First, purge all entries previously belonging to this owner */
+ if (!__isns_local_registry_read(owner, NULL, __isns_local_registry_rewrite_object, ofp))
+ goto failed;
+
+ if (objs) {
+ unsigned int i;
+
+ for (i = 0; i < objs->iol_count; ++i) {
+ isns_object_t *obj = objs->iol_data[i];
+ char *argv[256];
+ int i, argc;
+
+ argc = isns_print_attrs(obj, argv, 256);
+ for (i = 0; i < argc; ++i)
+ fprintf(ofp, "%s ", argv[i]);
+ fprintf(ofp, "owner=%s\n", owner);
+ }
+ }
+
+ return __isns_local_registry_finish_write(ofp, lock_name, 1);
+
+failed:
+ isns_error("%s: error rewriting registry file\n", __FUNCTION__);
+ __isns_local_registry_finish_write(ofp, lock_name, 0);
+ return 0;
+}
+
+/*
+ * Purge the local registry of all objects owned by the
+ * given service.
+ */
+int
+isns_local_registry_purge(const char *svcname, pid_t pid)
+{
+ return isns_local_registry_store(svcname, pid, NULL);
+}
diff --git a/utils/open-isns/logging.c b/utils/open-isns/logging.c
new file mode 100644
index 0000000..63ebbef
--- /dev/null
+++ b/utils/open-isns/logging.c
@@ -0,0 +1,228 @@
+/*
+ * Logging related utility functions.
+ *
+ * Copyright (C) 2004-2007 Olaf Kirch <okir@suse.de>
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "util.h"
+
+static unsigned int log_stdout = 1;
+static unsigned int debugging = 0;
+
+/*
+ * When backgrounding, any logging output should
+ * go to syslog instead of stdout
+ */
+void
+isns_log_background(void)
+{
+ log_stdout = 0;
+}
+
+/*
+ * For output to syslog, sanitize the format string
+ * by removing newlines.
+ */
+static const char *
+sanitize_format(const char *fmt)
+{
+ static char __fmt[1024];
+ unsigned int len;
+
+ /* Don't bother unless there's a newline */
+ if (!strchr(fmt, '\n'))
+ return fmt;
+
+ len = strlen(fmt);
+
+ /* Decline if the buffer would overflow */
+ if (len >= sizeof(__fmt))
+ return fmt;
+
+ strcpy(__fmt, fmt);
+ while (len-- && __fmt[len] == '\n')
+ __fmt[len] = '\0';
+
+ while (len) {
+ if (__fmt[len] == '\n')
+ __fmt[len] = ' ';
+ --len;
+ }
+
+ return __fmt;
+}
+
+/*
+ * Output to stderr or syslog
+ */
+static void
+voutput(int severity, const char *fmt, va_list ap)
+{
+ if (log_stdout) {
+ switch (severity) {
+ case LOG_ERR:
+ fprintf(stderr, "Error: ");
+ break;
+ case LOG_WARNING:
+ fprintf(stderr, "Warning: ");
+ break;
+ case LOG_DEBUG:
+ fprintf(stderr, " ");
+ break;
+ }
+ vfprintf(stderr, fmt, ap);
+ } else {
+ fmt = sanitize_format(fmt);
+ if (!fmt || !*fmt)
+ return;
+ vsyslog(severity, fmt, ap);
+ }
+}
+
+void
+isns_assert_failed(const char *condition, const char *file, unsigned int line)
+{
+ isns_error("Assertion failed (%s:%d): %s\n",
+ file, line, condition);
+ abort();
+}
+
+void
+isns_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (log_stdout)
+ fprintf(stderr, "** FATAL ERROR **\n");
+ voutput(LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+
+void
+isns_error(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ voutput(LOG_WARNING, fmt, ap);
+ va_end(ap);
+}
+
+void
+isns_warning(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ voutput(LOG_NOTICE, fmt, ap);
+ va_end(ap);
+}
+
+void
+isns_notice(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ voutput(LOG_INFO, fmt, ap);
+ va_end(ap);
+}
+
+void
+isns_enable_debugging(const char *what)
+{
+ char *copy, *s, *next;
+
+ if (!strcmp(what, "all")) {
+ debugging = ~0U;
+ return;
+ }
+
+ copy = isns_strdup(what);
+
+ for (s = copy; s; s = next) {
+ if ((next = strchr(s, ',')) != NULL)
+ *next++ = '\0';
+
+ if (!strcmp(s, "general"))
+ debugging |= (1 << DBG_GENERAL);
+ else if (!strcmp(s, "socket"))
+ debugging |= (1 << DBG_SOCKET);
+ else if (!strcmp(s, "protocol"))
+ debugging |= (1 << DBG_PROTOCOL);
+ else if (!strcmp(s, "state"))
+ debugging |= (1 << DBG_STATE);
+ else if (!strcmp(s, "message"))
+ debugging |= (1 << DBG_MESSAGE);
+ else if (!strcmp(s, "auth"))
+ debugging |= (1 << DBG_AUTH);
+ else if (!strcmp(s, "scn"))
+ debugging |= (1 << DBG_SCN);
+ else if (!strcmp(s, "esi"))
+ debugging |= (1 << DBG_ESI);
+ else {
+ isns_error("Ignoring unknown isns_debug facility <<%s>>\n",
+ s);
+ }
+ }
+ isns_free(copy);
+}
+
+#define DEFINE_DEBUG_FUNC(name, NAME) \
+void \
+isns_debug_##name(const char *fmt, ...) \
+{ \
+ va_list ap; \
+ \
+ if (!(debugging & (1 << DBG_##NAME))) \
+ return; \
+ \
+ va_start(ap, fmt); \
+ voutput(LOG_DEBUG, fmt, ap); \
+ va_end(ap); \
+}
+DEFINE_DEBUG_FUNC(general, GENERAL)
+DEFINE_DEBUG_FUNC(socket, SOCKET)
+DEFINE_DEBUG_FUNC(protocol, PROTOCOL)
+DEFINE_DEBUG_FUNC(message, MESSAGE)
+DEFINE_DEBUG_FUNC(auth, AUTH)
+DEFINE_DEBUG_FUNC(state, STATE)
+DEFINE_DEBUG_FUNC(scn, SCN)
+DEFINE_DEBUG_FUNC(esi, ESI)
+
+int
+isns_debug_enabled(int fac)
+{
+ return (debugging & (1 << fac)) != 0;
+}
+
+/*
+ * Misc isns_print_fn_t implementations
+ */
+void
+isns_print_stdout(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+}
+
+void
+isns_print_stderr(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
diff --git a/utils/open-isns/mdebug.c b/utils/open-isns/mdebug.c
new file mode 100644
index 0000000..90dcaf0
--- /dev/null
+++ b/utils/open-isns/mdebug.c
@@ -0,0 +1,295 @@
+/*
+ * Stupid malloc debugger. I think I wrote something like
+ * this a couple of times already. Where does all the old
+ * source code go?
+ */
+
+#ifdef MDEBUG
+
+#include <stdlib.h>
+#include <string.h>
+#include "util.h"
+
+static void * isns_malloc_default(size_t, const char *, unsigned int);
+static void * isns_calloc_default(unsigned int, size_t,
+ const char *, unsigned int);
+static void * isns_realloc_default(void *, size_t,
+ const char *, unsigned int);
+static char * isns_strdup_default(const char *, const char *, unsigned int);
+static void isns_free_default(void *, const char *, unsigned int);
+
+/*
+ * These are the function pointers used to redirect malloc and such.
+ */
+void * (*isns_malloc_fn)(size_t, const char *, unsigned int) = isns_malloc_default;
+void * (*isns_calloc_fn)(unsigned int, size_t,
+ const char *, unsigned int) = isns_calloc_default;
+void * (*isns_realloc_fn)(void *, size_t,
+ const char *, unsigned int) = isns_realloc_default;
+char * (*isns_strdup_fn)(const char *, const char *, unsigned int) = isns_strdup_default;
+void (*isns_free_fn)(void *, const char *, unsigned int) = isns_free_default;
+
+#define H_MAGIC 0xfeedbeef
+#define T_MAGIC 0xbadf00d
+#define CHUNK_OVERHEAD (sizeof(struct m_header) + sizeof(struct m_trailer))
+
+struct m_header {
+ struct isns_list h_list;
+ uint32_t h_magic;
+ size_t h_size;
+
+ const char * h_file;
+ unsigned int h_line;
+};
+
+struct m_trailer {
+ uint32_t t_magic[8];
+ size_t t_size;
+};
+
+static ISNS_LIST_DECLARE(m_list);
+static void * m_low_addr;
+static void * m_high_addr;
+static int m_init = 0;
+
+static void
+__isns_check_chunk(const struct m_header *head)
+{
+ const struct m_trailer *tail;
+ int i;
+
+ if ((void *) head < m_low_addr
+ || (void *) head > m_high_addr) {
+ isns_error("%s: m_list corrupted!\n", __FUNCTION__);
+ abort();
+ }
+
+ if (head->h_magic != H_MAGIC) {
+ isns_error("%s: m_list item %p with bad header magic %08x\n",
+ __FUNCTION__, head, head->h_magic);
+ isns_error(" Allocated from %s:%u\n",
+ head->h_file, head->h_line);
+ abort();
+ }
+
+ tail = ((void *) head) + sizeof(*head) + head->h_size;
+ for (i = 0; i < 8; ++i) {
+ if (tail->t_magic[i] == T_MAGIC)
+ continue;
+
+ isns_error("%s: m_list item %p with bad trailer magic[%d] %08x\n",
+ __FUNCTION__, head, i, tail->t_magic[i]);
+ isns_error(" Allocated from %s:%u\n",
+ head->h_file, head->h_line);
+ abort();
+ }
+
+ if (tail->t_size != head->h_size) {
+ isns_error("%s: m_list item %p size mismatch; head=%u tail=%u\n",
+ __FUNCTION__, head,
+ head->h_size, tail->t_size);
+ isns_error(" Allocated from %s:%u\n",
+ head->h_file, head->h_line);
+ abort();
+ }
+}
+
+static void
+__isns_verify_all(void)
+{
+ struct isns_list *pos, *next;
+
+ isns_list_foreach(&m_list, pos, next) {
+ __isns_check_chunk(isns_list_item(struct m_header, h_list, pos));
+ }
+}
+
+void *
+__isns_malloc(size_t size, const char *file, unsigned int line)
+{
+ struct m_header *head;
+ struct m_trailer *tail;
+ size_t true_size;
+ void *ptr;
+ int i;
+
+ __isns_verify_all();
+
+ true_size = size + sizeof(*head) + sizeof(*tail);
+ isns_assert(size < true_size);
+
+ ptr = malloc(true_size);
+ if (!ptr)
+ return NULL;
+
+ if (!m_low_addr) {
+ m_low_addr = m_high_addr = ptr;
+ } else if (ptr < m_low_addr) {
+ m_low_addr = ptr;
+ } else if (ptr > m_high_addr) {
+ m_high_addr = ptr;
+ }
+
+ head = ptr;
+ head->h_magic = H_MAGIC;
+ head->h_size = size;
+ head->h_file = file;
+ head->h_line = line;
+ isns_list_append(&m_list, &head->h_list);
+
+ ptr += sizeof(*head);
+
+ tail = ptr + size;
+ for (i = 0; i < 8; ++i)
+ tail->t_magic[i] = T_MAGIC;
+ tail->t_size = size;
+
+ return ptr;
+}
+
+void *
+__isns_calloc(unsigned int nele, size_t size,
+ const char *file, unsigned int line)
+{
+ void *ptr;
+
+ ptr = __isns_malloc(nele * size, file, line);
+ if (ptr)
+ memset(ptr, 0, nele * size);
+ return ptr;
+}
+
+void *
+__isns_realloc(void *old, size_t new_size,
+ const char *file, unsigned int line)
+{
+ struct m_header *old_head = NULL;
+ void *new;
+
+ if (old) {
+ old_head = (old - sizeof(struct m_header));
+ __isns_check_chunk(old_head);
+ }
+
+ new = __isns_malloc(new_size, file, line);
+ if (new && old) {
+ memcpy(new, old, old_head->h_size);
+ isns_free_fn(old, file, line);
+ }
+
+ return new;
+}
+
+
+char *
+__isns_strdup(const char *s, const char *file, unsigned int line)
+{
+ size_t len;
+ char *ptr;
+
+ len = s? strlen(s) : 0;
+ ptr = __isns_malloc(len + 1, file, line);
+ if (ptr) {
+ memcpy(ptr, s, len);
+ ptr[len] = '\0';
+ }
+ return ptr;
+}
+
+void
+__isns_free(void *ptr, const char *file, unsigned int line)
+{
+ struct m_header *head;
+ size_t true_size;
+
+ if (ptr == NULL)
+ return;
+
+ head = ptr - sizeof(struct m_header);
+ __isns_check_chunk(head);
+
+ /*
+ printf("__isns_free(%u from %s:%u): freed by %s:%u\n",
+ head->h_size, head->h_file, head->h_line,
+ file, line);
+ */
+ true_size = head->h_size + CHUNK_OVERHEAD;
+ isns_list_del(&head->h_list);
+
+ memset(head, 0xa5, true_size);
+ free(head);
+
+ __isns_verify_all();
+}
+
+/*
+ * Enable memory debugging
+ */
+static void
+__isns_mdebug_init(void)
+{
+ const char *tracefile;
+
+ tracefile = getenv("ISNS_MTRACE");
+ if (tracefile)
+ isns_error("MTRACE not yet supported\n");
+
+ if (getenv("ISNS_MDEBUG")) {
+ isns_malloc_fn = __isns_malloc;
+ isns_calloc_fn = __isns_calloc;
+ isns_realloc_fn = __isns_realloc;
+ isns_strdup_fn = __isns_strdup;
+ isns_free_fn = __isns_free;
+ isns_notice("Enabled memory debugging\n");
+ }
+
+ m_init = 1;
+}
+
+static inline void
+isns_mdebug_init(void)
+{
+ if (!m_init)
+ __isns_mdebug_init();
+}
+
+/*
+ * Default implementations of malloc and friends
+ */
+static void *
+isns_malloc_default(size_t size, const char *file, unsigned int line)
+{
+ isns_mdebug_init();
+ return malloc(size);
+}
+
+static void *
+isns_calloc_default(unsigned int nele, size_t size,
+ const char *file, unsigned int line)
+{
+ isns_mdebug_init();
+ return calloc(nele, size);
+}
+
+static void *
+isns_realloc_default(void *old, size_t size,
+ const char *file, unsigned int line)
+{
+ isns_mdebug_init();
+ return realloc(old, size);
+}
+
+static char *
+isns_strdup_default(const char *s, const char *file, unsigned int line)
+{
+ isns_mdebug_init();
+ return strdup(s);
+}
+
+static void
+isns_free_default(void *ptr, const char *file, unsigned int line)
+{
+ isns_mdebug_init();
+ return free(ptr);
+}
+#endif
diff --git a/utils/open-isns/message.c b/utils/open-isns/message.c
new file mode 100644
index 0000000..4cd40c3
--- /dev/null
+++ b/utils/open-isns/message.c
@@ -0,0 +1,681 @@
+/*
+ * iSNS message handling functions
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ *
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h> /* for timercmp */
+#include <unistd.h> /* gethostname */
+#include <ctype.h>
+#include "isns.h"
+#include "attrs.h"
+#include "message.h"
+#include "socket.h"
+#include "util.h"
+
+/* iSCSI qualified names include the year and
+ * month in which the domain was assigned.
+ * See RFC 3720, section 3.2.6.3.1.
+ * That's one of these wonderful committee
+ * type of ideas that makes it hard for everyone,
+ * from coder to sysadmin.
+ * Since we have no way of finding out here,
+ * we fake it by assigning a date before the
+ * dawn of time.
+ */
+#define DUMMY_IQN_PREFIX "iqn.1967-12."
+
+static uint32_t isns_xid = 1;
+
+/*
+ * Initialize a message object
+ */
+isns_message_t *
+__isns_alloc_message(uint32_t xid, size_t size, void (*destroy)(isns_message_t *))
+{
+ isns_message_t *msg;
+
+ isns_assert(size >= sizeof(*msg));
+ msg = isns_calloc(1, size);
+
+ isns_list_init(&msg->im_list);
+ msg->im_users = 1;
+ msg->im_xid = xid;
+ msg->im_destroy = destroy;
+
+ return msg;
+}
+
+static int
+__isns_message_init(isns_message_t *msg,
+ uint16_t function, uint16_t flags,
+ size_t payload_len)
+{
+ struct isns_hdr *hdr = &msg->im_header;
+
+ /* Pad to multiple of 4 octets */
+ payload_len = (payload_len + 3) & ~3UL;
+
+ /* For now, we don't do segmentation */
+ if (payload_len > ISNS_MAX_PDU_SIZE)
+ return 0;
+
+ /* msg->im_header is in host byte order */
+ hdr->i_version = ISNS_VERSION;
+ hdr->i_function = function;
+ hdr->i_flags = flags;
+ hdr->i_length = payload_len;
+ hdr->i_xid = msg->im_xid;
+ hdr->i_seq = 0;
+
+ /* Allocate buffer and reserve room for header */
+ msg->im_payload = buf_alloc(sizeof(*hdr) + payload_len);
+ buf_push(msg->im_payload, sizeof(*hdr));
+
+ return 1;
+}
+
+/*
+ * Allocate a message object.
+ */
+static isns_message_t *
+__isns_create_message(uint32_t xid, uint16_t function, uint16_t flags)
+{
+ isns_message_t *msg;
+
+ msg = __isns_alloc_message(xid, sizeof(*msg), NULL);
+ __isns_message_init(msg, function, flags, ISNS_MAX_MESSAGE);
+
+ return msg;
+}
+
+/*
+ * Allocate a request message
+ */
+isns_message_t *
+isns_create_message(uint16_t function, uint16_t flags)
+{
+ return __isns_create_message(isns_xid++, function, flags);
+}
+
+/*
+ * Allocate a response message
+ */
+isns_message_t *
+isns_create_reply(const isns_message_t *msg)
+{
+ uint16_t function = msg->im_header.i_function;;
+ isns_message_t *resp;
+
+ resp = __isns_create_message(msg->im_xid, function | 0x8000, ISNS_F_SERVER);
+ resp->im_addr = msg->im_addr;
+ resp->im_addrlen = msg->im_addrlen;
+
+ /* Default to ISNS_SUCCESS */
+ buf_put32(resp->im_payload, ISNS_SUCCESS);
+
+ return resp;
+}
+
+/*
+ * Delete a message
+ */
+void
+isns_message_release(isns_message_t *msg)
+{
+ if (msg == NULL)
+ return;
+
+ isns_assert(msg->im_users);
+ if (--(msg->im_users))
+ return;
+
+ if (msg->im_destroy)
+ msg->im_destroy(msg);
+ if (msg->im_payload)
+ buf_free(msg->im_payload);
+ isns_principal_free(msg->im_security);
+
+ isns_list_del(&msg->im_list);
+ isns_free(msg);
+}
+
+/*
+ * Extract the status from a reply message
+ */
+int
+isns_message_status(isns_message_t *msg)
+{
+ uint32_t status;
+
+ if (!(msg->im_header.i_function & 0x8000)
+ || !buf_get32(msg->im_payload, &status))
+ return ISNS_MESSAGE_FORMAT_ERROR;
+ return status;
+}
+
+/*
+ * Obtain the socket on which the message was received.
+ */
+isns_socket_t *
+isns_message_socket(const isns_message_t *msg)
+{
+ return msg->im_socket;
+}
+
+/*
+ * Obtain the message's security context
+ */
+isns_security_t *
+isns_message_security(const isns_message_t *msg)
+{
+ if (!msg->im_socket)
+ return NULL;
+ return msg->im_socket->is_security;
+}
+
+unsigned int
+isns_message_function(const isns_message_t *msg)
+{
+ return msg->im_header.i_function;
+}
+
+/*
+ * Reset the response message, and encode isns_error
+ * status
+ */
+void
+isns_message_set_error(isns_message_t *msg, uint32_t status)
+{
+ /* Clear the buffer. This just resets head + tail */
+ buf_clear(msg->im_payload);
+
+ /* Now move past the header, and overwrite the
+ * status word. */
+ buf_push(msg->im_payload, sizeof(struct isns_hdr));
+ buf_put32(msg->im_payload, status);
+}
+
+/*
+ * Message queue handling. Most related functions are
+ * in message.h
+ */
+void
+isns_message_queue_move(isns_message_queue_t *dstq,
+ isns_message_t *msg)
+{
+ unsigned int src_ref = 0;
+
+ /* If the message was on a different queue,
+ * the source queue will hold a reference
+ * to it. Account for that and fix up the
+ * refcount after we've appended it to the
+ * destination queue. */
+ if (isns_message_unlink(msg))
+ src_ref = 1;
+
+ isns_message_queue_append(dstq, msg);
+ msg->im_users -= src_ref;
+}
+
+/*
+ * Insert a messsage into a queue sorted by resend timeout
+ */
+void
+isns_message_queue_insert_sorted(isns_message_queue_t *q,
+ int sort, isns_message_t *msg)
+{
+ isns_list_t *pos;
+ isns_message_t *__m;
+
+ isns_assert(msg->im_queue == NULL);
+ if (sort == ISNS_MQ_SORT_RESEND_TIMEOUT) {
+ isns_message_queue_foreach(q, pos, __m) {
+ if (timercmp(&msg->im_resend_timeout,
+ &__m->im_resend_timeout, <))
+ break;
+ }
+ } else {
+ isns_message_queue_append(q, msg);
+ return;
+ }
+
+ /* Insert before pos */
+ __isns_list_insert(pos->prev, &msg->im_list, pos);
+ q->imq_count++;
+
+ msg->im_queue = q;
+ msg->im_users++;
+}
+
+/*
+ * Message queue handling
+ */
+void
+isns_message_queue_destroy(isns_message_queue_t *q)
+{
+ isns_message_t *msg;
+
+ while ((msg = isns_message_dequeue(q)) != NULL)
+ isns_message_release(msg);
+}
+
+/*
+ * Find a message with matching xid and address.
+ * (address, alen) may be NULL.
+ */
+isns_message_t *
+isns_message_queue_find(isns_message_queue_t *q, uint32_t xid,
+ const struct sockaddr_storage *addr, socklen_t alen)
+{
+ isns_message_t *msg;
+ isns_list_t *pos;
+
+ isns_message_queue_foreach(q, pos, msg) {
+ if (msg->im_xid != xid)
+ continue;
+ if (alen == 0)
+ return msg;
+
+ if (msg->im_addrlen == alen
+ && !memcmp(&msg->im_addr, addr, alen))
+ return msg;
+ }
+
+ return NULL;
+}
+
+/*
+ * Convert a hostname into an iSCSI qualified name
+ * We omit the dismbiguating YYYY-MM infix because
+ * we have no way of finding out, short of bothering
+ * whois.
+ */
+static char *
+__revert_fqdn(const char *prefix, const char *__fqdn, const char *suffix)
+{
+ static char namebuf[1024] = { '\0' };
+ char *fqdn, *result = NULL;
+ int pos, count = 0;
+
+ if (prefix)
+ strcpy(namebuf, prefix);
+ pos = strlen(namebuf);
+
+ fqdn = isns_strdup(__fqdn);
+ while (1) {
+ char *dot, *comp;
+ int comp_len;
+
+ if ((dot = strrchr(fqdn, '.')) != NULL) {
+ *dot++ = '\0';
+ comp = dot;
+ } else {
+ comp = fqdn;
+ }
+
+ if (*comp == '\0')
+ continue;
+ comp_len = strlen(comp);
+ if (pos + comp_len + 2 > sizeof(namebuf)) {
+ isns_error("%s: FQDN too long\n", __FUNCTION__);
+ goto out;
+ }
+ if (count++)
+ namebuf[pos++] = '.';
+ strcpy(namebuf + pos, comp);
+ pos += comp_len;
+
+ if (dot == NULL)
+ break;
+ }
+
+ if (suffix) {
+ int sfx_len = strlen(suffix);
+
+ if (pos + sfx_len + 2 > sizeof(namebuf)) {
+ isns_error("%s: name too long\n", __FUNCTION__);
+ goto out;
+ }
+ namebuf[pos++] = ':';
+ strcpy(namebuf + pos, suffix);
+ pos += sfx_len;
+ }
+
+ result = isns_strdup(namebuf);
+
+out: isns_free(fqdn);
+ return result;
+}
+
+/*
+ * Initialize all names
+ */
+int
+isns_init_names(void)
+{
+ if (isns_config.ic_host_name == NULL) {
+ char namebuf[1024], *fqdn;
+
+ if (gethostname(namebuf, sizeof(namebuf)) < 0) {
+ isns_error("gehostname: %m\n");
+ return 0;
+ }
+ fqdn = isns_get_canon_name(namebuf);
+ if (fqdn == NULL) {
+ /* FIXME: we could get some unique value here
+ * such as the IP address, and concat that
+ * with iqn.2005-01.org.open-iscsi.ip for the
+ * source name.
+ */
+ isns_error("Unable to get fully qualified hostname\n");
+ return 0;
+ }
+ isns_config.ic_host_name = fqdn;
+ }
+
+ if (isns_config.ic_auth_name == NULL) {
+ isns_config.ic_auth_name = isns_config.ic_host_name;
+ }
+
+ if (isns_config.ic_entity_name == NULL) {
+ isns_config.ic_entity_name = isns_config.ic_auth_name;
+ }
+
+ if (isns_config.ic_source_name == NULL) {
+ isns_config.ic_source_name = __revert_fqdn(DUMMY_IQN_PREFIX,
+ isns_config.ic_host_name,
+ isns_config.ic_source_suffix);
+ if (isns_config.ic_source_name == NULL) {
+ isns_error("Unable to build source name\n");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Match a source name to a pattern (which is really just
+ * the entity identifier, usually).
+ *
+ * If the pattern is of the form "match:rev-fqdn", the
+ * source name must match
+ * iqn.[YYYY-MM.]<rev-fqdn>
+ * optionally followed by dot, colon or hyphen and arbitrary
+ * text.
+ *
+ * If the pattern does not start with "match:", the source name
+ * must match the pattern literally (case insensitively).
+ */
+int
+isns_source_pattern_match(const char *pattern, const char *source)
+{
+ unsigned int rev_len;
+
+ isns_debug_message("%s(%s, %s)\n",
+ __FUNCTION__, pattern, source);
+
+ if (!strcmp(pattern, "*"))
+ return 1;
+
+ if (strncmp(pattern, "match:", 6))
+ return !strcasecmp(pattern, source);
+ pattern += 6;
+
+ if (strncasecmp(source, "iqn.", 4))
+ return 0;
+ source += 4;
+
+ rev_len = strlen(pattern);
+ if (strncasecmp(source, pattern, rev_len)) {
+ /* See if the next component is YYYY-MM */
+ if (!(isdigit(source[0])
+ && isdigit(source[1])
+ && isdigit(source[2])
+ && isdigit(source[3])
+ && source[4] == '-'
+ && isdigit(source[5])
+ && isdigit(source[6])
+ && source[7] == '.'))
+ return 0;
+ source += 8;
+
+ if (strncasecmp(source, pattern, rev_len))
+ return 0;
+ }
+
+ source += rev_len;
+ if (source[0] != '.'
+ && source[0] != ':'
+ && source[0] != '-'
+ && source[0] != '\0')
+ return 0;
+
+ return 1;
+}
+
+/*
+ * This really just reverts the FQDN so it can
+ * be used in isns_source_entity_match
+ */
+char *
+isns_build_source_pattern(const char *fqdn)
+{
+ return __revert_fqdn("match:", fqdn, NULL);
+}
+
+/*
+ * Manage source objects
+ */
+static isns_source_t *
+__isns_source_create(isns_attr_t *name_attr)
+{
+ isns_source_t *source = isns_calloc(1, sizeof(*source));
+
+ source->is_users = 1;
+ source->is_attr = name_attr;
+ return source;
+}
+
+isns_source_t *
+isns_source_create(isns_attr_t *name_attr)
+{
+ if (name_attr->ia_tag_id != ISNS_TAG_ISCSI_NAME
+ && name_attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
+ return NULL;
+
+ name_attr->ia_users++;
+ return __isns_source_create(name_attr);
+}
+
+isns_source_t *
+isns_source_from_object(const isns_object_t *node)
+{
+ isns_attr_t *attr;
+
+ if (!(attr = isns_storage_node_key_attr(node)))
+ return NULL;
+ return isns_source_create(attr);
+}
+
+isns_source_t *
+isns_source_create_iscsi(const char *name)
+{
+ isns_value_t var = ISNS_VALUE_INIT(string, (char *) name);
+ isns_attr_t *attr;
+
+ attr = isns_attr_alloc(ISNS_TAG_ISCSI_NAME, NULL, &var);
+ return __isns_source_create(attr);
+}
+
+/*
+ * This is used to attach a dummy source to iSNS responses
+ * until I fixed up all the code that relies on msg->is_source
+ * to be valid all the time.
+ */
+isns_source_t *
+isns_source_dummy(void)
+{
+ static isns_source_t *dummy = NULL;
+
+ if (!dummy)
+ dummy = isns_source_create_iscsi(".dummy.");
+ return isns_source_get(dummy);
+}
+
+uint32_t
+isns_source_type(const isns_source_t *source)
+{
+ return source->is_attr->ia_tag_id;
+}
+
+const char *
+isns_source_name(const isns_source_t *source)
+{
+ return source->is_attr->ia_value.iv_string;
+}
+
+isns_attr_t *
+isns_source_attr(const isns_source_t *source)
+{
+ return source->is_attr;
+}
+
+/*
+ * Obtain an additional reference on the source object
+ */
+isns_source_t *
+isns_source_get(isns_source_t *source)
+{
+ if (source)
+ source->is_users++;
+ return source;
+}
+
+/*
+ * Look up the node corresponding to this source name
+ * When we get here, we have already verified that the
+ * client is permitted (by policy) to use this source node.
+ */
+int
+isns_source_set_node(isns_source_t *source, isns_db_t *db)
+{
+ isns_object_t *node, *entity;
+ uint32_t node_type;
+
+ if (source->is_node)
+ return 1;
+
+ if (db == NULL)
+ return 0;
+
+ node = isns_db_lookup_source_node(db, source);
+ if (node == NULL)
+ return 0;
+
+ if (!isns_object_get_uint32(node, ISNS_TAG_ISCSI_NODE_TYPE, &node_type))
+ node_type = 0;
+
+ source->is_node = node;
+ source->is_node_type = node_type;
+
+ if ((entity = isns_object_get_entity(node)) != NULL)
+ source->is_entity = isns_object_get(entity);
+ return 1;
+}
+
+void
+isns_source_set_entity(isns_source_t *source, isns_object_t *obj)
+{
+ if (obj)
+ isns_object_get(obj);
+ isns_object_release(source->is_entity);
+ source->is_entity = obj;
+}
+
+/*
+ * Release a reference on the source object
+ */
+void
+isns_source_release(isns_source_t *source)
+{
+ if (source && --source->is_users == 0) {
+ isns_attr_release(source->is_attr);
+ isns_object_release(source->is_node);
+ isns_object_release(source->is_entity);
+ memset(source, 0xa5, sizeof(*source));
+ isns_free(source);
+ }
+}
+
+/*
+ * Compare two source objects
+ */
+int
+isns_source_match(const isns_source_t *a,
+ const isns_source_t *b)
+{
+ if (a && b)
+ return isns_attr_match(a->is_attr, b->is_attr);
+ return 0;
+}
+
+/*
+ * Encode/decode source object
+ */
+int
+isns_source_encode(buf_t *bp, const isns_source_t *source)
+{
+ if (source == NULL) {
+ isns_attr_t nil = ISNS_ATTR_INIT(ISNS_TAG_DELIMITER, nil, 0);
+
+ return isns_attr_encode(bp, &nil);
+ }
+ return isns_attr_encode(bp, source->is_attr);
+}
+
+int
+isns_source_decode(buf_t *bp, isns_source_t **result)
+{
+ isns_attr_t *attr;
+ int status;
+
+ status = isns_attr_decode(bp, &attr);
+ if (status == ISNS_SUCCESS) {
+ /*
+ * 5.6.1
+ * The Source Attribute uniquely identifies the source of the
+ * message. Valid Source Attribute types are shown below.
+ *
+ * Valid Source Attributes
+ * -----------------------
+ * iSCSI Name
+ * FC Port Name WWPN
+ */
+ switch (attr->ia_tag_id) {
+#if 0
+ case ISNS_TAG_DELIMITER:
+ *result = NULL;
+ break;
+#endif
+
+ case ISNS_TAG_ISCSI_NAME:
+ *result = __isns_source_create(attr);
+ break;
+
+ case ISNS_TAG_FC_PORT_NAME_WWPN:
+ *result = __isns_source_create(attr);
+ break;
+
+ default:
+ isns_attr_release(attr);
+ return ISNS_SOURCE_UNKNOWN;
+ }
+ }
+ return status;
+}
diff --git a/utils/open-isns/message.h b/utils/open-isns/message.h
new file mode 100644
index 0000000..f1f4ed6
--- /dev/null
+++ b/utils/open-isns/message.h
@@ -0,0 +1,196 @@
+/*
+ * iSNS message definitions and functions
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_MESSAGE_H
+#define ISNS_MESSAGE_H
+
+#include "attrs.h"
+#include "source.h"
+#include "util.h"
+
+typedef struct isns_message_queue isns_message_queue_t;
+
+struct isns_simple {
+ uint32_t is_function;
+ isns_source_t * is_source;
+ isns_policy_t * is_policy;
+ uint16_t is_xid;
+
+ unsigned int is_replace : 1;
+
+ isns_attr_list_t is_message_attrs;
+ isns_attr_list_t is_operating_attrs;
+};
+
+struct isns_message {
+ unsigned int im_users;
+ isns_list_t im_list;
+ struct sockaddr_storage im_addr;
+ socklen_t im_addrlen;
+ uint32_t im_xid;
+ struct isns_hdr im_header;
+ struct isns_buf * im_payload;
+ isns_socket_t * im_socket;
+ isns_principal_t * im_security;
+ struct ucred * im_creds;
+
+ isns_message_queue_t * im_queue;
+
+ /* When to retransmit */
+ struct timeval im_resend_timeout;
+ struct timeval im_timeout;
+
+ void (*im_destroy)(isns_message_t *);
+ void (*im_callback)(isns_message_t *,
+ isns_message_t *);
+ void * im_calldata;
+};
+
+enum {
+ ISNS_MQ_SORT_NONE,
+ ISNS_MQ_SORT_RESEND_TIMEOUT,
+};
+
+struct isns_message_queue {
+ isns_list_t imq_list;
+ size_t imq_count;
+};
+
+struct isns_server {
+ isns_source_t * is_source;
+ isns_db_t * is_db;
+
+ isns_scn_callback_fn_t *is_scn_callback;
+ struct isns_service_ops *is_ops;
+};
+
+extern isns_message_t * __isns_alloc_message(uint32_t, size_t, void (*)(isns_message_t *));
+extern isns_security_t *isns_message_security(const isns_message_t *);
+
+extern isns_message_t * isns_message_queue_find(isns_message_queue_t *, uint32_t,
+ const struct sockaddr_storage *, socklen_t);
+extern void isns_message_queue_insert_sorted(isns_message_queue_t *,
+ int, isns_message_t *);
+extern void isns_message_queue_move(isns_message_queue_t *,
+ isns_message_t *);
+extern void isns_message_queue_destroy(isns_message_queue_t *);
+
+extern isns_simple_t * isns_simple_create(uint32_t,
+ isns_source_t *,
+ const isns_attr_list_t *);
+extern void isns_simple_free(isns_simple_t *);
+extern int isns_simple_encode(isns_simple_t *,
+ isns_message_t **result);
+extern int isns_simple_decode(isns_message_t *,
+ isns_simple_t **);
+extern int isns_simple_encode_response(isns_simple_t *,
+ const isns_message_t *, isns_message_t **);
+extern int isns_simple_response_get_objects(isns_simple_t *,
+ isns_object_list_t *);
+extern const char * isns_function_name(uint32_t);
+
+extern isns_source_t * isns_simple_get_source(isns_simple_t *);
+
+extern int isns_process_registration(isns_server_t *, isns_simple_t *, isns_simple_t **);
+extern int isns_process_query(isns_server_t *, isns_simple_t *, isns_simple_t **);
+extern int isns_process_getnext(isns_server_t *, isns_simple_t *, isns_simple_t **);
+extern int isns_process_deregistration(isns_server_t *, isns_simple_t *, isns_simple_t **);
+extern int isns_process_scn_register(isns_server_t *, isns_simple_t *, isns_simple_t **);
+extern int isns_process_scn_deregistration(isns_server_t *, isns_simple_t *, isns_simple_t **);
+extern int isns_process_dd_registration(isns_server_t *, isns_simple_t *, isns_simple_t **);
+extern int isns_process_dd_deregistration(isns_server_t *, isns_simple_t *, isns_simple_t **);
+extern int isns_process_esi(isns_server_t *, isns_simple_t *, isns_simple_t **);
+extern int isns_process_scn(isns_server_t *, isns_simple_t *, isns_simple_t **);
+
+/*
+ * Inline functions for message queues.
+ */
+static inline void
+isns_message_queue_init(isns_message_queue_t *q)
+{
+ isns_list_init(&q->imq_list);
+ q->imq_count = 0;
+}
+
+static inline isns_message_t *
+isns_message_queue_head(const isns_message_queue_t *q)
+{
+ isns_list_t *pos = q->imq_list.next;
+
+ if (pos == &q->imq_list)
+ return NULL;
+ return isns_list_item(isns_message_t, im_list, pos);
+}
+
+static inline void
+isns_message_queue_append(isns_message_queue_t *q, isns_message_t *msg)
+{
+ isns_assert(msg->im_queue == NULL);
+ isns_list_append(&q->imq_list, &msg->im_list);
+ q->imq_count++;
+
+ msg->im_queue = q;
+ msg->im_users++;
+}
+
+static inline isns_message_t *
+isns_message_queue_remove(isns_message_queue_t *q, isns_message_t *msg)
+{
+ isns_assert(msg->im_queue == q);
+ isns_list_del(&msg->im_list);
+ msg->im_queue = NULL;
+ q->imq_count--;
+
+ return msg;
+}
+
+static inline isns_message_t *
+isns_message_unlink(isns_message_t *msg)
+{
+ if (msg->im_queue)
+ return isns_message_queue_remove(msg->im_queue, msg);
+ return NULL;
+}
+
+static inline isns_message_t *
+isns_message_dequeue(isns_message_queue_t *q)
+{
+ isns_message_t *msg;
+
+ if ((msg = isns_message_queue_head(q)) != NULL) {
+ isns_list_del(&msg->im_list);
+ msg->im_queue = NULL;
+ q->imq_count--;
+ }
+ return msg;
+}
+
+/*
+ * Iterator for looping over all messages in a queue
+ */
+static inline void
+isns_message_queue_begin(isns_message_queue_t *q, isns_list_t **pos)
+{
+ *pos = q->imq_list.next;
+}
+
+static inline isns_message_t *
+isns_message_queue_next(isns_message_queue_t *q, isns_list_t **pos)
+{
+ isns_list_t *next = *pos;
+
+ if (next == &q->imq_list)
+ return NULL;
+ *pos = next->next;
+ return isns_list_item(isns_message_t, im_list, next);
+}
+
+#define isns_message_queue_foreach(q, pos, item) \
+ for (isns_message_queue_begin(q, &pos); \
+ (item = isns_message_queue_next(q, &pos)) != NULL; \
+ )
+
+#endif /* ISNS_MESSAGE_H */
diff --git a/utils/open-isns/objects.c b/utils/open-isns/objects.c
new file mode 100644
index 0000000..1504026
--- /dev/null
+++ b/utils/open-isns/objects.c
@@ -0,0 +1,1320 @@
+/*
+ * iSNS object model
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "isns.h"
+#include "objects.h"
+#include "source.h"
+#include "vendor.h"
+#include "attrs.h"
+#include "util.h"
+
+/* For relationship stuff - should go */
+#include "db.h"
+
+static isns_object_template_t * isns_object_templates[] = {
+ &isns_entity_template,
+ &isns_portal_template,
+ &isns_iscsi_node_template,
+ &isns_fc_port_template,
+ &isns_fc_node_template,
+ &isns_iscsi_pg_template,
+ &isns_dd_template,
+ &isns_ddset_template,
+
+ /* vendor-specific templates */
+ &isns_policy_template,
+
+ NULL
+};
+
+/*
+ * Quick lookup of (key) tag to template
+ */
+#define MAX_QUICK_TAG 2100
+static isns_object_template_t * isns_object_template_key_map[MAX_QUICK_TAG];
+static isns_object_template_t * isns_object_template_any_map[MAX_QUICK_TAG];
+static isns_object_template_t * isns_object_template_idx_map[MAX_QUICK_TAG];
+static int isns_object_maps_inizialized = 0;
+
+
+static void
+__isns_object_maps_init(void)
+{
+ isns_object_template_t *tmpl;
+ uint32_t i, j, tag;
+
+ isns_object_maps_inizialized = 1;
+
+ for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) {
+ if (tmpl->iot_vendor_specific)
+ continue;
+
+ tag = tmpl->iot_keys[0];
+ isns_assert(tag < MAX_QUICK_TAG);
+ isns_object_template_key_map[tag] = tmpl;
+
+ for (j = 0; j < tmpl->iot_num_attrs; ++j) {
+ tag = tmpl->iot_attrs[j];
+ isns_assert(tag < MAX_QUICK_TAG);
+ isns_object_template_any_map[tag] = tmpl;
+ }
+
+ if ((tag = tmpl->iot_index) != 0)
+ isns_object_template_idx_map[tag] = tmpl;
+ }
+}
+
+static void
+isns_object_maps_init(void)
+{
+ if (!isns_object_maps_inizialized)
+ __isns_object_maps_init();
+}
+
+/*
+ * Based on a given key attribute, find the corresponding
+ * object type.
+ */
+isns_object_template_t *
+isns_object_template_find(uint32_t key_tag)
+{
+ isns_object_template_t *tmpl;
+ unsigned int i;
+
+ isns_object_maps_init();
+ if (key_tag < MAX_QUICK_TAG)
+ return isns_object_template_key_map[key_tag];
+
+ for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) {
+ if (tmpl->iot_keys[0] == key_tag)
+ return tmpl;
+ }
+
+ return NULL;
+}
+
+/*
+ * Given a set of attributes, find the corresponding
+ * object type.
+ * Any attributes in the list in *addition to* the keys
+ * attributes are ignored.
+ */
+isns_object_template_t *
+isns_object_template_for_key_attrs(const isns_attr_list_t *attrs)
+{
+ isns_object_template_t *tmpl;
+ const isns_attr_t *attr;
+ unsigned int i;
+
+ if (attrs->ial_count == 0)
+ return NULL;
+ attr = attrs->ial_data[0];
+
+ tmpl = isns_object_template_find(attr->ia_tag_id);
+ if (tmpl == NULL)
+ return NULL;
+
+ /*
+ * 5.6.4.
+ *
+ * Some objects are keyed by more than one object key attribute
+ * value. For example, the Portal object is keyed by attribute
+ * tags 16 and 17. When describing an object keyed by more than one
+ * key attribute, every object key attribute of that object MUST be
+ * listed sequentially by tag value in the message before non-key
+ * attributes of that object and key attributes of the next object.
+ * A group of key attributes of this kind is treated as a single
+ * logical key attribute when identifying an object.
+ */
+ for (i = 1; i < tmpl->iot_num_keys; ++i) {
+ attr = attrs->ial_data[i];
+
+ if (attr->ia_tag_id != tmpl->iot_keys[i])
+ return NULL;
+ }
+
+ return tmpl;
+}
+
+isns_object_template_t *
+isns_object_template_for_tag(uint32_t tag)
+{
+ isns_object_template_t *tmpl;
+ unsigned int i, j;
+
+ isns_object_maps_init();
+ if (tag < MAX_QUICK_TAG)
+ return isns_object_template_any_map[tag];
+
+ for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) {
+ for (j = 0; j < tmpl->iot_num_attrs; ++j) {
+ if (tmpl->iot_attrs[j] == tag)
+ return tmpl;
+ }
+ }
+
+ return NULL;
+}
+
+isns_object_template_t *
+isns_object_template_for_index_tag(uint32_t tag)
+{
+ isns_object_maps_init();
+ if (tag >= MAX_QUICK_TAG)
+ return NULL;
+
+ return isns_object_template_idx_map[tag];
+}
+
+isns_object_template_t *
+isns_object_template_by_name(const char *name)
+{
+ isns_object_template_t **pp, *tmpl;
+
+ pp = isns_object_templates;
+ while ((tmpl = *pp++) != NULL) {
+ if (!strcasecmp(tmpl->iot_name, name))
+ return tmpl;
+ }
+ return NULL;
+}
+
+const char *
+isns_object_template_name(isns_object_template_t *tmpl)
+{
+ if (!tmpl)
+ return NULL;
+ return tmpl->iot_name;
+}
+
+/*
+ * Notify any listeners that the object has changed,
+ * and mark it dirty.
+ * dd_or_dds is used for DD_MEMBER_ADDED and
+ * DD_MEMBER_REMOVED events, and refers to the
+ * domain or domain set the object was added to or
+ * removed from.
+ */
+void
+isns_mark_object(isns_object_t *obj, unsigned int how)
+{
+ obj->ie_flags |= ISNS_OBJECT_DIRTY;
+ obj->ie_mtime = time(NULL);
+ obj->ie_scn_bits |= (1 << how);
+ isns_object_event(obj, 0, NULL);
+}
+
+static void
+__isns_mark_object(isns_object_t *obj)
+{
+ obj->ie_flags |= ISNS_OBJECT_DIRTY;
+ obj->ie_mtime = time(NULL);
+}
+
+/*
+ * Create an object given its object template
+ */
+isns_object_t *
+isns_create_object(isns_object_template_t *tmpl,
+ const isns_attr_list_t *attrs,
+ isns_object_t *parent)
+{
+ isns_object_t *obj;
+ unsigned int i;
+
+ /* Enforce containment rules. */
+ if (parent)
+ isns_assert(tmpl->iot_container == parent->ie_template);
+
+#ifdef notdef
+ /* This check is somewhat costly: */
+ if (attrs && tmpl != isns_object_template_for_key_attrs(attrs))
+ return NULL;
+#endif
+
+ obj = isns_calloc(1, sizeof(*obj));
+
+ obj->ie_users = 1;
+ obj->ie_template = tmpl;
+ isns_attr_list_init(&obj->ie_attrs);
+
+ if (parent)
+ isns_object_attach(obj, parent);
+
+ if (attrs == NULL) {
+ /* Make sure that all key attrs are instantiated
+ * and in sequence. */
+ for (i = 0; i < tmpl->iot_num_keys; ++i)
+ isns_attr_list_append_nil(&obj->ie_attrs,
+ tmpl->iot_keys[i]);
+ } else {
+ /* We rely on the caller to ensure that
+ * attributes are in proper sequence. */
+ isns_attr_list_copy(&obj->ie_attrs, attrs);
+ }
+
+ /* Just mark it dirty, but do not schedule a
+ * SCN event. */
+ __isns_mark_object(obj);
+
+ return obj;
+}
+
+/*
+ * Obtain an additional reference on the object
+ */
+isns_object_t *
+isns_object_get(isns_object_t *obj)
+{
+ if (obj) {
+ isns_assert(obj->ie_users);
+ obj->ie_users++;
+ }
+ return obj;
+}
+
+/*
+ * Release a reference on the object
+ */
+void
+isns_object_release(isns_object_t *obj)
+{
+ unsigned int i;
+ isns_object_t *child;
+
+ if (!obj)
+ return;
+
+ isns_assert(obj->ie_users);
+ if (--(obj)->ie_users != 0)
+ return;
+
+ /* Must not have any live references to it */
+ isns_assert(obj->ie_references == 0);
+
+ /* Must be detached from parent */
+ isns_assert(obj->ie_container == NULL);
+
+ /* Release all children. We explicitly clear
+ * ie_container because the destructor
+ * checks for this (in order to catch
+ * refcounting bugs) */
+ for (i = 0; i < obj->ie_children.iol_count; ++i) {
+ child = obj->ie_children.iol_data[i];
+ child->ie_container = NULL;
+ }
+ isns_object_list_destroy(&obj->ie_children);
+
+ isns_attr_list_destroy(&obj->ie_attrs);
+
+ isns_bitvector_free(obj->ie_membership);
+ isns_free(obj);
+}
+
+/*
+ * Get the topmost container (ie Network Entity)
+ * for the given object
+ */
+isns_object_t *
+isns_object_get_entity(isns_object_t *obj)
+{
+ if (obj == NULL)
+ return NULL;
+ while (obj->ie_container)
+ obj = obj->ie_container;
+ if (!ISNS_IS_ENTITY(obj))
+ return NULL;
+ return obj;
+}
+
+int
+isns_object_contains(const isns_object_t *ancestor,
+ const isns_object_t *descendant)
+{
+ while (descendant) {
+ if (descendant == ancestor)
+ return 1;
+ descendant = descendant->ie_container;
+ }
+ return 0;
+}
+
+/*
+ * Get all children of the specified type
+ */
+void
+isns_object_get_descendants(const isns_object_t *obj,
+ isns_object_template_t *tmpl,
+ isns_object_list_t *result)
+{
+ isns_object_t *child;
+ unsigned int i;
+
+ for (i = 0; i < obj->ie_children.iol_count; ++i) {
+ child = obj->ie_children.iol_data[i];
+ if (!tmpl || child->ie_template == tmpl)
+ isns_object_list_append(result, child);
+ }
+}
+
+/*
+ * Attach an object to a new container
+ */
+int
+isns_object_attach(isns_object_t *obj, isns_object_t *parent)
+{
+ isns_assert(obj->ie_container == NULL);
+
+ if (parent) {
+ /* Copy the owner (ie source) from the parent
+ * object.
+ * Make sure the parent object type is a valid
+ * container for this object.
+ */
+ if (parent->ie_template != obj->ie_template->iot_container) {
+ isns_error("You are not allowed to add a %s object "
+ "to a %s!\n",
+ obj->ie_template->iot_name,
+ parent->ie_template->iot_name);
+ return 0;
+ }
+ obj->ie_flags = parent->ie_flags & ISNS_OBJECT_PRIVATE;
+ isns_object_list_append(&parent->ie_children, obj);
+ }
+ obj->ie_container = parent;
+ return 1;
+}
+
+int
+isns_object_is_valid_container(const isns_object_t *container,
+ isns_object_template_t *child_type)
+{
+ return child_type->iot_container == container->ie_template;
+}
+
+/*
+ * Detach an object from its container
+ */
+int
+isns_object_detach(isns_object_t *obj)
+{
+ isns_object_t *parent;
+
+ /* Detach from parent */
+ if ((parent = obj->ie_container) != NULL) {
+ int removed;
+
+ obj->ie_container = NULL;
+ removed = isns_object_list_remove(
+ &parent->ie_children, obj);
+
+ isns_assert(removed != 0);
+ }
+
+ return 0;
+}
+
+/*
+ * Check the type of an object
+ */
+int
+isns_object_is(const isns_object_t *obj,
+ isns_object_template_t *tmpl)
+{
+ return obj->ie_template == tmpl;
+}
+
+int
+isns_object_is_iscsi_node(const isns_object_t *obj)
+{
+ return ISNS_IS_ISCSI_NODE(obj);
+}
+
+int
+isns_object_is_fc_port(const isns_object_t *obj)
+{
+ return ISNS_IS_FC_PORT(obj);
+}
+
+int
+isns_object_is_fc_node(const isns_object_t *obj)
+{
+ return ISNS_IS_FC_NODE(obj);
+}
+
+int
+isns_object_is_portal(const isns_object_t *obj)
+{
+ return ISNS_IS_PORTAL(obj);
+}
+
+int
+isns_object_is_pg(const isns_object_t *obj)
+{
+ return ISNS_IS_PG(obj);
+}
+
+int
+isns_object_is_policy(const isns_object_t *obj)
+{
+ return ISNS_IS_POLICY(obj);
+}
+
+/*
+ * Match an object against a list of attributes.
+ */
+int
+isns_object_match(const isns_object_t *obj,
+ const isns_attr_list_t *attrs)
+{
+ isns_object_template_t *tmpl = obj->ie_template;
+ isns_attr_t *self, *match;
+ unsigned int i, j, from = 0;
+ uint32_t tag;
+
+ /* Fast path: try to compare in-order */
+ while (from < attrs->ial_count) {
+ match = attrs->ial_data[from];
+ self = obj->ie_attrs.ial_data[from];
+
+ if (match->ia_tag_id != self->ia_tag_id)
+ goto slow_path;
+
+ if (!isns_attr_match(self, match))
+ return 0;
+
+ from++;
+ }
+
+ return 1;
+
+slow_path:
+ for (i = from; i < attrs->ial_count; ++i) {
+ isns_attr_t *found = NULL;
+
+ match = attrs->ial_data[i];
+
+ /*
+ * 5.6.5.2
+ * A Message Key with zero-length TLV(s) is scoped to
+ * every object of the type indicated by the zero-length
+ * TLV(s)
+ */
+ if (match->ia_value.iv_type == &isns_attr_type_nil) {
+ tag = match->ia_tag_id;
+ if (isns_object_attr_valid(tmpl, tag))
+ continue;
+ return 0;
+ }
+
+ for (j = from; j < obj->ie_attrs.ial_count; ++j) {
+ self = obj->ie_attrs.ial_data[j];
+
+ if (match->ia_tag_id == self->ia_tag_id) {
+ found = self;
+ break;
+ }
+ }
+
+ if (found == NULL)
+ return 0;
+
+ if (!isns_attr_match(self, match))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Find descendant object matching the given key
+ */
+isns_object_t *
+isns_object_find_descendant(isns_object_t *obj, const isns_attr_list_t *keys)
+{
+ isns_object_list_t list = ISNS_OBJECT_LIST_INIT;
+ isns_object_t *found;
+
+ if (!isns_object_find_descendants(obj, NULL, keys, &list))
+ return NULL;
+
+ found = isns_object_get(list.iol_data[0]);
+ isns_object_list_destroy(&list);
+
+ return found;
+}
+
+int
+isns_object_find_descendants(isns_object_t *obj,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *keys,
+ isns_object_list_t *result)
+{
+ isns_object_t *child;
+ unsigned int i;
+
+ if ((tmpl == NULL || tmpl == obj->ie_template)
+ && (keys == NULL || isns_object_match(obj, keys)))
+ isns_object_list_append(result, obj);
+
+ for (i = 0; i < obj->ie_children.iol_count; ++i) {
+ child = obj->ie_children.iol_data[i];
+ isns_object_find_descendants(child, tmpl, keys, result);
+ }
+
+ return result->iol_count;
+}
+
+/*
+ * Return the object's modification time stamp
+ */
+time_t
+isns_object_last_modified(const isns_object_t *obj)
+{
+ return obj->ie_mtime;
+}
+
+/*
+ * Set the SCN bitmap
+ */
+void
+isns_object_set_scn_mask(isns_object_t *obj, uint32_t bitmap)
+{
+ obj->ie_scn_mask = bitmap;
+ __isns_mark_object(obj);
+}
+
+/*
+ * Debugging utility: print the object
+ */
+void
+isns_object_print(isns_object_t *obj, isns_print_fn_t *fn)
+{
+ isns_attr_list_print(&obj->ie_attrs, fn);
+}
+
+/*
+ * Return a string representing the object state
+ */
+const char *
+isns_object_state_string(unsigned int state)
+{
+ switch (state) {
+ case ISNS_OBJECT_STATE_LARVAL:
+ return "larval";
+ case ISNS_OBJECT_STATE_MATURE:
+ return "mature";
+ case ISNS_OBJECT_STATE_LIMBO:
+ return "limbo";
+ case ISNS_OBJECT_STATE_DEAD:
+ return "dead";
+ }
+ return "UNKNOWN";
+}
+
+/*
+ * This is needed when deregistering an object.
+ * Remove all attributes except the key and index attrs.
+ */
+void
+isns_object_prune_attrs(isns_object_t *obj)
+{
+ isns_object_template_t *tmpl = obj->ie_template;
+ uint32_t tags[16];
+ unsigned int i;
+
+ isns_assert(tmpl->iot_num_keys + 1 <= 16);
+ for (i = 0; i < tmpl->iot_num_keys; ++i)
+ tags[i] = tmpl->iot_keys[i];
+ if (tmpl->iot_index)
+ tags[i++] = tmpl->iot_index;
+ isns_attr_list_prune(&obj->ie_attrs, tags, i);
+}
+
+/*
+ * Convenience functions
+ */
+
+/*
+ * Create a portal object.
+ * For now, always assume TCP.
+ */
+isns_object_t *
+isns_create_portal(const isns_portal_info_t *info,
+ isns_object_t *parent)
+{
+ isns_object_t *obj;
+
+ obj = isns_create_object(&isns_portal_template, NULL, parent);
+ isns_portal_to_object(info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ obj);
+ return obj;
+}
+
+/*
+ * Extract all key attrs and place them
+ * in the attribute list.
+ */
+int
+isns_object_extract_keys(const isns_object_t *obj,
+ isns_attr_list_t *list)
+{
+ isns_object_template_t *tmpl = obj->ie_template;
+ const isns_attr_list_t *src = &obj->ie_attrs;
+ unsigned int i;
+
+ for (i = 0; i < tmpl->iot_num_keys; ++i) {
+ isns_attr_t *attr;
+
+ if (!isns_attr_list_get_attr(src, tmpl->iot_keys[i], &attr))
+ return 0;
+ isns_attr_list_append_attr(list, attr);
+ }
+
+ return 1;
+}
+
+/*
+ * Extract all attributes we are permitted to overwrite and place them
+ * in the attribute list.
+ */
+int
+isns_object_extract_writable(const isns_object_t *obj,
+ isns_attr_list_t *list)
+{
+ const isns_attr_list_t *src = &obj->ie_attrs;
+ unsigned int i;
+
+ for (i = 0; i < src->ial_count; ++i) {
+ isns_attr_t *attr = src->ial_data[i];
+
+ if (attr->ia_tag->it_readonly)
+ continue;
+ isns_attr_list_append_attr(list, attr);
+ }
+
+ return 1;
+}
+
+/*
+ * Extract all attrs and place them
+ * in the attribute list. We copy the attributes
+ * as they appear inside the object; which allows
+ * duplicate attributes (eg inside a discovery domain).
+ */
+int
+isns_object_extract_all(const isns_object_t *obj, isns_attr_list_t *list)
+{
+ isns_attr_list_append_list(list, &obj->ie_attrs);
+ return 1;
+}
+
+/*
+ * Check if the given object is valid
+ */
+int
+isns_object_attr_valid(isns_object_template_t *tmpl, uint32_t tag)
+{
+ const uint32_t *attr_tags = tmpl->iot_attrs;
+ unsigned int i;
+
+ for (i = 0; i < tmpl->iot_num_attrs; ++i) {
+ if (*attr_tags == tag)
+ return 1;
+ ++attr_tags;
+ }
+ return 0;
+}
+
+/*
+ * Set an object attribute
+ */
+static int
+__isns_object_set_attr(isns_object_t *obj, uint32_t tag,
+ const isns_attr_type_t *type,
+ const isns_value_t *value)
+{
+ const isns_tag_type_t *tag_type;
+
+ if (!isns_object_attr_valid(obj->ie_template, tag))
+ return 0;
+
+ tag_type = isns_tag_type_by_id(tag);
+ if (type != &isns_attr_type_nil
+ && type != tag_type->it_type) {
+ isns_warning("application bug: cannot set attr %s(id=%u, "
+ "type=%s) to a value of type %s\n",
+ tag_type->it_name, tag,
+ tag_type->it_type->it_name,
+ type->it_name);
+ return 0;
+ }
+
+ isns_attr_list_update_value(&obj->ie_attrs,
+ tag, tag_type, value);
+
+ /* Timestamp updates should just be written out, but we
+ * do not want to trigger SCN messages and such. */
+ if (tag != ISNS_TAG_TIMESTAMP)
+ isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED);
+ else
+ __isns_mark_object(obj);
+ return 1;
+}
+
+/*
+ * Copy an attribute to the object
+ */
+int
+isns_object_set_attr(isns_object_t *obj, isns_attr_t *attr)
+{
+ isns_attr_list_t *list = &obj->ie_attrs;
+ uint32_t tag = attr->ia_tag_id;
+
+ /* If this attribute exists within the object,
+ * and it cannot occur multiple times, replace it. */
+ if (!attr->ia_tag->it_multiple
+ && isns_attr_list_replace_attr(list, attr))
+ goto done;
+
+ /* It doesn't exist; make sure it's a valid
+ * attribute. */
+ if (!isns_object_attr_valid(obj->ie_template, tag))
+ return 0;
+
+ isns_attr_list_append_attr(list, attr);
+
+done:
+ isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED);
+ return 1;
+}
+
+int
+isns_object_set_attrlist(isns_object_t *obj, const isns_attr_list_t *attrs)
+{
+ unsigned int i;
+
+ for (i = 0; i < attrs->ial_count; ++i) {
+ isns_attr_t *attr = attrs->ial_data[i];
+ if (!isns_object_set_attr(obj, attr))
+ return 0;
+ }
+ isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED);
+ return 1;
+}
+
+/*
+ * Untyped version of isns_object_set.
+ * Any type checking must be done by the caller;
+ * failure to do so will result in the end of the world.
+ */
+int
+isns_object_set_value(isns_object_t *obj, uint32_t tag, const void *data)
+{
+ return isns_attr_list_update(&obj->ie_attrs, tag, data);
+}
+
+/*
+ * Typed versions of isns_object_set
+ */
+int
+isns_object_set_nil(isns_object_t *obj, uint32_t tag)
+{
+ return __isns_object_set_attr(obj, tag,
+ &isns_attr_type_nil, NULL);
+}
+
+int
+isns_object_set_string(isns_object_t *obj, uint32_t tag,
+ const char *value)
+{
+ isns_value_t var = ISNS_VALUE_INIT(string, (char *) value);
+ int rc;
+
+ rc = __isns_object_set_attr(obj, tag,
+ &isns_attr_type_string, &var);
+ return rc;
+}
+
+int
+isns_object_set_uint32(isns_object_t *obj, uint32_t tag,
+ uint32_t value)
+{
+ isns_value_t var = ISNS_VALUE_INIT(uint32, value);
+
+ return __isns_object_set_attr(obj, tag,
+ &isns_attr_type_uint32, &var);
+}
+
+int
+isns_object_set_uint64(isns_object_t *obj,
+ uint32_t tag,
+ uint64_t value)
+{
+ isns_value_t var = ISNS_VALUE_INIT(uint64, value);
+
+ return __isns_object_set_attr(obj, tag,
+ &isns_attr_type_uint64, &var);
+}
+
+int
+isns_object_set_ipaddr(isns_object_t *obj, uint32_t tag,
+ const struct in6_addr *value)
+{
+ isns_value_t var = ISNS_VALUE_INIT(ipaddr, *value);
+
+ return __isns_object_set_attr(obj, tag,
+ &isns_attr_type_ipaddr, &var);
+}
+
+/*
+ * Query object attributes
+ */
+int
+isns_object_get_attr(const isns_object_t *obj, uint32_t tag,
+ isns_attr_t **result)
+{
+ return isns_attr_list_get_attr(&obj->ie_attrs, tag, result);
+}
+
+int
+isns_object_get_attrlist(isns_object_t *obj,
+ isns_attr_list_t *result,
+ const isns_attr_list_t *req_attrs)
+{
+ isns_attr_list_t *attrs = &obj->ie_attrs;
+ isns_attr_t *attr;
+ unsigned int i;
+
+ if (req_attrs == NULL) {
+ /* Retrieve all attributes */
+ isns_attr_list_append_list(result, attrs);
+ } else {
+ for (i = 0; i < req_attrs->ial_count; ++i) {
+ uint32_t tag = req_attrs->ial_data[i]->ia_tag_id;
+
+ if (tag == obj->ie_template->iot_next_index) {
+ /* FIXME: for now, we fake this value.
+ * We need the DB object at this point
+ * to find out what the next unused
+ * index is.
+ */
+ isns_attr_list_append_uint32(result,
+ tag, 0);
+ } else
+ if (isns_attr_list_get_attr(attrs, tag, &attr))
+ isns_attr_list_append_attr(result, attr);
+ }
+ }
+ return 1;
+}
+
+int
+isns_object_get_key_attrs(isns_object_t *obj,
+ isns_attr_list_t *result)
+{
+ isns_object_template_t *tmpl = obj->ie_template;
+ isns_attr_list_t *attrs = &obj->ie_attrs;
+ isns_attr_t *attr;
+ unsigned int i;
+
+ for (i = 0; i < tmpl->iot_num_keys; ++i) {
+ uint32_t tag = tmpl->iot_keys[i];
+
+ if (!isns_attr_list_get_attr(attrs, tag, &attr)) {
+ isns_error("%s: %s object is missing key attr %u\n",
+ __FUNCTION__,
+ tmpl->iot_name,
+ tag);
+ return 0;
+ }
+ isns_attr_list_append_attr(result, attr);
+ }
+ return 1;
+}
+
+int
+isns_object_get_string(const isns_object_t *obj, uint32_t tag,
+ const char **result)
+{
+ isns_attr_t *attr;
+
+ if (!isns_object_get_attr(obj, tag, &attr)
+ || !ISNS_ATTR_IS_STRING(attr))
+ return 0;
+
+ *result = attr->ia_value.iv_string;
+ return 1;
+}
+
+int
+isns_object_get_ipaddr(const isns_object_t *obj, uint32_t tag,
+ struct in6_addr *result)
+{
+ isns_attr_t *attr;
+
+ if (!isns_object_get_attr(obj, tag, &attr)
+ || !ISNS_ATTR_IS_IPADDR(attr))
+ return 0;
+
+ *result = attr->ia_value.iv_ipaddr;
+ return 1;
+}
+
+int
+isns_object_get_uint32(const isns_object_t *obj, uint32_t tag,
+ uint32_t *result)
+{
+ isns_attr_t *attr;
+
+ if (!isns_object_get_attr(obj, tag, &attr)
+ || !ISNS_ATTR_IS_UINT32(attr))
+ return 0;
+
+ *result = attr->ia_value.iv_uint32;
+ return 1;
+}
+
+int
+isns_object_get_uint64(const isns_object_t *obj, uint32_t tag,
+ uint64_t *result)
+{
+ isns_attr_t *attr;
+
+ if (!isns_object_get_attr(obj, tag, &attr)
+ || !ISNS_ATTR_IS_UINT64(attr))
+ return 0;
+
+ *result = attr->ia_value.iv_uint64;
+ return 1;
+}
+
+int
+isns_object_get_opaque(const isns_object_t *obj, uint32_t tag,
+ const void **ptr, size_t *len)
+{
+ isns_attr_t *attr;
+
+ if (!isns_object_get_attr(obj, tag, &attr)
+ || !ISNS_ATTR_IS_OPAQUE(attr))
+ return 0;
+
+ *ptr = attr->ia_value.iv_opaque.ptr;
+ *len = attr->ia_value.iv_opaque.len;
+ return 1;
+}
+
+int
+isns_object_delete_attr(isns_object_t *obj, uint32_t tag)
+{
+ return isns_attr_list_remove_tag(&obj->ie_attrs, tag);
+}
+
+int
+isns_object_remove_member(isns_object_t *obj,
+ const isns_attr_t *attr,
+ const uint32_t *subordinate_tags)
+{
+ return isns_attr_list_remove_member(&obj->ie_attrs,
+ attr, subordinate_tags);
+}
+
+/*
+ * Object list functions
+ */
+void
+isns_object_list_init(isns_object_list_t *list)
+{
+ memset(list, 0, sizeof(*list));
+}
+
+static inline void
+__isns_object_list_resize(isns_object_list_t *list, unsigned int count)
+{
+ unsigned int max;
+
+ max = (list->iol_count + 15) & ~15;
+ if (count < max)
+ return;
+
+ count = (count + 15) & ~15;
+ list->iol_data = isns_realloc(list->iol_data, count * sizeof(isns_object_t *));
+ if (!list->iol_data)
+ isns_fatal("Out of memory!\n");
+}
+
+void
+isns_object_list_append(isns_object_list_t *list, isns_object_t *obj)
+{
+ __isns_object_list_resize(list, list->iol_count + 1);
+ list->iol_data[list->iol_count++] = obj;
+ obj->ie_users++;
+}
+
+void
+isns_object_list_append_list(isns_object_list_t *dst,
+ const isns_object_list_t *src)
+{
+ unsigned int i, j;
+
+ __isns_object_list_resize(dst, dst->iol_count + src->iol_count);
+ j = dst->iol_count;
+ for (i = 0; i < src->iol_count; ++i, ++j) {
+ isns_object_t *obj = src->iol_data[i];
+
+ dst->iol_data[j] = obj;
+ obj->ie_users++;
+ }
+ dst->iol_count = j;
+}
+
+int
+isns_object_list_contains(const isns_object_list_t *list,
+ isns_object_t *obj)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ if (obj == list->iol_data[i])
+ return 1;
+ }
+ return 0;
+}
+
+isns_object_t *
+isns_object_list_lookup(const isns_object_list_t *list,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *keys)
+{
+ unsigned int i;
+
+ if (!tmpl && !keys)
+ return NULL;
+
+ if (!tmpl && !(tmpl = isns_object_template_for_key_attrs(keys)))
+ return NULL;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ if (obj->ie_template != tmpl)
+ continue;
+ if (keys && !isns_object_match(obj, keys))
+ continue;
+
+ obj->ie_users++;
+ return obj;
+ }
+
+ return NULL;
+}
+
+
+int
+isns_object_list_gang_lookup(const isns_object_list_t *list,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *keys,
+ isns_object_list_t *result)
+{
+ unsigned int i;
+
+ if (!tmpl && !keys)
+ return ISNS_INVALID_QUERY;
+
+ if (!tmpl && !(tmpl = isns_object_template_for_key_attrs(keys)))
+ return ISNS_INVALID_QUERY;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ if (obj->ie_template != tmpl)
+ continue;
+ if (keys && !isns_object_match(obj, keys))
+ continue;
+
+ isns_object_list_append(result, obj);
+ }
+
+ return ISNS_SUCCESS;
+}
+
+
+void
+isns_object_list_destroy(isns_object_list_t *list)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ isns_object_release(obj);
+ }
+
+ isns_free(list->iol_data);
+ memset(list, 0, sizeof(*list));
+}
+
+int
+isns_object_list_remove(isns_object_list_t *list, isns_object_t *tbr)
+{
+ unsigned int i, last;
+
+ last = list->iol_count - 1;
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ if (obj == tbr) {
+ list->iol_data[i] = list->iol_data[last];
+ list->iol_count--;
+ isns_object_release(tbr);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+isns_object_compare_id(const void *pa, const void *pb)
+{
+ const isns_object_t *a = *(const isns_object_t **) pa;
+ const isns_object_t *b = *(const isns_object_t **) pb;
+
+ return (int) a->ie_index - (int) b->ie_index;
+}
+
+void
+isns_object_list_sort(isns_object_list_t *list)
+{
+ if (list->iol_count == 0)
+ return;
+
+ qsort(list->iol_data, list->iol_count,
+ sizeof(void *), isns_object_compare_id);
+}
+
+void
+isns_object_list_uniq(isns_object_list_t *list)
+{
+ isns_object_t *prev = NULL, *this;
+ unsigned int i, j;
+
+ isns_object_list_sort(list);
+ for (i = j = 0; i < list->iol_count; i++) {
+ this = list->iol_data[i];
+ if (this != prev)
+ list->iol_data[j++] = this;
+ prev = this;
+ }
+ list->iol_count = j;
+}
+
+void
+isns_object_list_print(const isns_object_list_t *list, isns_print_fn_t *fn)
+{
+ unsigned int i;
+
+ if (list->iol_count == 0) {
+ fn("(Object list empty)\n");
+ return;
+ }
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj;
+
+ obj = list->iol_data[i];
+ fn("object[%u] = <%s>\n", i,
+ obj->ie_template->iot_name);
+ isns_object_print(obj, fn);
+ }
+}
+
+/*
+ * Handle object references
+ */
+void
+isns_object_reference_set(isns_object_ref_t *ref, isns_object_t *obj)
+{
+ isns_object_t *old;
+
+ if (obj) {
+ isns_assert(obj->ie_users);
+ obj->ie_references++;
+ obj->ie_users++;
+ }
+ if ((old = ref->obj) != NULL) {
+ isns_assert(old->ie_references);
+ old->ie_references--;
+ isns_object_release(old);
+ }
+ ref->obj = obj;
+}
+
+void
+isns_object_reference_drop(isns_object_ref_t *ref)
+{
+ isns_object_reference_set(ref, NULL);
+}
+
+/*
+ * Helper function for portal/object conversion
+ */
+int
+isns_portal_from_object(isns_portal_info_t *portal,
+ uint32_t addr_tag, uint32_t port_tag,
+ const isns_object_t *obj)
+{
+ return isns_portal_from_attr_list(portal,
+ addr_tag, port_tag, &obj->ie_attrs);
+}
+
+int
+isns_portal_to_object(const isns_portal_info_t *portal,
+ uint32_t addr_tag, uint32_t port_tag,
+ isns_object_t *obj)
+{
+ return isns_portal_to_attr_list(portal,
+ addr_tag, port_tag,
+ &obj->ie_attrs);
+}
+
+/*
+ * Portal
+ */
+static uint32_t portal_attrs[] = {
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ ISNS_TAG_PORTAL_SYMBOLIC_NAME,
+ ISNS_TAG_ESI_INTERVAL,
+ ISNS_TAG_ESI_PORT,
+ ISNS_TAG_PORTAL_INDEX,
+ ISNS_TAG_SCN_PORT,
+ ISNS_TAG_PORTAL_NEXT_INDEX,
+ ISNS_TAG_PORTAL_SECURITY_BITMAP,
+ ISNS_TAG_PORTAL_ISAKMP_PHASE_1,
+ ISNS_TAG_PORTAL_ISAKMP_PHASE_2,
+ ISNS_TAG_PORTAL_CERTIFICATE,
+};
+
+static uint32_t portal_key_attrs[] = {
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+};
+
+isns_object_template_t isns_portal_template = {
+ .iot_name = "Portal",
+ .iot_handle = ISNS_OBJECT_TYPE_PORTAL,
+ .iot_attrs = portal_attrs,
+ .iot_num_attrs = array_num_elements(portal_attrs),
+ .iot_keys = portal_key_attrs,
+ .iot_num_keys = array_num_elements(portal_key_attrs),
+ .iot_index = ISNS_TAG_PORTAL_INDEX,
+ .iot_next_index = ISNS_TAG_PORTAL_NEXT_INDEX,
+ .iot_container = &isns_entity_template,
+};
diff --git a/utils/open-isns/objects.h b/utils/open-isns/objects.h
new file mode 100644
index 0000000..93ce208
--- /dev/null
+++ b/utils/open-isns/objects.h
@@ -0,0 +1,167 @@
+/*
+ * iSNS object model
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_OBJECTS_H
+#define ISNS_OBJECTS_H
+
+#include "isns.h"
+#include "attrs.h"
+
+enum isns_object_id {
+ ISNS_OBJECT_TYPE_ENTITY = 1,
+ ISNS_OBJECT_TYPE_NODE,
+ ISNS_OBJECT_TYPE_PORTAL,
+ ISNS_OBJECT_TYPE_PG,
+ ISNS_OBJECT_TYPE_DD,
+ ISNS_OBJECT_TYPE_DDSET,
+ ISNS_OBJECT_TYPE_POLICY,
+ ISNS_OBJECT_TYPE_FC_PORT,
+ ISNS_OBJECT_TYPE_FC_NODE,
+
+ __ISNS_OBJECT_TYPE_MAX
+};
+
+
+struct isns_object_template {
+ const char * iot_name;
+ unsigned int iot_handle; /* internal handle */
+ unsigned int iot_num_attrs;
+ unsigned int iot_num_keys;
+ uint32_t * iot_attrs;
+ uint32_t * iot_keys;
+ uint32_t iot_index;
+ uint32_t iot_next_index;
+
+ isns_object_template_t *iot_container;
+
+ unsigned int iot_relation_type;
+ isns_relation_t * (*iot_build_relation)(isns_db_t *,
+ isns_object_t *,
+ const isns_object_list_t *);
+
+ unsigned int iot_vendor_specific : 1;
+};
+
+struct isns_object {
+ /* There are two kinds of users of an object
+ * - Temporary references that result from the
+ * object being examined; being on a list,
+ * etc. The main purpose of these references
+ * is to make sure the object doesn't go away
+ * while being used.
+ *
+ * These are accounted for by ie_users.
+ *
+ * - Permanent references that result from the
+ * object being references by other objects
+ * (usually relations) such as a Portal Group,
+ * or a Discovery Domain.
+ *
+ * These are accounted for by ie_references.
+ *
+ * The main purpose of these references is to
+ * model some of the weirder life cycle states
+ * described in RFC 4711.
+ *
+ * Every reference via ie_references implies a
+ * reference via ie_users.
+ */
+ unsigned int ie_users;
+ unsigned int ie_references;
+
+ uint32_t ie_index;
+
+ unsigned int ie_state;
+ unsigned int ie_flags;
+ time_t ie_mtime;
+
+ uint32_t ie_scn_mask; /* Events this node listens for */
+ uint32_t ie_scn_bits; /* Current event bits */
+
+ isns_attr_list_t ie_attrs;
+ isns_object_t * ie_container;
+ isns_object_template_t *ie_template;
+
+ isns_relation_t * ie_relation;
+ isns_object_list_t ie_children;
+
+ /* Bit vector describing DD membership */
+ isns_bitvector_t * ie_membership;
+
+ /* Support for virtual objects */
+ int (*ie_rebuild)(isns_object_t *, isns_db_t *);
+};
+
+typedef struct isns_object_ref {
+ isns_object_t * obj;
+} isns_object_ref_t;
+
+enum {
+ ISNS_RELATION_NONE = 0,
+ ISNS_RELATION_PORTAL_GROUP,
+};
+
+struct isns_relation {
+ unsigned int ir_type;
+ unsigned int ir_users;
+ isns_object_t * ir_object;
+ isns_object_ref_t ir_subordinate[2];
+};
+
+typedef struct isns_relation_soup isns_relation_soup_t;
+
+typedef struct isns_relation_list isns_relation_list_t;
+struct isns_relation_list {
+ unsigned int irl_count;
+ isns_relation_t ** irl_data;
+};
+#define ISNS_RELATION_LIST_INIT { .irl_count = 0, .irl_data = NULL }
+
+#define ISNS_OBJECT_DIRTY 0x0001
+#define ISNS_OBJECT_PRIVATE 0x0002
+#define ISNS_OBJECT_DEAD 0x0004
+
+enum {
+ ISNS_OBJECT_STATE_LARVAL,
+ ISNS_OBJECT_STATE_MATURE,
+ ISNS_OBJECT_STATE_LIMBO,
+ ISNS_OBJECT_STATE_DEAD,
+};
+
+extern int isns_object_remove_member(isns_object_t *obj,
+ const isns_attr_t *attr,
+ const uint32_t *subordinate_tags);
+
+extern void isns_object_reference_set(isns_object_ref_t *ref,
+ isns_object_t *obj);
+extern void isns_object_reference_drop(isns_object_ref_t *ref);
+
+extern const char *isns_object_state_string(unsigned int);
+
+extern isns_object_template_t *isns_object_template_by_name(const char *);
+extern int isns_object_is_valid_container(const isns_object_t *,
+ isns_object_template_t *);
+
+extern void isns_object_set_scn_mask(isns_object_t *, uint32_t);
+
+extern isns_object_t *isns_create_default_domain(void);
+
+/*
+ * Helper macros for object type check
+ */
+#define __ISNS_OBJECT_TYPE_CHECK(obj, type) \
+ ((obj)->ie_template == &isns_##type##_template)
+#define ISNS_IS_ENTITY(obj) __ISNS_OBJECT_TYPE_CHECK(obj, entity)
+#define ISNS_IS_ISCSI_NODE(obj) __ISNS_OBJECT_TYPE_CHECK(obj, iscsi_node)
+#define ISNS_IS_FC_PORT(obj) __ISNS_OBJECT_TYPE_CHECK(obj, fc_port)
+#define ISNS_IS_FC_NODE(obj) __ISNS_OBJECT_TYPE_CHECK(obj, fc_node)
+#define ISNS_IS_PORTAL(obj) __ISNS_OBJECT_TYPE_CHECK(obj, portal)
+#define ISNS_IS_PG(obj) __ISNS_OBJECT_TYPE_CHECK(obj, iscsi_pg)
+#define ISNS_IS_POLICY(obj) __ISNS_OBJECT_TYPE_CHECK(obj, policy)
+#define ISNS_IS_DD(obj) __ISNS_OBJECT_TYPE_CHECK(obj, dd)
+#define ISNS_IS_DDSET(obj) __ISNS_OBJECT_TYPE_CHECK(obj, ddset)
+
+#endif /* ISNS_OBJECTS_H */
diff --git a/utils/open-isns/parser.c b/utils/open-isns/parser.c
new file mode 100644
index 0000000..378f2c8
--- /dev/null
+++ b/utils/open-isns/parser.c
@@ -0,0 +1,134 @@
+/*
+ * parser.c - simple line based parser
+ *
+ * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <err.h>
+#include "util.h"
+
+/*
+ * By default, the parser will recognize any white space
+ * as "word" separators.
+ * If you need additional separators, you can put them
+ * here.
+ */
+const char * parser_separators = NULL;
+const char * parser_punctuation = "=";
+
+char *
+parser_get_next_line(FILE *fp)
+{
+ static char buffer[8192];
+ unsigned int n = 0, count = 0;
+ int c, continuation = 0;
+
+ while (n < sizeof(buffer) - 1) {
+ c = fgetc(fp);
+ if (c == EOF)
+ break;
+
+ count++;
+ if (c == '\r')
+ continue;
+ /* Discard all blanks
+ * following a backslash-newline
+ */
+ if (continuation) {
+ if (c == ' ' || c == '\t')
+ continue;
+ continuation = 0;
+ }
+
+ if (c == '\n') {
+ if (n && buffer[n-1] == '\\') {
+ buffer[--n] = '\0';
+ continuation = 1;
+ }
+ while (n && isspace(buffer[n-1]))
+ buffer[--n] = '\0';
+ if (!continuation)
+ break;
+ buffer[n++] = ' ';
+ continue;
+ }
+
+ buffer[n++] = c;
+ }
+
+ if (count == 0)
+ return NULL;
+
+ buffer[n] = '\0';
+ return buffer;
+}
+
+static inline int
+is_separator(char c)
+{
+ if (isspace(c))
+ return 1;
+ return parser_separators && c && strchr(parser_separators, c);
+}
+
+static inline int
+is_punctuation(char c)
+{
+ return parser_punctuation && c && strchr(parser_punctuation, c);
+}
+
+char *
+parser_get_next_word(char **sp)
+{
+ static char buffer[512];
+ char *s = *sp, *p = buffer;
+
+ while (is_separator(*s))
+ ++s;
+
+ if (*s == '\0')
+ goto done;
+
+ if (is_punctuation(*s)) {
+ *p++ = *s++;
+ goto done;
+ }
+
+ while (*s && !is_separator(*s) && !is_punctuation(*s))
+ *p++ = *s++;
+
+done:
+ *p++ = '\0';
+ *sp = s;
+ return buffer[0]? buffer : NULL;
+}
+
+int
+parser_split_line(char *line, unsigned int argsmax, char **argv)
+{
+ unsigned int argc = 0;
+ char *s;
+
+ while (argc < argsmax && (s = parser_get_next_word(&line)))
+ argv[argc++] = strdup(s);
+ return argc;
+}
+
+char *
+parser_get_rest_of_line(char **sp)
+{
+ char *s = *sp, *res = NULL;
+
+ while (is_separator(*s))
+ ++s;
+
+ *sp = "";
+ if (*s != '\0')
+ res = s;
+ return res;
+}
diff --git a/utils/open-isns/paths.h b/utils/open-isns/paths.h
new file mode 100644
index 0000000..b54612c
--- /dev/null
+++ b/utils/open-isns/paths.h
@@ -0,0 +1,22 @@
+/*
+ * Compile time configuration.
+ * For now, let's keep it simple and ignore autoconf...
+ *
+ * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_CONFIG_H
+#define ISNS_CONFIG_H
+
+#define __OPENISNS_MKVERSION(maj, min) (((maj) << 8) + (min))
+#define OPENISNS_VERSION __OPENISNS_MKVERSION(0, 90);
+#define OPENISNS_VERSION_STRING "0.90"
+
+#define ISNS_ETCDIR "/etc/isns"
+#define ISNS_RUNDIR "/var/run"
+#define ISNS_DEFAULT_ISNSD_CONFIG ISNS_ETCDIR "/isnsd.conf"
+#define ISNS_DEFAULT_ISNSDD_CONFIG ISNS_ETCDIR "/isnsdd.conf"
+#define ISNS_DEFAULT_ISNSADM_CONFIG ISNS_ETCDIR "/isnsadm.conf"
+#define ISNS_DEFAULT_LOCAL_REGISTRY ISNS_RUNDIR "/isns.registry"
+
+#endif /* ISNS_CONFIG_H */
diff --git a/utils/open-isns/pidfile.c b/utils/open-isns/pidfile.c
new file mode 100644
index 0000000..3384373
--- /dev/null
+++ b/utils/open-isns/pidfile.c
@@ -0,0 +1,98 @@
+/*
+ * write pidfile
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+__update_pidfile(int fd)
+{
+ char pidbuf[32];
+
+ snprintf(pidbuf, sizeof(pidbuf), "%u\n", getpid());
+ if (write(fd, pidbuf, strlen(pidbuf)) < 0)
+ isns_fatal("Error writing pid file: %m\n");
+ close(fd);
+}
+
+static pid_t
+__read_pidfile(const char *filename)
+{
+ char pidbuf[32];
+ FILE *fp;
+ pid_t pid = -1;
+
+ fp = fopen(filename, "r");
+ if (fp != NULL) {
+ if (fgets(pidbuf, sizeof(pidbuf), fp))
+ pid = strtoul(pidbuf, NULL, 0);
+ fclose(fp);
+ }
+ return pid;
+}
+
+void
+isns_write_pidfile(const char *filename)
+{
+ int fd;
+ pid_t pid;
+
+ fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0644);
+ if (fd >= 0) {
+ __update_pidfile(fd);
+ return;
+ }
+
+ if (errno != EEXIST)
+ isns_fatal("Error creating pid file %s: %m\n",
+ filename);
+
+ /* If the pid file is stale, remove it.
+ * Not really needed in real life, but
+ * highly convenient for debugging :) */
+ if ((pid = __read_pidfile(filename)) > 0
+ && kill(pid, 0) < 0
+ && errno == ESRCH) {
+ isns_debug_general(
+ "Removing stale PID file %s\n",
+ filename);
+ unlink(filename);
+ }
+
+ /* Try again */
+ fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0644);
+ if (fd < 0)
+ isns_fatal("PID file exists; another daemon "
+ "seems to be running\n");
+
+ __update_pidfile(fd);
+}
+
+void
+isns_update_pidfile(const char *filename)
+{
+ int fd;
+
+ fd = open(filename, O_WRONLY);
+ if (fd < 0) {
+ isns_fatal("Error opening pid file %s: %m\n",
+ filename);
+ }
+
+ __update_pidfile(fd);
+}
+
+void
+isns_remove_pidfile(const char *filename)
+{
+ unlink(filename);
+}
diff --git a/utils/open-isns/pki.c b/utils/open-isns/pki.c
new file mode 100644
index 0000000..f3af922
--- /dev/null
+++ b/utils/open-isns/pki.c
@@ -0,0 +1,536 @@
+/*
+ * PKI related functions
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <fcntl.h>
+#include "isns.h"
+#include "security.h"
+#include "util.h"
+#include "config.h"
+
+#ifdef WITH_SECURITY
+
+/* versions prior to 9.6.8 didn't seem to have these */
+#if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906080L
+# define EVP_MD_CTX_init(c) do { } while (0)
+# define EVP_MD_CTX_cleanup(c) do { } while (0)
+#endif
+#if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906070L
+# define i2d_DSA_PUBKEY i2d_DSA_PUBKEY_backwards
+
+static int i2d_DSA_PUBKEY_backwards(DSA *, unsigned char **);
+#endif
+
+static int isns_openssl_init = 0;
+
+static int isns_dsasig_verify(isns_security_t *ctx,
+ isns_principal_t *peer,
+ buf_t *pdu,
+ const struct isns_authblk *);
+static int isns_dsasig_sign(isns_security_t *ctx,
+ isns_principal_t *peer,
+ buf_t *pdu,
+ struct isns_authblk *);
+static EVP_PKEY *isns_dsasig_load_private_pem(isns_security_t *ctx,
+ const char *filename);
+static EVP_PKEY *isns_dsasig_load_public_pem(isns_security_t *ctx,
+ const char *filename);
+static DSA * isns_dsa_load_params(const char *);
+
+
+/*
+ * Create a DSA security context
+ */
+isns_security_t *
+isns_create_dsa_context(void)
+{
+ isns_security_t *ctx;
+
+ if (!isns_openssl_init) {
+ ERR_load_crypto_strings();
+ OpenSSL_add_all_algorithms();
+ OpenSSL_add_all_ciphers();
+ OpenSSL_add_all_digests();
+ isns_openssl_init = 1;
+ }
+
+ ctx = isns_calloc(1, sizeof(*ctx));
+
+ ctx->is_name = "DSA";
+ ctx->is_type = ISNS_AUTH_TYPE_SHA1_DSA;
+ ctx->is_replay_window = isns_config.ic_auth.replay_window;
+ ctx->is_timestamp_jitter = isns_config.ic_auth.timestamp_jitter;
+
+ ctx->is_verify = isns_dsasig_verify;
+ ctx->is_sign = isns_dsasig_sign;
+ ctx->is_load_private = isns_dsasig_load_private_pem;
+ ctx->is_load_public = isns_dsasig_load_public_pem;
+
+ isns_debug_auth("Created DSA authentication context\n");
+ return ctx;
+}
+
+/*
+ * DSA signature generation and verification
+ */
+static void
+isns_message_digest(EVP_MD_CTX *md, const buf_t *pdu,
+ const struct isns_authblk *blk)
+{
+ uint64_t stamp;
+
+ EVP_DigestUpdate(md, buf_head(pdu), buf_avail(pdu));
+
+ /* The RFC doesn't say which pieces of the
+ * message should be hashed.
+ * We make an educated guess.
+ */
+ stamp = htonll(blk->iab_timestamp);
+ EVP_DigestUpdate(md, &stamp, sizeof(stamp));
+}
+
+static void
+isns_dsasig_report_errors(const char *msg, isns_print_fn_t *fn)
+{
+ unsigned long code;
+
+ fn("%s - OpenSSL errors follow:\n", msg);
+ while ((code = ERR_get_error()) != 0)
+ fn("> %s: %s\n",
+ ERR_func_error_string(code),
+ ERR_reason_error_string(code));
+}
+
+int
+isns_dsasig_sign(isns_security_t *ctx,
+ isns_principal_t *peer,
+ buf_t *pdu,
+ struct isns_authblk *blk)
+{
+ static unsigned char signature[1024];
+ unsigned int sig_len = sizeof(signature);
+ EVP_MD_CTX md_ctx;
+ EVP_PKEY *pkey;
+ int err;
+
+ if ((pkey = peer->is_key) == NULL)
+ return 0;
+
+ if (pkey->type != EVP_PKEY_DSA) {
+ isns_debug_message(
+ "Incompatible public key (spi=%s)\n",
+ peer->is_name);
+ return 0;
+ }
+ if (EVP_PKEY_size(pkey) > sizeof(signature)) {
+ isns_error("isns_dsasig_sign: signature buffer too small\n");
+ return 0;
+ }
+ if (pkey->pkey.dsa->priv_key == NULL) {
+ isns_error("isns_dsasig_sign: oops, seems to be a public key\n");
+ return 0;
+ }
+
+ isns_debug_auth("Signing messages with spi=%s, DSA/%u\n",
+ peer->is_name, EVP_PKEY_bits(pkey));
+
+ EVP_MD_CTX_init(&md_ctx);
+ EVP_SignInit(&md_ctx, EVP_dss1());
+ isns_message_digest(&md_ctx, pdu, blk);
+ err = EVP_SignFinal(&md_ctx,
+ signature, &sig_len,
+ pkey);
+ EVP_MD_CTX_cleanup(&md_ctx);
+
+ if (err == 0) {
+ isns_dsasig_report_errors("EVP_SignFinal failed", isns_error);
+ return 0;
+ }
+
+ blk->iab_sig = signature;
+ blk->iab_sig_len = sig_len;
+ return 1;
+}
+
+int
+isns_dsasig_verify(isns_security_t *ctx,
+ isns_principal_t *peer,
+ buf_t *pdu,
+ const struct isns_authblk *blk)
+{
+ EVP_MD_CTX md_ctx;
+ EVP_PKEY *pkey;
+ int err;
+
+ if ((pkey = peer->is_key) == NULL)
+ return 0;
+
+ if (pkey->type != EVP_PKEY_DSA) {
+ isns_debug_message(
+ "Incompatible public key (spi=%s)\n",
+ peer->is_name);
+ return 0;
+ }
+
+ EVP_MD_CTX_init(&md_ctx);
+ EVP_VerifyInit(&md_ctx, EVP_dss1());
+ isns_message_digest(&md_ctx, pdu, blk);
+ err = EVP_VerifyFinal(&md_ctx,
+ blk->iab_sig, blk->iab_sig_len,
+ pkey);
+ EVP_MD_CTX_cleanup(&md_ctx);
+
+ if (err == 0) {
+ isns_debug_auth("*** Incorrect signature ***\n");
+ return 0;
+ }
+ if (err < 0) {
+ isns_dsasig_report_errors("EVP_VerifyFinal failed", isns_error);
+ return 0;
+ }
+
+ isns_debug_message("Good signature from %s\n",
+ peer->is_name?: "<server>");
+ return 1;
+}
+
+EVP_PKEY *
+isns_dsasig_load_private_pem(isns_security_t *ctx, const char *filename)
+{
+ EVP_PKEY *pkey;
+ FILE *fp;
+
+ if (!(fp = fopen(filename, "r"))) {
+ isns_error("Unable to open DSA keyfile %s: %m\n",
+ filename);
+ return 0;
+ }
+
+ pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
+ fclose(fp);
+ return pkey;
+}
+
+EVP_PKEY *
+isns_dsasig_load_public_pem(isns_security_t *ctx, const char *filename)
+{
+ EVP_PKEY *pkey;
+ FILE *fp;
+
+ if (!(fp = fopen(filename, "r"))) {
+ isns_error("Unable to open DSA keyfile %s: %m\n",
+ filename);
+ return 0;
+ }
+
+ pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
+ if (pkey == NULL) {
+ isns_dsasig_report_errors("Error loading DSA public key",
+ isns_error);
+ }
+
+ fclose(fp);
+ return pkey;
+}
+
+EVP_PKEY *
+isns_dsa_decode_public(const void *ptr, size_t len)
+{
+ const unsigned char *der = ptr;
+ EVP_PKEY *evp;
+ DSA *dsa;
+
+ /* Assigning ptr to a temporary variable avoids a silly
+ * compiled warning about type-punning. */
+ dsa = d2i_DSA_PUBKEY(NULL, &der, len);
+ if (dsa == NULL)
+ return NULL;
+
+ evp = EVP_PKEY_new();
+ EVP_PKEY_assign_DSA(evp, dsa);
+ return evp;
+}
+
+int
+isns_dsa_encode_public(EVP_PKEY *pkey, void **ptr, size_t *len)
+{
+ int bytes;
+
+ *ptr = NULL;
+ bytes = i2d_DSA_PUBKEY(pkey->pkey.dsa, (unsigned char **) ptr);
+ if (bytes < 0)
+ return 0;
+
+ *len = bytes;
+ return 1;
+}
+
+EVP_PKEY *
+isns_dsa_load_public(const char *name)
+{
+ return isns_dsasig_load_public_pem(NULL, name);
+}
+
+int
+isns_dsa_store_private(const char *name, EVP_PKEY *key)
+{
+ FILE *fp;
+ int rv, fd;
+
+ if ((fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
+ isns_error("Cannot save DSA key to %s: %m\n", name);
+ return 0;
+ }
+
+ if (!(fp = fdopen(fd, "w"))) {
+ isns_error("fdopen(%s): %m\n", name);
+ close(fd);
+ return 0;
+ }
+
+ rv = PEM_write_PrivateKey(fp, key, NULL, NULL, 0, 0, NULL);
+ fclose(fp);
+
+ if (rv == 0)
+ isns_dsasig_report_errors("Failed to store private key",
+ isns_error);
+
+ return rv;
+}
+
+int
+isns_dsa_store_public(const char *name, EVP_PKEY *key)
+{
+ FILE *fp;
+ int rv;
+
+ if (!(fp = fopen(name, "w"))) {
+ isns_error("Unable to open %s: %m\n", name);
+ return 0;
+ }
+
+ rv = PEM_write_PUBKEY(fp, key);
+ fclose(fp);
+
+ if (rv == 0)
+ isns_dsasig_report_errors("Failed to store public key",
+ isns_error);
+
+ return rv;
+}
+
+
+/*
+ * DSA key generation
+ */
+EVP_PKEY *
+isns_dsa_generate_key(void)
+{
+ EVP_PKEY *pkey;
+ DSA *dsa = NULL;
+
+ if (!(dsa = isns_dsa_load_params(isns_config.ic_dsa.param_file)))
+ goto failed;
+
+ if (!DSA_generate_key(dsa)) {
+ isns_dsasig_report_errors("Failed to generate DSA key",
+ isns_error);
+ goto failed;
+ }
+
+ pkey = EVP_PKEY_new();
+ EVP_PKEY_assign_DSA(pkey, dsa);
+ return pkey;
+
+failed:
+ if (dsa)
+ DSA_free(dsa);
+ return NULL;
+}
+
+DSA *
+isns_dsa_load_params(const char *filename)
+{
+ FILE *fp;
+ DSA *dsa;
+
+ if (!filename) {
+ isns_error("Cannot generate key - no DSA parameter file\n");
+ return NULL;
+ }
+ if (!(fp = fopen(filename, "r"))) {
+ isns_error("Unable to open %s: %m\n", filename);
+ return NULL;
+ }
+
+ dsa = PEM_read_DSAparams(fp, NULL, NULL, NULL);
+ fclose(fp);
+
+ if (dsa == NULL) {
+ isns_dsasig_report_errors("Error loading DSA parameters",
+ isns_error);
+ }
+
+ return dsa;
+}
+
+static void
+isns_dsa_param_gen_callback(int stage, int index, void *dummy)
+{
+ if (stage == 0)
+ write(1, "+", 1);
+ else if (stage == 1)
+ write(1, ".", 1);
+ else if (stage == 2)
+ write(1, "/", 1);
+}
+
+int
+isns_dsa_init_params(const char *filename)
+{
+ FILE *fp;
+ DSA *dsa;
+
+ if (access(filename, R_OK) == 0)
+ return 1;
+
+ isns_mkdir_recursive(isns_dirname(filename));
+ if (!(fp = fopen(filename, "w"))) {
+ isns_error("Unable to open %s: %m\n", filename);
+ return 0;
+ }
+
+ isns_notice("Generating DSA parameters; this may take a while\n");
+ dsa = DSA_generate_parameters(1024, NULL, 0,
+ NULL, NULL, isns_dsa_param_gen_callback, NULL);
+ write(1, "\n", 1);
+
+ if (dsa == NULL) {
+ isns_dsasig_report_errors("Error generating DSA parameters",
+ isns_error);
+ fclose(fp);
+ return 0;
+ }
+
+ if (!PEM_write_DSAparams(fp, dsa)) {
+ isns_dsasig_report_errors("Error writing DSA parameters",
+ isns_error);
+ DSA_free(dsa);
+ fclose(fp);
+ return 0;
+ }
+ DSA_free(dsa);
+ fclose(fp);
+ return 1;
+}
+
+/*
+ * Make sure the authentication key is present.
+ */
+int
+isns_dsa_init_key(const char *filename)
+{
+ char pubkey_path[1024];
+ EVP_PKEY *pkey;
+
+ isns_mkdir_recursive(isns_dirname(filename));
+ snprintf(pubkey_path, sizeof(pubkey_path),
+ "%s.pub", filename);
+ if (access(filename, R_OK) == 0
+ && access(pubkey_path, R_OK) == 0)
+ return 1;
+
+ if (!(pkey = isns_dsa_generate_key())) {
+ isns_error("Failed to generate AuthKey\n");
+ return 0;
+ }
+
+ if (!isns_dsa_store_private(filename, pkey)) {
+ isns_error("Unable to write private key to %s\n", filename);
+ return 0;
+ }
+ isns_notice("Stored private key in %s\n", filename);
+
+ if (!isns_dsa_store_public(pubkey_path, pkey)) {
+ isns_error("Unable to write public key to %s\n", pubkey_path);
+ return 0;
+ }
+ isns_notice("Stored private key in %s\n", pubkey_path);
+
+ return 1;
+}
+
+/*
+ * Simple keystore - this is a flat directory, with
+ * public key files using the SPI as their name.
+ */
+typedef struct isns_simple_keystore isns_simple_keystore_t;
+struct isns_simple_keystore {
+ isns_keystore_t sc_base;
+ char * sc_dirpath;
+};
+
+/*
+ * Load a DSA key from the cert store
+ * In fact, this will load RSA keys as well.
+ */
+static EVP_PKEY *
+__isns_simple_keystore_find(isns_keystore_t *store_base,
+ const char *name, size_t namelen)
+{
+ isns_simple_keystore_t *store = (isns_simple_keystore_t *) store_base;
+ char pathname[PATH_MAX];
+
+ /* Refuse to open key files with names
+ * that refer to parent directories */
+ if (memchr(name, '/', namelen) || name[0] == '.')
+ return NULL;
+
+ snprintf(pathname, sizeof(pathname),
+ "%s/%.*s", store->sc_dirpath,
+ (int) namelen, name);
+ if (access(pathname, R_OK) < 0)
+ return NULL;
+ return isns_dsasig_load_public_pem(NULL, pathname);
+}
+
+isns_keystore_t *
+isns_create_simple_keystore(const char *dirname)
+{
+ isns_simple_keystore_t *store;
+
+ store = isns_calloc(1, sizeof(*store));
+ store->sc_base.ic_name = "simple key store";
+ store->sc_base.ic_find = __isns_simple_keystore_find;
+ store->sc_dirpath = isns_strdup(dirname);
+
+ return (isns_keystore_t *) store;
+}
+
+#if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906070L
+#undef i2d_DSA_PUBKEY
+
+int
+i2d_DSA_PUBKEY_backwards(DSA *dsa, unsigned char **ptr)
+{
+ unsigned char *buf;
+ int len;
+
+ len = i2d_DSA_PUBKEY(dsa, NULL);
+ if (len < 0)
+ return 0;
+
+ *ptr = buf = OPENSSL_malloc(len);
+ return i2d_DSA_PUBKEY(dsa, &buf);
+}
+#endif
+
+#endif /* WITH_SECURITY */
diff --git a/utils/open-isns/policy.c b/utils/open-isns/policy.c
new file mode 100644
index 0000000..46de15b
--- /dev/null
+++ b/utils/open-isns/policy.c
@@ -0,0 +1,577 @@
+/*
+ * Open-iSNS policy engine
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ *
+ * For now, policy is static. We can make it configurable
+ * later.
+ */
+
+#include <string.h>
+#include "isns.h"
+#include "security.h"
+#include "objects.h"
+#include "message.h"
+#include "util.h"
+
+/*
+ A brief discussion of policy
+
+ For now, a principal's name (ie its SPI string) *must* match
+ the iSNS source name it uses.
+
+ Special care needs to be taken to restrict which principals
+ are permitted to act as a control node. For now, we don't
+ implement control node semantics.
+
+ */
+
+static unsigned int isns_policy_gen = 0;
+
+/*
+ * Administrative policy (everything allowed,
+ * talks to entity "CONTROL"
+ */
+static isns_policy_t isns_superhero_powers = {
+ .ip_name = "administrator",
+ .ip_users = 1,
+ .ip_gen = 0,
+
+ .ip_entity = ISNS_ENTITY_CONTROL,
+ .ip_functions = ~0,
+ .ip_object_types = ~0,
+ .ip_node_types = ~0,
+};
+
+/*
+ * Policy for anon user
+ */
+static isns_policy_t isns_dweeb_powers = {
+ .ip_name = "anonymous",
+ .ip_users = 1,
+ .ip_gen = 0,
+
+ .ip_functions = 1 << ISNS_DEVICE_ATTRIBUTE_QUERY,
+ .ip_object_types = 0,
+ .ip_node_types = 0,
+};
+
+#define IS_ANON_POLICY(p) ((p) == &isns_dweeb_powers)
+
+/*
+ * These are used when security is turned off.
+ * Essentially the same as superhero, except
+ * no eid specified.
+ */
+static isns_policy_t isns_flyingpigs_powers = {
+ .ip_name = "insecure",
+ .ip_users = 1,
+ .ip_gen = 0,
+
+ .ip_functions = ~0,
+ .ip_object_types = ~0,
+ .ip_node_types = ~0,
+};
+
+
+isns_policy_t *
+isns_policy_bind(const isns_message_t *msg)
+{
+ isns_policy_t *policy = NULL;
+ isns_principal_t *princ = NULL;
+
+ /* When the admin turns off gravity,
+ * pigs can fly, too. */
+ if (isns_config.ic_security == 0) {
+ policy = &isns_flyingpigs_powers;
+ goto found;
+ }
+
+ /* If the caller is the local root user, s/he can
+ * do anything. */
+ if (msg->im_creds && msg->im_creds->uid == 0) {
+ policy = &isns_superhero_powers;
+ goto found;
+ }
+
+ /* Tie the SPI given in the auth block to a
+ * source name.
+ * For now, the names have to match. Down the road,
+ * there may be more flexible schemes.
+ */
+ if ((princ = msg->im_security) != NULL) {
+ if ((policy = princ->is_policy) != NULL)
+ goto found;
+
+ isns_error("Internal error - no policy for "
+ "principal %s!\n",
+ princ->is_name);
+ }
+
+ policy = &isns_dweeb_powers;
+
+found:
+ policy->ip_users++;
+ return policy;
+}
+
+/*
+ * Check whether the call is permitted.
+ * This is particularly useful to prevent rogue
+ * clients from messing with Discovery Domains.
+ */
+int
+isns_policy_validate_function(const isns_policy_t *policy,
+ const isns_message_t *msg)
+{
+ uint32_t function = msg->im_header.i_function;
+ int rv = 0;
+
+ if (function >= 32) {
+ isns_debug_auth("Bad function code %08x\n", function);
+ return 0;
+ }
+
+ if (!(policy->ip_functions & (1 << function)))
+ goto reject;
+
+ rv = 1;
+
+reject:
+ isns_debug_auth(":: policy %s function %s (%04x) %s\n",
+ policy->ip_name,
+ isns_function_name(function), function,
+ rv? "permitted" : "DENIED");
+ return rv;
+}
+
+/*
+ * Helper function to validate node names and source names
+ */
+static int
+__validate_node_name(const isns_policy_t *policy, const char *name)
+{
+ const struct string_array *ap;
+ unsigned int i;
+
+ /* Control nodes get to do everything */
+ if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
+ return 1;
+
+ ap = &policy->ip_node_names;
+ for (i = 0; i < ap->count; ++i) {
+ const char *s;
+
+ s = ap->list[i];
+ if (s == NULL)
+ continue;
+ if (isns_source_pattern_match(s, name))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Validate the source of a message
+ */
+int
+isns_policy_validate_source(const isns_policy_t *policy,
+ const isns_source_t *source)
+{
+ const char *src_name = isns_source_name(source);
+ int rv = 0;
+
+ if (!__validate_node_name(policy, src_name))
+ goto reject;
+
+ rv = 1;
+
+reject:
+ isns_debug_auth(":: policy %s source %s %s\n",
+ policy->ip_name, src_name,
+ rv? "permitted" : "DENIED");
+ return rv;
+}
+
+/*
+ * Check whether the entity name specified by the client
+ * is actually his to use.
+ */
+int
+isns_policy_validate_entity(const isns_policy_t *policy,
+ const char *eid)
+{
+ int rv = 0, eidlen;
+
+ /* Control nodes get to do everything */
+ if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
+ goto accept;
+
+ /* For anonymous clients, refuse any attempt to
+ * create an entity */
+ if (IS_ANON_POLICY(policy))
+ goto reject;
+
+ /* If no entity is assigned, this means the client
+ * is not permitted to specify its own entity name,
+ * and accept what we assign it.
+ */
+ if (policy->ip_entity == NULL)
+ goto reject;
+
+ eidlen = strlen(policy->ip_entity);
+ if (strncasecmp(policy->ip_entity, eid, eidlen)
+ && (eid[eidlen] == ':' || eid[eidlen] == '\0'))
+ goto reject;
+
+accept: rv = 1;
+
+reject:
+ isns_debug_auth(":: policy %s entity ID %s %s\n",
+ policy->ip_name, eid,
+ rv? "permitted" : "DENIED");
+ return rv;
+}
+
+const char *
+isns_policy_default_entity(const isns_policy_t *policy)
+{
+ return policy->ip_entity;
+}
+
+int
+isns_policy_validate_node_name(const isns_policy_t *policy,
+ const char *node_name)
+{
+ int rv = 0;
+
+ /* Control nodes get to do everything */
+ if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
+ goto accept;
+
+ if (!__validate_node_name(policy, node_name))
+ goto reject;
+
+accept: rv = 1;
+reject:
+ isns_debug_auth(":: policy %s storage node name %s %s\n",
+ policy->ip_name, node_name,
+ rv? "permitted" : "DENIED");
+ return rv;
+}
+
+/*
+ * Check whether the client is allowed to access
+ * the given object in a particular way.
+ */
+static int
+__isns_policy_validate_object_access(const isns_policy_t *policy,
+ const isns_source_t *source,
+ const isns_object_t *obj,
+ isns_object_template_t *tmpl,
+ unsigned int function)
+{
+ uint32_t mask, perm = ISNS_PERMISSION_WRITE;
+ int rv = 0;
+
+ /* Control nodes get to do everything */
+ if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
+ goto accept;
+
+ if (function == ISNS_DEVICE_ATTRIBUTE_QUERY
+ || function == ISNS_DEVICE_GET_NEXT)
+ perm = ISNS_PERMISSION_READ;
+
+ /*
+ * 5.6.1. Source Attribute
+ *
+ * For messages that change the contents of the iSNS
+ * database, the iSNS server MUST verify that the Source
+ * Attribute identifies either a Control Node or a Storage
+ * Node that is a part of the Network Entity containing
+ * the added, deleted, or modified objects.
+ *
+ * Note: this statement makes sense for nodes, portals
+ * etc, but not for discovery domains, which are not
+ * part of any network entity (but the Control Node clause
+ * above still applies).
+ */
+ if (perm == ISNS_PERMISSION_WRITE && obj != NULL) {
+ const isns_object_t *entity;
+
+ entity = obj->ie_container;
+ if (entity && entity != source->is_entity)
+ goto refuse;
+
+ /* You're not allowed to modify virtual objects */
+ if (obj->ie_rebuild)
+ goto refuse;
+ }
+
+ /* Check whether the client is permitted
+ to access such an object */
+ mask = ISNS_ACCESS(tmpl->iot_handle, perm);
+ if (!(policy->ip_object_types & mask))
+ goto refuse;
+
+ if (source->is_untrusted && (obj->ie_flags & ISNS_OBJECT_PRIVATE))
+ goto refuse;
+
+accept:
+ rv = 1;
+
+refuse:
+ if (obj) {
+ isns_debug_auth(":: policy %s operation %s on object %08x (%s) %s\n",
+ policy->ip_name,
+ isns_function_name(function),
+ obj->ie_index,
+ tmpl->iot_name,
+ rv? "permitted" : "DENIED");
+ } else {
+ isns_debug_auth(":: policy %s operation %s on %s object %s\n",
+ policy->ip_name,
+ isns_function_name(function),
+ tmpl->iot_name,
+ rv? "permitted" : "DENIED");
+ }
+ return rv;
+}
+
+/*
+ * Check whether the client is allowed to access
+ * the given object. This is called for read functions.
+ */
+int
+isns_policy_validate_object_access(const isns_policy_t *policy,
+ const isns_source_t *source,
+ const isns_object_t *obj,
+ unsigned int function)
+{
+ return __isns_policy_validate_object_access(policy, source,
+ obj, obj->ie_template,
+ function);
+}
+
+/*
+ * Check whether the client is allowed to update
+ * the given object.
+ */
+int
+isns_policy_validate_object_update(const isns_policy_t *policy,
+ const isns_source_t *source,
+ const isns_object_t *obj,
+ const isns_attr_list_t *attrs,
+ unsigned int function)
+{
+ return __isns_policy_validate_object_access(policy, source,
+ obj, obj->ie_template,
+ function);
+}
+
+/*
+ * Check whether the client is allowed to create an object
+ * with the given attrs.
+ */
+int
+isns_policy_validate_object_creation(const isns_policy_t *policy,
+ const isns_source_t *source,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *keys,
+ const isns_attr_list_t *attrs,
+ unsigned int function)
+{
+ const char *name = NULL;
+
+ if (tmpl == &isns_entity_template) {
+ /* DevReg messages may contain an empty EID
+ * string, which means the server should select
+ * one. */
+ if (isns_attr_list_get_string(keys,
+ ISNS_TAG_ENTITY_IDENTIFIER, &name)
+ && !isns_policy_validate_entity(policy, name))
+ return 0;
+ }
+
+ if (tmpl == &isns_iscsi_node_template) {
+ if (isns_attr_list_get_string(keys,
+ ISNS_TAG_ISCSI_NAME, &name)
+ && !isns_policy_validate_node_name(policy, name))
+ return 0;
+ }
+
+ /* Should we also include the permitted portals
+ * in the policy? */
+
+ return __isns_policy_validate_object_access(policy, source,
+ NULL, tmpl, function);
+}
+
+/*
+ * Check whether the client is permitted to access
+ * or create an object of this type.
+ * FIXME: Pass R/W permission bit
+ */
+int
+isns_policy_validate_object_type(const isns_policy_t *policy,
+ isns_object_template_t *tmpl,
+ unsigned int function)
+{
+ uint32_t mask;
+ int rv = 0;
+
+ /* Control nodes get to do everything */
+ if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
+ goto accept;
+
+ mask = ISNS_ACCESS_R(tmpl->iot_handle);
+ if (!(policy->ip_object_types & mask))
+ goto reject;
+
+accept: rv = 1;
+
+reject:
+ isns_debug_auth(":: policy %s operation %s on object type %s %s\n",
+ policy->ip_name,
+ isns_function_name(function),
+ tmpl->iot_name,
+ rv? "permitted" : "DENIED");
+ return rv;
+}
+
+int
+isns_policy_validate_node_type(const isns_policy_t *policy, uint32_t type)
+{
+ int rv = 0;
+
+ if ((~policy->ip_node_types & type) == 0)
+ rv = 1;
+
+ isns_debug_auth(":: policy %s registration of node type 0x%x %s\n",
+ policy->ip_name, type,
+ rv? "permitted" : "DENIED");
+ return rv;
+}
+
+/*
+ * 6.4.4.
+ * Management SCNs provide information about all changes to the network,
+ * regardless of discovery domain membership. Registration for management
+ * SCNs is indicated by setting bit 26 to 1. Only Control Nodes may
+ * register for management SCNs. Bits 30 and 31 may only be enabled if
+ * bit 26 is set to 1.
+ */
+int
+isns_policy_validate_scn_bitmap(const isns_policy_t *policy,
+ uint32_t bitmap)
+{
+ int rv = 1;
+
+ if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
+ goto accept;
+
+ if (!(bitmap & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK)) {
+ if (bitmap & (ISNS_SCN_DD_MEMBER_ADDED_MASK |
+ ISNS_SCN_DD_MEMBER_REMOVED_MASK))
+ goto reject;
+ goto accept;
+ }
+
+reject:
+ rv = 0;
+
+accept:
+ isns_debug_auth(":: policy %s scn bitmap 0x%x %s\n",
+ policy->ip_name, bitmap,
+ rv? "permitted" : "DENIED");
+ return rv;
+}
+
+/*
+ * Create the default policy for a given SPI
+ */
+isns_policy_t *
+isns_policy_default(const char *spi, size_t len)
+{
+ return __isns_policy_alloc(spi, len);
+}
+
+/*
+ * Create the policy object for the server we're
+ * talking to. The server is allowed to send us
+ * ESI and SCN messages, and that's about it.
+ */
+isns_policy_t *
+isns_policy_server(void)
+{
+ isns_policy_t *policy;
+
+ policy = __isns_policy_alloc("<server>", 8);
+
+ policy->ip_functions =
+ (1 << ISNS_ENTITY_STATUS_INQUIRY) |
+ (1 << ISNS_STATE_CHANGE_NOTIFICATION);
+ policy->ip_node_types = 0;
+ policy->ip_object_types = 0;
+ isns_string_array_append(&policy->ip_node_names, "*");
+ return policy;
+}
+
+/*
+ * Allocate an empty policy object
+ */
+isns_policy_t *
+__isns_policy_alloc(const char *spi, size_t len)
+{
+ isns_policy_t *policy;
+
+ policy = isns_calloc(1, sizeof(*policy));
+ policy->ip_name = isns_malloc(len + 1);
+ policy->ip_users = 1;
+ policy->ip_gen = isns_policy_gen;
+
+ memcpy(policy->ip_name, spi, len);
+ policy->ip_name[len] = '\0';
+
+ /* Only register/query allowed */
+ policy->ip_functions =
+ (1 << ISNS_DEVICE_ATTRIBUTE_REGISTER) |
+ (1 << ISNS_DEVICE_ATTRIBUTE_QUERY) |
+ (1 << ISNS_DEVICE_GET_NEXT) |
+ (1 << ISNS_DEVICE_DEREGISTER) |
+ (1 << ISNS_SCN_REGISTER);
+
+ /* Can only register initiator node(s) */
+ policy->ip_node_types =
+ ISNS_ISCSI_INITIATOR_MASK;
+
+ /* Can only view/modify standard objects */
+ policy->ip_object_types = ISNS_DEFAULT_OBJECT_ACCESS;
+
+ return policy;
+}
+
+/*
+ * Release a policy object
+ */
+void
+isns_policy_release(isns_policy_t *policy)
+{
+ if (!policy)
+ return;
+
+ isns_assert(policy->ip_users);
+ if (--(policy->ip_users))
+ return;
+
+ isns_assert(policy != &isns_superhero_powers);
+ isns_assert(policy != &isns_flyingpigs_powers);
+ isns_assert(policy != &isns_dweeb_powers);
+
+ isns_free(policy->ip_name);
+ isns_free(policy->ip_entity);
+ isns_free(policy->ip_dd_default);
+ isns_string_array_destroy(&policy->ip_node_names);
+
+ isns_free(policy);
+}
diff --git a/utils/open-isns/portal-group.c b/utils/open-isns/portal-group.c
new file mode 100644
index 0000000..647bbde
--- /dev/null
+++ b/utils/open-isns/portal-group.c
@@ -0,0 +1,307 @@
+/*
+ * iSNS object model - portal group specific code
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "objects.h"
+#include "vendor.h"
+#include "attrs.h"
+#include "util.h"
+
+/* For relationship stuff - should go */
+#include "db.h"
+
+
+/*
+ * Retrieve attribute @old_tag from object @obj, create a copy with
+ * tag @new_tag, and append it to list @dst.
+ * (Helper function for the portal group stuff)
+ */
+static int
+__isns_object_translate_attr(isns_object_t *obj,
+ uint32_t old_tag, uint32_t new_tag,
+ isns_attr_list_t *dst)
+{
+ isns_value_t value;
+
+ if (!isns_attr_list_get_value(&obj->ie_attrs, old_tag, &value))
+ return 0;
+ isns_attr_list_append_value(dst, new_tag, NULL, &value);
+ return 1;
+}
+
+
+/*
+ * Portal Group
+ */
+static isns_object_t *
+__isns_pg_create(const isns_attr_list_t *attrs, uint32_t pg_tag,
+ isns_object_t *portal, isns_object_t *node)
+{
+ isns_object_t *obj;
+
+ obj = isns_create_object(&isns_iscsi_pg_template, attrs,
+ isns_object_get_entity(portal));
+
+ /*
+ * 3.4
+ *
+ * Each Portal and iSCSI Storage Node registered in an Entity can
+ * be associated using a Portal Group (PG) object. The PG Tag
+ * (PGT), if non-NULL, indicates that the associated Portal
+ * provides access to the associated iSCSI Storage Node in
+ * the Entity. All Portals that have the same PGT value for
+ * a specific iSCSI Storage Node allow coordinated access to
+ * that node.
+ *
+ * 5.6.5.2
+ *
+ * If the PGT of the Portal Group is not NULL, then a relationship
+ * exists between the indicated Storage Node and Portal; if the
+ * PGT is NULL, then no relationship exists.
+ */
+ if (pg_tag != 0) {
+ isns_object_set_uint32(obj,
+ ISNS_TAG_PG_TAG, pg_tag);
+ } else {
+ /* A NULL PGT indicates that the
+ * storage node cannot be accessed through
+ * this portal. */
+ isns_object_set_nil(obj, ISNS_TAG_PG_TAG);
+ }
+
+ /* This object represents a relationship between portal
+ and storage node. Create a relation. */
+ obj->ie_relation = isns_create_relation(obj,
+ ISNS_RELATION_PORTAL_GROUP,
+ portal, node);
+
+ return obj;
+}
+
+/*
+ * Find the portal for a given portal group
+ */
+static isns_object_t *
+__isns_pg_find_portal(isns_db_t *db, isns_object_t *pg,
+ const isns_object_list_t *extra_objs)
+{
+ isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
+ isns_object_t *obj = NULL;
+
+ /* FIXME: ISNS_TAG_PG_PORTAL_IP_ADDR -> ...ADDRESS */
+ if (!__isns_object_translate_attr(pg,
+ ISNS_TAG_PG_PORTAL_IP_ADDR,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ &key_attrs))
+ goto out;
+ if (!__isns_object_translate_attr(pg,
+ ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ &key_attrs))
+ goto out;
+
+ obj = isns_db_lookup(db, &isns_portal_template, &key_attrs);
+ if (!obj && extra_objs)
+ obj = isns_object_list_lookup(extra_objs,
+ &isns_portal_template, &key_attrs);
+
+out:
+ isns_attr_list_destroy(&key_attrs);
+ return obj;
+}
+
+/*
+ * Find the node for a given portal group
+ */
+static isns_object_t *
+__isns_pg_find_node(isns_db_t *db, isns_object_t *pg,
+ const isns_object_list_t *extra_objs)
+{
+ isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
+ isns_object_t *obj = NULL;
+
+ if (!__isns_object_translate_attr(pg,
+ ISNS_TAG_PG_ISCSI_NAME,
+ ISNS_TAG_ISCSI_NAME,
+ &key_attrs))
+ goto out;
+
+ obj = isns_db_lookup(db, &isns_iscsi_node_template, &key_attrs);
+ if (!obj && extra_objs)
+ obj = isns_object_list_lookup(extra_objs,
+ &isns_iscsi_node_template, &key_attrs);
+
+out:
+ isns_attr_list_destroy(&key_attrs);
+ return obj;
+}
+
+/*
+ * When creating a portal group, it must not connect nodes and
+ * portals from other entities. However, it is perfectly fine to
+ * link objects in limbo.
+ */
+static inline int
+__isns_pg_may_relate(isns_object_t *entity, isns_object_t *subordinate)
+{
+ isns_object_t *other;
+
+ other = isns_object_get_entity(subordinate);
+ return other == NULL || other == entity;
+}
+
+/*
+ * Given a portal group object, create the relationship
+ */
+isns_relation_t *
+isns_db_build_pg_relation(isns_db_t *db, isns_object_t *pg,
+ const isns_object_list_t *extra_objs)
+{
+ isns_object_t *entity, *node = NULL, *portal = NULL;
+
+ entity = isns_object_get_entity(pg);
+
+ node = __isns_pg_find_node(db, pg, extra_objs);
+ if (node == NULL) {
+ isns_error("Trying to register PG for non-existant node\n");
+ goto failed;
+ }
+ if (!__isns_pg_may_relate(entity, node)) {
+ isns_error("Trying to register PG for node in other entity\n");
+ goto failed;
+ }
+
+ portal = __isns_pg_find_portal(db, pg, extra_objs);
+ if (portal == NULL) {
+ isns_error("Trying to register PG for non-existant portal\n");
+ goto failed;
+ }
+ if (!__isns_pg_may_relate(entity, portal)) {
+ isns_error("Trying to register PG for portal in other entity\n");
+ goto failed;
+ }
+
+ pg->ie_relation = isns_create_relation(pg,
+ ISNS_RELATION_PORTAL_GROUP,
+ node, portal);
+ isns_object_release(portal);
+ isns_object_release(node);
+
+ return pg->ie_relation;
+
+failed:
+ if (portal)
+ isns_object_release(portal);
+ if (node)
+ isns_object_release(node);
+ return NULL;
+}
+
+/*
+ * Create a portal group given node, portal and PGT
+ */
+isns_object_t *
+isns_create_portal_group(isns_object_t *portal,
+ isns_object_t *node, uint32_t pg_tag)
+{
+ isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
+ isns_object_t *obj = NULL;
+
+ if (portal == NULL || node == NULL)
+ return NULL;
+
+ if (node->ie_container != portal->ie_container) {
+ isns_error("Refusing to create portal group "
+ "linking objects from different entities\n");
+ return NULL;
+ }
+
+ if (__isns_object_translate_attr(node,
+ ISNS_TAG_ISCSI_NAME,
+ ISNS_TAG_PG_ISCSI_NAME,
+ &key_attrs)
+ && __isns_object_translate_attr(portal,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PG_PORTAL_IP_ADDR,
+ &key_attrs)
+ && __isns_object_translate_attr(portal,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
+ &key_attrs)) {
+ obj = __isns_pg_create(&key_attrs, pg_tag, portal, node);
+ }
+
+ isns_attr_list_destroy(&key_attrs);
+ return obj;
+}
+
+/*
+ * 5.6.5.1
+ * New PG objects are registered when an associated Portal or
+ * iSCSI Node object is registered. An explicit PG object
+ * registration MAY follow a Portal or iSCSI Node object
+ * registration in a DevAttrReg message.
+ * [...]
+ * If the PGT value is not included in the Storage Node or
+ * Portal object registration, and if a PGT value was not
+ * previously registered for the relationship, then the PGT for
+ * the corresponding PG object SHALL be registered with a value
+ * of 0x00000001.
+ *
+ * We return non-NULL if the object was created.
+ */
+isns_object_t *
+isns_create_default_portal_group(isns_db_t *db,
+ isns_object_t *portal, isns_object_t *node)
+{
+ isns_object_t *obj;
+
+ if (portal == NULL || node == NULL)
+ return 0;
+
+ /* See if there is a PG already */
+ obj = isns_db_get_relationship_object(db, node, portal,
+ ISNS_RELATION_PORTAL_GROUP);
+ if (obj != NULL) {
+ isns_object_release(obj);
+ return NULL;
+ }
+
+ return isns_create_portal_group(portal, node, 1);
+}
+
+static uint32_t iscsi_pg_attrs[] = {
+ ISNS_TAG_PG_ISCSI_NAME,
+ ISNS_TAG_PG_PORTAL_IP_ADDR,
+ ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
+ ISNS_TAG_PG_TAG,
+ ISNS_TAG_PG_INDEX,
+};
+
+static uint32_t iscsi_pg_key_attrs[] = {
+ ISNS_TAG_PG_ISCSI_NAME,
+ ISNS_TAG_PG_PORTAL_IP_ADDR,
+ ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
+};
+
+isns_object_template_t isns_iscsi_pg_template = {
+ .iot_name = "iSCSI Portal Group",
+ .iot_handle = ISNS_OBJECT_TYPE_PG,
+ .iot_attrs = iscsi_pg_attrs,
+ .iot_num_attrs = array_num_elements(iscsi_pg_attrs),
+ .iot_keys = iscsi_pg_key_attrs,
+ .iot_num_keys = array_num_elements(iscsi_pg_key_attrs),
+ .iot_attrs = iscsi_pg_attrs,
+ .iot_keys = iscsi_pg_key_attrs,
+ .iot_index = ISNS_TAG_PG_INDEX,
+ .iot_next_index = ISNS_TAG_PG_NEXT_INDEX,
+ .iot_container = &isns_entity_template,
+ .iot_relation_type = ISNS_RELATION_PORTAL_GROUP,
+ .iot_build_relation = isns_db_build_pg_relation,
+};
+
diff --git a/utils/open-isns/query.c b/utils/open-isns/query.c
new file mode 100644
index 0000000..b2cfbc9
--- /dev/null
+++ b/utils/open-isns/query.c
@@ -0,0 +1,238 @@
+/*
+ * Handle iSNS Device Attribute Query
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "attrs.h"
+#include "message.h"
+#include "security.h"
+#include "objects.h"
+#include "db.h"
+#include "util.h"
+
+/*
+ * Create a query, and set the source name
+ */
+static isns_simple_t *
+__isns_create_query(isns_source_t *source, const isns_attr_list_t *key)
+{
+ return isns_simple_create(ISNS_DEVICE_ATTRIBUTE_QUERY, source, key);
+}
+
+isns_simple_t *
+isns_create_query(isns_client_t *clnt, const isns_attr_list_t *key)
+{
+ return __isns_create_query(clnt->ic_source, key);
+}
+
+isns_simple_t *
+isns_create_query2(isns_client_t *clnt, const isns_attr_list_t *key, isns_source_t *source)
+{
+ return __isns_create_query(source?: clnt->ic_source, key);
+}
+
+int
+isns_query_request_attr_tag(isns_simple_t *qry, uint32_t tag)
+{
+ isns_attr_list_append_nil(&qry->is_operating_attrs, tag);
+ return ISNS_SUCCESS;
+}
+
+int
+isns_query_request_attr(isns_simple_t *qry, isns_attr_t *attr)
+{
+ if (!ISNS_ATTR_IS_NIL(attr)) {
+ isns_error("Query operating attribute must be NIL\n");
+ return ISNS_INVALID_QUERY;
+ }
+ isns_attr_list_append_attr(&qry->is_operating_attrs, attr);
+ return ISNS_SUCCESS;
+}
+
+static unsigned int
+isns_query_get_requested_types(const isns_attr_list_t *attrs)
+{
+ unsigned int i, mask = 0;
+
+ for (i = 0; i < attrs->ial_count; ++i) {
+ uint32_t tag = attrs->ial_data[i]->ia_tag_id;
+ isns_object_template_t *tmpl;
+
+ tmpl = isns_object_template_find(tag);
+ /* Ignore unknown tags */
+ if (tmpl == NULL)
+ continue;
+
+ mask |= 1 << tmpl->iot_handle;
+ }
+ return mask;
+}
+
+/*
+ * Get the list of objects matching this query
+ */
+static int
+isns_query_get_objects(isns_simple_t *qry, isns_db_t *db, isns_object_list_t *result)
+{
+ isns_scope_t *scope = NULL;
+ isns_object_list_t matching = ISNS_OBJECT_LIST_INIT;
+ isns_attr_list_t *keys = &qry->is_message_attrs;
+ isns_object_template_t *query_type = NULL;
+ unsigned int i, qry_mask = 0;
+ int status;
+
+ /* 5.6.5.2
+ * If multiple attributes are used as the Message Key, then they
+ * MUST all be from the same object type (e.g., IP address and
+ * TCP/UDP Port are attributes of the Portal object type).
+ */
+ for (i = 0; i < keys->ial_count; ++i) {
+ isns_object_template_t *tmpl;
+ uint32_t tag = keys->ial_data[i]->ia_tag_id;
+
+ tmpl = isns_object_template_for_tag(tag);
+ if (tmpl == NULL)
+ return ISNS_ATTRIBUTE_NOT_IMPLEMENTED;
+ if (query_type == NULL)
+ query_type = tmpl;
+ else if (tmpl != query_type)
+ return ISNS_INVALID_QUERY;
+ }
+
+ /*
+ * 5.6.5.2
+ * An empty Message Key field indicates the query is scoped to
+ * the entire database accessible by the source Node.
+ */
+ if (keys->ial_count == 0) {
+ query_type = &isns_entity_template;
+ keys = NULL;
+ }
+
+ /* Policy: check whether the client is allowed to
+ * query this type of object. */
+ if (!isns_policy_validate_object_type(qry->is_policy,
+ query_type, qry->is_function))
+ return ISNS_SOURCE_UNAUTHORIZED;
+
+ /* No scope means that the source is not part of
+ * any discovery domain, and there's no default DD.
+ * Just return an empty reply. */
+ scope = isns_scope_for_call(db, qry);
+ if (scope == NULL)
+ return ISNS_SUCCESS;
+
+ status = isns_scope_gang_lookup(scope, query_type, keys, &matching);
+ if (status != ISNS_SUCCESS)
+ goto out;
+
+ /* Extract the mask of requested objects */
+ qry_mask = isns_query_get_requested_types(&qry->is_operating_attrs);
+
+ /*
+ * 5.6.5.2
+ * The DevAttrQry response message returns attributes of objects
+ * listed in the Operating Attributes that are related to the
+ * Message Key of the original DevAttrQry message.
+ */
+ for (i = 0; i < matching.iol_count; ++i) {
+ isns_object_t *obj = matching.iol_data[i];
+
+ if (!isns_policy_validate_object_access(qry->is_policy,
+ qry->is_source, obj,
+ qry->is_function))
+ continue;
+
+ if (obj->ie_container)
+ isns_object_list_append(result, obj->ie_container);
+ isns_object_list_append(result, obj);
+ isns_scope_get_related(scope, obj, qry_mask, result);
+ }
+
+out:
+ isns_object_list_destroy(&matching);
+ isns_scope_release(scope);
+ return status;
+}
+
+/*
+ * Create a Query Response
+ */
+static isns_simple_t *
+isns_create_query_response(isns_server_t *srv,
+ const isns_simple_t *qry, const isns_object_list_t *objects)
+{
+ const isns_attr_list_t *req_attrs = NULL;
+ isns_simple_t *resp;
+ unsigned int i;
+
+ resp = __isns_create_query(srv->is_source, &qry->is_message_attrs);
+
+ /*
+ * 5.7.5.2.
+ * If no Operating Attributes are included in the original
+ * query, then all Operating Attributes SHALL be returned
+ * in the response.
+ */
+ if (qry->is_operating_attrs.ial_count != 0)
+ req_attrs = &qry->is_operating_attrs;
+
+ for (i = 0; i < objects->iol_count; ++i) {
+ isns_object_t *obj = objects->iol_data[i];
+
+ if (obj->ie_rebuild)
+ obj->ie_rebuild(obj, srv->is_db);
+ isns_object_get_attrlist(obj,
+ &resp->is_operating_attrs,
+ req_attrs);
+ }
+ return resp;
+}
+
+int
+isns_process_query(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
+{
+ isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
+ isns_simple_t *reply = NULL;
+ isns_db_t *db = srv->is_db;
+ int status;
+
+ /* Get the objects matching the query */
+ status = isns_query_get_objects(call, db, &objects);
+ if (status != ISNS_SUCCESS)
+ goto done;
+
+ /* Success: build the response */
+ reply = isns_create_query_response(srv, call, &objects);
+ if (reply == NULL) {
+ status = ISNS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ /* There's nothing in the spec that tells us what to
+ * return if the query matches no object.
+ */
+ if (objects.iol_count == 0) {
+ status = ISNS_NO_SUCH_ENTRY;
+ goto done;
+ }
+
+done:
+ isns_object_list_destroy(&objects);
+ *result = reply;
+ return status;
+}
+
+/*
+ * Parse the list of objects in a query response
+ */
+int
+isns_query_response_get_objects(isns_simple_t *qry,
+ isns_object_list_t *result)
+{
+ return isns_simple_response_get_objects(qry, result);
+}
diff --git a/utils/open-isns/register.c b/utils/open-isns/register.c
new file mode 100644
index 0000000..120deae
--- /dev/null
+++ b/utils/open-isns/register.c
@@ -0,0 +1,934 @@
+/*
+ * Handle iSNS Device Attribute Registration
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "isns.h"
+#include "attrs.h"
+#include "objects.h"
+#include "message.h"
+#include "security.h"
+#include "util.h"
+#include "db.h"
+
+
+static int isns_create_default_pgs_for_object(isns_db_t *, isns_object_t *);
+
+/*
+ * Create a registration, and set the source name
+ */
+static isns_simple_t *
+__isns_create_registration(isns_source_t *source, isns_object_t *key_obj)
+{
+ isns_simple_t *reg;
+
+ reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, source, NULL);
+ if (reg == NULL)
+ return NULL;
+
+ /*
+ * When sending a registration, you can either specify
+ * the object to be modified in the key attrs, or leave
+ * the key empty.
+ */
+ if (key_obj == NULL)
+ return reg;
+
+ /* User gave us a key object. We need to put the key
+ * attributes into the message attrs, and *all* attrs
+ * into the operating attrs. */
+ if (!isns_object_extract_keys(key_obj, &reg->is_message_attrs)) {
+ /* bummer - seems the object is missing some
+ * vital organs. */
+ isns_warning("%s: object not fully specified, key attrs missing\n",
+ __FUNCTION__);
+ goto failed;
+ }
+
+ /*
+ * The Message Key identifies the object the DevAttrReg message
+ * acts upon. [...] The key attribute(s) identifying this object
+ * MUST also be included among the Operating Attributes.
+ *
+ * We do not enforce this here, we rely on the caller to get this
+ * right.
+ */
+#if 0
+ if (!isns_object_extract_all(key_obj, &reg->is_operating_attrs)) {
+ isns_warning("%s: unable to extract attrs from key objects\n",
+ __FUNCTION__);
+ goto failed;
+ }
+#endif
+
+ return reg;
+
+failed:
+ isns_simple_free(reg);
+ return NULL;
+}
+
+isns_simple_t *
+isns_create_registration(isns_client_t *clnt, isns_object_t *key_obj)
+{
+ return __isns_create_registration(clnt->ic_source, key_obj);
+}
+
+isns_simple_t *
+isns_create_registration2(isns_client_t *clnt, isns_object_t *key_obj,
+ isns_source_t *source)
+{
+ return __isns_create_registration(source?: clnt->ic_source, key_obj);
+}
+
+/*
+ * Set the replace flag
+ */
+void
+isns_registration_set_replace(isns_simple_t *reg, int replace)
+{
+ reg->is_replace = !!replace;
+}
+
+/*
+ * Add an object to the registration
+ */
+void
+isns_registration_add_object(isns_simple_t *reg, isns_object_t *obj)
+{
+ isns_object_extract_writable(obj, &reg->is_operating_attrs);
+}
+
+void
+isns_registration_add_object_list(isns_simple_t *reg, isns_object_list_t *list)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_extract_writable(list->iol_data[i],
+ &reg->is_operating_attrs);
+ }
+}
+
+/*
+ * Get the key object given in this message
+ *
+ * It doesn't say anywhere explicitly in the RFC, but
+ * the message key can contain both key and non-key
+ * attributes. For instance, you can search by
+ * Portal Group Index (section 3.4).
+ */
+static int
+isns_registration_get_key(isns_simple_t *reg, isns_db_t *db, isns_object_t **key_obj)
+{
+ isns_attr_list_t *keys = &reg->is_message_attrs;
+ isns_attr_list_t dummy_keys = ISNS_ATTR_LIST_INIT;
+ isns_attr_t *attr;
+ isns_object_t *obj = NULL;
+ const char *eid = NULL;
+ char eidbuf[128];
+ int status = ISNS_SUCCESS;
+ int obj_must_exist = 0;
+
+ /*
+ * 5.6.5.1
+ * If the Message Key is not present, then the DevAttrReg message
+ * implicitly registers a new Network Entity. In this case,
+ * the replace bit SHALL be ignored; a new Network Entity SHALL
+ * be created.
+ *
+ * Note that some clients seem to leave the message key
+ * empty, but hide the entity identifier in the operating
+ * attrs.
+ */
+ if (keys->ial_count != 0) {
+ attr = keys->ial_data[0];
+
+ /*
+ * 5.6.5.1
+ * If the Message Key does not contain an EID, and no
+ * pre-existing objects match the Message Key, then the
+ * DevAttrReg message SHALL be rejected with a status
+ * code of 3 (Invalid Registration).
+ */
+ if (keys->ial_count != 1
+ || attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER)
+ obj_must_exist = 1;
+ } else {
+ /* Empty message key. But the client may have hidden
+ * the EID in the operating attrs :-/
+ */
+ if (reg->is_operating_attrs.ial_count == 0)
+ goto create_entity;
+
+ attr = reg->is_operating_attrs.ial_data[0];
+ if (attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER)
+ goto create_entity;
+
+ isns_attr_list_append_attr(&dummy_keys, attr);
+ keys = &dummy_keys;
+ }
+
+ /* If the caller specifies an EID, extract it while
+ * we know what we're doing :-) */
+ if (attr->ia_tag_id == ISNS_TAG_ENTITY_IDENTIFIER
+ && ISNS_ATTR_IS_STRING(attr))
+ eid = attr->ia_value.iv_string;
+
+ /* Look up the object identified by the keys.
+ * We do not scope the lookup, as the client
+ * may want to add nodes to an entity that's
+ * currently empty - and hence not visible to
+ * any DD. */
+ if (!ISNS_ATTR_IS_NIL(attr))
+ obj = isns_db_lookup(db, NULL, keys);
+
+ if (obj == NULL && obj_must_exist)
+ goto err_invalid;
+
+ if (obj != NULL) {
+ /*
+ * Policy: verify that the client is permitted
+ * to access this object.
+ *
+ * This includes
+ * - the client node must be the object owner,
+ * or a control node.
+ * - the policy must allow modification of
+ * this object type.
+ */
+ if (!isns_policy_validate_object_access(reg->is_policy,
+ reg->is_source,
+ obj, reg->is_function))
+ goto err_unauthorized;
+
+found_object:
+ if (reg->is_replace) {
+ isns_object_t *container = NULL;
+
+ if (!ISNS_IS_ENTITY(obj)) {
+ container = isns_object_get_entity(obj);
+ if (container == NULL) {
+ isns_error("Trying to replace %s (id %u) "
+ "which has no container\n",
+ obj->ie_template->iot_name,
+ obj->ie_index);
+ goto err_invalid;
+ }
+ }
+
+ isns_debug_state("Replacing %s (id %u)\n",
+ obj->ie_template->iot_name,
+ obj->ie_index);
+ isns_db_remove(db, obj);
+ isns_object_release(obj);
+
+ /* Purge the deleted objects from the database now */
+ isns_db_purge(db);
+
+ /* We need to flush pending SCNs because the
+ * objects may be resurrected from limbo,
+ * and we might be looking at stale data. */
+ isns_scn_transmit_all();
+
+ /* It's an entity. Nuke it and create
+ * a new one. */
+ if (container == NULL) {
+ isns_source_set_entity(reg->is_source, NULL);
+ goto create_entity;
+ }
+
+ obj = isns_object_get(container);
+ }
+
+ goto out;
+ }
+
+ /*
+ * If the Message Key contains an EID and no pre-existing objects
+ * match the Message Key, then the DevAttrReg message SHALL create a
+ * new Entity with the specified EID and any new object(s) specified
+ * by the Operating Attributes. The replace bit SHALL be ignored.
+ *
+ * Implementer's note: the EID attribute may be empty, in which case
+ * we also create a new entity.
+ */
+
+create_entity:
+ if (!isns_policy_validate_object_creation(reg->is_policy,
+ reg->is_source,
+ &isns_entity_template, keys, NULL,
+ reg->is_function))
+ goto err_unauthorized;
+
+ /*
+ * 5.6.5.1
+ * A registration message that creates a new Network Entity object
+ * MUST contain at least one Portal or one Storage Node. If the
+ * message does not, then it SHALL be considered invalid and result
+ * in a response with Status Code of 3 (Invalid Registration).
+ */
+ /* FIXME: Implement this check */
+
+ /* We try to play nice with lazy clients and attempt to
+ * look up the network entity given the source name.
+ * But we don't do this if a non-NULL EID was given,
+ * because the client may explicitly want to specify more
+ * than one Network Entity.
+ */
+ if (eid == NULL) {
+ obj = reg->is_source->is_entity;
+ if (obj != NULL) {
+ isns_object_get(obj);
+ goto found_object;
+ }
+
+ /* The policy may define a default entity name.
+ * If that is the case, use it.
+ */
+ eid = isns_policy_default_entity(reg->is_policy);
+ if (eid) {
+ obj = isns_db_vlookup(db, &isns_entity_template,
+ ISNS_TAG_ENTITY_IDENTIFIER, eid,
+ 0);
+ if (obj) {
+ reg->is_source->is_entity = isns_object_get(obj);
+ goto found_object;
+ }
+ }
+ }
+
+ /*
+ * 5.6.5.1
+ * If the Message Key and Operating Attributes do not contain
+ * an EID attribute, or if the EID attribute has a length of 0,
+ * then a new Network Entity object SHALL be created and the iSNS
+ * server SHALL supply a unique EID value for it.
+ */
+ if (eid == NULL)
+ eid = isns_db_generate_eid(db, eidbuf, sizeof(eidbuf));
+
+ /*
+ * 6.2.2. Entity Protocol
+ *
+ * This attribute is required during initial registration of
+ * the Network Entity.
+ *
+ * Implementer's note: we don't rely on this. Instead, the
+ * Entity Protocol is selected based on the source type.
+ * If the client specifies the protocol, the auto-selected
+ * value is overwritten.
+ */
+ obj = isns_create_entity_for_source(reg->is_source, eid);
+ if (obj == NULL)
+ goto err_invalid;
+
+ isns_source_set_entity(reg->is_source, obj);
+
+ /*
+ * 6.2.6
+ * If a Registration Period is not requested by the iSNS
+ * client and Entity Status Inquiry (ESI) messages are not
+ * enabled for that client, then the Registration Period
+ * SHALL be set to a non-zero value by the iSNS server.
+ * This implementation-specific value for the Registration
+ * Period SHALL be returned in the registration response to the
+ * iSNS client. The Registration Period may be set to zero,
+ * indicating its non-use, only if ESI messages are enabled for
+ * that Network Entity.
+ *
+ * Implementer's note: we diverge from this in two ways:
+ * - the admin may choose to disable registration timeout,
+ * by setting RegistrationPeriod=0 in the config file
+ *
+ * - When a new entity is created, we always set the
+ * registration interval because we cannot know yet
+ * whether the client will subsequently enable ESI or
+ * not.
+ *
+ * - The control entity (holding policy objects) will
+ * not expire.
+ */
+ if (isns_config.ic_registration_period
+ && strcasecmp(eid, ISNS_ENTITY_CONTROL)) {
+ isns_object_set_uint32(obj,
+ ISNS_TAG_REGISTRATION_PERIOD,
+ isns_config.ic_registration_period);
+ isns_object_set_uint64(obj,
+ ISNS_TAG_TIMESTAMP,
+ time(NULL));
+ }
+
+ /* Insert into database, and set the object's owner */
+ isns_db_insert(db, obj);
+
+ reg->is_replace = 0;
+
+out:
+ *key_obj = obj;
+ isns_attr_list_destroy(&dummy_keys);
+ return ISNS_SUCCESS;
+
+error:
+ if (obj)
+ isns_object_release(obj);
+ isns_attr_list_destroy(&dummy_keys);
+ return status;
+
+err_unauthorized:
+ status = ISNS_SOURCE_UNAUTHORIZED;
+ goto error;
+
+err_invalid:
+ status = ISNS_INVALID_REGISTRATION;
+ goto error;
+}
+
+static int
+isns_registration_get_next_object(isns_db_t *db,
+ struct isns_attr_list_scanner *st,
+ isns_object_list_t *result)
+{
+ isns_object_t *current;
+ int status, esi = 0;
+
+ status = isns_attr_list_scanner_next(st);
+ /* We get here if the registration has a trailing PGT */
+ if (status == ISNS_NO_SUCH_ENTRY)
+ return ISNS_SUCCESS;
+ if (status)
+ return status;
+
+ /*
+ * Validate the attrlist.
+ * This makes sure the client does not include
+ * duplicate attributes, readonly attributes
+ * such as Registration Timestamp, Index and Next Index,
+ * or privileged data (such as marking a storage node as
+ * control node).
+ */
+ status = isns_attr_list_validate(&st->attrs,
+ st->policy,
+ ISNS_DEVICE_ATTRIBUTE_REGISTER);
+ if (status) {
+ isns_debug_protocol("invalid attr in message\n");
+ return status;
+ }
+
+ /*
+ * 6.3.4. Entity Status Inquiry Interval
+ *
+ * If the iSNS server is unable to support ESI messages
+ * or the ESI Interval requested, it SHALL [...] reject
+ * the ESI request by returning an "ESI Not Available"
+ * Status Code [...]
+ *
+ * Implementer's note: In section 5.7.5.1, the RFC talks
+ * about modifying the requested ESI interval; so it seems
+ * it's okay to be liberal about the ESI intervals we accept,
+ * and update them quietly.
+ */
+ if (isns_attr_list_contains(&st->attrs, ISNS_TAG_ESI_PORT)) {
+ if (!isns_esi_enabled) {
+ isns_debug_esi("Refusing to accept portal "
+ "registration with ESI port\n");
+ return ISNS_ESI_NOT_AVAILABLE;
+ }
+ esi = 1;
+ }
+
+ /*
+ * Override any registration period specified by the client.
+ */
+ if (isns_attr_list_contains(&st->attrs, ISNS_TAG_REGISTRATION_PERIOD)) {
+ isns_value_t value = ISNS_VALUE_INIT(uint32,
+ isns_config.ic_registration_period);
+
+ isns_attr_list_update_value(&st->attrs,
+ ISNS_TAG_REGISTRATION_PERIOD, NULL,
+ &value);
+ }
+
+ if (st->tmpl == &isns_entity_template) {
+ /*
+ * 5.6.5.1.
+ * A maximum of one Network Entity object can be
+ * created or updated with a single DevAttrReg
+ * message. Consequently, the Operating Attributes
+ * MUST NOT contain more than one Network Entity
+ * object.
+ */
+ if (st->entities++) {
+ isns_debug_protocol("More than one entity in DevAttrReg msg\n");
+ return ISNS_INVALID_REGISTRATION;
+ }
+
+ /* This should be the key object.
+ * The EID specified by by the client may be
+ * empty, so don't overwrite the value we
+ * assigned with something else.
+ */
+ if (!isns_object_match(st->key_obj, &st->keys)) {
+ isns_debug_protocol("Entity mismatch in message vs. operating attrs\n");
+ return ISNS_INVALID_REGISTRATION;
+ }
+ current = isns_object_get(st->key_obj);
+ } else
+ if (st->tmpl == &isns_dd_template || st->tmpl == &isns_ddset_template) {
+ isns_debug_protocol("DevAttrReg of type %s not allowed\n",
+ st->tmpl->iot_name);
+ return ISNS_INVALID_REGISTRATION;
+ } else {
+ /* This will also catch objects in limbo. */
+ current = isns_db_lookup(db, st->tmpl, &st->keys);
+ }
+
+ if (current != NULL) {
+ /*
+ * If the replace bit is not set, then the message updates
+ * the attributes of the object identified by the Message Key
+ * and its subordinate objects. Existing object containment
+ * relationships MUST NOT be changed. For existing objects,
+ * key attributes MUST NOT be modified, but new subordinate
+ * objects MAY be added.
+ */
+
+ /*
+ * [...]
+ * If the Node identified by the Source Attribute is
+ * not a Control Node, then the objects in the operating
+ * attributes MUST be members of the same Network Entity
+ * as the Source Node.
+ */
+ if (!isns_policy_validate_object_update(st->policy,
+ st->source, current, &st->attrs,
+ ISNS_DEVICE_ATTRIBUTE_REGISTER)) {
+ isns_object_release(current);
+ return ISNS_SOURCE_UNAUTHORIZED;
+ }
+
+ /* We shouldn't allow messages affecting one Entity
+ * to modify objects owned by a different Entity.
+ *
+ * However, there may be orphan objects (created
+ * while populating discovery domains). These will
+ * not be associated with any Network Entity, so
+ * they're up for grabs.
+ */
+ if (st->key_obj == current
+ || st->key_obj == current->ie_container) {
+ /* All is well. The current object is the
+ * key object itself, or a direct descendant of the
+ * key object. */
+ /* FIXME: with FC we can get deeper nesting;
+ * this needs work. */
+ } else
+ if (!isns_object_is_valid_container(st->key_obj, st->tmpl)) {
+ isns_error("Client attempts to add %s object to a %s - tsk tsk.\n",
+ st->tmpl->iot_name,
+ st->key_obj->ie_template->iot_name);
+ goto invalid_registration;
+ } else if (current->ie_container) {
+ /* We shouldn't get here in authenticated mode,
+ * but in insecure mode we still may. */
+ isns_error("Client attempts to move %s %u to a different %s\n",
+ current->ie_template->iot_name,
+ current->ie_index,
+ st->key_obj->ie_template->iot_name);
+ goto invalid_registration;
+ }
+ } else {
+ if (!isns_object_is_valid_container(st->key_obj, st->tmpl)) {
+ isns_error("Client attempts to add %s object to a %s - tsk tsk.\n",
+ st->tmpl->iot_name,
+ st->key_obj->ie_template->iot_name);
+ goto invalid_registration;
+ }
+
+ if (!isns_policy_validate_object_creation(st->policy,
+ st->source, st->tmpl,
+ &st->keys, &st->attrs,
+ ISNS_DEVICE_ATTRIBUTE_REGISTER)) {
+ return ISNS_SOURCE_UNAUTHORIZED;
+ }
+ current = isns_create_object(st->tmpl, &st->keys,
+ isns_object_get_entity(st->key_obj));
+
+ /* We do not insert the new object into the database yet.
+ * That happens after we're done with parsing *all*
+ * objects. */
+ }
+
+ if (!isns_object_set_attrlist(current, &st->attrs)) {
+ isns_debug_state("Error updating object's attrlist\n");
+ isns_object_release(current);
+ return ISNS_INTERNAL_ERROR;
+ }
+
+ /* If the client specifies an ESI port, make sure the
+ * ESI interval is set and within bounds. */
+ if (esi) {
+ uint32_t esi_interval;
+
+ if (!isns_object_get_uint32(current,
+ ISNS_TAG_ESI_INTERVAL, &esi_interval)) {
+ esi_interval = isns_config.ic_esi_min_interval;
+ } else
+ if (esi_interval < isns_config.ic_esi_min_interval) {
+ esi_interval = isns_config.ic_esi_min_interval;
+ } else
+ if (esi_interval > isns_config.ic_esi_max_interval) {
+ esi_interval = isns_config.ic_esi_max_interval;
+ } else {
+ esi_interval = 0;
+ }
+
+ if (esi_interval)
+ isns_object_set_uint32(current,
+ ISNS_TAG_ESI_INTERVAL, esi_interval);
+ }
+
+ /* Append it to the result list.
+ * We do not return the key object, otherwise
+ * we end up putting it into the response twice.
+ */
+ if (current != st->key_obj)
+ isns_object_list_append(result, current);
+
+ /*
+ * When a Portal is registered, the Portal attributes MAY immediately be
+ * followed by a PGT attribute.
+ * [...]
+ * When an iSCSI Storage Node is registered, the Storage Node attributes
+ * MAY immediately be followed by a PGT attribute.
+ */
+ if (st->tmpl == &isns_portal_template
+ || st->tmpl == &isns_iscsi_node_template) {
+ st->pgt_next_attr = ISNS_TAG_PG_TAG;
+ st->pgt_base_object = current;
+ } else if (st->tmpl != &isns_iscsi_pg_template) {
+ st->pgt_next_attr = 0;
+ st->pgt_base_object = NULL;
+ }
+
+ isns_object_release(current);
+ return ISNS_SUCCESS;
+
+invalid_registration:
+ if (current)
+ isns_object_release(current);
+ return ISNS_INVALID_REGISTRATION;
+}
+
+/*
+ * Extract the list of objects to be registered from
+ * the list of operating attributes.
+ */
+static int
+isns_registration_get_objects(isns_simple_t *reg, isns_db_t *db,
+ isns_object_t *key_obj,
+ isns_object_list_t *result)
+{
+ struct isns_attr_list_scanner state;
+ int status = ISNS_SUCCESS;
+
+ isns_attr_list_scanner_init(&state, key_obj, &reg->is_operating_attrs);
+ state.source = reg->is_source;
+ state.policy = reg->is_policy;
+
+ /*
+ * 5.6.4.
+ * The ordering of Operating Attributes in the message is
+ * important for determining the relationships among objects
+ * and their ownership of non-key attributes. iSNS protocol
+ * messages that violate these ordering rules SHALL be rejected
+ * with the Status Code of 2 (Message Format Error).
+ */
+ /* FIXME: Implement this check */
+
+ while (state.pos < state.orig_attrs.ial_count) {
+ status = isns_registration_get_next_object(db,
+ &state, result);
+
+ if (status)
+ break;
+ }
+
+ isns_attr_list_scanner_destroy(&state);
+ return status;
+}
+
+/*
+ * 5.6.5.1
+ * New PG objects are registered when an associated Portal or
+ * iSCSI Node object is registered. An explicit PG object
+ * registration MAY follow a Portal or iSCSI Node object
+ * registration in a DevAttrReg message.
+ * [...]
+ * If the PGT value is not included in the Storage Node or
+ * Portal object registration, and if a PGT value was not
+ * previously registered for the relationship, then the PGT for
+ * the corresponding PG object SHALL be registered with a value
+ * of 0x00000001.
+ */
+static int
+isns_create_registration_pgs(isns_db_t *db,
+ const isns_object_list_t *list)
+{
+ unsigned int i, num_created = 0;
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+
+ if (ISNS_IS_ISCSI_NODE(obj) || ISNS_IS_PORTAL(obj))
+ num_created += isns_create_default_pgs_for_object(db, obj);
+ }
+ return num_created;
+}
+
+static int
+isns_create_default_pgs_for_object(isns_db_t *db, isns_object_t *this)
+{
+ isns_object_template_t *match_tmpl;
+ isns_object_t *entity;
+ unsigned int i, num_created = 0;
+
+ if (ISNS_IS_ISCSI_NODE(this))
+ match_tmpl = &isns_portal_template;
+ else
+ match_tmpl = &isns_iscsi_node_template;
+
+ entity = isns_object_get_entity(this);
+ for (i = 0; i < entity->ie_children.iol_count; ++i) {
+ isns_object_t *that = entity->ie_children.iol_data[i], *pg;
+
+ if (that->ie_template != match_tmpl)
+ continue;
+
+ /* Create the portal group if it does not
+ * exist.
+ * Note: we do not return these implicitly
+ * created portal groups - that's a matter
+ * of sheer laziness. We would have to
+ * splice these into the list in the
+ * appropriate location, and I guess it's
+ * not really worth the hassle.
+ */
+ if (ISNS_IS_ISCSI_NODE(this))
+ pg = isns_create_default_portal_group(db, that, this);
+ else
+ pg = isns_create_default_portal_group(db, this, that);
+
+ /* There already is a PG linking these two
+ * objects. */
+ if (pg == NULL)
+ continue;
+
+ isns_db_insert(db, pg);
+
+ isns_debug_message("--Created default PG:--\n");
+ isns_object_print(pg, isns_debug_message);
+
+ isns_object_release(pg);
+ num_created++;
+ }
+
+ return num_created;
+}
+
+/*
+ * Commit all changes to the DB
+ */
+static int
+isns_commit_registration(isns_db_t *db, isns_object_t *key_obj, isns_object_list_t *list)
+{
+ unsigned int i;
+
+ /*
+ * If there are any Portal Groups in this registration, build
+ * the relationship handle:
+ *
+ * 3.4
+ * A new PG object can only be registered by referencing
+ * its associated iSCSI Storage Node or Portal object.
+ * A pre-existing PG object can be modified or queried
+ * by using its Portal Group Index as message key, or
+ * by referencing its associated iSCSI Storage Node or
+ * Portal object.
+ *
+ * Implementation note: isns_db_create_pg_relation
+ * checks whether the referenced node and portal exist,
+ * and belong to the same entity as the PG. If this is
+ * not the case, NULL is returned, and no relation is
+ * defined.
+ */
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+ isns_object_template_t *tmpl;
+
+ tmpl = obj->ie_template;
+ if (tmpl->iot_build_relation && !obj->ie_relation
+ && !tmpl->iot_build_relation(db, obj, list)) {
+ isns_debug_protocol("Unable to build relation for new %s\n",
+ tmpl->iot_name);
+ return ISNS_INVALID_REGISTRATION;
+ }
+ }
+
+ for (i = 0; i < list->iol_count; ++i) {
+ isns_object_t *obj = list->iol_data[i];
+ isns_object_template_t *tmpl;
+
+ tmpl = obj->ie_template;
+ if (key_obj != obj && !obj->ie_container) {
+ if (!isns_object_attach(obj, key_obj)) {
+ /* This should not fail any longer */
+ isns_debug_protocol("Unable to attach %s %u to %s\n",
+ tmpl->iot_name, obj->ie_index,
+ key_obj->ie_template->iot_name);
+ return ISNS_INVALID_REGISTRATION;
+ }
+ }
+
+ if (obj->ie_state != ISNS_OBJECT_STATE_MATURE)
+ isns_db_insert(db, obj);
+ }
+
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Process a registration
+ */
+int
+isns_process_registration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
+{
+ isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
+ isns_simple_t *reply = NULL;
+ isns_object_t *key_obj = NULL;
+ isns_db_t *db = srv->is_db;
+ int status;
+ unsigned int i;
+
+ /*
+ * 5.6.1
+ * For messages that change the contents of the iSNS database,
+ * the iSNS server MUST verify that the Source Attribute
+ * identifies either a Control Node or a Storage Node that is
+ * a part of the Network Entity containing the added, deleted,
+ * or modified objects.
+ *
+ * This check happens in isns_registration_get_key by calling
+ * isns_policy_validate_object_access.
+ */
+
+ /* Get the key object (usually a Network Entity) */
+ status = isns_registration_get_key(call, db, &key_obj);
+ if (status)
+ goto done;
+
+ /* Get the objects to register */
+ status = isns_registration_get_objects(call, db, key_obj, &objects);
+ if (status != ISNS_SUCCESS)
+ goto done;
+
+ /* We parsed the request alright; all semantic checks passed.
+ * Now insert the modified/new objects.
+ * We do this in two passes, by first committing all nodes and
+ * portals, and then committing the portal groups.
+ */
+ status = isns_commit_registration(db, key_obj, &objects);
+ if (status != ISNS_SUCCESS)
+ goto done;
+
+ /* The client may have registered a bunch of storage nodes,
+ * and created an entity in the process. However, there's the
+ * odd chance that the source node name it used was not
+ * registered. However, we need to be able to later find
+ * the entity it registered based on its source name.
+ * So we implicitly create a dummy storage node with the given
+ * source name and attach it.
+ */
+#if 1
+ if (ISNS_IS_ENTITY(key_obj)
+ && !isns_source_set_node(call->is_source, db)) {
+ isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
+ isns_source_t *source = call->is_source;
+ isns_object_t *obj;
+
+ isns_attr_list_append_attr(&attrs, isns_source_attr(source));
+ isns_attr_list_append_uint32(&attrs,
+ ISNS_TAG_ISCSI_NODE_TYPE,
+ 0);
+ obj = isns_create_object(&isns_iscsi_node_template,
+ &attrs, key_obj);
+ if (obj) {
+ isns_db_insert(db, obj);
+ } else {
+ isns_warning("Unable to create dummy storage node "
+ "for source %s\n",
+ isns_source_name(source));
+ }
+ isns_attr_list_destroy(&attrs);
+ }
+#endif
+
+ /*
+ * 5.6.5.1
+ * New PG objects are registered when an associated Portal or
+ * iSCSI Node object is registered. An explicit PG object
+ * registration MAY follow a Portal or iSCSI Node object
+ * registration in a DevAttrReg message.
+ * [...]
+ * If the PGT value is not included in the Storage Node or
+ * Portal object registration, and if a PGT value was not
+ * previously registered for the relationship, then the PGT for
+ * the corresponding PG object SHALL be registered with a value
+ * of 0x00000001.
+ */
+ isns_create_registration_pgs(db, &objects);
+
+ /* Success: create a new registration message, and
+ * send it in our reply. */
+ reply = __isns_create_registration(srv->is_source, key_obj);
+ if (reply == NULL) {
+ status = ISNS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ /* If the key object was modified (or created)
+ * include it in the response.
+ * We really ought to restrict ourselves to the
+ * key attrs plus those that were modified by this
+ * registration. But right now have no way of
+ * finding out.
+ */
+ if (key_obj->ie_flags & ISNS_OBJECT_DIRTY)
+ isns_registration_add_object(reply, key_obj);
+
+ for (i = 0; i < objects.iol_count; ++i) {
+ isns_registration_add_object(reply,
+ objects.iol_data[i]);
+ }
+
+
+done:
+ isns_object_list_destroy(&objects);
+ isns_object_release(key_obj);
+ *result = reply;
+ return status;
+}
+
+/*
+ * Extract the list of objects from the DevAttrReg response
+ */
+int
+isns_registration_response_get_objects(isns_simple_t *reg,
+ isns_object_list_t *result)
+{
+ return isns_simple_response_get_objects(reg, result);
+}
diff --git a/utils/open-isns/relation.c b/utils/open-isns/relation.c
new file mode 100644
index 0000000..caac38b
--- /dev/null
+++ b/utils/open-isns/relation.c
@@ -0,0 +1,281 @@
+/*
+ * iSNS object relationships
+ *
+ * Relations are used to express a connection between two
+ * objects. Currently, two relationship types are implemented:
+ *
+ * - portal group: this relates a storage node and a portal
+ * - visibility: this relates a nodes nodes that share a
+ * common discovery domain.
+ *
+ * Relation objects are nice for portals groups, but kind of
+ * awkward for DDs. A better way of expressing DD membership
+ * (which also allows for a fast visibility check) could be
+ * to store a [bit] vector of DD IDs in each storage node.
+ * A visibility check would amount to just doing the bitwise
+ * AND of two vectors, and checking for NULL. The only thing
+ * to take care of would be to make sure a DD object takes a
+ * reference on its members (this is necessary so that objects
+ * maintain their ID/name associations even when removed from
+ * the database).
+ *
+ * Aug 22 2007 - changed DD code to use bit vectors. A lot
+ * of code in this file is now obsolete.
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+
+#include "isns.h"
+#include "objects.h"
+#include "util.h"
+#include "db.h"
+
+struct isns_relation_soup {
+ /* For now, use one plain list. For better
+ * scalability, we'll need a hash table or
+ * something similar later. */
+ isns_relation_list_t irs_data;
+};
+
+static void isns_relation_list_append(isns_relation_list_t *,
+ isns_relation_t *);
+static int isns_relation_list_remove(isns_relation_list_t *,
+ isns_relation_t *);
+
+isns_relation_soup_t *
+isns_relation_soup_alloc(void)
+{
+ return isns_calloc(1, sizeof(isns_relation_soup_t));
+}
+
+void
+isns_relation_add(isns_relation_soup_t *soup,
+ isns_relation_t *rp)
+{
+ isns_relation_list_append(&soup->irs_data, rp);
+}
+
+isns_relation_t *
+isns_relation_find_edge(isns_relation_soup_t *soup,
+ const isns_object_t *left,
+ const isns_object_t *right,
+ unsigned int relation_type)
+{
+ isns_relation_list_t *list = &soup->irs_data;
+ unsigned int i;
+
+ for (i = 0; i < list->irl_count; ++i) {
+ isns_relation_t *rp = list->irl_data[i];
+
+ if (rp->ir_type != relation_type)
+ continue;
+ if (rp->ir_subordinate[0].obj == left
+ && rp->ir_subordinate[1].obj == right)
+ return rp;
+ if (rp->ir_subordinate[0].obj == right
+ && rp->ir_subordinate[1].obj == left)
+ return rp;
+ }
+ return NULL;
+}
+
+void
+isns_relation_get_edge_objects(isns_relation_soup_t *soup,
+ const isns_object_t *left,
+ unsigned int relation_type,
+ isns_object_list_t *result)
+{
+ isns_relation_list_t *list = &soup->irs_data;
+ unsigned int i;
+
+ for (i = 0; i < list->irl_count; ++i) {
+ isns_relation_t *rp = list->irl_data[i];
+
+ if (rp->ir_type != relation_type)
+ continue;
+ if (rp->ir_object == NULL)
+ continue;
+ if (rp->ir_subordinate[0].obj == left
+ || rp->ir_subordinate[1].obj == left) {
+ isns_object_list_append(result,
+ rp->ir_object);
+ }
+ }
+}
+
+
+
+void
+isns_relation_halfspace(isns_relation_soup_t *soup,
+ const isns_object_t *left,
+ unsigned int relation_type,
+ isns_object_list_t *result)
+{
+ isns_relation_list_t *list = &soup->irs_data;
+ unsigned int i;
+
+ for (i = 0; i < list->irl_count; ++i) {
+ isns_relation_t *rp = list->irl_data[i];
+
+ if (rp->ir_type != relation_type)
+ continue;
+ if (rp->ir_subordinate[0].obj == left) {
+ isns_object_list_append(result,
+ rp->ir_subordinate[1].obj);
+ } else
+ if (rp->ir_subordinate[1].obj == left) {
+ isns_object_list_append(result,
+ rp->ir_subordinate[0].obj);
+ }
+ }
+}
+
+int
+isns_relation_exists(isns_relation_soup_t *soup,
+ const isns_object_t *relating_object,
+ const isns_object_t *left,
+ const isns_object_t *right,
+ unsigned int relation_type)
+{
+ isns_relation_list_t *list = &soup->irs_data;
+ unsigned int i;
+
+ for (i = 0; i < list->irl_count; ++i) {
+ isns_relation_t *rp = list->irl_data[i];
+
+ if (rp->ir_type != relation_type)
+ continue;
+ if (rp->ir_object != relating_object)
+ continue;
+ if (rp->ir_subordinate[0].obj == left
+ && rp->ir_subordinate[1].obj == right)
+ return 1;
+ if (rp->ir_subordinate[0].obj == right
+ && rp->ir_subordinate[1].obj == left)
+ return 1;
+ }
+ return 0;
+}
+
+isns_object_t *
+isns_relation_get_other(const isns_relation_t *rp,
+ const isns_object_t *this)
+{
+ if (rp->ir_subordinate[0].obj == this)
+ return rp->ir_subordinate[1].obj;
+ if (rp->ir_subordinate[1].obj == this)
+ return rp->ir_subordinate[0].obj;
+ return NULL;
+}
+
+void
+isns_relation_remove(isns_relation_soup_t *soup,
+ isns_relation_t *rp)
+{
+ isns_object_release(rp->ir_object);
+ rp->ir_object = NULL;
+
+ isns_relation_list_remove(&soup->irs_data, rp);
+}
+
+isns_relation_t *
+isns_create_relation(isns_object_t *relating_object,
+ unsigned int relation_type,
+ isns_object_t *subordinate_object1,
+ isns_object_t *subordinate_object2)
+{
+ isns_relation_t *rp;
+
+ rp = isns_calloc(1, sizeof(*rp));
+ rp->ir_type = relation_type;
+ rp->ir_users = 1;
+ rp->ir_object = isns_object_get(relating_object);
+ isns_object_reference_set(&rp->ir_subordinate[0], subordinate_object1);
+ isns_object_reference_set(&rp->ir_subordinate[1], subordinate_object2);
+
+#if 0
+ if (relating_object) {
+ relating_object->ie_relation = rp;
+ rp->ir_users++;
+ }
+#endif
+
+ return rp;
+}
+
+void
+isns_relation_sever(isns_relation_t *rp)
+{
+ isns_object_release(rp->ir_object);
+ rp->ir_object = NULL;
+
+ isns_object_reference_drop(&rp->ir_subordinate[0]);
+ isns_object_reference_drop(&rp->ir_subordinate[1]);
+}
+
+void
+isns_relation_release(isns_relation_t *rp)
+{
+ if (--(rp->ir_users))
+ return;
+
+ isns_relation_sever(rp);
+ isns_free(rp);
+}
+
+/*
+ * Check whether the relation references two dead/limbo objects.
+ * This is used for dead PG removal.
+ */
+int
+isns_relation_is_dead(const isns_relation_t *rel)
+{
+ isns_object_t *left, *right;
+
+ left = rel->ir_subordinate[0].obj;
+ right = rel->ir_subordinate[1].obj;
+ if ((left->ie_flags & ISNS_OBJECT_DEAD)
+ && (right->ie_flags & ISNS_OBJECT_DEAD))
+ return 1;
+
+ return 0;
+}
+
+void
+isns_relation_list_append(isns_relation_list_t *list,
+ isns_relation_t *rp)
+{
+ if ((list->irl_count % 128) == 0) {
+ list->irl_data = isns_realloc(list->irl_data,
+ (list->irl_count + 128) * sizeof(void *));
+ if (list->irl_data == NULL)
+ isns_fatal("out of memory!\n");
+ }
+
+ list->irl_data[list->irl_count++] = rp;
+ rp->ir_users++;
+}
+
+int
+isns_relation_list_remove(isns_relation_list_t *list,
+ isns_relation_t *rp)
+{
+ unsigned int i, count = list->irl_count;
+
+ for (i = 0; i < count; ++i) {
+ if (list->irl_data[i] != rp)
+ continue;
+ if (i < count - 1)
+ list->irl_data[i] = list->irl_data[count-1];
+ isns_relation_release(rp);
+ list->irl_count -= 1;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/utils/open-isns/scn.c b/utils/open-isns/scn.c
new file mode 100644
index 0000000..d8fda1d
--- /dev/null
+++ b/utils/open-isns/scn.c
@@ -0,0 +1,926 @@
+/*
+ * Handle SCN registration/deregistration/events
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "isns.h"
+#include "attrs.h"
+#include "objects.h"
+#include "message.h"
+#include "security.h"
+#include "util.h"
+#include "db.h"
+
+typedef struct isns_scn isns_scn_t;
+typedef struct isns_scn_funnel isns_scn_funnel_t;
+
+struct isns_scn {
+ isns_scn_t * scn_next;
+ char * scn_name;
+ isns_object_t * scn_entity;
+ isns_object_t * scn_owner;
+ isns_attr_t * scn_attr;
+
+ isns_simple_t * scn_message;
+ isns_simple_t * scn_pending;
+ unsigned int scn_retries;
+ time_t scn_timeout;
+ uint16_t scn_xid;
+
+ time_t scn_last_update;
+ isns_scn_funnel_t * scn_current_funnel;
+ isns_scn_funnel_t * scn_funnels;
+};
+
+struct isns_scn_funnel {
+ isns_scn_funnel_t * scn_next;
+ isns_portal_info_t scn_portal;
+ isns_socket_t * scn_socket;
+ unsigned int scn_bad;
+};
+
+static isns_server_t * isns_scn_server = NULL;
+static isns_scn_t * isns_scn_list;
+
+static isns_scn_t * isns_scn_create_scn(isns_object_t *, uint32_t, isns_db_t *);
+static void isns_scn_delete_scn(isns_object_t *);
+static isns_scn_t * isns_scn_setup(isns_scn_t *, isns_object_t *);
+static void isns_scn_callback(const isns_db_event_t *, void *);
+static void isns_scn_free(isns_scn_t *);
+
+/*
+ * Initialize SCN machinery
+ */
+void
+isns_scn_init(isns_server_t *srv)
+{
+ isns_db_t *db = srv->is_db;
+ isns_object_list_t nodes = ISNS_OBJECT_LIST_INIT;
+ isns_scn_t **tail;
+ unsigned int i;
+
+ isns_scn_server = srv;
+ isns_register_callback(isns_scn_callback, db);
+
+ /* Recover SCN state. */
+ isns_db_gang_lookup(db, &isns_iscsi_node_template, NULL, &nodes);
+#ifdef notyet
+ isns_db_gang_lookup(db, &isns_fc_node_template, NULL, &nodes);
+#endif
+
+ tail = &isns_scn_list;
+ for (i = 0; i < nodes.iol_count; ++i) {
+ isns_object_t *node = nodes.iol_data[i];
+ isns_scn_t *scn;
+
+ if (!node->ie_scn_mask)
+ continue;
+
+ isns_debug_state("Recovering SCN state for %s %u\n",
+ node->ie_template->iot_name,
+ node->ie_index);
+ scn = isns_scn_setup(NULL, node);
+ if (scn) {
+ *tail = scn;
+ tail = &scn->scn_next;
+ }
+ }
+}
+
+/*
+ * Support for SCNRegister calls
+ */
+isns_simple_t *
+isns_create_scn_registration2(isns_client_t *clnt, unsigned int bitmap, isns_source_t *source)
+{
+ isns_simple_t *call;
+
+ if (!source)
+ source = clnt->ic_source;
+ call = isns_simple_create(ISNS_SCN_REGISTER, source, NULL);
+ if (call) {
+ isns_attr_list_append_attr(&call->is_message_attrs,
+ isns_source_attr(source));
+ isns_attr_list_append_uint32(&call->is_operating_attrs,
+ ISNS_TAG_ISCSI_SCN_BITMAP,
+ bitmap);
+ }
+ return call;
+}
+
+isns_simple_t *
+isns_create_scn_registration(isns_client_t *clnt, unsigned int bitmap)
+{
+ return isns_create_scn_registration2(clnt, bitmap, clnt->ic_source);
+}
+
+/*
+ * Create an SCN
+ */
+isns_simple_t *
+isns_create_scn(isns_source_t *source, isns_attr_t *nodeattr, isns_attr_t *tsattr)
+{
+ isns_simple_t *call;
+
+ call = isns_simple_create(ISNS_STATE_CHANGE_NOTIFICATION, source, NULL);
+ if (call && nodeattr)
+ isns_attr_list_append_attr(&call->is_message_attrs, nodeattr);
+ if (call && tsattr)
+ isns_attr_list_append_attr(&call->is_message_attrs, tsattr);
+ return call;
+}
+
+static void
+isns_scn_add_event(isns_simple_t *call, uint32_t scn_bits,
+ const isns_object_t *obj,
+ const isns_object_t *dd)
+{
+ isns_attr_list_t *attrs = &call->is_message_attrs;
+
+ isns_attr_list_append_uint32(attrs,
+ ISNS_TAG_ISCSI_SCN_BITMAP,
+ scn_bits);
+ isns_object_extract_keys(obj, attrs);
+ if (dd)
+ isns_object_extract_keys(dd, attrs);
+}
+
+/*
+ * Process a SCN registration
+ */
+int
+isns_process_scn_register(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
+{
+ isns_attr_list_t *keys = &call->is_message_attrs;
+ isns_attr_list_t *attrs = &call->is_operating_attrs;
+ isns_db_t *db = srv->is_db;
+ isns_attr_t *attr;
+ isns_object_t *node = NULL;
+ uint32_t scn_bitmap;
+ isns_scn_t *scn;
+ int status = ISNS_SUCCESS;
+
+ /*
+ * 5.6.5.5
+ * The SCNReg request PDU Payload contains a Source Attribute, a Message
+ * Key Attribute, and an Operating Attribute. Valid Message Key
+ * Attributes for a SCNReg are shown below:
+ *
+ * Valid Message Key Attributes for SCNReg
+ * ---------------------------------------
+ * iSCSI Name
+ * FC Port Name WWPN
+ */
+ if (keys->ial_count != 1 || attrs->ial_count != 1)
+ return ISNS_SCN_REGISTRATION_REJECTED;
+
+ attr = keys->ial_data[0];
+ if (attr->ia_tag_id != ISNS_TAG_ISCSI_NAME &&
+ attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
+ return ISNS_SCN_REGISTRATION_REJECTED;
+
+ /* Look up the storage node for this source. If it does
+ * not exist, reject the message. */
+ node = isns_db_lookup(db, NULL, keys);
+ if (node == NULL)
+ return ISNS_SOURCE_UNKNOWN;
+
+ /*
+ * Policy: verify that the client is permitted
+ * to access this entity.
+ *
+ * This includes
+ * - the client node must be the object owner,
+ * or a control node.
+ * - the policy must allow monitoring of
+ * this object type.
+ */
+ if (!isns_policy_validate_object_access(call->is_policy,
+ call->is_source,
+ node, call->is_function))
+ goto unauthorized;
+
+ /*
+ * 5.6.5.5
+ * The SCN Bitmap is the only operating attribute of this message
+ * [...]
+ * Control Nodes MAY conduct registrations for management SCNs;
+ * iSNS clients that are not supporting Control Nodes MUST NOT
+ * conduct registrations for management SCNs.
+ *
+ * Implementer's note: for iFCP sources, we should check for
+ * ISNS_TAG_IFCP_SCN_BITMAP.
+ */
+ attr = attrs->ial_data[0];
+ if (attr->ia_tag_id != ISNS_TAG_ISCSI_SCN_BITMAP
+ || !ISNS_ATTR_IS_UINT32(attr))
+ goto rejected;
+
+ scn_bitmap = attr->ia_value.iv_uint32;
+ if (!isns_policy_validate_scn_bitmap(call->is_policy, scn_bitmap))
+ goto unauthorized;
+
+ /*
+ * 5.6.5.5
+ * If no SCN Port fields of any Portals of the Storage Node are
+ * registered to receive SCN messages, then the SCNReg message SHALL
+ * be rejected with Status Code 17 (SCN Registration Rejected).
+ */
+ if (!(scn = isns_scn_create_scn(node, scn_bitmap, db)))
+ goto rejected;
+
+ *result = isns_simple_create(ISNS_SCN_REGISTER, srv->is_source, NULL);
+ status = ISNS_SUCCESS;
+
+out:
+ if (node)
+ isns_object_release(node);
+
+ return status;
+
+rejected:
+ status = ISNS_SCN_REGISTRATION_REJECTED;
+ goto out;
+
+unauthorized:
+ status = ISNS_SOURCE_UNAUTHORIZED;
+ goto out;
+}
+
+/*
+ * Process a SCNDereg message
+ */
+int
+isns_process_scn_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
+{
+ isns_attr_list_t *keys = &call->is_message_attrs;
+ isns_db_t *db = srv->is_db;
+ isns_attr_t *attr;
+ isns_object_t *node = NULL;
+ int status = ISNS_SUCCESS;
+
+ /*
+ * 5.6.5.6
+ * The SCNDereg request message PDU Payload contains a Source Attribute
+ * and Message Key Attribute(s). Valid Message Key Attributes for a
+ * SCNDereg are shown below:
+ *
+ * Valid Message Key Attributes for SCNDereg
+ * -----------------------------------------
+ * iSCSI Name
+ * FC Port Name WWPN
+ *
+ * There are no Operating Attributes in the SCNDereg message.
+ */
+
+ if (keys->ial_count != 1)
+ return ISNS_SCN_REGISTRATION_REJECTED;
+
+ attr = keys->ial_data[0];
+ if (attr->ia_tag_id != ISNS_TAG_ISCSI_NAME &&
+ attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
+ return ISNS_SCN_REGISTRATION_REJECTED;
+
+ /* Look up the storage node for this source. If it does
+ * not exist, reject the message. */
+ node = isns_db_lookup(db, NULL, keys);
+ if (node == NULL)
+ return ISNS_SUCCESS;
+
+ /*
+ * Policy: verify that the client is permitted
+ * to access this entity.
+ *
+ * This includes
+ * - the client node must be the object owner,
+ * or a control node.
+ * - the policy must allow monitoring of
+ * this object type.
+ */
+ if (!isns_policy_validate_object_access(call->is_policy,
+ call->is_source,
+ node, call->is_function))
+ goto unauthorized;
+
+ isns_object_set_scn_mask(node, 0);
+ isns_scn_delete_scn(node);
+
+ *result = isns_simple_create(ISNS_SCN_DEREGISTER, srv->is_source, NULL);
+ status = ISNS_SUCCESS;
+
+out:
+ if (node)
+ isns_object_release(node);
+
+ return status;
+
+unauthorized:
+ status = ISNS_SOURCE_UNAUTHORIZED;
+ goto out;
+}
+
+/*
+ * Set up the SCN object.
+ */
+static isns_scn_t *
+isns_scn_setup(isns_scn_t *scn, isns_object_t *node)
+{
+ isns_object_list_t portals = ISNS_OBJECT_LIST_INIT;
+ isns_object_t *entity;
+ unsigned int i;
+
+ entity = isns_object_get_entity(node);
+ if (entity == NULL
+ || !isns_object_find_descendants(entity,
+ &isns_portal_template, NULL, &portals))
+ return NULL;
+
+ for (i = 0; i < portals.iol_count; ++i) {
+ isns_object_t *portal = portals.iol_data[i];
+ isns_portal_info_t info;
+ isns_scn_funnel_t *funnel;
+
+ /* Extract address and SCN port from portal */
+ if (!isns_portal_from_object(&info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_SCN_PORT,
+ portal))
+ continue;
+
+ /* We know where to send our notifications! */
+ if (scn == NULL) {
+ isns_attr_t *attr;
+
+ if (!isns_object_get_attr(node, ISNS_TAG_ISCSI_NAME, &attr)
+ && !isns_object_get_attr(node, ISNS_TAG_FC_PORT_NAME_WWPN, &attr)) {
+ isns_error("Attempt to set up SCN for strange node type\n");
+ return NULL;
+ }
+
+ scn = isns_calloc(1, sizeof(*scn));
+ scn->scn_entity = isns_object_get(entity);
+ scn->scn_owner = isns_object_get(node);
+ scn->scn_attr = isns_attr_get(attr);
+ scn->scn_name = isns_strdup(attr->ia_value.iv_string);
+ }
+
+ funnel = isns_calloc(1, sizeof(*funnel));
+ funnel->scn_portal = info;
+ funnel->scn_next = scn->scn_funnels;
+ scn->scn_funnels = funnel;
+ }
+
+ isns_object_list_destroy(&portals);
+ return scn;
+}
+
+/*
+ * See if an SCN object exists for the given target;
+ * if it doesn't, then create one.
+ */
+static isns_scn_t *
+isns_scn_create_scn(isns_object_t *node, uint32_t bitmap, isns_db_t *db)
+{
+ isns_scn_t *scn;
+
+ for (scn = isns_scn_list; scn; scn = scn->scn_next) {
+ if (scn->scn_owner == node)
+ goto done;
+ }
+
+ /* Not found - create it */
+ scn = isns_scn_setup(NULL, node);
+ if (scn == NULL)
+ return NULL;
+
+ scn->scn_next = isns_scn_list;
+ isns_scn_list = scn;
+
+done:
+ /* We're all set - update the bitmap */
+ isns_object_set_scn_mask(node, bitmap);
+ return scn;
+}
+
+static void
+isns_scn_delete_scn(isns_object_t *node)
+{
+ isns_scn_t *scn, **pos;
+
+ pos = &isns_scn_list;
+ while ((scn = *pos) != NULL) {
+ if (scn->scn_owner == node) {
+ isns_debug_scn("Deregistering SCN for node %u\n",
+ node->ie_index);
+ *pos = scn->scn_next;
+ isns_scn_free(scn);
+ return;
+ }
+ pos = &scn->scn_next;
+ }
+}
+
+static void
+isns_scn_release_funnels(isns_scn_t *scn)
+{
+ isns_scn_funnel_t *funnel;
+
+ while ((funnel = scn->scn_funnels) != NULL) {
+ scn->scn_funnels = funnel->scn_next;
+ if (funnel->scn_socket)
+ isns_socket_free(funnel->scn_socket);
+ isns_free(funnel);
+ }
+}
+
+static void
+isns_scn_free(isns_scn_t *scn)
+{
+ isns_scn_release_funnels(scn);
+ isns_object_release(scn->scn_owner);
+ isns_object_release(scn->scn_entity);
+ isns_attr_release(scn->scn_attr);
+ isns_free(scn->scn_name);
+ isns_free(scn);
+}
+
+/*
+ * Check whether we should send an event to the target
+ */
+static inline int
+isns_scn_match(isns_scn_t *scn, uint32_t event,
+ const isns_object_t *node,
+ uint32_t node_type)
+{
+ if (event == 0)
+ return 0;
+
+ if (node->ie_scn_mask & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK)
+ return event | ISNS_SCN_MANAGEMENT_REGISTRATION_MASK;
+
+#if 0
+ /* This is a normal (non-control) node. Check whether the object
+ * is in the scope of this client. */
+ if (!isns_object_in_scope(scn->scn_owner, node))
+ return 0;
+#endif
+
+ if (node->ie_scn_mask & ISNS_SCN_TARGET_AND_SELF_ONLY_MASK) {
+ if (node != scn->scn_owner && !(node_type & ISNS_ISCSI_TARGET_MASK))
+ return 0;
+ }
+ if (node->ie_scn_mask & ISNS_SCN_INITIATOR_AND_SELF_ONLY_MASK) {
+ if (node != scn->scn_owner && !(node_type & ISNS_ISCSI_INITIATOR_MASK))
+ return 0;
+ }
+
+ return event;
+}
+
+/*
+ * Helper to create time stamp attr
+ */
+static isns_attr_t *
+isns_create_timestamp_attr(void)
+{
+ isns_value_t value = ISNS_VALUE_INIT(uint64, time(NULL));
+
+ return isns_attr_alloc(ISNS_TAG_TIMESTAMP, NULL, &value);
+}
+
+/*
+ * This function is invoked whenever someone changes the
+ * database.
+ *
+ * SCNs are another area where the RFC is fabulously wishy washy.
+ * It is not entirely clear when DD/DDS information should be
+ * included in a management SCN - one *reasonable* interpretation
+ * would be that this happens for DDReg/DDDereg/DDSReg/DDSDereg
+ * events only. But some sections make it sound as if DD
+ * information is included for all management SCNs.
+ */
+void
+isns_scn_callback(const isns_db_event_t *ev, void *ptr)
+{
+ isns_object_t *obj = ev->ie_object;
+ isns_scn_t *scn, **pos;
+ isns_attr_t *timestamp;
+ uint32_t node_type;
+
+ /* Never send out notifications for policy objects and the like. */
+ if (obj->ie_flags & ISNS_OBJECT_PRIVATE)
+ return;
+
+ /* When an entity is nuked, remove all SCNs to nodes
+ * that registered from there */
+ if (ISNS_IS_ENTITY(obj) && (ev->ie_bits & ISNS_SCN_OBJECT_REMOVED_MASK)) {
+ pos = &isns_scn_list;
+ while ((scn = *pos) != NULL) {
+ if (scn->scn_entity != obj) {
+ pos = &scn->scn_next;
+ continue;
+ }
+ isns_debug_scn("Deleting SCN registration for %s\n",
+ scn->scn_name);
+ *pos = scn->scn_next;
+ isns_scn_free(scn);
+ }
+ return;
+ }
+
+ /* For now we handle iSCSI nodes only. Maybe later we'll
+ * do iFC nodes as well. */
+ if (!ISNS_IS_ISCSI_NODE(obj))
+ return;
+ if (!isns_object_get_uint32(obj, ISNS_TAG_ISCSI_NODE_TYPE, &node_type))
+ return;
+
+ if (ev->ie_recipient) {
+ isns_object_t *dst = ev->ie_recipient;
+
+ isns_debug_scn("SCN unicast <%s %u, %s> -> %s %u\n",
+ obj->ie_template->iot_name, obj->ie_index,
+ isns_event_string(ev->ie_bits),
+ dst->ie_template->iot_name, dst->ie_index);
+ } else {
+ isns_debug_scn("SCN multicast <%s %u, %s>\n",
+ obj->ie_template->iot_name, obj->ie_index,
+ isns_event_string(ev->ie_bits));
+ }
+ timestamp = isns_create_timestamp_attr();
+
+ pos = &isns_scn_list;
+ while ((scn = *pos) != NULL) {
+ unsigned int scn_bits, management;
+ isns_object_t *recipient, *dd = NULL;
+ isns_simple_t *call;
+
+ recipient = scn->scn_owner;
+
+ /* Check if the node has gone away completely. */
+ if (recipient->ie_scn_mask == 0) {
+ *pos = scn->scn_next;
+ isns_scn_free(scn);
+ continue;
+ }
+
+ if (recipient->ie_container == NULL) {
+ isns_warning("Internal bug - SCN recipient without container\n");
+ /* Clear the bitmask and loop over - this will remove it */
+ recipient->ie_scn_mask = 0;
+ continue;
+ }
+
+ /* See if portals were added/removed.
+ * This does not catch updates that modified *just*
+ * the SCN port */
+ if (recipient->ie_container->ie_mtime != scn->scn_last_update) {
+ /* Rebuild the list of SCN portals */
+ isns_scn_release_funnels(scn);
+ scn->scn_last_update = 0;
+ }
+ pos = &scn->scn_next;
+
+ /* Check for unicast events (triggered for DD addition/removal).
+ * For unicast events, we do not mask the SCN bits, so that
+ * clients who have registered for non-management events
+ * will see the membership events for their DDs nevertheless. */
+ if (ev->ie_recipient == NULL) {
+ scn_bits = ev->ie_bits & recipient->ie_scn_mask;
+ if (scn_bits == 0)
+ continue;
+ /* Management SCNs should not be delivered to nodes
+ * that have not registered for them. */
+ if ((ev->ie_bits & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK)
+ && !(recipient->ie_scn_mask & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK))
+ continue;
+ } else if (recipient == ev->ie_recipient) {
+ scn_bits = ev->ie_bits;
+ } else {
+ /* No match, skip this recipient */
+ continue;
+ }
+
+ if (scn->scn_last_update == 0) {
+ scn->scn_last_update = recipient->ie_container->ie_mtime;
+ isns_scn_setup(scn, recipient);
+ }
+
+ /* We check for SCN capable portals when processing
+ * the SCN registration. But the portals may go away
+ * in the meantime. */
+ if (scn->scn_funnels == NULL)
+ continue;
+
+ /* Check SCN bitmask. This will modify the event bits. */
+ scn_bits = isns_scn_match(scn, scn_bits, obj, node_type);
+ if (scn_bits == 0)
+ continue;
+ management = !!(scn_bits & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK);
+
+ /*
+ * 2.2.3
+ * A regular SCN registration indicates that the
+ * Discovery Domain Service SHALL be used to control the
+ * distribution of SCN messages. Receipt of regular
+ * SCNs is limited to the discovery domains in which
+ * the SCN-triggering event takes place. Regular SCNs
+ * do not contain information about discovery domains.
+ *
+ * Implementer's note: We override check for unicast events.
+ * The reason is that DDDereg will sever the
+ * relationship, and we would never send an SCN for that
+ * event.
+ */
+ if (!management && !ev->ie_recipient) {
+ if (!isns_object_test_visibility(obj, recipient))
+ continue;
+ }
+
+ isns_debug_scn("preparing to send SCN to %s\n",
+ scn->scn_name);
+
+ if ((call = scn->scn_message) == NULL) {
+ call = isns_create_scn(isns_scn_server->is_source,
+ scn->scn_attr,
+ timestamp);
+ if (call == NULL)
+ continue;
+ scn->scn_message = call;
+ }
+
+ /*
+ * If the SCN is a Management SCN, then the SCN message
+ * SHALL also list the DD_ID and/or DDS_ID of the
+ * Discovery Domains and Discovery Domain Sets (if any)
+ * that caused the change in state for that Storage Node.
+ * These additional attributes (i.e., DD_ID and/or DDS_ID)
+ * shall immediately follow the iSCSI Name or FC Port
+ * Name and precede the next SCN bitmap for the next
+ * notification message (if any).
+ */
+ if (management && ev->ie_trigger)
+ dd = ev->ie_trigger;
+
+ isns_scn_add_event(call, scn_bits, obj, dd);
+
+ }
+
+ isns_attr_release(timestamp);
+}
+
+/*
+ * Obtain a socket to talk to this guy.
+ * Not entirely trivial - this can be both an established
+ * (incoming) connection, or one that we should establish.
+ *
+ * Note, we do not support transmission on the incoming
+ * connection yet.
+ */
+static isns_socket_t *
+isns_scn_get_socket(isns_scn_t *scn)
+{
+ isns_scn_funnel_t *f, *best = NULL;
+ isns_socket_t *sock;
+ unsigned int worst = 0, loops = 0, nfunnels;
+
+ /* Keep it simple for now */
+ if ((f = scn->scn_current_funnel) != NULL && f->scn_socket) {
+ if (!f->scn_bad)
+ return f->scn_socket;
+ /* Oops, we've seen timeouts on this socket. */
+ isns_socket_free(f->scn_socket);
+ f->scn_socket = NULL;
+ }
+
+again:
+ nfunnels = 0;
+ for (f = scn->scn_funnels; f; f = f->scn_next) {
+ unsigned int badness = f->scn_bad;
+
+ if (!best || badness < best->scn_bad)
+ best = f;
+ if (badness > worst)
+ worst = badness;
+ nfunnels++;
+ }
+
+ if (!best)
+ return NULL;
+
+ sock = isns_connect_to_portal(&best->scn_portal);
+ if (sock == NULL) {
+ /* Make sure we try each funnel exactly once */
+ best->scn_bad = worst + 1;
+ if (++loops < nfunnels)
+ goto again;
+ return NULL;
+ }
+
+ /* Set the security context */
+ isns_socket_set_security_ctx(sock,
+ isns_default_security_context(1));
+
+ isns_debug_scn("SCN: %s using portal %s\n",
+ scn->scn_name,
+ isns_portal_string(&best->scn_portal));
+ scn->scn_current_funnel = best;
+ best->scn_socket = sock;
+ return sock;
+}
+
+/*
+ * This is the callback function invoked when the SCN message reply
+ * comes in, or when the message timed out.
+ */
+static void
+isns_process_scn_response(uint32_t xid, isns_simple_t *msg)
+{
+ isns_scn_t *scn;
+
+ if (msg == NULL) {
+ isns_debug_scn("SCN timed out\n");
+ return;
+ }
+
+ isns_debug_scn("Received an SCN response\n");
+ for (scn = isns_scn_list; scn; scn = scn->scn_next) {
+ if (scn->scn_pending && scn->scn_xid == xid) {
+ isns_debug_scn("SCN: %s acknowledged notification\n",
+ scn->scn_name);
+ isns_simple_free(scn->scn_pending);
+ scn->scn_pending = NULL;
+
+ if (scn->scn_current_funnel)
+ scn->scn_current_funnel->scn_bad = 0;
+ }
+ }
+}
+/*
+ * Transmit all pending SCN messages
+ *
+ * 2.9.2
+ * If a Network Entity has multiple Portals with registered SCN UDP Ports,
+ * then SCN messages SHALL be delivered to each Portal registered to
+ * receive such messages.
+ *
+ * FIXME: we should make this timer based just as the ESI code.
+ */
+time_t
+isns_scn_transmit_all(void)
+{
+ time_t now = time(NULL), next_timeout;
+ isns_scn_t *scn;
+
+ for (scn = isns_scn_list; scn; scn = scn->scn_next) {
+ isns_simple_t *call;
+ isns_socket_t *sock;
+
+ /* We do not allow more than one outstanding
+ * notification for now. */
+ if ((call = scn->scn_pending) != NULL) {
+ if (scn->scn_timeout > now)
+ continue;
+ scn->scn_current_funnel->scn_bad++;
+ if (--(scn->scn_retries))
+ goto retry;
+ isns_warning("SCN for %s timed out\n",
+ scn->scn_name);
+ isns_simple_free(call);
+ scn->scn_pending = NULL;
+ }
+
+ if ((call = scn->scn_message) == NULL)
+ continue;
+
+ isns_debug_scn("SCN: transmit pending message for %s\n",
+ scn->scn_name);
+ scn->scn_retries = isns_config.ic_scn_retries;
+ scn->scn_pending = call;
+ scn->scn_message = NULL;
+
+retry:
+ if ((sock = isns_scn_get_socket(scn)) == NULL) {
+ /* Sorry, no can do. */
+ isns_warning("SCN for %s dropped - no portal\n",
+ scn->scn_name);
+ scn->scn_pending = NULL;
+ isns_simple_free(call);
+ continue;
+ }
+
+ isns_simple_transmit(sock, call, NULL,
+ isns_config.ic_scn_timeout,
+ isns_process_scn_response);
+ scn->scn_xid = call->is_xid;
+ scn->scn_timeout = now + isns_config.ic_scn_timeout;
+ }
+
+ next_timeout = now + 3600;
+ for (scn = isns_scn_list; scn; scn = scn->scn_next) {
+ if (scn->scn_pending && scn->scn_timeout < next_timeout)
+ next_timeout = scn->scn_timeout;
+ }
+
+ return next_timeout;
+}
+
+/*
+ * Process an incoming State Change Notification
+ */
+int
+isns_process_scn(isns_server_t *srv, isns_simple_t *call, isns_simple_t **reply)
+{
+ isns_attr_list_t *list = &call->is_message_attrs;
+ isns_attr_t *dstattr, *tsattr;
+ const char *dst_name;
+ unsigned int i;
+
+ /* The first attribute is the destination, and should match
+ * our source name. Don't bother checking. The second is the
+ * time stamp.
+ */
+ if (list->ial_count < 2)
+ goto rejected;
+
+ dstattr = list->ial_data[0];
+ if (dstattr->ia_tag_id != ISNS_TAG_ISCSI_NAME
+ && dstattr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
+ goto rejected;
+ if (!ISNS_ATTR_IS_STRING(dstattr))
+ goto rejected;
+ dst_name = dstattr->ia_value.iv_string;
+
+ tsattr = list->ial_data[1];
+ if (tsattr->ia_tag_id != ISNS_TAG_TIMESTAMP)
+ return ISNS_SCN_EVENT_REJECTED;
+
+ for (i = 2; i < list->ial_count; ) {
+ isns_object_template_t *tmpl;
+ isns_attr_t *bmattr, *srcattr;
+ const char *node_name;
+ uint32_t bitmap;
+
+ if (i + 1 >= list->ial_count)
+ goto rejected;
+
+ bmattr = list->ial_data[i++];
+ srcattr = list->ial_data[i++];
+
+ /* Validate that bitmap and node type match */
+ switch (bmattr->ia_tag_id) {
+ case ISNS_TAG_ISCSI_SCN_BITMAP:
+ if (srcattr->ia_tag_id != ISNS_TAG_ISCSI_NAME)
+ goto rejected;
+ tmpl = &isns_iscsi_node_template;
+ break;
+
+ case ISNS_TAG_IFCP_SCN_BITMAP:
+ if (srcattr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
+ goto rejected;
+ tmpl = &isns_fc_port_template;
+ break;
+
+ default:
+ goto rejected;
+ }
+
+ /* Skip over and DD_ID or DDS_ID attrs */
+ while (i < list->ial_count) {
+ isns_attr_t *ddattr = list->ial_data[i];
+
+ if (ddattr->ia_tag_id == ISNS_TAG_ISCSI_SCN_BITMAP
+ || ddattr->ia_tag_id == ISNS_TAG_IFCP_SCN_BITMAP)
+ break;
+ ++i;
+ }
+
+ if (!ISNS_ATTR_IS_UINT32(bmattr))
+ goto rejected;
+ bitmap = bmattr->ia_value.iv_uint32;
+
+ if (!ISNS_ATTR_IS_STRING(srcattr))
+ goto rejected;
+ node_name = srcattr->ia_value.iv_string;
+
+ if (srv->is_scn_callback)
+ srv->is_scn_callback(srv->is_db, bitmap, tmpl, node_name, dst_name);
+ }
+
+ /*
+ * 5.7.5.8. SCN Response (SCNRsp)
+ * The SCNRsp response contains the SCN Destination Attribute
+ * representing the Node identifier that received the SCN.
+ */
+ *reply = isns_create_scn(srv->is_source,
+ list->ial_data[0],
+ NULL);
+ return ISNS_SUCCESS;
+
+rejected:
+ return ISNS_SCN_EVENT_REJECTED;
+}
diff --git a/utils/open-isns/scope.c b/utils/open-isns/scope.c
new file mode 100644
index 0000000..9ee7f9a
--- /dev/null
+++ b/utils/open-isns/scope.c
@@ -0,0 +1,513 @@
+/*
+ * Handle object visibility and scope.
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "attrs.h"
+#include "objects.h"
+#include "message.h"
+#include "security.h"
+#include "util.h"
+#include "db.h"
+
+struct isns_scope {
+ isns_db_t * ic_db;
+ unsigned int ic_users;
+ isns_object_t * ic_source_node;
+
+ isns_object_template_t * ic_query_class;
+
+ isns_object_list_t ic_dd_nodes;
+ isns_object_list_t ic_dd_portals;
+ isns_object_list_t ic_objects;
+};
+
+static int __isns_scope_collect_dd(uint32_t, void *);
+
+/*
+ * Allocate an empty scope
+ */
+isns_scope_t *
+isns_scope_alloc(isns_db_t *db)
+{
+ isns_scope_t *scope;
+
+ scope = isns_calloc(1, sizeof(*scope));
+
+ scope->ic_db = db;
+ scope->ic_users = 1;
+ return scope;
+}
+
+isns_scope_t *
+isns_scope_get(isns_scope_t *scope)
+{
+ if (scope) {
+ isns_assert(scope->ic_users);
+ scope->ic_users++;
+ }
+ return scope;
+}
+
+void
+isns_scope_release(isns_scope_t *scope)
+{
+ if (!scope)
+ return;
+
+ isns_assert(scope->ic_users);
+ if (--(scope->ic_users))
+ return;
+
+ isns_object_release(scope->ic_source_node);
+ isns_object_list_destroy(&scope->ic_dd_nodes);
+ isns_object_list_destroy(&scope->ic_dd_portals);
+ isns_object_list_destroy(&scope->ic_objects);
+ isns_free(scope);
+}
+
+/*
+ * Get the scope for this operation
+ */
+isns_scope_t *
+isns_scope_for_call(isns_db_t *db, const isns_simple_t *call)
+{
+ isns_source_t *source = call->is_source;
+ isns_object_t *node;
+ isns_scope_t *scope;
+ uint32_t node_type;
+
+ /* FIXME use source->is_node and source->is_node_type */
+
+ /* When we get here, we already know that the client
+ * represents the specified source node. */
+ node = isns_db_lookup_source_node(db, source);
+
+ /* Allow unknown nodes to query the DB */
+ if (node == NULL) {
+ node = isns_create_storage_node2(source, 0, NULL);
+ if (node == NULL)
+ return NULL;
+ source->is_untrusted = 1;
+ }
+
+ if (isns_object_get_uint32(node, ISNS_TAG_ISCSI_NODE_TYPE, &node_type)
+ && (node_type & ISNS_ISCSI_CONTROL_MASK)) {
+ isns_object_release(node);
+ return isns_scope_get(db->id_global_scope);
+ }
+
+ scope = isns_scope_alloc(db);
+ scope->ic_source_node = node;
+
+ {
+ isns_object_list_t members = ISNS_OBJECT_LIST_INIT;
+ unsigned int i;
+
+ isns_object_get_visible(node, db, &members);
+ isns_object_list_uniq(&members);
+
+ /* If the node is not a member of any DD, allow it
+ * to at least talk to itself. */
+ if (members.iol_count == 0)
+ isns_object_list_append(&members, node);
+
+ /* Sort DD members into nodes and portals */
+ for (i = 0; i < members.iol_count; ++i) {
+ isns_object_t *obj = members.iol_data[i];
+
+ if (obj->ie_state != ISNS_OBJECT_STATE_MATURE)
+ continue;
+ if (!isns_policy_validate_object_access(call->is_policy,
+ source, obj,
+ call->is_function))
+ continue;
+ if (ISNS_IS_ISCSI_NODE(obj))
+ isns_object_list_append(&scope->ic_dd_nodes, obj);
+ else
+ if (ISNS_IS_PORTAL(obj))
+ isns_object_list_append(&scope->ic_dd_portals, obj);
+ }
+ isns_object_list_destroy(&members);
+ }
+
+ return scope;
+}
+
+/*
+ * Add an object to a scope
+ */
+void
+isns_scope_add(isns_scope_t *scope, isns_object_t *obj)
+{
+ isns_object_list_append(&scope->ic_objects, obj);
+}
+
+int
+isns_scope_remove(isns_scope_t *scope, isns_object_t *obj)
+{
+ return isns_object_list_remove(&scope->ic_objects, obj);
+}
+
+/*
+ * Get all objects related through a portal group, optionally
+ * including the portal group objects themselves
+ */
+static void
+__isns_scope_get_pg_related(isns_scope_t *scope,
+ const isns_object_t *obj,
+ isns_object_list_t *result)
+{
+ isns_object_list_t temp = ISNS_OBJECT_LIST_INIT;
+ unsigned int i;
+
+ /* Get all portal groups related to this object */
+ isns_db_get_relationship_objects(scope->ic_db,
+ obj, ISNS_RELATION_PORTAL_GROUP, &temp);
+
+ /* Include all portals/nodes that we can reach. */
+ for (i = 0; i < temp.iol_count; ++i) {
+ isns_object_t *pg, *other;
+ uint32_t pgt;
+
+ pg = temp.iol_data[i];
+
+ /* Skip any portal group objects with a PG tag of 0;
+ * these actually deny access. */
+ if (!isns_object_get_uint32(pg, ISNS_TAG_PG_TAG, &pgt)
+ || pgt == 0)
+ continue;
+
+ /* Get the other object.
+ * Note that isns_relation_get_other doesn't
+ * bump the reference count, so there's no need
+ * to call isns_object_release(other). */
+ other = isns_relation_get_other(pg->ie_relation, obj);
+ if (other->ie_state != ISNS_OBJECT_STATE_MATURE)
+ continue;
+
+ isns_object_list_append(result, other);
+ isns_object_list_append(result, pg);
+ }
+
+ isns_object_list_destroy(&temp);
+}
+
+/*
+ * Get all portals related to the given node.
+ *
+ * 2.2.2
+ * Placing Portals of a Network Entity into Discovery Domains allows
+ * administrators to indicate the preferred IP Portal interface through
+ * which storage traffic should access specific Storage Nodes of that
+ * Network Entity. If no Portals of a Network Entity have been placed
+ * into a DD, then queries scoped to that DD SHALL report all Portals of
+ * that Network Entity. If one or more Portals of a Network Entity have
+ * been placed into a DD, then queries scoped to that DD SHALL report
+ * only those Portals that have been explicitly placed in the DD.
+ */
+static void
+__isns_scope_get_portals(isns_scope_t *scope,
+ const isns_object_t *node,
+ isns_object_list_t *portals,
+ isns_object_list_t *pgs,
+ int unique)
+{
+ isns_object_list_t related = ISNS_OBJECT_LIST_INIT;
+ unsigned int i, specific = 0;
+
+ /* Get all portals and portal groups related to the
+ * given node. This will put pairs of (portal, portal-group)
+ * on the list.
+ */
+ __isns_scope_get_pg_related(scope, node, &related);
+
+ /* If we're querying for our own portals, don't limit
+ * visibility. */
+ if (node == scope->ic_source_node)
+ goto report_all_portals;
+
+ /* Check if any of the portals is mentioned in the DD
+ * FIXME: There is some ambiguity over what the right
+ * answer is when you have two nodes (initiator, target),
+ * and two discovery domains linking the two. One
+ * DD mentions a specific portal through which target
+ * should be accessed; the other DD does not (allowing
+ * use of any portal in that entity). Which portals
+ * to return here?
+ * We go for the strict interpretation, ie if *any* DD
+ * restricts access to certain portals, we report only
+ * those.
+ */
+ for (i = 0; i < related.iol_count; i += 2) {
+ isns_object_t *portal = related.iol_data[i];
+
+ if (isns_object_list_contains(&scope->ic_dd_portals, portal)) {
+ if (portals
+ && !(unique || isns_object_list_contains(portals, portal)))
+ isns_object_list_append(portals, portal);
+ if (pgs)
+ isns_object_list_append(pgs,
+ related.iol_data[i + 1]);
+ specific++;
+ }
+ }
+
+ if (specific)
+ goto out;
+
+report_all_portals:
+ /* No specific portal given for this node. Add them all. */
+ for (i = 0; i < related.iol_count; i += 2) {
+ isns_object_t *portal = related.iol_data[i];
+
+ if (portals
+ && !(unique && isns_object_list_contains(portals, portal)))
+ isns_object_list_append(portals, portal);
+ if (pgs)
+ isns_object_list_append(pgs,
+ related.iol_data[i + 1]);
+ }
+
+out:
+ isns_object_list_destroy(&related);
+}
+
+/*
+ * Get all nodes reachable through a given portal
+ * This is really the same as __isns_scope_get_portals
+ * minus the special casing for preferred portals.
+ * Still, let's put this into it's own function - the whole
+ * thing is already complex enough already.
+ */
+static void
+__isns_scope_get_nodes(isns_scope_t *scope,
+ const isns_object_t *portal,
+ isns_object_list_t *nodes,
+ isns_object_list_t *pgs,
+ int unique)
+{
+ isns_object_list_t related = ISNS_OBJECT_LIST_INIT;
+ unsigned int i;
+
+ /* Get all nodes and portal groups related to the
+ * given node. This will put pairs of (nodes, portal-group)
+ * on the list.
+ */
+ __isns_scope_get_pg_related(scope, portal, &related);
+
+ for (i = 0; i < related.iol_count; i += 2) {
+ isns_object_t *node = related.iol_data[i];
+
+ if (nodes
+ && !(unique && isns_object_list_contains(nodes, node)))
+ isns_object_list_append(nodes, node);
+ if (pgs)
+ isns_object_list_append(pgs,
+ related.iol_data[i + 1]);
+ }
+
+ isns_object_list_destroy(&related);
+}
+
+static void
+__isns_scope_get_default_dd(isns_scope_t *scope)
+{
+ isns_object_t *obj;
+
+ if (isns_config.ic_use_default_domain) {
+ obj = isns_create_default_domain();
+ isns_object_list_append(&scope->ic_objects, obj);
+ isns_object_release(obj);
+ }
+}
+
+
+/*
+ * Scope the query
+ */
+static void
+__isns_scope_prepare_query(isns_scope_t *scope,
+ isns_object_template_t *tmpl)
+{
+ isns_object_list_t *nodes;
+ unsigned int i;
+
+ /* Global and default scope have no source node; they're just
+ * a list of objects.
+ */
+ if (scope->ic_source_node == NULL)
+ return;
+
+ if (scope->ic_query_class) {
+ if (scope->ic_query_class == tmpl)
+ return;
+ isns_object_list_destroy(&scope->ic_objects);
+ }
+ scope->ic_query_class = tmpl;
+
+ nodes = &scope->ic_dd_nodes;
+ if (tmpl == &isns_entity_template) {
+ for (i = 0; i < nodes->iol_count; ++i) {
+ isns_object_t *obj = nodes->iol_data[i];
+
+ if (obj->ie_container)
+ isns_object_list_append(&scope->ic_objects,
+ obj->ie_container);
+ }
+ } else
+ if (tmpl == &isns_iscsi_node_template) {
+ for (i = 0; i < nodes->iol_count; ++i) {
+ isns_object_t *obj = nodes->iol_data[i];
+
+ isns_object_list_append(&scope->ic_objects, obj);
+ }
+ } else
+ if (tmpl == &isns_portal_template) {
+ for (i = 0; i < nodes->iol_count; ++i) {
+ isns_object_t *obj = nodes->iol_data[i];
+
+ __isns_scope_get_portals(scope, obj,
+ &scope->ic_objects, NULL, 0);
+ }
+ } else
+ if (tmpl == &isns_iscsi_pg_template) {
+ for (i = 0; i < nodes->iol_count; ++i) {
+ isns_object_t *obj = nodes->iol_data[i];
+
+ __isns_scope_get_portals(scope, obj,
+ NULL, &scope->ic_objects, 0);
+ }
+ } else
+ if (tmpl == &isns_dd_template) {
+ isns_object_t *node = scope->ic_source_node;
+
+ if (node && !isns_bitvector_is_empty(node->ie_membership))
+ isns_bitvector_foreach(node->ie_membership,
+ __isns_scope_collect_dd,
+ scope);
+ else
+ __isns_scope_get_default_dd(scope);
+ }
+
+ isns_object_list_uniq(&scope->ic_objects);
+}
+
+static int
+__isns_scope_collect_dd(uint32_t dd_id, void *ptr)
+{
+ isns_scope_t *scope = ptr;
+ isns_object_t *dd;
+
+ dd = isns_db_vlookup(scope->ic_db, &isns_dd_template,
+ ISNS_TAG_DD_ID, dd_id,
+ 0);
+ if (dd) {
+ isns_object_list_append(&scope->ic_objects, dd);
+ isns_object_release(dd);
+ }
+
+ return 0;
+}
+
+/*
+ * Lookup functions for scope
+ */
+int
+isns_scope_gang_lookup(isns_scope_t *scope,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *match,
+ isns_object_list_t *result)
+{
+ isns_assert(tmpl);
+
+ if (!scope)
+ return 0;
+
+ __isns_scope_prepare_query(scope, tmpl);
+ return isns_object_list_gang_lookup(&scope->ic_objects,
+ tmpl, match, result);
+}
+
+/*
+ * Get related objects.
+ * This is used by the query code.
+ */
+void
+isns_scope_get_related(isns_scope_t *scope,
+ const isns_object_t *origin,
+ unsigned int type_mask,
+ isns_object_list_t *result)
+{
+ isns_object_template_t *tmpl = origin->ie_template;
+ isns_object_list_t nodes_result = ISNS_OBJECT_LIST_INIT;
+ isns_object_list_t portals_result = ISNS_OBJECT_LIST_INIT;
+ isns_object_list_t *members = &scope->ic_dd_nodes;
+ unsigned int i;
+
+ if (tmpl == &isns_entity_template) {
+ /* Entity: include all storage nodes contained,
+ * the portals through which to reach them, and
+ * the portal groups for those. */
+ for (i = 0; i < members->iol_count; ++i) {
+ isns_object_t *obj = members->iol_data[i];
+
+ if (obj->ie_container != origin)
+ continue;
+
+ isns_object_list_append(&nodes_result, obj);
+ __isns_scope_get_portals(scope, obj,
+ &portals_result,
+ &portals_result, 1);
+ }
+ } else
+ if (tmpl == &isns_iscsi_node_template) {
+ /* Storage node: include all portals through
+ * which it can be reached, and the portal
+ * groups for those. */
+ __isns_scope_get_portals(scope, origin,
+ &portals_result,
+ &portals_result, 1);
+ /* FIXME: Include all discovery domains the
+ * node is a member of. */
+ } else
+ if (tmpl == &isns_portal_template) {
+ /* Portal: include all storage nodes which can
+ * be reached through it, and the portal groups
+ * for those. */
+ __isns_scope_get_nodes(scope, origin,
+ &portals_result,
+ &portals_result, 1);
+ } else
+ if (tmpl == &isns_iscsi_pg_template) {
+ /* Portal group: PGs *are* a relationship, but
+ * unclear how this should be handled.
+ * Return nothing for now. */
+ } else
+ if (tmpl == &isns_dd_template) {
+ /* Discovery domain: no related objects. */
+ }
+
+ isns_object_list_append_list(result, &nodes_result);
+ isns_object_list_append_list(result, &portals_result);
+
+ isns_object_list_destroy(&nodes_result);
+ isns_object_list_destroy(&portals_result);
+}
+
+isns_object_t *
+isns_scope_get_next(isns_scope_t *scope,
+ isns_object_template_t *tmpl,
+ const isns_attr_list_t *current,
+ const isns_attr_list_t *match)
+{
+ if (!tmpl || !scope)
+ return NULL;
+
+ __isns_scope_prepare_query(scope, tmpl);
+ return __isns_db_get_next(&scope->ic_objects, tmpl, current, match);
+}
diff --git a/utils/open-isns/security.c b/utils/open-isns/security.c
new file mode 100644
index 0000000..548ce18
--- /dev/null
+++ b/utils/open-isns/security.c
@@ -0,0 +1,437 @@
+/*
+ * Security functions for iSNS
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "isns.h"
+#include "security.h"
+#include "source.h"
+#include "util.h"
+#include "config.h"
+
+#ifdef WITH_SECURITY
+
+/*
+ * Allocate a security peer
+ */
+static isns_principal_t *
+isns_create_principal(const char *spi, size_t spi_len, EVP_PKEY *pk)
+{
+ char keydesc[32];
+ isns_principal_t *peer;
+
+ peer = isns_calloc(1, sizeof(*peer));
+ peer->is_users = 1;
+ if (spi) {
+ peer->is_name = isns_malloc(spi_len + 1);
+ memcpy(peer->is_name, spi, spi_len);
+ peer->is_name[spi_len] = '\0';
+ peer->is_namelen = spi_len;
+ }
+
+ peer->is_key = pk;
+ if (pk) {
+ const char *algo;
+
+ switch (pk->type) {
+ case EVP_PKEY_DSA: algo = "DSA"; break;
+ case EVP_PKEY_RSA: algo = "RSA"; break;
+ default: algo = "unknown"; break;
+ }
+
+ snprintf(keydesc, sizeof(keydesc), " (%s/%u)",
+ algo, EVP_PKEY_bits(pk));
+ }
+
+ isns_debug_auth("Created security principal \"%s\"%s\n",
+ peer->is_name, keydesc);
+ return peer;
+}
+
+static void
+isns_principal_set_key(isns_principal_t *princ, EVP_PKEY *key)
+{
+ if (princ->is_key == key)
+ return;
+ if (princ->is_key)
+ EVP_PKEY_free(princ->is_key);
+ princ->is_key = key;
+}
+
+void
+isns_principal_free(isns_principal_t *peer)
+{
+ if (!peer)
+ return;
+
+ isns_assert(peer->is_users);
+ if (--(peer->is_users))
+ return;
+
+ if (peer->is_name)
+ isns_free(peer->is_name);
+ if (peer->is_key)
+ EVP_PKEY_free(peer->is_key);
+ isns_policy_release(peer->is_policy);
+ isns_free(peer);
+}
+
+/*
+ * Set the principal's name
+ */
+void
+isns_principal_set_name(isns_principal_t *princ, const char *spi)
+{
+ isns_assign_string(&princ->is_name, spi);
+ isns_debug_auth("Setting principal name to \"%s\"\n", spi);
+}
+
+const char *
+isns_principal_name(const isns_principal_t *princ)
+{
+ return princ->is_name;
+}
+
+/*
+ * Cache policy in the principal object.
+ */
+void
+isns_principal_set_policy(isns_principal_t *princ,
+ isns_policy_t *policy)
+{
+ if (policy)
+ policy->ip_users++;
+ isns_policy_release(princ->is_policy);
+ princ->is_policy = policy;
+}
+
+/*
+ * Key management functions for a security context.
+ */
+isns_principal_t *
+isns_security_load_privkey(isns_security_t *ctx, const char *filename)
+{
+ EVP_PKEY *pkey;
+
+ isns_debug_auth("Loading private %s key from %s\n",
+ ctx->is_name, filename);
+ if (!ctx->is_load_private)
+ return NULL;
+ if (!(pkey = ctx->is_load_private(ctx, filename))) {
+ isns_error("Unable to load private %s key from %s\n",
+ ctx->is_name, filename);
+ return NULL;
+ }
+
+ return isns_create_principal(NULL, 0, pkey);
+}
+
+isns_principal_t *
+isns_security_load_pubkey(isns_security_t *ctx, const char *filename)
+{
+ EVP_PKEY *pkey;
+
+ isns_debug_auth("Loading public %s key from %s\n",
+ ctx->is_name, filename);
+ if (!ctx->is_load_public)
+ return NULL;
+ if (!(pkey = ctx->is_load_public(ctx, filename))) {
+ isns_error("Unable to load public %s key from %s\n",
+ ctx->is_name, filename);
+ return NULL;
+ }
+
+ return isns_create_principal(NULL, 0, pkey);
+}
+
+void
+isns_security_set_identity(isns_security_t *ctx, isns_principal_t *princ)
+{
+ if (princ)
+ princ->is_users++;
+ if (ctx->is_self)
+ isns_principal_free(ctx->is_self);
+ ctx->is_self = princ;
+}
+
+void
+isns_add_principal(isns_security_t *ctx, isns_principal_t *princ)
+{
+ if (princ)
+ princ->is_users++;
+ princ->is_next = ctx->is_peers;
+ ctx->is_peers = princ;
+}
+
+isns_principal_t *
+isns_get_principal(isns_security_t *ctx, const char *spi, size_t spi_len)
+{
+ isns_principal_t *princ;
+ isns_policy_t *policy;
+ isns_keystore_t *ks;
+ EVP_PKEY *pk;
+
+ ks = ctx->is_peer_keys;
+
+ for (princ = ctx->is_peers; princ; princ = princ->is_next) {
+ /* In a client socket, we set the (expected)
+ * public key of the peer through
+ * isns_security_set_peer_key, which will
+ * just put it on the peers list.
+ * This key usually has no name.
+ */
+ if (princ->is_name == NULL) {
+ princ->is_users++;
+ return princ;
+ }
+ if (spi_len == princ->is_namelen
+ && !memcmp(princ->is_name, spi, spi_len)) {
+ /* Check whether the cached key and policy
+ * might be stale. */
+ if (ks && ks->ic_generation != princ->is_generation) {
+ pk = ks->ic_find(ks, spi, spi_len);
+ if (pk == NULL) {
+ isns_debug_auth("Unable to refresh key "
+ "for principal %.*s - probably deleted\n",
+ spi_len, spi);
+ return NULL;
+ }
+ isns_debug_auth("Refresh key for principal %.*s\n",
+ spi_len, spi);
+ isns_principal_set_key(princ, pk);
+ princ->is_users++;
+ goto refresh_policy;
+ }
+ princ->is_users++;
+ return princ;
+ }
+ }
+
+ if ((ks = ctx->is_peer_keys) == NULL)
+ return NULL;
+
+ if (!(pk = ks->ic_find(ks, spi, spi_len)))
+ return NULL;
+ princ = isns_create_principal(spi, spi_len, pk);
+
+ /* Add it to the list */
+ princ->is_next = ctx->is_peers;
+ ctx->is_peers = princ;
+ princ->is_users++;
+
+ /* Bind the policy for this peer */
+refresh_policy:
+ if (!ks->ic_get_policy
+ || !(policy = ks->ic_get_policy(ks, spi, spi_len)))
+ policy = isns_policy_default(spi, spi_len);
+
+ /* If no entity is set, use the SPI */
+ if (policy->ip_entity == NULL)
+ isns_assign_string(&policy->ip_entity, policy->ip_name);
+
+ /* If the list of permitted node names is empty,
+ * default to the standard pattern derived from
+ * the reversed entity name */
+ if (policy->ip_node_names.count == 0) {
+ char *pattern;
+
+ pattern = isns_build_source_pattern(policy->ip_entity);
+ if (pattern != NULL)
+ isns_string_array_append(&policy->ip_node_names,
+ pattern);
+ isns_free(pattern);
+ }
+
+ isns_principal_set_policy(princ, policy);
+ isns_policy_release(policy);
+
+ /* Remember the keystore generation number */
+ princ->is_generation = ks->ic_generation;
+
+ return princ;
+}
+
+/*
+ * Create a keystore for a security context.
+ * Key stores let the server side retrieve the
+ * keys associated with a given SPI.
+ *
+ * For now, we support just simple key stores,
+ * but this could be extended to support
+ * URLs such as ldaps://ldap.example.com
+ */
+isns_keystore_t *
+isns_create_keystore(const char *spec)
+{
+ if (*spec != '/')
+ return NULL;
+
+ return isns_create_simple_keystore(spec);
+}
+
+/*
+ * Attach the keystore to the security context
+ */
+void
+isns_security_set_keystore(isns_security_t *ctx,
+ isns_keystore_t *ks)
+{
+ ctx->is_peer_keys = ks;
+}
+
+/*
+ * Check that the client supplied time stamp is within a
+ * certain window.
+ */
+static int
+isns_security_check_timestamp(isns_security_t *ctx,
+ isns_principal_t *peer,
+ uint64_t timestamp)
+{
+ int64_t delta;
+
+ /* The time stamp must not be earlier than timestamp_jitter
+ * before the last message received. */
+ if (peer->is_timestamp) {
+ delta = timestamp - peer->is_timestamp;
+ if (delta < -(int64_t) ctx->is_timestamp_jitter)
+ return 0;
+ }
+
+ /* We allow the client's clock to diverge from ours, within
+ * certain limits. */
+ if (ctx->is_replay_window != 0) {
+ time_t now = time(NULL);
+
+ delta = timestamp - now;
+ if (delta < 0)
+ delta = -delta;
+ if (delta > ctx->is_replay_window)
+ return 0;
+ }
+
+ peer->is_timestamp = timestamp;
+ return 1;
+}
+
+int
+isns_security_sign(isns_security_t *ctx, isns_principal_t *peer,
+ buf_t *bp, struct isns_authblk *auth)
+{
+ if (!ctx->is_sign) {
+ isns_debug_auth("isns_security_sign: auth context without "
+ "sign handler.\n");
+ return 0;
+ }
+ if (!ctx->is_sign(ctx, peer, bp, auth)) {
+ isns_debug_auth("Failed to sign message, spi=%s\n",
+ peer->is_name);
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+isns_security_verify(isns_security_t *ctx, isns_principal_t *peer,
+ buf_t *bp, struct isns_authblk *auth)
+{
+ if (!isns_security_check_timestamp(ctx, peer, auth->iab_timestamp)) {
+ isns_debug_auth("Possible replay attack (bad timestamp) "
+ "from spi=%s\n", peer->is_name);
+ return 0;
+ }
+
+ if (!ctx->is_verify) {
+ isns_debug_auth("isns_security_verify: auth context without "
+ "verify handler.\n");
+ return 0;
+ }
+ if (!ctx->is_verify(ctx, peer, bp, auth)) {
+ isns_debug_auth("Failed to authenticate message, spi=%s\n",
+ peer->is_name);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Initialize security services.
+ */
+int
+isns_security_init(void)
+{
+ if (!isns_config.ic_dsa.param_file) {
+ isns_error("No DSA parameter file - please edit configuration\n");
+ return 0;
+ }
+
+ if (!isns_dsa_init_params(isns_config.ic_dsa.param_file))
+ return 0;
+
+ if (!isns_config.ic_auth_key_file) {
+ isns_error("No AuthKey specified; please edit configuration\n");
+ return 0;
+ }
+
+ if (!isns_dsa_init_key(isns_config.ic_auth_key_file))
+ return 0;
+
+ return 1;
+}
+
+#else /* WITH_SECURITY */
+
+static void
+isns_no_security(void)
+{
+ static int complain = 0;
+
+ if (complain++ < 5)
+ isns_error("iSNS authentication disabled in this build\n");
+}
+
+int
+isns_security_init(void)
+{
+ isns_no_security();
+ return 0;
+}
+
+isns_keystore_t *
+isns_create_keystore(const char *spec)
+{
+ isns_no_security();
+ return NULL;
+}
+
+void
+isns_security_set_keystore(isns_security_t *ctx,
+ isns_keystore_t *ks)
+{
+ isns_no_security();
+}
+
+void
+isns_principal_free(isns_principal_t *peer)
+{
+}
+
+isns_principal_t *
+isns_get_principal(isns_security_t *ctx, const char *spi, size_t spi_len)
+{
+ return NULL;
+}
+
+const char *
+isns_principal_name(const isns_principal_t *princ)
+{
+ return NULL;
+}
+
+#endif /* WITH_SECURITY */
diff --git a/utils/open-isns/security.h b/utils/open-isns/security.h
new file mode 100644
index 0000000..9ba0f0d
--- /dev/null
+++ b/utils/open-isns/security.h
@@ -0,0 +1,180 @@
+/*
+ * Security functions for iSNS
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_SECURITY_H
+#define ISNS_SECURITY_H
+
+#include <openssl/evp.h>
+#include "buffer.h"
+#include "util.h"
+
+/*
+ * Security context
+ */
+struct isns_security {
+ const char * is_name;
+ unsigned int is_type;
+ unsigned int is_replay_window;
+ unsigned int is_timestamp_jitter;
+
+ /* Our own key and identity */
+ isns_principal_t * is_self;
+
+ /* Key store for peer keys */
+ isns_principal_t * is_peers;
+ isns_keystore_t * is_peer_keys;
+
+ EVP_PKEY * (*is_load_private)(isns_security_t *ctx,
+ const char *filename);
+ EVP_PKEY * (*is_load_public)(isns_security_t *ctx,
+ const char *filename);
+ int (*is_verify)(isns_security_t *ctx,
+ isns_principal_t *peer,
+ buf_t *pdu,
+ const struct isns_authblk *);
+ int (*is_sign)(isns_security_t *ctx,
+ isns_principal_t *peer,
+ buf_t *pdu,
+ struct isns_authblk *);
+};
+
+struct isns_principal {
+ unsigned int is_users;
+ isns_principal_t * is_next;
+ char * is_name;
+ unsigned int is_namelen;
+ EVP_PKEY * is_key;
+ unsigned int is_generation;
+ uint64_t is_timestamp;
+
+ isns_policy_t * is_policy;
+};
+
+struct isns_policy {
+ unsigned int ip_users;
+ unsigned int ip_gen;
+
+ /* SPI */
+ char * ip_name;
+
+ /* The client's entity name. This is usually
+ * the FQDN. */
+ char * ip_entity;
+
+ /* Bitmap of functions the client is
+ * permitted to call. */
+ unsigned int ip_functions;
+
+ /* Bitmap of object types the client is
+ * permitted to register (uses iot_handle) */
+ unsigned int ip_object_types;
+
+ /* Names of storage nodes the client is permitted
+ * to register. */
+ struct string_array ip_node_names;
+
+ /* Storage node types the client is permitted
+ * to read or modify. */
+ unsigned int ip_node_types;
+
+ /* The client's default Discovery Domain */
+ char * ip_dd_default;
+};
+
+#define ISNS_PERMISSION_READ 0x01
+#define ISNS_PERMISSION_WRITE 0x02
+#define ISNS_ACCESS(t, p) ((p) << (2 * (t)))
+#define ISNS_ACCESS_W(t) ISNS_ACCESS(t, ISNS_PERMISSION_WRITE)
+#define ISNS_ACCESS_R(t) ISNS_ACCESS(t, ISNS_PERMISSION_READ)
+#define ISNS_ACCESS_RW(t) ISNS_ACCESS(t, ISNS_PERMISSION_READ|ISNS_PERMISSION_WRITE)
+
+#define ISNS_DEFAULT_OBJECT_ACCESS \
+ ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_ENTITY) | \
+ ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_NODE) | \
+ ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_FC_PORT) | \
+ ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_FC_NODE) | \
+ ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_PORTAL) | \
+ ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_PG) | \
+ ISNS_ACCESS_R(ISNS_OBJECT_TYPE_DD)
+
+struct isns_keystore {
+ char * ic_name;
+ unsigned int ic_generation;
+ EVP_PKEY * (*ic_find)(isns_keystore_t *,
+ const char *, size_t);
+ isns_policy_t * (*ic_get_policy)(isns_keystore_t *,
+ const char *, size_t);
+};
+
+extern isns_principal_t * isns_get_principal(isns_security_t *,
+ const char *, size_t);
+extern int isns_security_sign(isns_security_t *,
+ isns_principal_t *, buf_t *,
+ struct isns_authblk *);
+extern int isns_security_verify(isns_security_t *,
+ isns_principal_t *, buf_t *,
+ struct isns_authblk *);
+extern int isns_security_protected_entity(isns_security_t *,
+ const char *);
+
+extern isns_keystore_t * isns_create_keystore(const char *);
+extern isns_keystore_t * isns_create_simple_keystore(const char *);
+extern isns_keystore_t * isns_create_db_keystore(isns_db_t *);
+
+extern int isns_authblock_encode(buf_t *,
+ const struct isns_authblk *);
+extern int isns_authblock_decode(buf_t *,
+ struct isns_authblk *);
+
+extern isns_policy_t * __isns_policy_alloc(const char *, size_t);
+extern isns_policy_t * isns_policy_bind(const isns_message_t *);
+extern void isns_principal_set_policy(isns_principal_t *,
+ isns_policy_t *);
+extern void isns_policy_release(isns_policy_t *);
+extern int isns_policy_validate_function(const isns_policy_t *,
+ const isns_message_t *);
+extern int isns_policy_validate_source(const isns_policy_t *,
+ const isns_source_t *);
+extern int isns_policy_validate_object_access(const isns_policy_t *,
+ const isns_source_t *,
+ const isns_object_t *,
+ unsigned int);
+extern int isns_policy_validate_object_update(const isns_policy_t *,
+ const isns_source_t *,
+ const isns_object_t *,
+ const isns_attr_list_t *,
+ unsigned int);
+extern int isns_policy_validate_object_creation(const isns_policy_t *,
+ const isns_source_t *,
+ isns_object_template_t *,
+ const isns_attr_list_t *,
+ const isns_attr_list_t *,
+ unsigned int);
+extern int isns_policy_validate_object_type(const isns_policy_t *,
+ isns_object_template_t *,
+ unsigned int function);
+extern int isns_policy_validate_node_type(const isns_policy_t *,
+ uint32_t type);
+extern int isns_policy_validate_entity(const isns_policy_t *,
+ const char *);
+extern int isns_policy_validate_node_name(const isns_policy_t *,
+ const char *);
+extern int isns_policy_validate_scn_bitmap(const isns_policy_t *,
+ uint32_t);
+extern const char * isns_policy_default_entity(const isns_policy_t *);
+extern isns_policy_t * isns_policy_default(const char *, size_t);
+extern isns_policy_t * isns_policy_server(void);
+
+extern EVP_PKEY * isns_dsa_decode_public(const void *, size_t);
+extern int isns_dsa_encode_public(EVP_PKEY *,
+ void **, size_t *);
+extern EVP_PKEY * isns_dsa_load_public(const char *);
+extern int isns_dsa_store_private(const char *, EVP_PKEY *);
+extern EVP_PKEY * isns_dsa_generate_key(void);
+extern int isns_dsa_init_params(const char *);
+extern int isns_dsa_init_key(const char *);
+
+#endif /* ISNS_SECURITY_H */
diff --git a/utils/open-isns/server.c b/utils/open-isns/server.c
new file mode 100644
index 0000000..0f1c937
--- /dev/null
+++ b/utils/open-isns/server.c
@@ -0,0 +1,236 @@
+/*
+ * iSNS server side functions
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include "isns.h"
+#include "util.h"
+#include "security.h"
+#include "message.h"
+
+static int isns_not_supported(isns_server_t *, isns_simple_t *, isns_simple_t **);
+
+struct isns_service_ops isns_default_service_ops = {
+ .process_registration = isns_process_registration,
+ .process_query = isns_process_query,
+ .process_getnext = isns_process_getnext,
+ .process_deregistration = isns_process_deregistration,
+ .process_scn_registration = isns_process_scn_register,
+ .process_scn_deregistration = isns_process_scn_deregistration,
+ .process_scn_event = isns_not_supported,
+ .process_dd_registration = isns_process_dd_registration,
+ .process_dd_deregistration= isns_process_dd_deregistration,
+};
+
+struct isns_service_ops isns_callback_service_ops = {
+ .process_esi = isns_process_esi,
+ .process_scn = isns_process_scn,
+};
+
+/*
+ * Create a server object
+ */
+isns_server_t *
+isns_create_server(isns_source_t *source, isns_db_t *db,
+ struct isns_service_ops *ops)
+{
+ isns_server_t *srv;
+
+ if (source == NULL) {
+ isns_error("%s: source name not set\n", __FUNCTION__);
+ return NULL;
+ }
+
+ srv = isns_calloc(1, sizeof(*srv));
+ srv->is_source = isns_source_get(source);
+ srv->is_db = db;
+ srv->is_ops = ops;
+
+ return srv;
+}
+
+void
+isns_server_set_scn_callback(isns_server_t *srv, isns_scn_callback_fn_t *func)
+{
+ srv->is_scn_callback = func;
+}
+
+/*
+ * Try to handle transactions safely.
+ * This isn't perfect, because there's state outside the DB (for instance
+ * the DD information)
+ */
+static int
+isns_begin_write_operation(isns_server_t *srv, isns_simple_t *msg, int *status)
+{
+ isns_db_begin_transaction(srv->is_db);
+ return 1;
+}
+
+static void
+isns_end_write_operation(isns_server_t *srv, isns_simple_t *msg, int *status)
+{
+ if (*status == ISNS_SUCCESS)
+ isns_db_commit(srv->is_db);
+ else
+ isns_db_rollback(srv->is_db);
+}
+
+static inline int
+isns_begin_read_operation(isns_server_t *srv, isns_simple_t *msg, int *status)
+{
+ return 1;
+}
+
+static void
+isns_end_read_operation(isns_server_t *srv, isns_simple_t *msg, int *status)
+{
+}
+
+/*
+ * Process an incoming message
+ */
+isns_message_t *
+isns_process_message(isns_server_t *srv, isns_message_t *msg)
+{
+ struct isns_service_ops *ops = srv->is_ops;
+ uint16_t function = msg->im_header.i_function;
+ int status = ISNS_SUCCESS;
+ isns_simple_t *call = NULL, *reply = NULL;
+ isns_message_t *res_msg = NULL;
+ isns_db_t *db = srv->is_db;
+
+ status = isns_simple_decode(msg, &call);
+ if (status) {
+ isns_debug_message("Failed to decode %s request: %s\n",
+ isns_function_name(msg->im_header.i_function),
+ isns_strerror(status));
+ goto reply;
+ }
+
+ isns_simple_print(call, isns_debug_message);
+
+ /* Set policy and privileges based on the
+ * sender's identity. */
+ if (!(call->is_policy = isns_policy_bind(msg)))
+ goto err_unauthorized;
+
+ if (!isns_policy_validate_function(call->is_policy, msg))
+ goto err_unauthorized;
+
+ /* Checks related to the message source.
+ * Note - some messages do not use a source.
+ */
+ if (call->is_source) {
+ /* Validate the message source. This checks whether the client
+ * is permitted to use this source node name.
+ * Beware - not all messages include a source.
+ */
+ if (!isns_policy_validate_source(call->is_policy, call->is_source))
+ goto err_unauthorized;
+
+ /* This may fail if the source node isn't in the DB yet. */
+ isns_source_set_node(call->is_source, db);
+
+ /*
+ * 6.2.6. Registration Period
+ *
+ * The registration SHALL be removed from the iSNS database
+ * if an iSNS Protocol message is not received from the
+ * iSNS client before the registration period has expired.
+ * Receipt of any iSNS Protocol message from the iSNS client
+ * automatically refreshes the Entity Registration Period and
+ * Entity Registration Timestamp. To prevent a registration
+ * from expiring, the iSNS client should send an iSNS Protocol
+ * message to the iSNS server at intervals shorter than the
+ * registration period. Such a message can be as simple as a
+ * query for one of its own attributes, using its associated
+ * iSCSI Name or FC Port Name WWPN as the Source attribute.
+ */
+ /* Thusly, we update the timestamps of all entities
+ * registered by this source. */
+ isns_entity_touch(call->is_source->is_entity);
+ }
+
+ /* Handle the requested function. If the function vector is
+ * NULL, silently discard the message. */
+ switch (function) {
+#define DO(rw, FUNCTION, __function) \
+ case FUNCTION: \
+ if (!ops->__function) \
+ goto no_reply; \
+ \
+ if (!isns_begin_##rw##_operation(srv, call, &status)) \
+ break; \
+ status = ops->__function(srv, call, &reply); \
+ isns_end_##rw##_operation(srv, call, &status); \
+ break
+
+ DO(write, ISNS_DEVICE_ATTRIBUTE_REGISTER, process_registration);
+ DO(read, ISNS_DEVICE_ATTRIBUTE_QUERY, process_query);
+ DO(read, ISNS_DEVICE_GET_NEXT, process_getnext);
+ DO(write, ISNS_DEVICE_DEREGISTER, process_deregistration);
+ DO(write, ISNS_DD_REGISTER, process_dd_registration);
+ DO(write, ISNS_DD_DEREGISTER, process_dd_deregistration);
+ DO(read, ISNS_SCN_REGISTER, process_scn_registration);
+ DO(read, ISNS_SCN_DEREGISTER, process_scn_deregistration);
+ DO(read, ISNS_SCN_EVENT, process_scn_event);
+ DO(read, ISNS_STATE_CHANGE_NOTIFICATION, process_scn);
+ DO(read, ISNS_ENTITY_STATUS_INQUIRY, process_esi);
+ DO(read, ISNS_HEARTBEAT, process_heartbeat);
+#undef DO
+
+ default:
+ isns_error("Function %s not supported\n",
+ isns_function_name(function));
+ status = ISNS_MESSAGE_NOT_SUPPORTED;
+ break;
+ }
+
+reply:
+ /* Commit any changes to the DB before we reply */
+ if (db)
+ isns_db_sync(db);
+
+ /* Send out SCN notifications */
+ isns_flush_events();
+
+ if (reply != NULL) {
+ reply->is_function |= 0x8000;
+ isns_simple_print(reply, isns_debug_message);
+
+ /* Encode the whole thing */
+ status = isns_simple_encode_response(reply, msg, &res_msg);
+ }
+
+ /* No reply, or error when encoding it:
+ * just send the error, nothing else. */
+ if (res_msg == NULL) {
+ res_msg = isns_create_reply(msg);
+ if (status == ISNS_SUCCESS)
+ status = ISNS_INTERNAL_ERROR;
+ }
+
+ isns_debug_message("response status 0x%04x (%s)\n",
+ status, isns_strerror(status));
+
+ if (status != ISNS_SUCCESS)
+ isns_message_set_error(res_msg, status);
+
+no_reply:
+ isns_simple_free(call);
+ if (reply)
+ isns_simple_free(reply);
+ return res_msg;
+
+err_unauthorized:
+ status = ISNS_SOURCE_UNAUTHORIZED;
+ goto reply;
+}
+
+int
+isns_not_supported(isns_server_t *srv, isns_simple_t *call, isns_simple_t **replyp)
+{
+ return ISNS_MESSAGE_NOT_SUPPORTED;
+}
diff --git a/utils/open-isns/simple.c b/utils/open-isns/simple.c
new file mode 100644
index 0000000..97e181f
--- /dev/null
+++ b/utils/open-isns/simple.c
@@ -0,0 +1,725 @@
+/*
+ * Common handling for iSNS message parsing
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "attrs.h"
+#include "message.h"
+#include "objects.h"
+#include "security.h"
+#include "socket.h"
+#include "util.h"
+
+typedef void isns_simple_callback_fn_t(uint32_t, isns_simple_t *);
+
+static int isns_attr_list_scanner_get_pg(struct isns_attr_list_scanner *st);
+
+/*
+ * Allocate an empty simple message
+ */
+static isns_simple_t *
+__isns_alloc_simple(void)
+{
+ isns_simple_t *simp;
+
+ simp = isns_calloc(1, sizeof(*simp));
+
+ isns_attr_list_init(&simp->is_message_attrs);
+ isns_attr_list_init(&simp->is_operating_attrs);
+
+ return simp;
+}
+
+/*
+ * Create a simple message, and set the source name
+ */
+isns_simple_t *
+isns_simple_create(uint32_t function, isns_source_t *source,
+ const isns_attr_list_t *key)
+{
+ isns_simple_t *simp;
+
+ simp = __isns_alloc_simple();
+ simp->is_function = function;
+ simp->is_source = source;
+ if (source != NULL)
+ source->is_users++;
+
+ if (key)
+ isns_attr_list_copy(&simp->is_message_attrs, key);
+
+ return simp;
+}
+
+/*
+ * Perform a call to the server, waiting for the response.
+ */
+int
+isns_simple_call(isns_socket_t *sock, isns_simple_t **inout)
+{
+ isns_simple_t *simp = *inout;
+ isns_message_t *msg, *resp;
+ int status;
+
+ isns_simple_print(simp, isns_debug_message);
+
+ status = isns_simple_encode(simp, &msg);
+ if (status != ISNS_SUCCESS) {
+ isns_error("Unable to encode %s: %s\n",
+ isns_function_name(simp->is_function),
+ isns_strerror(status));
+ return status;
+ }
+
+ isns_debug_message("Sending request, len=%d\n",
+ buf_avail(msg->im_payload));
+
+ resp = isns_socket_call(sock, msg,
+ isns_config.ic_network.call_timeout);
+ isns_assert(msg->im_users == 1);
+ isns_message_release(msg);
+
+ if (resp == NULL) {
+ isns_error("Timed out while waiting for reply\n");
+ return ISNS_INTERNAL_ERROR;
+ }
+
+ isns_debug_message("Received reply, len=%d\n",
+ buf_avail(resp->im_payload));
+ isns_assert(resp->im_users == 1);
+
+ status = isns_message_status(resp);
+ if (status != ISNS_SUCCESS) {
+ isns_message_release(resp);
+ return status;
+ }
+
+ status = isns_simple_decode(resp, &simp);
+ isns_message_release(resp);
+
+ if (status) {
+ isns_error("Unable to decode server response: %s (status 0x%04x)\n",
+ isns_strerror(status), status);
+ return status;
+ }
+
+ isns_simple_print(simp, isns_debug_message);
+
+ isns_simple_free(*inout);
+ *inout = simp;
+ return ISNS_SUCCESS;
+}
+
+/*
+ * This callback is invoked from the network layer when
+ * we received a response to an async message
+ */
+static void
+isns_simple_recv_response(isns_message_t *cmsg, isns_message_t *rmsg)
+{
+ isns_simple_callback_fn_t *user_callback;
+ isns_simple_t *resp = NULL;
+ int status;
+
+ /* rmsg being NULL means the call timed out. */
+ if (rmsg == NULL)
+ goto callback;
+
+ status = isns_message_status(rmsg);
+ if (status != ISNS_SUCCESS) {
+ isns_error("Server flags error: %s (status 0x%04x)\n",
+ isns_strerror(status), status);
+ return;
+ }
+
+ status = isns_simple_decode(rmsg, &resp);
+ if (status) {
+ isns_error("Unable to decode server response: %s (status 0x%04x)\n",
+ isns_strerror(status), status);
+ return;
+ }
+
+ isns_simple_print(resp, isns_debug_message);
+
+callback:
+ user_callback = cmsg->im_calldata;
+ if (user_callback)
+ user_callback(cmsg->im_xid, resp);
+ isns_simple_free(resp);
+}
+
+/*
+ * Transmit a call, without waiting for the response.
+ */
+int
+isns_simple_transmit(isns_socket_t *sock, isns_simple_t *call,
+ const isns_portal_info_t *dest,
+ unsigned int timeout,
+ isns_simple_callback_fn_t *user_callback)
+{
+ isns_message_t *msg;
+ int status;
+
+ isns_simple_print(call, isns_debug_message);
+
+ status = isns_simple_encode(call, &msg);
+ if (status != ISNS_SUCCESS) {
+ isns_error("Unable to encode %s: %s\n",
+ isns_function_name(call->is_function),
+ isns_strerror(status));
+ return status;
+ }
+
+ isns_debug_message("Sending message, len=%d\n",
+ buf_avail(msg->im_payload));
+
+ if (user_callback) {
+ msg->im_callback = isns_simple_recv_response;
+ msg->im_calldata = user_callback;
+ }
+
+ if (!isns_socket_submit(sock, msg, timeout))
+ status = ISNS_INTERNAL_ERROR;
+ isns_message_release(msg);
+ return status;
+}
+
+/*
+ * Delete the simple message object
+ */
+void
+isns_simple_free(isns_simple_t *simp)
+{
+ if (simp == NULL)
+ return;
+
+ isns_attr_list_destroy(&simp->is_message_attrs);
+ isns_attr_list_destroy(&simp->is_operating_attrs);
+ isns_source_release(simp->is_source);
+ isns_policy_release(simp->is_policy);
+ isns_free(simp);
+}
+
+/*
+ * Get the source associated with this simple message
+ */
+isns_source_t *
+isns_simple_get_source(isns_simple_t *simp)
+{
+ return simp->is_source;
+}
+
+const isns_attr_list_t *
+isns_simple_get_attrs(isns_simple_t *simp)
+{
+ return &simp->is_operating_attrs;
+}
+
+/*
+ * Determine whether message includes a source attr.
+ */
+static inline int
+isns_simple_include_source(uint16_t function)
+{
+ if (function & 0x8000)
+ return 0;
+ switch (function) {
+ case ISNS_STATE_CHANGE_NOTIFICATION:
+ case ISNS_ENTITY_STATUS_INQUIRY:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Decode a simple message
+ */
+int
+isns_simple_decode(isns_message_t *msg, isns_simple_t **result)
+{
+ isns_simple_t *simp = __isns_alloc_simple();
+ buf_t *bp = msg->im_payload;
+ int status = ISNS_SUCCESS;
+
+ simp->is_function = msg->im_header.i_function;
+ simp->is_xid = msg->im_xid;
+
+ if (isns_simple_include_source(simp->is_function)) {
+ status = isns_source_decode(bp, &simp->is_source);
+ if (status != ISNS_SUCCESS)
+ goto out;
+ }
+
+ switch (simp->is_function & 0x7FFF) {
+ case ISNS_ENTITY_STATUS_INQUIRY:
+ case ISNS_STATE_CHANGE_NOTIFICATION:
+ /* Server messages do not include a source */
+ status = isns_attr_list_decode(bp,
+ &simp->is_message_attrs);
+ break;
+
+ default:
+ status = isns_attr_list_decode_delimited(bp,
+ &simp->is_message_attrs);
+ if (status == ISNS_SUCCESS)
+ status = isns_attr_list_decode(bp,
+ &simp->is_operating_attrs);
+ }
+
+ if (msg->im_header.i_flags & ISNS_F_REPLACE)
+ simp->is_replace = 1;
+
+out:
+ if (status == ISNS_SUCCESS) {
+ *result = simp;
+ } else {
+ isns_simple_free(simp);
+ *result = NULL;
+ }
+ return status;
+}
+
+/*
+ * Encode a simple message reply or response
+ */
+static int
+__isns_simple_encode(isns_simple_t *simp, buf_t *bp)
+{
+ int status = ISNS_SUCCESS;
+
+ if (isns_simple_include_source(simp->is_function)) {
+ if (simp->is_source == NULL) {
+ isns_error("Cannot encode %s message - caller forgot to set source\n",
+ isns_function_name(simp->is_function));
+ return ISNS_SOURCE_UNKNOWN;
+ }
+ status = isns_source_encode(bp, simp->is_source);
+ }
+
+ if (status == ISNS_SUCCESS)
+ status = isns_attr_list_encode(bp, &simp->is_message_attrs);
+
+ /* Some functions have just one set of attrs. */
+ switch (simp->is_function & 0x7fff) {
+ /* It's not entirely clear which calls actually have the delimiter.
+ * The spec is sometimes a little vague on this. */
+ case ISNS_SCN_DEREGISTER:
+ case ISNS_ENTITY_STATUS_INQUIRY:
+ case ISNS_STATE_CHANGE_NOTIFICATION:
+ break;
+
+ default:
+ if (status == ISNS_SUCCESS)
+ status = isns_encode_delimiter(bp);
+ if (status == ISNS_SUCCESS)
+ status = isns_attr_list_encode(bp, &simp->is_operating_attrs);
+ break;
+ }
+
+ return status;
+}
+
+int
+isns_simple_encode(isns_simple_t *simp, isns_message_t **result)
+{
+ isns_message_t *msg;
+ int status, flags;
+
+ flags = ISNS_F_CLIENT;
+ if (simp->is_replace)
+ flags |= ISNS_F_REPLACE;
+ msg = isns_create_message(simp->is_function, flags);
+
+ /* FIXME: for UDP sockets, isns_simple_t may contain a
+ destination address. */
+
+ status = __isns_simple_encode(simp, msg->im_payload);
+ if (status != ISNS_SUCCESS) {
+ isns_message_release(msg);
+ msg = NULL;
+ }
+
+ /* Report the XID to the caller */
+ simp->is_xid = msg->im_xid;
+
+ *result = msg;
+ return status;
+}
+
+int
+isns_simple_encode_response(isns_simple_t *reg,
+ const isns_message_t *request, isns_message_t **result)
+{
+ isns_message_t *msg;
+ int status;
+
+ msg = isns_create_reply(request);
+
+ status = __isns_simple_encode(reg, msg->im_payload);
+ if (status != ISNS_SUCCESS) {
+ isns_message_release(msg);
+ msg = NULL;
+ }
+
+ *result = msg;
+ return status;
+}
+
+int
+isns_simple_decode_response(isns_message_t *resp, isns_simple_t **result)
+{
+ return isns_simple_decode(resp, result);
+}
+
+/*
+ * Extract the list of objects from a DevAttrReg/DevAttrQry
+ * response or similar.
+ */
+int
+isns_simple_response_get_objects(isns_simple_t *resp,
+ isns_object_list_t *result)
+{
+ struct isns_attr_list_scanner state;
+ int status = ISNS_SUCCESS;
+
+ isns_attr_list_scanner_init(&state, NULL, &resp->is_operating_attrs);
+ while (1) {
+ isns_object_t *obj;
+
+ status = isns_attr_list_scanner_next(&state);
+ if (status == ISNS_NO_SUCH_ENTRY) {
+ status = ISNS_SUCCESS;
+ break;
+ }
+ if (status)
+ break;
+
+ obj = isns_create_object(state.tmpl, &state.keys, NULL);
+
+ isns_object_set_attrlist(obj, &state.attrs);
+ if (obj != state.key_obj)
+ isns_object_list_append(result, obj);
+ isns_object_release(obj);
+ }
+
+ isns_attr_list_scanner_destroy(&state);
+ return status;
+}
+
+/*
+ * Print a simple message object
+ */
+void
+isns_simple_print(isns_simple_t *simp, isns_print_fn_t *fn)
+{
+ char buffer[256];
+
+ if (fn == isns_debug_message
+ && !isns_debug_enabled(DBG_MESSAGE))
+ return;
+
+ fn("---%s%s---\n",
+ isns_function_name(simp->is_function),
+ simp->is_replace? "[REPLACE]" : "");
+ if (simp->is_source) {
+ fn("Source:\n", buffer);
+ isns_attr_print(simp->is_source->is_attr, fn);
+ } else {
+ fn("Source: <empty>\n");
+ }
+
+ if (simp->is_message_attrs.ial_count == 0) {
+ fn("Message attributes: <empty list>\n");
+ } else {
+ fn("Message attributes:\n");
+ isns_attr_list_print(&simp->is_message_attrs, fn);
+ }
+ if (simp->is_operating_attrs.ial_count == 0) {
+ fn("Operating attributes: <empty list>\n");
+ } else {
+ fn("Operating attributes:\n");
+ isns_attr_list_print(&simp->is_operating_attrs, fn);
+ }
+}
+
+/*
+ * This set of functions analyzes the operating attrs of a registration,
+ * or a query response, and chops it up into separate chunks, one
+ * per objects.
+ *
+ * It always returns the keys and attrs for one object,
+ * following the ordering constraints laid out in the RFC.
+ */
+void
+isns_attr_list_scanner_init(struct isns_attr_list_scanner *st,
+ isns_object_t *key_obj,
+ const isns_attr_list_t *attrs)
+{
+ memset(st, 0, sizeof(*st));
+ st->orig_attrs = *attrs;
+ st->key_obj = key_obj;
+}
+
+void
+isns_attr_list_scanner_destroy(struct isns_attr_list_scanner *st)
+{
+ isns_attr_list_destroy(&st->keys);
+ isns_attr_list_destroy(&st->attrs);
+ memset(st, 0, sizeof(*st));
+}
+
+int
+isns_attr_list_scanner_next(struct isns_attr_list_scanner *st)
+{
+ isns_attr_t *attr;
+ unsigned int i, pos = st->pos;
+
+ isns_attr_list_destroy(&st->keys);
+ isns_attr_list_destroy(&st->attrs);
+
+ if (st->orig_attrs.ial_count <= pos)
+ return ISNS_NO_SUCH_ENTRY;
+
+ attr = st->orig_attrs.ial_data[pos];
+
+ /* handle those funky inlined PGT definitions */
+ if (st->pgt_next_attr && attr->ia_tag_id == st->pgt_next_attr)
+ return isns_attr_list_scanner_get_pg(st);
+
+ /* This isn't really structured programming anymore */
+ if (st->index_acceptable
+ && (st->tmpl = isns_object_template_for_index_tag(attr->ia_tag_id)))
+ goto copy_attrs;
+
+ /*
+ * Find the object template for the given key attr(s).
+ * This function also enforces restrictions on the
+ * order of key attributes.
+ */
+ st->tmpl = isns_object_template_find(attr->ia_tag_id);
+ if (st->tmpl == NULL) {
+ isns_debug_protocol("%s: attr %u is not a key attr\n",
+ __FUNCTION__, attr->ia_tag_id);
+ return ISNS_INVALID_REGISTRATION;
+ }
+
+ /* Copy the key attrs */
+ for (i = 0; i < st->tmpl->iot_num_keys; ++i, ++pos) {
+ if (pos >= st->orig_attrs.ial_count) {
+ isns_debug_protocol("%s: incomplete %s object "
+ "(key attr %u missing)\n",
+ __FUNCTION__, st->tmpl->iot_name, pos);
+ return ISNS_INVALID_REGISTRATION;
+ }
+ attr = st->orig_attrs.ial_data[pos];
+
+ /* Make sure key attrs are complete and in order */
+ if (attr->ia_tag_id != st->tmpl->iot_keys[i]) {
+ isns_debug_protocol("%s: incomplete %s object "
+ "(key attr %u missing)\n",
+ __FUNCTION__, st->tmpl->iot_name, pos);
+ return ISNS_INVALID_REGISTRATION;
+ }
+
+ isns_attr_list_append_attr(&st->keys, attr);
+ }
+
+ /*
+ * Consume all non-key attributes corresponding to the
+ * object class. We stop whenever we hit another
+ * key attribute, or an attribute that does not belong to
+ * the object type (eg when a storage node is followed by
+ * a PGT attribute, as described in section 5.6.5.1).
+ */
+copy_attrs:
+ while (pos < st->orig_attrs.ial_count) {
+ uint32_t tag;
+
+ attr = st->orig_attrs.ial_data[pos];
+ tag = attr->ia_tag_id;
+
+ if (!isns_object_attr_valid(st->tmpl, tag)
+ || isns_object_template_find(tag) != NULL)
+ break;
+
+ pos++;
+ isns_attr_list_append_attr(&st->attrs, attr);
+ }
+ st->pos = pos;
+
+ return ISNS_SUCCESS;
+}
+
+int
+isns_attr_list_scanner_get_pg(struct isns_attr_list_scanner *st)
+{
+ isns_attr_t *attr, *next = NULL;
+ unsigned int pos = st->pos;
+
+
+ attr = st->orig_attrs.ial_data[st->pos++];
+ if (st->pgt_next_attr == ISNS_TAG_PG_TAG) {
+ isns_object_t *base = st->pgt_base_object;
+
+ if (ISNS_ATTR_IS_NIL(attr))
+ st->pgt_value = 0;
+ else if (ISNS_ATTR_IS_UINT32(attr))
+ st->pgt_value = attr->ia_value.iv_uint32;
+ else
+ return ISNS_INVALID_REGISTRATION;
+
+ if (ISNS_IS_PORTAL(base)
+ && isns_portal_from_object(&st->pgt_portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ base)) {
+ st->pgt_next_attr = ISNS_TAG_PG_ISCSI_NAME;
+ } else
+ if (ISNS_IS_ISCSI_NODE(base)
+ && isns_object_get_string(base,
+ ISNS_TAG_ISCSI_NAME,
+ &st->pgt_iscsi_name)) {
+ st->pgt_next_attr = ISNS_TAG_PORTAL_IP_ADDRESS;
+ } else {
+ return ISNS_INTERNAL_ERROR;
+ }
+
+ /* Trailing PGT at end of list. Shrug. */
+ if (st->pos >= st->orig_attrs.ial_count)
+ return ISNS_NO_SUCH_ENTRY;
+
+ attr = st->orig_attrs.ial_data[st->pos++];
+ if (attr->ia_tag_id != st->pgt_next_attr) {
+ /* Some clients may do this; catch them so
+ * we can fix it. */
+ isns_error("Oops, client sends PGT followed by <%s>\n",
+ attr->ia_tag->it_name);
+ return ISNS_INVALID_REGISTRATION;
+ }
+ }
+
+ st->tmpl = &isns_iscsi_pg_template;
+ if (st->pgt_next_attr == ISNS_TAG_PG_ISCSI_NAME) {
+ isns_attr_list_append_attr(&st->keys, attr);
+ isns_portal_to_attr_list(&st->pgt_portal_info,
+ ISNS_TAG_PG_PORTAL_IP_ADDR,
+ ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
+ &st->keys);
+ } else
+ if (st->pgt_next_attr == ISNS_TAG_PG_PORTAL_IP_ADDR) {
+ if (st->pos >= st->orig_attrs.ial_count)
+ return ISNS_INVALID_REGISTRATION;
+
+ next = st->orig_attrs.ial_data[st->pos++];
+ if (next->ia_tag_id != ISNS_TAG_PORTAL_TCP_UDP_PORT)
+ return ISNS_INVALID_REGISTRATION;
+
+ isns_attr_list_append_string(&st->keys,
+ ISNS_TAG_PG_ISCSI_NAME,
+ st->pgt_iscsi_name);
+ isns_attr_list_append_attr(&st->keys, attr);
+ isns_attr_list_append_attr(&st->keys, next);
+ } else {
+ return ISNS_INTERNAL_ERROR;
+ }
+
+ isns_attr_list_append_uint32(&st->attrs,
+ ISNS_TAG_PG_TAG,
+ st->pgt_value);
+
+ /* Copy other PG attributes if present */
+ for (pos = st->pos; pos < st->orig_attrs.ial_count; ++pos) {
+ uint32_t tag;
+
+ attr = st->orig_attrs.ial_data[pos];
+ tag = attr->ia_tag_id;
+
+ /*
+ * Additional sets of PGTs and PG iSCSI Names to be
+ * associated to the registered Portal MAY follow.
+ */
+ if (tag == ISNS_TAG_PG_TAG) {
+ st->pgt_next_attr = tag;
+ break;
+ }
+
+ if (tag == ISNS_TAG_PG_ISCSI_NAME
+ || tag == ISNS_TAG_PG_PORTAL_IP_ADDR
+ || tag == ISNS_TAG_PG_PORTAL_TCP_UDP_PORT
+ || !isns_object_attr_valid(st->tmpl, tag))
+ break;
+
+ isns_attr_list_append_attr(&st->attrs, attr);
+ }
+ st->pos = pos;
+
+ return ISNS_SUCCESS;
+}
+
+/*
+ * Get the name of a function
+ */
+#define __ISNS_MAX_FUNCTION 16
+static const char * isns_req_function_names[__ISNS_MAX_FUNCTION] = {
+[ISNS_DEVICE_ATTRIBUTE_REGISTER]= "DevAttrReg",
+[ISNS_DEVICE_ATTRIBUTE_QUERY] = "DevAttrQry",
+[ISNS_DEVICE_GET_NEXT] = "DevGetNext",
+[ISNS_DEVICE_DEREGISTER] = "DevDereg",
+[ISNS_SCN_REGISTER] = "SCNReg",
+[ISNS_SCN_DEREGISTER] = "SCNDereg",
+[ISNS_SCN_EVENT] = "SCNEvent",
+[ISNS_STATE_CHANGE_NOTIFICATION]= "SCN",
+[ISNS_DD_REGISTER] = "DDReg",
+[ISNS_DD_DEREGISTER] = "DDDereg",
+[ISNS_DDS_REGISTER] = "DDSReg",
+[ISNS_DDS_DEREGISTER] = "DDSDereg",
+[ISNS_ENTITY_STATUS_INQUIRY] = "ESI",
+[ISNS_HEARTBEAT] = "Heartbeat",
+};
+static const char * isns_resp_function_names[__ISNS_MAX_FUNCTION] = {
+[ISNS_DEVICE_ATTRIBUTE_REGISTER]= "DevAttrRegResp",
+[ISNS_DEVICE_ATTRIBUTE_QUERY] = "DevAttrQryResp",
+[ISNS_DEVICE_GET_NEXT] = "DevGetNextResp",
+[ISNS_DEVICE_DEREGISTER] = "DevDeregResp",
+[ISNS_SCN_REGISTER] = "SCNRegResp",
+[ISNS_SCN_DEREGISTER] = "SCNDeregResp",
+[ISNS_SCN_EVENT] = "SCNEventResp",
+[ISNS_STATE_CHANGE_NOTIFICATION]= "SCNResp",
+[ISNS_DD_REGISTER] = "DDRegResp",
+[ISNS_DD_DEREGISTER] = "DDDeregResp",
+[ISNS_DDS_REGISTER] = "DDSRegResp",
+[ISNS_DDS_DEREGISTER] = "DDSDeregResp",
+[ISNS_ENTITY_STATUS_INQUIRY] = "ESIRsp",
+/* No response code for heartbeat */
+};
+
+const char *
+isns_function_name(uint32_t function)
+{
+ static char namebuf[32];
+ const char **names, *name;
+ unsigned int num = function;
+
+ names = isns_req_function_names;
+ if (num & 0x8000) {
+ names = isns_resp_function_names;
+ num &= 0x7fff;
+ }
+ name = NULL;
+ if (num < __ISNS_MAX_FUNCTION)
+ name = names[num];
+ if (name == NULL) {
+ snprintf(namebuf, sizeof(namebuf),
+ "<function %08x>",
+ function);
+ name = namebuf;
+ }
+
+ return name;
+}
+
diff --git a/utils/open-isns/slp.c b/utils/open-isns/slp.c
new file mode 100644
index 0000000..43075b3
--- /dev/null
+++ b/utils/open-isns/slp.c
@@ -0,0 +1,242 @@
+/*
+ * SLP registration and query of iSNS
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#ifdef HAVE_SLP_H
+# include <slp.h>
+#endif
+
+#include "isns.h"
+#include "util.h"
+#include "internal.h"
+
+#define ISNS_SLP_SERVICE_NAME "iscsi:sms"
+/*
+ * RFC 4018 says we would use scope initiator-scope-list.
+ * But don't we want targets to find the iSNS server, too?
+ */
+#define ISNS_SLP_SCOPE "initiator-scope-list"
+
+#ifdef WITH_SLP
+
+struct isns_slp_url_state {
+ SLPError slp_err;
+ char * slp_url;
+};
+
+static void
+isns_slp_report(SLPHandle handle, SLPError err, void *cookie)
+{
+ *(SLPError *) cookie = err;
+}
+
+/*
+ * Register a service with SLP
+ */
+int
+isns_slp_register(const char *url)
+{
+ SLPError err, callbackerr;
+ SLPHandle handle = NULL;
+
+ err = SLPOpen("en", SLP_FALSE, &handle);
+ if(err != SLP_OK) {
+ isns_error("Unable to obtain SLP handle (err %d)\n", err);
+ return 0;
+ }
+
+ err = SLPReg(handle, url, SLP_LIFETIME_MAXIMUM,
+ ISNS_SLP_SCOPE,
+ "(description=iSNS Server),(protocols=isns)",
+ SLP_TRUE,
+ isns_slp_report, &callbackerr);
+
+ SLPClose(handle);
+
+ if (err == SLP_OK)
+ err = callbackerr;
+ if (err != SLP_OK) {
+ isns_error("Failed to register with SLP (err %d)\n", err);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * DeRegister a service
+ */
+int
+isns_slp_unregister(const char *url)
+{
+ SLPError err, callbackerr;
+ SLPHandle handle = NULL;
+
+ isns_debug_general("SLP: Unregistering \"%s\"\n", url);
+
+ err = SLPOpen("en", SLP_FALSE, &handle);
+ if(err != SLP_OK) {
+ isns_error("Unable to obtain SLP handle (err %d)\n", err);
+ return 0;
+ }
+
+ err = SLPDereg(handle, url, isns_slp_report, &callbackerr);
+
+ SLPClose(handle);
+
+ if (err == SLP_OK)
+ err = callbackerr;
+ if (err != SLP_OK) {
+ isns_error("Failed to deregister with SLP (err %d)\n", err);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Find an iSNS server through SLP
+ */
+static SLPBoolean
+isns_slp_url_callback(SLPHandle handle,
+ const char *url, unsigned short lifetime,
+ SLPError err, void *cookie)
+{
+ struct isns_slp_url_state *sp = cookie;
+ SLPSrvURL *parsed_url = NULL;
+ int want_more = SLP_TRUE;
+ char buffer[1024];
+
+ if (err != SLP_OK && err != SLP_LAST_CALL)
+ return SLP_FALSE;
+
+ if (!url)
+ goto out;
+
+ isns_debug_general("SLP: Found URL \"%s\"\n", url);
+ err = SLPParseSrvURL(url, &parsed_url);
+ if (err != SLP_OK) {
+ isns_error("Error parsing SLP service URL \"%s\"\n", url);
+ goto out;
+ }
+
+ if (parsed_url->s_pcNetFamily
+ && parsed_url->s_pcNetFamily[0]
+ && strcasecmp(parsed_url->s_pcNetFamily, "ip")) {
+ isns_error("Ignoring SLP service URL \"%s\"\n", url);
+ goto out;
+ }
+
+ if (parsed_url->s_iPort) {
+ snprintf(buffer, sizeof(buffer), "%s:%u",
+ parsed_url->s_pcHost,
+ parsed_url->s_iPort);
+ isns_assign_string(&sp->slp_url, buffer);
+ } else {
+ isns_assign_string(&sp->slp_url,
+ parsed_url->s_pcHost);
+ }
+ want_more = SLP_FALSE;
+
+out:
+ if (parsed_url)
+ SLPFree(parsed_url);
+ sp->slp_err = SLP_OK;
+
+ return want_more;
+}
+
+/*
+ * Locate the iSNS server using SLP.
+ * This is not really an instantaneous process. Maybe we could
+ * speed this up by using a cache.
+ */
+char *
+isns_slp_find(void)
+{
+ static struct isns_slp_url_state state;
+ SLPHandle handle = NULL;
+ SLPError err;
+
+ if (state.slp_url)
+ return state.slp_url;
+
+ isns_debug_general("Using SLP to locate iSNS server\n");
+
+ err = SLPOpen("en", SLP_FALSE, &handle);
+ if(err != SLP_OK) {
+ isns_error("Unable to obtain SLP handle (err %d)\n", err);
+ return NULL;
+ }
+
+ err = SLPFindSrvs(handle, ISNS_SLP_SERVICE_NAME,
+ NULL, "(protocols=isns)",
+ isns_slp_url_callback, &state);
+
+ SLPClose(handle);
+
+ if (err == SLP_OK)
+ err = state.slp_err;
+ if (err != SLP_OK) {
+ isns_error("Failed to find service in SLP (err %d)\n", err);
+ return NULL;
+ }
+
+ if (state.slp_url == NULL) {
+ isns_error("Service %s not registered with SLP\n",
+ ISNS_SLP_SERVICE_NAME);
+ return NULL;
+
+ }
+
+ isns_debug_general("Using iSNS server at %s\n", state.slp_url);
+ return state.slp_url;
+}
+
+#else /* WITH_SLP */
+
+int
+isns_slp_register(const char *url)
+{
+ isns_error("SLP support disabled in this build\n");
+ return 0;
+}
+
+int
+isns_slp_unregister(const char *url)
+{
+ isns_error("SLP support disabled in this build\n");
+ return 0;
+}
+
+char *
+isns_slp_find(void)
+{
+ isns_error("SLP support disabled in this build\n");
+ return NULL;
+}
+
+#endif /* WITH_SLP */
+
+char *
+isns_slp_build_url(uint16_t port)
+{
+ char buffer[1024];
+
+ if (port)
+ snprintf(buffer, sizeof(buffer),
+ "service:%s://%s:%u",
+ ISNS_SLP_SERVICE_NAME,
+ isns_config.ic_host_name, port);
+ else
+ snprintf(buffer, sizeof(buffer),
+ "service:%s://%s",
+ ISNS_SLP_SERVICE_NAME,
+ isns_config.ic_host_name);
+ return isns_strdup(buffer);
+}
+
diff --git a/utils/open-isns/socket.c b/utils/open-isns/socket.c
new file mode 100644
index 0000000..f0a758b
--- /dev/null
+++ b/utils/open-isns/socket.c
@@ -0,0 +1,2289 @@
+/*
+ * Socket handling code
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+#include "buffer.h"
+#include "isns.h"
+#include "socket.h"
+#include "security.h"
+#include "util.h"
+#include "config.h"
+
+#define SOCK_DEBUG_VERBOSE 0
+
+#ifndef AI_ADDRCONFIG
+# define AI_ADDRCONFIG 0
+#endif
+#ifndef AI_V4MAPPED
+# define AI_V4MAPPED 0
+#endif
+
+enum {
+ ISNS_MSG_DISCARD,
+ ISNS_MSG_DONE,
+ ISNS_MSG_RETURN
+};
+
+static isns_socket_t *__isns_create_socket(struct addrinfo *src,
+ struct addrinfo *dst,
+ int sock_type);
+static struct addrinfo *isns_get_address_list(const char *, const char *,
+ int, int, int);
+static void release_addrinfo(struct addrinfo *);
+static void isns_net_dgram_recv(isns_socket_t *);
+static void isns_net_dgram_xmit(isns_socket_t *);
+static void isns_net_stream_accept(isns_socket_t *);
+static void isns_net_stream_recv(isns_socket_t *);
+static void isns_net_stream_xmit(isns_socket_t *);
+static void isns_net_stream_hup(isns_socket_t *);
+static void isns_net_stream_error(isns_socket_t *, int);
+static void isns_net_stream_reconnect(isns_socket_t *);
+static void isns_net_stream_disconnect(isns_socket_t *);
+static isns_socket_t *isns_net_alloc(int);
+static int isns_socket_open(isns_socket_t *);
+static int isns_socket_queue_message(isns_socket_t *, isns_message_t *);
+static int isns_socket_retransmit_queued(isns_socket_t *);
+
+static ISNS_LIST_DECLARE(all_sockets);
+
+#define debug_verbose(args ...) do { \
+ if (SOCK_DEBUG_VERBOSE >= 1) isns_debug_socket(args); \
+} while (0)
+#define debug_verbose2(args ...) do { \
+ if (SOCK_DEBUG_VERBOSE >= 2) isns_debug_socket(args); \
+} while (0)
+
+/*
+ * Helper function for looking at incoming PDUs
+ */
+static inline buf_t *
+isns_socket_next_pdu(isns_socket_t *sock)
+{
+ buf_t *bp = sock->is_recv_buf;
+ unsigned int avail;
+ struct isns_hdr *hdr;
+ uint32_t pdu_len = 0;
+
+ if (bp == NULL)
+ return NULL;
+
+ avail = buf_avail(bp);
+ if (avail < sizeof(*hdr))
+ return NULL;
+ hdr = buf_head(bp);
+ pdu_len = sizeof(*hdr) + ntohs(hdr->i_length);
+
+ if (avail < pdu_len)
+ return NULL;
+
+ /* Check for presence of authentication block */
+ if (hdr->i_flags & htons(ISNS_F_AUTHBLK_PRESENT)) {
+ uint32_t *authblk, authlen;
+
+ authblk = (uint32_t *) ((char *) hdr + pdu_len);
+ if (avail < pdu_len + ISNS_AUTHBLK_SIZE)
+ return NULL;
+
+ authlen = ntohl(authblk[1]);
+ if (authlen < 20 || authlen > ISNS_MAX_MESSAGE) {
+ /* The authblock is garbage.
+ * The only reliable way to signal such a problem
+ * is by dropping the connection.
+ */
+ isns_error("socket error: bad auth block\n");
+ sock->is_state = ISNS_SOCK_DEAD;
+ return NULL;
+ }
+
+ pdu_len += authlen;
+ if (avail < pdu_len)
+ return NULL;
+ }
+
+ return buf_split(&sock->is_recv_buf, pdu_len);
+}
+
+/*
+ * Try to assemble the message from PDUs
+ */
+static inline int
+isns_msg_complete(struct isns_partial_msg *msg)
+{
+ buf_t *msg_buf, **chain, *bp;
+
+ /* Return if we haven't seen first and last frag */
+ if (((~msg->imp_flags) & (ISNS_F_FIRST_PDU|ISNS_F_LAST_PDU)))
+ return 0;
+
+ /* Simple - unfragmented case: just move
+ * the PDU on the chain to the payload */
+ if (msg->imp_first_seq == msg->imp_last_seq) {
+ msg->imp_payload = msg->imp_chain;
+ buf_pull(msg->imp_payload, sizeof(struct isns_hdr));
+ msg->imp_chain = NULL;
+ return 1;
+ }
+
+ /* Do we have all fragments? */
+ if (msg->imp_last_seq - msg->imp_first_seq + 1
+ != msg->imp_pdu_count)
+ return 0;
+
+ msg_buf = buf_alloc(msg->imp_msg_size);
+
+ chain = &msg->imp_chain;
+ while ((bp = *chain) != NULL) {
+ /* Pull the header off */
+ buf_pull(bp, sizeof(struct isns_hdr));
+ buf_put(msg_buf, buf_head(bp), buf_avail(bp));
+
+ *chain = bp->next;
+ buf_free(bp);
+ }
+
+ return 0;
+}
+
+/*
+ * Clear the "partial" part of the message
+ */
+static void
+__isns_msg_clear_partial(struct isns_partial_msg *msg)
+{
+ buf_list_free(msg->imp_chain);
+ msg->imp_chain = NULL;
+}
+
+/*
+ * Add an authentication block to an outgoing PDU
+ */
+#ifdef WITH_SECURITY
+static int
+isns_pdu_seal(isns_security_t *ctx, buf_t *pdu)
+{
+ struct isns_authblk auth;
+ isns_principal_t *self;
+
+ if (!(self = ctx->is_self)) {
+ isns_error("Cannot sign PDU: no sender identity for socket\n");
+ return 0;
+ }
+
+ auth.iab_bsd = ctx->is_type;
+ auth.iab_timestamp = time(NULL);
+ auth.iab_spi = self->is_name;
+ auth.iab_spi_len = strlen(self->is_name);
+
+ if (!isns_security_sign(ctx, self, pdu, &auth)) {
+ isns_error("Cannot sign PDU: error creating signature\n");
+ return 0;
+ }
+
+ auth.iab_length = ISNS_AUTHBLK_SIZE +
+ auth.iab_spi_len +
+ auth.iab_sig_len;
+ if (!isns_authblock_encode(pdu, &auth))
+ return 0;
+
+ isns_debug_message("Successfully signed message (authlen=%u, spilen=%u, siglen=%u)\n",
+ auth.iab_length, auth.iab_spi_len, auth.iab_sig_len);
+
+ return 1;
+}
+
+/*
+ * Authenticate a PDU
+ *
+ * The RFC is doing a bit of handwaving around the
+ * authentication issue. For example, it never
+ * spells out exactly which parts of the message
+ * are included in the SHA1 hash to be signed.
+ *
+ * It also says that the auth block "is identical in format
+ * to the SLP authentication block", but all fields
+ * are twice as wide.
+ *
+ * There's not even an error code to tell the client
+ * we were unable to authenticate him :-(
+ *
+ * Interoperability problems, here I come...
+ */
+static int
+isns_pdu_authenticate(isns_security_t *sec,
+ struct isns_partial_msg *msg, buf_t *bp)
+{
+ struct isns_hdr *hdr = buf_head(bp);
+ unsigned int pdu_len, avail;
+ struct isns_authblk authblk;
+ isns_principal_t * peer = NULL;
+ buf_t auth_buf;
+
+ isns_debug_auth("Message has authblock; trying to authenticate\n");
+
+ /* In the TCP path, we checked this before, but
+ * better safe than sorry. */
+ avail = buf_avail(bp);
+ pdu_len = sizeof(*hdr) + ntohs(hdr->i_length);
+ if (avail < pdu_len + ISNS_AUTHBLK_SIZE) {
+ isns_debug_auth("authblock truncated\n");
+ return 0;
+ }
+
+ /* Get the auth block */
+ buf_set(&auth_buf, buf_head(bp) + pdu_len, avail - pdu_len);
+ if (!isns_authblock_decode(&auth_buf, &authblk)) {
+ isns_debug_auth("error decoding authblock\n");
+ return 0;
+ }
+
+ /* Truncate the buffer (this just sets the
+ * tail pointer, but doesn't free memory */
+ if (!buf_truncate(bp, pdu_len)) {
+ isns_debug_auth("buf_truncate failed - cosmic particles?\n");
+ return 0;
+ }
+
+ /* If the socket doesn't have a security context,
+ * just ignore the auth block. */
+ if (sec == NULL) {
+ msg->imp_header.i_flags &= ~ISNS_F_AUTHBLK_PRESENT;
+ return 1;
+ }
+
+ if (authblk.iab_bsd != sec->is_type)
+ goto failed;
+
+ peer = isns_get_principal(sec, authblk.iab_spi, authblk.iab_spi_len);
+ if (peer == NULL) {
+ /* If the admin allows unknown peers, we must make
+ * sure, however, to not allow an unauthenticated
+ * PDU to be inserted into an authenticated message.
+ */
+ if (isns_config.ic_auth.allow_unknown_peers
+ && msg->imp_security == NULL) {
+ isns_debug_message(
+ "Accepting unknown peer spi=\"%.*s\" as "
+ "anonymous peer\n",
+ authblk.iab_spi_len, authblk.iab_spi);
+ return 1;
+ }
+
+ isns_debug_message(
+ "Unable to create security peer for spi=%.*s\n",
+ authblk.iab_spi_len, authblk.iab_spi);
+
+ goto failed;
+ }
+
+ if (!isns_security_verify(sec, peer, bp, &authblk)) {
+ /* Authentication failed */
+ goto failed;
+ }
+
+ /* The RFC doesn't say how to deal with fragmented
+ * messages with different BSDs or SPIs.
+ * kickban seems the right approach.
+ * We discard this segment rather than failing
+ * the entire message.
+ */
+ if (msg->imp_chain == NULL) {
+ msg->imp_security = peer;
+ peer->is_users++;
+ } else
+ if (msg->imp_security != peer) {
+ goto failed;
+ }
+
+ isns_principal_free(peer);
+ return 1;
+
+failed:
+ isns_principal_free(peer);
+ return 0;
+}
+#else /* WITH_SECURITY */
+static int
+isns_pdu_authenticate(isns_security_t *sec,
+ struct isns_partial_msg *msg, buf_t *bp)
+{
+ return 0;
+}
+
+#endif
+
+/*
+ * Enqueue an incoming PDU on the socket.
+ *
+ * A single iSNS message may be split up into
+ * several PDUs, so we need to perform
+ * reassembly here.
+ *
+ * This function also verifies the authentication
+ * block, if present.
+ */
+static void
+isns_pdu_enqueue(isns_socket_t *sock,
+ struct sockaddr_storage *addr, socklen_t alen,
+ buf_t *segment, struct ucred *creds)
+{
+ isns_message_queue_t *q = &sock->is_partial;
+ struct isns_partial_msg *msg;
+ buf_t **chain, *bp;
+ struct isns_hdr *hdr;
+ uint32_t xid, seq, flags;
+
+ hdr = (struct isns_hdr *) buf_head(segment);
+ xid = ntohs(hdr->i_xid);
+ seq = ntohs(hdr->i_seq);
+ flags = ntohs(hdr->i_flags);
+
+ isns_debug_socket("Incoming PDU xid=%04x seq=%u len=%u func=%s%s%s%s%s%s\n",
+ xid, seq, ntohs(hdr->i_length),
+ isns_function_name(ntohs(hdr->i_function)),
+ (flags & ISNS_F_CLIENT)? " client" : "",
+ (flags & ISNS_F_SERVER)? " server" : "",
+ (flags & ISNS_F_AUTHBLK_PRESENT)? " authblk" : "",
+ (flags & ISNS_F_FIRST_PDU)? " first" : "",
+ (flags & ISNS_F_LAST_PDU)? " last" : "");
+
+ /* Find the message matching (addr, xid) */
+ msg = (struct isns_partial_msg *) isns_message_queue_find(q, xid, addr, alen);
+ if (msg != NULL) {
+ if (msg->imp_creds
+ && (!creds || memcmp(msg->imp_creds, creds, sizeof(*creds)))) {
+ isns_warning("socket: credentials mismatch! Dropping PDU\n");
+ goto drop;
+ }
+ hdr = &msg->imp_header;
+ goto found;
+ }
+
+ msg = (struct isns_partial_msg *) __isns_alloc_message(xid, sizeof(*msg),
+ (void (*)(isns_message_t *)) __isns_msg_clear_partial);
+ memcpy(&msg->imp_addr, addr, alen);
+ msg->imp_addrlen = alen;
+
+ msg->imp_header = *hdr;
+ msg->imp_header.i_seq = 0;
+
+ isns_message_queue_append(q, &msg->imp_base);
+ isns_message_release(&msg->imp_base);
+ /* Message is owned by is_partial now */
+
+ /* Fix up the PDU header */
+ hdr = &msg->imp_header;
+ hdr->i_version = ntohs(hdr->i_version);
+ hdr->i_function = ntohs(hdr->i_function);
+ hdr->i_length = ntohs(hdr->i_length);
+ hdr->i_flags = ntohs(hdr->i_flags);
+ hdr->i_xid = ntohs(hdr->i_xid);
+ hdr->i_seq = ntohs(hdr->i_seq);
+
+ if (creds) {
+ msg->imp_credbuf = *creds;
+ msg->imp_creds = &msg->imp_credbuf;
+ }
+
+found:
+ if (flags & ISNS_F_AUTHBLK_PRESENT) {
+ /* When authentication fails - should we drop the
+ * message or treat it as unauthenticated?
+ * For now we drop it, but a more user friendly
+ * approach might be to just treat it as
+ * unauthenticated.
+ */
+ if (!isns_pdu_authenticate(sock->is_security, msg, segment))
+ goto drop;
+ } else
+ if (msg->imp_header.i_flags & ISNS_F_AUTHBLK_PRESENT) {
+ /* Oops, unauthenticated fragment in an
+ * authenticated message. */
+ isns_debug_message(
+ "Oops, unauthenticated fragment in an "
+ "authenticated message!\n");
+ goto drop;
+ }
+
+ if ((flags & ISNS_F_FIRST_PDU)
+ && !(msg->imp_flags & ISNS_F_FIRST_PDU)) {
+ /* FIXME: first seq must be zero */
+ msg->imp_first_seq = seq;
+ msg->imp_flags |= ISNS_F_FIRST_PDU;
+ }
+ if ((flags & ISNS_F_LAST_PDU)
+ && !(msg->imp_flags & ISNS_F_LAST_PDU)) {
+ msg->imp_last_seq = seq;
+ msg->imp_flags |= ISNS_F_LAST_PDU;
+ }
+
+ chain = &msg->imp_chain;
+ while ((bp = *chain) != NULL) {
+ struct isns_hdr *ohdr = buf_head(bp);
+
+ /* Duplicate? Drop it! */
+ if (seq == ohdr->i_seq)
+ goto drop;
+ if (seq < ohdr->i_seq)
+ break;
+ chain = &bp->next;
+ }
+ segment->next = *chain;
+ *chain = segment;
+
+ msg->imp_msg_size += buf_avail(segment) - sizeof(*hdr);
+ msg->imp_pdu_count++;
+
+ /* We received first and last PDU - check if the
+ * chain is complete */
+ if (isns_msg_complete(msg)) {
+ /* Remove from partial queue.
+ * We clean the part of the message that is
+ * not in imp_base, so that we can pass this
+ * to the caller and have him call
+ * isns_message_release on it.
+ */
+ __isns_msg_clear_partial(msg);
+
+ /* Move from partial queue to complete queue. */
+ isns_message_queue_move(&sock->is_complete,
+ &msg->imp_base);
+ msg->imp_base.im_socket = sock;
+ }
+
+ return;
+
+drop:
+ buf_free(segment);
+ return;
+}
+
+/*
+ * Send side handling
+ */
+static void
+isns_send_update(isns_socket_t *sock)
+{
+ buf_t *bp = sock->is_xmit_buf;
+
+ if (bp && buf_avail(bp) == 0) {
+ sock->is_xmit_buf = bp->next;
+ buf_free(bp);
+ }
+
+ if (sock->is_xmit_buf)
+ sock->is_poll_mask |= POLLOUT;
+ else
+ sock->is_poll_mask &= ~POLLOUT;
+}
+
+/*
+ * Close the socket
+ */
+static void
+isns_net_close(isns_socket_t *sock, int next_state)
+{
+ if (sock->is_desc >= 0) {
+ close(sock->is_desc);
+ sock->is_desc = -1;
+ }
+ sock->is_poll_mask &= ~(POLLIN|POLLOUT);
+ sock->is_state = next_state;
+
+ buf_list_free(sock->is_xmit_buf);
+ sock->is_xmit_buf = NULL;
+
+ buf_free(sock->is_recv_buf);
+ sock->is_recv_buf = NULL;
+
+ isns_message_queue_destroy(&sock->is_partial);
+ isns_message_queue_destroy(&sock->is_complete);
+}
+
+static void
+isns_net_set_timeout(isns_socket_t *sock,
+ void (*func)(isns_socket_t *),
+ unsigned int timeout)
+{
+ gettimeofday(&sock->is_deadline, NULL);
+ sock->is_deadline.tv_sec += timeout;
+ sock->is_timeout = func;
+}
+
+static void
+isns_net_cancel_timeout(isns_socket_t *sock)
+{
+ timerclear(&sock->is_deadline);
+}
+
+void
+isns_net_error(isns_socket_t *sock, int err_code)
+{
+ if (sock->is_error)
+ sock->is_error(sock, err_code);
+}
+
+/*
+ * Create a passive socket (server side)
+ */
+isns_socket_t *
+isns_create_server_socket(const char *src_spec, const char *portspec, int af_hint, int sock_type)
+{
+ struct addrinfo *src;
+
+ src = isns_get_address_list(src_spec, portspec,
+ af_hint, sock_type, AI_PASSIVE);
+ if (src == NULL)
+ return NULL;
+
+ return __isns_create_socket(src, NULL, sock_type);
+}
+
+/*
+ * Accept incoming connections.
+ */
+void
+isns_net_stream_accept(isns_socket_t *sock)
+{
+ isns_socket_t *child;
+ size_t optlen;
+ int fd, passcred = 0;
+
+ fd = accept(sock->is_desc, NULL, NULL);
+ if (fd < 0) {
+ if (errno != EINTR)
+ isns_error("Error accepting connection: %m\n");
+ return;
+ }
+
+ optlen = sizeof(passcred);
+ if (getsockopt(sock->is_desc, SOL_SOCKET, SO_PASSCRED,
+ &passcred, &optlen) >= 0) {
+ setsockopt(fd, SOL_SOCKET, SO_PASSCRED,
+ &passcred, sizeof(passcred));
+ }
+
+ child = isns_net_alloc(fd);
+ child->is_type = SOCK_STREAM;
+ child->is_autoclose = 1;
+ child->is_disconnect_fatal = 1;
+ child->is_poll_in = isns_net_stream_recv;
+ child->is_poll_out = isns_net_stream_xmit;
+ child->is_poll_hup = isns_net_stream_hup;
+ child->is_error = isns_net_stream_error;
+ child->is_poll_mask = POLLIN|POLLHUP;
+ child->is_security = sock->is_security;
+
+ if (isns_config.ic_network.idle_timeout)
+ isns_net_set_timeout(child,
+ isns_net_stream_disconnect,
+ isns_config.ic_network.idle_timeout);
+
+ isns_list_append(&all_sockets, &child->is_list);
+}
+
+/*
+ * This is called from the socket code when it detects
+ * an error condition.
+ */
+static void
+isns_net_stream_error(isns_socket_t *sock, int err_code)
+{
+ int timeo = 0, next_state = ISNS_SOCK_DEAD;
+
+ if (err_code == EAGAIN)
+ return;
+
+ isns_debug_socket("isns_net_stream_error: %s\n", strerror(err_code));
+
+ switch (err_code) {
+ case EINTR: /* ignored */
+ return;
+
+ case ECONNREFUSED:
+ case ECONNRESET:
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ case ENOTCONN:
+ case EPIPE:
+ if (sock->is_disconnect_fatal) {
+ isns_warning("socket disconnect, killing socket\n");
+ break;
+ }
+
+ /* fallthrough to disconnect */
+ timeo = isns_config.ic_network.reconnect_timeout;
+
+ case ETIMEDOUT:
+ /* Disconnect and try to reconnect */
+ if (sock->is_client) {
+ /* FIXME: We don't want this warning for ESI and
+ * SCN sockets on the server side. */
+ isns_warning("socket disconnect, retrying in %u sec\n",
+ timeo);
+ isns_net_set_timeout(sock,
+ isns_net_stream_reconnect,
+ timeo);
+ next_state = ISNS_SOCK_DISCONNECTED;
+ break;
+ }
+
+ /* fallthru */
+
+ default:
+ isns_error("socket error: %s\n", strerror(err_code));
+ }
+
+ /* Close the socket right away */
+ isns_net_close(sock, next_state);
+}
+
+/*
+ * recvmsg wrapper handling SCM_CREDENTIALS passing
+ */
+static int
+isns_net_recvmsg(isns_socket_t *sock,
+ void *buffer, size_t count,
+ struct sockaddr *addr, socklen_t *alen,
+ struct ucred **cred)
+{
+ static struct ucred cred_buf;
+ unsigned int control[128];
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct iovec iov;
+ int len;
+
+ *cred = NULL;
+
+ iov.iov_base = buffer;
+ iov.iov_len = count;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = addr;
+ msg.msg_namelen = *alen;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ len = recvmsg(sock->is_desc, &msg, MSG_DONTWAIT);
+
+ if (len < 0)
+ return len;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ while (cmsg) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_CREDENTIALS) {
+ memcpy(&cred_buf, CMSG_DATA(cmsg), sizeof(cred_buf));
+ *cred = &cred_buf;
+ break;
+ }
+
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+
+ *alen = msg.msg_namelen;
+ return len;
+}
+
+void
+isns_net_stream_recv(isns_socket_t *sock)
+{
+ unsigned char buffer[ISNS_MAX_BUFFER];
+ struct sockaddr_storage addr;
+ struct ucred *creds = NULL;
+ socklen_t alen = sizeof(addr);
+ buf_t *bp;
+ size_t count, total = 0;
+ int len;
+
+again:
+ if ((bp = sock->is_recv_buf) == NULL) {
+ bp = buf_alloc(ISNS_MAX_MESSAGE);
+ sock->is_recv_buf = bp;
+ }
+
+ if ((count = buf_tailroom(bp)) > sizeof(buffer))
+ count = sizeof(buffer);
+
+ if (count == 0) {
+ /* Message too large */
+ isns_net_stream_error(sock, EMSGSIZE);
+ return;
+ }
+
+#if 0
+ len = recvfrom(sock->is_desc, buffer, count, MSG_DONTWAIT,
+ (struct sockaddr *) &addr, &alen);
+#else
+ len = isns_net_recvmsg(sock, buffer, count,
+ (struct sockaddr *) &addr, &alen,
+ &creds);
+#endif
+ if (len < 0) {
+ isns_net_stream_error(sock, errno);
+ return;
+ }
+ if (len == 0) {
+ if (total == 0)
+ sock->is_poll_mask &= ~POLLIN;
+ return;
+ }
+
+ /* We received some data from client, re-arm the
+ * idle disconnect timer */
+ if (sock->is_autoclose
+ && isns_config.ic_network.idle_timeout)
+ isns_net_set_timeout(sock,
+ isns_net_stream_disconnect,
+ isns_config.ic_network.idle_timeout);
+
+ buf_put(bp, buffer, len);
+ total += len;
+
+ /* Chop up the recv buffer into PDUs */
+ while ((bp = isns_socket_next_pdu(sock)) != NULL) {
+ /* We have a full PDU; enqueue it */
+ /* We shouldn't have more than one partial message
+ * on a TCP connection; we could check this here.
+ */
+ isns_pdu_enqueue(sock, &addr, alen, bp, creds);
+ }
+
+ goto again;
+}
+
+void
+isns_net_stream_xmit(isns_socket_t *sock)
+{
+ unsigned int count;
+ buf_t *bp = sock->is_xmit_buf;
+ int len;
+
+ /* If a connecting socket can send, it has
+ * the TCP three-way handshake. */
+ if (sock->is_state == ISNS_SOCK_CONNECTING) {
+ sock->is_state = ISNS_SOCK_IDLE;
+ sock->is_poll_mask |= POLLIN;
+ isns_net_cancel_timeout(sock);
+ }
+
+ if (bp == NULL)
+ return;
+
+ count = buf_avail(bp);
+ len = send(sock->is_desc, buf_head(bp), count, MSG_DONTWAIT);
+ if (len < 0) {
+ isns_net_stream_error(sock, errno);
+ return;
+ }
+
+ debug_verbose("isns_net_stream_xmit(%p, count=%u): transmitted %d\n",
+ sock, count, len);
+ buf_pull(bp, len);
+ isns_send_update(sock);
+}
+
+void
+isns_net_stream_hup(isns_socket_t *sock)
+{
+ sock->is_poll_mask &= ~POLLIN;
+ /* POLLHUP while connecting means we failed */
+ if (sock->is_state == ISNS_SOCK_CONNECTING)
+ isns_net_stream_error(sock, ECONNREFUSED);
+}
+
+/*
+ * Clone an addrinfo list
+ */
+static struct addrinfo *
+clone_addrinfo(const struct addrinfo *ai)
+{
+ struct addrinfo *res = NULL, **p;
+
+ p = &res;
+ for (; ai; ai = ai->ai_next) {
+ struct addrinfo *new;
+
+ if (ai->ai_addrlen > sizeof(struct sockaddr_storage))
+ continue;
+
+ new = isns_calloc(1, sizeof(*new) + ai->ai_addrlen);
+ new->ai_family = ai->ai_family;
+ new->ai_socktype = ai->ai_socktype;
+ new->ai_protocol = ai->ai_protocol;
+ new->ai_addrlen = ai->ai_addrlen;
+ new->ai_addr = (struct sockaddr *) (new + 1);
+ memcpy(new->ai_addr, ai->ai_addr, new->ai_addrlen);
+
+ *p = new;
+ p = &new->ai_next;
+ }
+
+ return res;
+}
+
+static struct addrinfo *
+__make_addrinfo(const struct sockaddr *ap, socklen_t alen, int socktype)
+{
+ struct addrinfo *new;
+
+ new = isns_calloc(1, sizeof(*new) + alen);
+ new->ai_family = ap->sa_family;
+ new->ai_socktype = socktype;
+ new->ai_protocol = 0;
+ new->ai_addrlen = alen;
+ new->ai_addr = (struct sockaddr *) (new + 1);
+ memcpy(new->ai_addr, ap, alen);
+
+ return new;
+}
+
+static struct addrinfo *
+make_addrinfo_unix(const char *pathname, int socktype)
+{
+ unsigned int len = strlen(pathname);
+ struct sockaddr_un sun;
+
+ if (len + 1 > sizeof(sun.sun_path)) {
+ isns_error("Can't set AF_LOCAL address: path too long!\n");
+ return NULL;
+ }
+
+ sun.sun_family = AF_LOCAL;
+ strcpy(sun.sun_path, pathname);
+ return __make_addrinfo((struct sockaddr *) &sun, SUN_LEN(&sun) + 1, socktype);
+}
+
+static struct addrinfo *
+make_addrinfo_any(int family, int socktype)
+{
+ struct sockaddr_storage addr = { .ss_family = AF_UNSPEC };
+ struct addrinfo *res;
+
+ if (family != AF_UNSPEC) {
+ addr.ss_family = family;
+ res = __make_addrinfo((struct sockaddr *) &addr, sizeof(addr), socktype);
+ } else {
+ addr.ss_family = AF_INET6;
+ res = __make_addrinfo((struct sockaddr *) &addr, sizeof(addr), socktype);
+ addr.ss_family = AF_INET;
+ res->ai_next = __make_addrinfo((struct sockaddr *) &addr, sizeof(addr), socktype);
+ }
+
+ return res;
+}
+
+/*
+ * Release addrinfo created by functions above.
+ * We cannot use freeaddrinfo, as we don't know how it
+ * is implemented.
+ */
+static void
+release_addrinfo(struct addrinfo *ai)
+{
+ struct addrinfo *next;
+
+ for (; ai; ai = next) {
+ next = ai->ai_next;
+ isns_free(ai);
+ }
+}
+
+static void
+__isns_sockaddr_set_current(struct __isns_socket_addr *info,
+ const struct addrinfo *ai)
+{
+ if (!ai)
+ return;
+
+ /* Cannot overflow; we check addrlen in clone_addrinfo */
+ memcpy(&info->addr, ai->ai_addr, ai->ai_addrlen);
+ info->addrlen = ai->ai_addrlen;
+}
+
+static void
+isns_sockaddr_init(struct __isns_socket_addr *info,
+ struct addrinfo *ai)
+{
+ if (ai == NULL)
+ return;
+
+ __isns_sockaddr_set_current(info, ai);
+
+ /* keep a copy so that we can loop through
+ * all addrs */
+ info->list = ai;
+
+ /* Make the list circular */
+ while (ai->ai_next)
+ ai = ai->ai_next;
+ ai->ai_next = info->list;
+}
+
+static void
+isns_sockaddr_destroy(struct __isns_socket_addr *info)
+{
+ struct addrinfo *ai, *next;
+
+ if ((ai = info->list) != NULL) {
+ /* Break the circular list */
+ info->list = NULL;
+ next = ai->ai_next;
+ ai->ai_next = NULL;
+ isns_assert(next);
+
+ /* Can't use freeaddrinfo on homegrown
+ * addrinfo lists. */
+ release_addrinfo(next);
+ }
+}
+
+static int
+isns_sockaddr_set_next(struct __isns_socket_addr *info)
+{
+ struct addrinfo *ai;
+
+ if (!(ai = info->list))
+ return 0;
+
+ info->list = ai->ai_next;
+ __isns_sockaddr_set_current(info, info->list);
+ return 1;
+}
+
+/*
+ * This function is used to pick a matching source address
+ * when connecting to some server.
+ */
+static int
+isns_sockaddr_select(struct __isns_socket_addr *info,
+ const struct sockaddr_storage *hint)
+{
+ struct addrinfo *head = info->list, *ai;
+
+ if (info->list == NULL)
+ return 0;
+
+ if (hint->ss_family == AF_INET6) {
+ struct addrinfo *good = NULL, *best = NULL;
+
+ ai = head;
+ do {
+ if (ai->ai_family == AF_INET) {
+ /* Possible improvement: when
+ * destination is not a private network,
+ * prefer non-private source. */
+ good = ai;
+ } else
+ if (ai->ai_family == AF_INET6) {
+ /* Possible improvement: prefer IPv6 addr
+ * with same address scope (local, global)
+ */
+ best = ai;
+ break;
+ }
+
+ ai = ai->ai_next;
+ } while (ai != head);
+
+ if (!best)
+ best = good;
+ if (best) {
+ __isns_sockaddr_set_current(info, best);
+ return 1;
+ }
+ } else
+ if (hint->ss_family == AF_INET || hint->ss_family == AF_LOCAL) {
+ ai = head;
+ do {
+ if (ai->ai_family == hint->ss_family) {
+ __isns_sockaddr_set_current(info, ai);
+ return 1;
+ }
+ ai = ai->ai_next;
+ } while (ai != head);
+ }
+
+ return 0;
+}
+
+void
+isns_net_stream_reconnect(isns_socket_t *sock)
+{
+ struct sockaddr *addr = (struct sockaddr *) &sock->is_dst.addr;
+
+ debug_verbose("isns_net_stream_reconnect(%p)\n", sock);
+
+ /* If we timed out while connecting, close the socket
+ * and try again. */
+ if (sock->is_state == ISNS_SOCK_CONNECTING) {
+ isns_net_close(sock, ISNS_SOCK_DISCONNECTED);
+ isns_sockaddr_set_next(&sock->is_dst);
+ }
+
+ if (!isns_socket_open(sock)) {
+ isns_error("isns_net_stream_reconnect: cannot create socket\n");
+ sock->is_state = ISNS_SOCK_DEAD;
+ return;
+ }
+
+ if (connect(sock->is_desc, addr, sock->is_dst.addrlen) >= 0) {
+ sock->is_state = ISNS_SOCK_IDLE;
+ sock->is_poll_mask |= POLLIN;
+ } else
+ if (errno == EINTR || errno == EINPROGRESS) {
+ sock->is_state = ISNS_SOCK_CONNECTING;
+ isns_net_set_timeout(sock,
+ isns_net_stream_reconnect,
+ isns_config.ic_network.connect_timeout);
+ sock->is_poll_mask |= POLLOUT;
+ } else {
+ isns_net_stream_error(sock, errno);
+ return;
+ }
+
+ /* We're connected, or in the process of doing so.
+ * Check if there are any pending messages, and
+ * retransmit them. */
+ isns_socket_retransmit_queued(sock);
+}
+
+void
+isns_net_stream_disconnect(isns_socket_t *sock)
+{
+ isns_debug_socket("Disconnecting idle socket\n");
+ isns_net_close(sock, ISNS_SOCK_DEAD);
+}
+
+/*
+ * Datagram send/recv
+ */
+static int
+isns_net_dgram_connect(isns_socket_t *sock)
+{
+ return connect(sock->is_desc,
+ (struct sockaddr *) &sock->is_dst.addr,
+ sock->is_dst.addrlen);
+}
+
+void
+isns_net_dgram_recv(isns_socket_t *sock)
+{
+ unsigned char buffer[ISNS_MAX_BUFFER];
+ struct sockaddr_storage addr;
+ socklen_t alen = sizeof(addr);
+ buf_t *bp;
+ int len;
+
+ len = recvfrom(sock->is_desc, buffer, sizeof(buffer),
+ MSG_DONTWAIT, (struct sockaddr *) &addr, &alen);
+ if (len < 0) {
+ isns_error("recv: %m\n");
+ return;
+ }
+ if (len == 0)
+ return;
+
+ bp = buf_alloc(len);
+ if (bp == NULL)
+ return;
+
+ buf_put(bp, buffer, len);
+ isns_pdu_enqueue(sock, &addr, alen, bp, NULL);
+}
+
+void
+isns_net_dgram_xmit(isns_socket_t *sock)
+{
+ unsigned int count;
+ buf_t *bp = sock->is_xmit_buf;
+ int len;
+
+ count = buf_avail(bp);
+ if (bp->addrlen) {
+ len = sendto(sock->is_desc, buf_head(bp), count, MSG_DONTWAIT,
+ (struct sockaddr *) &bp->addr, bp->addrlen);
+ } else {
+ len = sendto(sock->is_desc, buf_head(bp), count, MSG_DONTWAIT,
+ NULL, 0);
+ }
+
+ /* Even if sendto failed, we will pull the pending buffer
+ * off the send chain. Else we'll loop forever on an
+ * unreachable host. */
+ if (len < 0)
+ isns_error("send: %m\n");
+
+ buf_pull(bp, count);
+ isns_send_update(sock);
+}
+
+/*
+ * Bind socket to random port
+ */
+static int
+__isns_socket_bind_random(int fd,
+ const struct sockaddr *orig_addr,
+ socklen_t src_len)
+{
+ struct sockaddr_storage addr;
+ struct sockaddr *src_addr;
+ uint16_t min = 888, max = 1024;
+ unsigned int loop = 0;
+
+ /* Copy the address to a writable location */
+ isns_assert(src_len <= sizeof(addr));
+ memcpy(&addr, orig_addr, src_len);
+ src_addr = (struct sockaddr *) &addr;
+
+ /* Bind to a random port */
+ do {
+ uint16_t port;
+
+ port = random();
+ port = min + (port % (max - min));
+
+ isns_addr_set_port(src_addr, port);
+
+ if (bind(fd, src_addr, src_len) == 0)
+ return 1;
+
+ if (errno == EACCES && min < 1024) {
+ min = 1024;
+ max = 65535;
+ continue;
+ }
+ } while (errno == EADDRINUSE && ++loop < 128);
+
+ isns_error("Unable to bind socket\n");
+ return 0;
+}
+
+/*
+ * Create a socket
+ */
+isns_socket_t *
+__isns_create_socket(struct addrinfo *src, struct addrinfo *dst, int sock_type)
+{
+ isns_socket_t *sock;
+
+ sock = isns_net_alloc(-1);
+ sock->is_type = sock_type;
+
+ /* Set address lists */
+ isns_sockaddr_init(&sock->is_dst, dst);
+ isns_sockaddr_init(&sock->is_src, src);
+
+ if (dst) {
+ /* This is an outgoing connection. */
+ sock->is_client = 1;
+
+ if (!isns_socket_open(sock))
+ goto failed;
+
+ if (sock_type == SOCK_DGRAM) {
+ sock->is_poll_in = isns_net_dgram_recv;
+ sock->is_poll_out = isns_net_dgram_xmit;
+ sock->is_poll_mask = POLLIN;
+
+ sock->is_retrans_timeout = isns_config.ic_network.udp_retrans_timeout;
+
+ while (isns_net_dgram_connect(sock) < 0) {
+ if (isns_sockaddr_set_next(&sock->is_dst)
+ && sock->is_dst.list != dst)
+ continue;
+ isns_error("Unable to connect: %m\n");
+ goto failed;
+ }
+ } else {
+ /* Stream socket */
+ sock->is_poll_in = isns_net_stream_recv;
+ sock->is_poll_out = isns_net_stream_xmit;
+ sock->is_poll_hup = isns_net_stream_hup;
+ sock->is_error = isns_net_stream_error;
+ sock->is_poll_mask = POLLHUP;
+
+ sock->is_retrans_timeout = isns_config.ic_network.tcp_retrans_timeout;
+
+ isns_net_stream_reconnect(sock);
+ }
+ } else {
+ if (!isns_socket_open(sock))
+ goto failed;
+
+ if (sock_type == SOCK_DGRAM) {
+ sock->is_poll_in = isns_net_dgram_recv;
+ sock->is_poll_out = isns_net_dgram_xmit;
+ sock->is_state = ISNS_SOCK_IDLE;
+ } else {
+ sock->is_poll_in = isns_net_stream_accept;
+ sock->is_error = isns_net_stream_error;
+ sock->is_state = ISNS_SOCK_LISTENING;
+ }
+ sock->is_poll_mask = POLLIN;
+ }
+
+ isns_list_append(&all_sockets, &sock->is_list);
+ return sock;
+
+failed:
+ isns_socket_free(sock);
+ return NULL;
+}
+
+/*
+ * Connect to the master process
+ */
+isns_socket_t *
+isns_create_bound_client_socket(const char *src_spec, const char *dst_spec,
+ const char *portspec, int af_hint, int sock_type)
+{
+ struct addrinfo *src = NULL, *dst;
+
+ if (src_spec) {
+ src = isns_get_address_list(src_spec, NULL, af_hint, sock_type, 0);
+ if (src == NULL)
+ return NULL;
+ }
+
+ dst = isns_get_address_list(dst_spec, portspec, af_hint, sock_type, 0);
+ if (dst == NULL) {
+ release_addrinfo(src);
+ return NULL;
+ }
+
+ return __isns_create_socket(src, dst, sock_type);
+}
+
+isns_socket_t *
+isns_create_client_socket(const char *dst_spec, const char *portspec, int af_hint, int sock_type)
+{
+ return isns_create_bound_client_socket(NULL, dst_spec, portspec, af_hint, sock_type);
+}
+
+static inline int
+isns_socket_type_from_portal(const isns_portal_info_t *info)
+{
+ switch (info->proto) {
+ case IPPROTO_TCP:
+ return SOCK_STREAM;
+ case IPPROTO_UDP:
+ return SOCK_DGRAM;
+ default:
+ isns_error("Unknown protocol %d in portal\n", info->proto);
+ }
+ return -1;
+}
+
+isns_socket_t *
+isns_connect_to_portal(const isns_portal_info_t *info)
+{
+ struct sockaddr_storage dst_addr;
+ struct addrinfo *ai;
+ int dst_alen, sock_type;
+
+ if ((sock_type = isns_socket_type_from_portal(info)) < 0)
+ return NULL;
+
+ dst_alen = isns_portal_to_sockaddr(info, &dst_addr);
+ ai = __make_addrinfo((struct sockaddr *) &dst_addr, dst_alen, sock_type);
+
+ return __isns_create_socket(NULL, ai, sock_type);
+}
+
+/*
+ * Make server side disconnects isns_fatal.
+ * Nice for command line apps.
+ */
+void
+isns_socket_set_disconnect_fatal(isns_socket_t *sock)
+{
+ sock->is_disconnect_fatal = 1;
+}
+
+/*
+ * Set the socket's security context
+ */
+void
+isns_socket_set_security_ctx(isns_socket_t *sock,
+ isns_security_t *ctx)
+{
+ sock->is_security = ctx;
+}
+
+/*
+ * Create a socket
+ */
+static isns_socket_t *
+isns_net_alloc(int fd)
+{
+ isns_socket_t *new;
+
+ new = isns_calloc(1, sizeof(*new));
+ new->is_desc = fd;
+ if (fd >= 0)
+ new->is_state = ISNS_SOCK_IDLE;
+ else
+ new->is_state = ISNS_SOCK_DISCONNECTED;
+
+ isns_message_queue_init(&new->is_partial);
+ isns_message_queue_init(&new->is_complete);
+ isns_message_queue_init(&new->is_pending);
+ isns_list_init(&new->is_list);
+
+ return new;
+}
+
+/*
+ * Open the socket
+ */
+static int
+isns_socket_open(isns_socket_t *sock)
+{
+ int af, fd, state = ISNS_SOCK_IDLE;
+
+ if (sock->is_desc >= 0)
+ return 1;
+
+ af = sock->is_dst.addr.ss_family;
+ if (af != AF_UNSPEC) {
+ /* Select a matching source address */
+ if (sock->is_src.list
+ && !isns_sockaddr_select(&sock->is_src, &sock->is_dst.addr)) {
+ isns_warning("No matching source address for given destination\n");
+ return 0;
+ }
+ } else {
+ af = sock->is_src.addr.ss_family;
+ if (af == AF_UNSPEC)
+ return 0;
+ }
+
+ if ((fd = socket(af, sock->is_type, 0)) < 0) {
+ isns_error("Unable to create socket: %m\n");
+ return 0;
+ }
+
+ if (sock->is_src.addr.ss_family != AF_UNSPEC) {
+ const struct sockaddr *src_addr;
+ int src_len, on = 1, bound = 0;
+
+ src_addr = (struct sockaddr *) &sock->is_src.addr;
+ src_len = sock->is_src.addrlen;
+
+ /* For debugging only! */
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+ isns_error("setsockopt(SO_REUSEADDR) failed: %m\n");
+ goto failed;
+ }
+
+ switch (af) {
+ case AF_LOCAL:
+ unlink(((struct sockaddr_un *) src_addr)->sun_path);
+
+ if (sock->is_type == SOCK_STREAM
+ && setsockopt(fd, SOL_SOCKET, SO_PASSCRED,
+ &on, sizeof(on)) < 0) {
+ isns_error("setsockopt(SO_PASSCRED) failed: %m\n");
+ goto failed;
+ }
+ break;
+
+ case AF_INET:
+ case AF_INET6:
+ if (isns_addr_get_port(src_addr) == 0) {
+ if (!__isns_socket_bind_random(fd, src_addr, src_len))
+ goto failed;
+ bound++;
+ }
+ break;
+ }
+
+ if (!bound && bind(fd, src_addr, src_len) < 0) {
+ isns_error("Unable to bind socket: %m\n");
+ goto failed;
+ }
+ }
+
+ if (sock->is_client) {
+ /* Set to nonblocking behavior; makes the connect
+ * call return instantly. */
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ } else {
+ if (sock->is_type == SOCK_STREAM) {
+ if (listen(fd, 128) < 0) {
+ isns_error("Unable to listen on socket: %m\n");
+ goto failed;
+ }
+ state = ISNS_SOCK_LISTENING;
+ }
+ }
+
+ sock->is_desc = fd;
+ sock->is_state = state;
+ return 1;
+
+failed:
+ close(fd);
+ return 0;
+}
+
+/*
+ * Destroy a socket
+ */
+static inline void
+isns_socket_destroy(isns_socket_t *sock)
+{
+ isns_sockaddr_destroy(&sock->is_dst);
+ isns_sockaddr_destroy(&sock->is_src);
+ isns_free(sock);
+}
+
+void
+isns_socket_free(isns_socket_t *sock)
+{
+ isns_net_close(sock, ISNS_SOCK_DEAD);
+ isns_list_del(&sock->is_list);
+
+ sock->is_destroy = 1;
+ if (sock->is_users == 0)
+ isns_socket_destroy(sock);
+}
+
+int
+isns_socket_release(isns_socket_t *sock)
+{
+ isns_assert(sock->is_users);
+ sock->is_users -= 1;
+
+ if (sock->is_destroy) {
+ if (!sock->is_users)
+ isns_socket_destroy(sock);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Display a socket
+ */
+#if SOCK_DEBUG_VERBOSE > 0
+static const char *
+isns_socket_state_name(int state)
+{
+ static char xbuf[16];
+
+ switch (state) {
+ case ISNS_SOCK_LISTENING:
+ return "listening";
+ case ISNS_SOCK_CONNECTING:
+ return "connecting";
+ case ISNS_SOCK_IDLE:
+ return "idle";
+ case ISNS_SOCK_FAILED:
+ return "failed";
+ case ISNS_SOCK_DISCONNECTED:
+ return "disconnected";
+ case ISNS_SOCK_DEAD:
+ return "dead";
+ }
+ snprintf(xbuf, sizeof(xbuf), "<%u>", state);
+ return xbuf;
+}
+
+static void
+isns_print_socket(const isns_socket_t *sock)
+{
+ isns_message_t *msg = NULL;
+ char buffer[8192];
+ size_t pos = 0, size = sizeof(buffer);
+
+ snprintf(buffer + pos, size - pos,
+ "socket %p desc %d state %s",
+ sock, sock->is_desc,
+ isns_socket_state_name(sock->is_state));
+ pos = strlen(buffer);
+
+ if (timerisset(&sock->is_deadline)) {
+ snprintf(buffer + pos, size - pos, " deadline=%ldms",
+ __timeout_millisec(NULL, &sock->is_deadline));
+ pos = strlen(buffer);
+ }
+
+ if ((msg = isns_message_queue_head(&sock->is_pending)) != NULL) {
+ snprintf(buffer + pos, size - pos, " msg timeout=%ldms",
+ __timeout_millisec(NULL, &msg->im_timeout));
+ pos = strlen(buffer);
+ }
+
+ isns_debug_socket("%s\n", buffer);
+}
+#else
+#define isns_print_socket(p) do { } while (0)
+#endif
+
+/*
+ * Process incoming messages, and timeouts
+ */
+static int
+isns_net_validate(isns_socket_t *sock, isns_message_t *msg,
+ const isns_message_t *check_msg)
+{
+ isns_message_t *orig = NULL;
+ int verdict = ISNS_MSG_DISCARD;
+
+ if (sock->is_security && !msg->im_security) {
+ /* Rude server, or malicious man in the
+ * middle. */
+ isns_debug_message("Ignoring unauthenticated message\n");
+ goto out;
+ }
+
+ /* If this is a request, return it. */
+ if (!(msg->im_header.i_function & 0x8000)) {
+ if (check_msg == NULL) {
+ verdict = ISNS_MSG_RETURN;
+ } else {
+ /* Else: see if there's a server attached to this
+ * socket. */
+ }
+ goto out;
+ }
+
+ orig = isns_message_queue_find(&sock->is_pending, msg->im_xid, NULL, 0);
+ if (orig == NULL) {
+ isns_debug_message("Ignoring spurious response message (xid=%04x)\n",
+ msg->im_xid);
+ goto out;
+ }
+
+ isns_message_unlink(orig);
+ if (orig->im_header.i_function != (msg->im_header.i_function & 0x7FFF)) {
+ isns_debug_message("Response message doesn't match function\n");
+ goto out;
+ }
+
+ if (check_msg == orig) {
+ verdict = ISNS_MSG_RETURN;
+ } else {
+ isns_debug_message("Received response for pending message 0x%x\n",
+ msg->im_xid);
+ if (orig->im_callback)
+ orig->im_callback(orig, msg);
+ verdict = ISNS_MSG_DONE;
+ }
+
+out:
+ isns_message_release(orig);
+ return verdict;
+}
+
+static void
+isns_net_timeout(isns_socket_t *sock, isns_message_t *msg)
+{
+ if (msg->im_callback)
+ msg->im_callback(msg, NULL);
+ isns_message_release(msg);
+}
+
+/*
+ * Helper function to update timeout
+ */
+static inline void
+__set_timeout(struct timeval *end, unsigned long timeout)
+{
+ gettimeofday(end, NULL);
+ end->tv_sec += timeout;
+}
+
+static inline int
+__timeout_expired(const struct timeval *now, const struct timeval *expires)
+{
+ /* FIXME: Should ignore sub-millisecond remainder */
+ return timercmp(now, expires, >=);
+}
+
+static long
+__timeout_millisec(const struct timeval *now, const struct timeval *expires)
+{
+ struct timeval __now, delta = { 0, 0 };
+
+ if (now == NULL) {
+ gettimeofday(&__now, NULL);
+ now = &__now;
+ }
+
+ timersub(expires, now, &delta);
+
+ return delta.tv_sec * 1000 + delta.tv_usec / 1000;
+}
+
+static inline void
+__update_timeout(struct timeval *end, const struct timeval *timeout)
+{
+ if (!timerisset(end) || timercmp(timeout, end, <))
+ *end = *timeout;
+}
+
+/*
+ * Get the next iSNS message
+ */
+isns_message_t *
+__isns_recv_message(const struct timeval *end_time, isns_message_t *watch_msg)
+{
+ isns_socket_t *sock, **sock_list;
+ isns_list_t *pos, *next;
+ struct pollfd *pfd;
+ unsigned int i, count, max_sockets;
+ struct timeval now, this_end;
+ int r;
+
+ max_sockets = isns_config.ic_network.max_sockets;
+ sock_list = alloca(max_sockets * sizeof(sock_list[0]));
+ pfd = alloca(max_sockets * sizeof(pfd[0]));
+
+again:
+ timerclear(&this_end);
+ gettimeofday(&now, NULL);
+
+ if (end_time) {
+ if (__timeout_expired(&now, end_time))
+ return NULL;
+ this_end = *end_time;
+ }
+
+ i = 0;
+ isns_list_foreach(&all_sockets, pos, next) {
+ isns_socket_t *sock = isns_list_item(isns_socket_t, is_list, pos);
+ isns_message_t *msg = NULL;
+
+ /* We need to be a little careful here; callbacks may
+ * mark the socket for destruction.
+ * Bumping is_users while we're busy with the socket
+ * prevents mayhem. */
+ sock->is_users++;
+
+ while ((msg = isns_message_dequeue(&sock->is_complete)) != NULL) {
+ switch (isns_net_validate(sock, msg, watch_msg)) {
+ case ISNS_MSG_RETURN:
+ isns_assert(!sock->is_destroy);
+ isns_socket_release(sock);
+ return msg;
+
+ default:
+ isns_message_release(msg);
+ }
+ }
+
+ /* This will return 0 if the socket was marked for
+ * destruction. */
+ if (!isns_socket_release(sock))
+ continue;
+
+ isns_print_socket(sock);
+
+ /* This handles reconnect, idle disconnect etc. */
+ while (timerisset(&sock->is_deadline)) {
+ if (__timeout_expired(&now, &sock->is_deadline)) {
+ timerclear(&sock->is_deadline);
+ sock->is_timeout(sock);
+ isns_print_socket(sock);
+ continue;
+ }
+ __update_timeout(&this_end, &sock->is_deadline);
+ break;
+ }
+
+ /* No more input and output means closed&dead */
+ if (sock->is_state == ISNS_SOCK_IDLE
+ && !(sock->is_poll_mask & (POLLIN|POLLOUT))) {
+ isns_debug_socket("connection closed by peer, killing socket\n");
+ isns_net_close(sock, ISNS_SOCK_FAILED);
+ }
+
+ /* Check whether pending messages have timed out. */
+ while (sock->is_state == ISNS_SOCK_IDLE
+ && (msg = isns_message_queue_head(&sock->is_pending)) != NULL) {
+ if (__timeout_expired(&now, &msg->im_timeout)) {
+ isns_debug_socket("sock %p message %04x timed out\n",
+ sock, msg->im_xid);
+ isns_message_unlink(msg);
+ if (msg == watch_msg) {
+ isns_message_release(msg);
+ return NULL;
+ }
+ isns_net_timeout(sock, msg);
+ continue;
+ }
+
+ if (!__timeout_expired(&now, &msg->im_resend_timeout)) {
+ __update_timeout(&this_end,
+ &msg->im_resend_timeout);
+ /* In odd configurations, the call_timeout
+ * may be lower than the resend_timeout */
+ __update_timeout(&this_end,
+ &msg->im_timeout);
+ break;
+ }
+
+ isns_debug_socket("sock %p message %04x - "
+ "minor timeout, resending.\n",
+ sock, msg->im_xid);
+
+ /* If a TCP socket times out, something is
+ * fishy. Force a reconnect, which will resend
+ * all pending messages. */
+ if (sock->is_type == SOCK_STREAM) {
+ isns_net_close(sock, ISNS_SOCK_DISCONNECTED);
+ isns_net_set_timeout(sock,
+ isns_net_stream_reconnect,
+ 0);
+ break;
+ }
+
+ /* UDP socket - retransmit this one message */
+ isns_message_queue_remove(&sock->is_pending, msg);
+ isns_socket_queue_message(sock, msg);
+ isns_message_release(msg);
+ }
+
+ /*
+ * If the socket on which we're waiting right
+ * now got disconnected, or had any other kind of
+ * error, return right away to let the caller know.
+ */
+ if (sock->is_state == ISNS_SOCK_FAILED) {
+ if (sock->is_disconnect_fatal)
+ goto kill_socket;
+ if (sock->is_report_failure)
+ return NULL;
+ sock->is_state = ISNS_SOCK_DISCONNECTED;
+ continue;
+ }
+
+ if (sock->is_state == ISNS_SOCK_DEAD) {
+kill_socket:
+ isns_list_del(&sock->is_list);
+ if (sock->is_report_failure)
+ return NULL;
+ if (!sock->is_client)
+ isns_socket_free(sock);
+ continue;
+ }
+
+ /* should not happen */
+ if (i >= max_sockets)
+ break;
+
+ pfd[i].fd = sock->is_desc;
+ pfd[i].events = sock->is_poll_mask;
+ sock_list[i] = sock;
+ i++;
+ }
+ count = i;
+
+ if (timerisset(&this_end)) {
+ long millisec;
+
+ /* timeval arithmetic can yield sub-millisecond timeouts.
+ * Round up to prevent looping. */
+ millisec = __timeout_millisec(&now, &this_end);
+ if (millisec == 0)
+ millisec += 1;
+
+ debug_verbose2("poll(%p, %u, %d)\n", pfd, count, millisec);
+ r = poll(pfd, count, millisec);
+ } else {
+ r = poll(pfd, count, -1);
+ }
+
+ if (r < 0) {
+ if (errno != EINTR)
+ isns_error("poll returned error: %m\n");
+ return NULL;
+ }
+
+ /* Any new incoming connections will be added to the
+ * head of the list. */
+ for (i = 0; i < count; ++i) {
+ sock = sock_list[i];
+ if (pfd[i].revents & POLLIN)
+ sock->is_poll_in(sock);
+ if (pfd[i].revents & POLLOUT)
+ sock->is_poll_out(sock);
+ if (pfd[i].revents & POLLHUP)
+ sock->is_poll_hup(sock);
+ }
+
+ goto again;
+}
+
+isns_message_t *
+isns_recv_message(struct timeval *timeout)
+{
+ isns_message_t *msg;
+ struct timeval end;
+
+ if (timeout == NULL)
+ return __isns_recv_message(NULL, NULL);
+
+ gettimeofday(&end, NULL);
+ timeradd(&end, timeout, &end);
+ msg = __isns_recv_message(&end, NULL);
+
+ if (msg == NULL)
+ return msg;
+ isns_debug_socket("Next message xid=%04x\n", msg->im_xid);
+ if (msg->im_security) {
+ isns_debug_message("Received authenticated message from \"%s\"\n",
+ isns_principal_name(msg->im_security));
+ } else if (isns_config.ic_security) {
+ isns_debug_message("Received unauthenticated message\n");
+ } else {
+ isns_debug_message("Received message\n");
+ }
+ return msg;
+}
+
+int
+isns_socket_send(isns_socket_t *sock, isns_message_t *msg)
+{
+ struct isns_hdr *hdr;
+ size_t pdu_len;
+ buf_t *bp;
+
+ /* If the socket is disconnected, and the
+ * reconnect timeout is not set, force a
+ * reconnect right away. */
+ if (sock->is_state == ISNS_SOCK_DISCONNECTED
+ && !timerisset(&sock->is_deadline)) {
+ isns_net_set_timeout(sock,
+ isns_net_stream_reconnect, 0);
+ }
+
+ if (!(bp = msg->im_payload))
+ return 0;
+
+ pdu_len = buf_avail(bp);
+ if (pdu_len < sizeof(*hdr))
+ return 0;
+
+ /* Pad PDU to multiple of 4 bytes, if needed */
+ if (pdu_len & 3) {
+ unsigned int pad = 4 - (pdu_len & 3);
+
+ if (!buf_put(bp, "\0\0\0", pad))
+ return 0;
+ pdu_len += pad;
+ }
+
+ if (!(bp = buf_dup(bp)))
+ return 0;
+
+ hdr = buf_head(bp);
+
+ hdr->i_version = htons(msg->im_header.i_version);
+ hdr->i_function = htons(msg->im_header.i_function);
+ hdr->i_flags = htons(msg->im_header.i_flags);
+ hdr->i_length = htons(pdu_len - sizeof(*hdr));
+ hdr->i_xid = htons(msg->im_header.i_xid);
+ hdr->i_seq = htons(msg->im_header.i_seq);
+
+ /* For now, we deal with unfragmented messages only. */
+ hdr->i_flags |= htons(ISNS_F_FIRST_PDU|ISNS_F_LAST_PDU);
+
+ if (sock->is_security) {
+#ifdef WITH_SECURITY
+ hdr->i_flags |= htons(ISNS_F_AUTHBLK_PRESENT);
+ if (!isns_pdu_seal(sock->is_security, bp)) {
+ isns_debug_message("Error adding auth block to outgoing PDU\n");
+ goto error;
+ }
+#else
+ isns_debug_message("%s: Authentication not supported\n",
+ __FUNCTION__);
+ goto error;
+#endif
+ }
+
+ bp->addr = msg->im_addr;
+ bp->addrlen = msg->im_addrlen;
+
+ buf_list_append(&sock->is_xmit_buf, bp);
+ sock->is_poll_mask |= POLLOUT;
+
+ /* Set the retransmit timeout */
+ __set_timeout(&msg->im_resend_timeout, sock->is_retrans_timeout);
+ return 1;
+
+error:
+ buf_free(bp);
+ return 0;
+}
+
+/*
+ * Queue a message to a socket
+ */
+int
+isns_socket_queue_message(isns_socket_t *sock, isns_message_t *msg)
+{
+ if (!isns_socket_send(sock, msg))
+ return 0;
+
+ /* Insert sorted by timeout. For now, this amounts to
+ * appending at the end of the list, but that may change
+ * if we implement exponential backoff for UDP */
+ isns_message_queue_insert_sorted(&sock->is_pending,
+ ISNS_MQ_SORT_RESEND_TIMEOUT, msg);
+ msg->im_socket = sock;
+ return 1;
+}
+
+/*
+ * Retransmit any queued messages
+ */
+int
+isns_socket_retransmit_queued(isns_socket_t *sock)
+{
+ isns_message_t *msg;
+ isns_list_t *pos;
+
+ isns_debug_socket("%s(%p)\n", __FUNCTION__, sock);
+ isns_message_queue_foreach(&sock->is_pending, pos, msg) {
+ if (!isns_socket_send(sock, msg))
+ isns_warning("Unable to retransmit message\n");
+ }
+ return 1;
+}
+
+/*
+ * Submit a message to the socket, for asynchronous calls
+ */
+int
+isns_socket_submit(isns_socket_t *sock, isns_message_t *msg, long timeout)
+{
+ if (timeout <= 0)
+ timeout = isns_config.ic_network.call_timeout;
+
+ __set_timeout(&msg->im_timeout, timeout);
+ return isns_socket_queue_message(sock, msg);
+}
+
+/*
+ * Transmit a message and wait for a response.
+ */
+isns_message_t *
+isns_socket_call(isns_socket_t *sock, isns_message_t *msg, long timeout)
+{
+ isns_message_t *resp;
+
+ debug_verbose("isns_socket_call(sock=%p, msg=%p, timeout=%ld)\n",
+ sock, msg, timeout);
+ if (timeout <= 0)
+ timeout = isns_config.ic_network.call_timeout;
+
+ __set_timeout(&msg->im_timeout, timeout);
+ if (!isns_socket_queue_message(sock, msg))
+ return NULL;
+
+ sock->is_report_failure = 1;
+ resp = __isns_recv_message(NULL, msg);
+ sock->is_report_failure = 0;
+
+ if (isns_message_unlink(msg)) {
+ /* We can get here if __isns_recv_message returned
+ * due to a fatal socket error. */
+ isns_debug_socket("%s: msg not unlinked!\n", __FUNCTION__);
+ isns_message_release(msg);
+ }
+
+ if (resp == NULL && sock->is_type == SOCK_STREAM)
+ isns_net_close(sock, ISNS_SOCK_DISCONNECTED);
+
+ return resp;
+}
+
+/*
+ * Resolve a hostname
+ */
+struct addrinfo *
+isns_get_address_list(const char *addrspec, const char *port,
+ int af_hint, int sock_type, int flags)
+{
+ struct addrinfo hints, *found = NULL, *res = NULL;
+ char *copy = NULL, *host = NULL, *s;
+ int rv;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ADDRCONFIG;
+
+ if (addrspec && addrspec[0] == '/') {
+ if (af_hint != AF_LOCAL && af_hint != AF_UNSPEC) {
+ isns_debug_socket("Path as address, but af_hint=%d\n",
+ af_hint);
+ goto bad_address;
+ }
+
+ res = make_addrinfo_unix(addrspec, SOCK_STREAM);
+ goto out;
+ }
+
+ if (addrspec) {
+ copy = host = isns_strdup(addrspec);
+ if (*host == '[') {
+ hints.ai_flags |= AI_NUMERICHOST;
+ if ((s = strchr(host, ']')) == NULL)
+ goto bad_address;
+
+ *s++ = '\0';
+ if (*s == ':')
+ port = ++s;
+ else if (*s)
+ goto bad_address;
+ } else if ((s = strchr(host, ':')) != NULL) {
+ *s++ = '\0';
+ if (!*s)
+ goto bad_address;
+ port = s;
+ }
+
+ if (*host == '\0')
+ host = NULL;
+ } else if (port == NULL) {
+ /* Just wildcard */
+ res = make_addrinfo_any(af_hint, sock_type);
+ goto out;
+ }
+
+ hints.ai_family = af_hint;
+ hints.ai_flags |= flags;
+ hints.ai_socktype = sock_type;
+ if (af_hint == AF_INET6)
+ hints.ai_flags |= AI_V4MAPPED;
+
+ rv = getaddrinfo(host, port, &hints, &found);
+ if (rv) {
+ isns_error("Cannot resolve address \"%s\": %s\n",
+ addrspec, gai_strerror(rv));
+ goto out;
+ }
+
+ if (found == NULL) {
+ isns_error("No useable addresses returned.\n");
+ goto out;
+ }
+
+ res = clone_addrinfo(found);
+
+out:
+ if (found)
+ freeaddrinfo(found);
+ isns_free(copy);
+ return res;
+
+bad_address:
+ isns_error("Cannot parse address spec \"%s\"\n",
+ addrspec);
+ goto out;
+}
+
+int
+isns_get_address(struct sockaddr_storage *result,
+ const char *addrspec,
+ const char *port,
+ int af_hint, int sock_type, int flags)
+{
+ struct addrinfo *ai;
+ int alen;
+
+ if (!(ai = isns_get_address_list(addrspec, port, af_hint, sock_type, flags)))
+ return -1;
+
+ alen = ai->ai_addrlen;
+ if (alen > sizeof(*result))
+ return -1;
+ memcpy(result, ai->ai_addr, alen);
+ release_addrinfo(ai);
+ return alen;
+}
+
+/*
+ * Get the canonical hostname
+ */
+char *
+isns_get_canon_name(const char *hostname)
+{
+ struct addrinfo hints, *res = NULL;
+ char *fqdn = NULL;
+ int rv;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+
+ rv = getaddrinfo(hostname, NULL, &hints, &res);
+ if (rv) {
+ isns_error("Cannot resolve hostname \"%s\": %s\n",
+ hostname, gai_strerror(rv));
+ goto out;
+ }
+
+ if (res == NULL) {
+ isns_error("No useable addresses returned.\n");
+ goto out;
+ }
+
+
+ fqdn = isns_strdup(res->ai_canonname);
+
+out:
+ if (res)
+ freeaddrinfo(res);
+ return fqdn;
+}
+
+int
+isns_socket_get_local_addr(const isns_socket_t *sock,
+ struct sockaddr_storage *addr)
+{
+ socklen_t alen;
+
+ if (sock->is_desc < 0)
+ return 0;
+ if (getsockname(sock->is_desc,
+ (struct sockaddr *) addr, &alen) < 0) {
+ isns_error("getsockname: %m\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+isns_socket_get_portal_info(const isns_socket_t *sock,
+ isns_portal_info_t *portal)
+{
+ struct sockaddr_storage addr;
+ socklen_t alen;
+ int fd, success = 0;
+
+ memset(portal, 0, sizeof(*portal));
+
+ /* If the socket is currently closed (eg because the
+ * server shut down the connection), we cannot get the
+ * local address easily. Create a temporary UDP socket,
+ * connect it, and query that socket. */
+ if ((fd = sock->is_desc) < 0) {
+ const struct sockaddr *daddr;
+
+ daddr = (struct sockaddr *) &sock->is_dst.addr;
+ fd = socket(daddr->sa_family, SOCK_DGRAM, 0);
+ if (fd < 0)
+ goto out;
+ if (connect(fd, daddr, sizeof(sock->is_dst.addr)) < 0)
+ goto out;
+ }
+
+ alen = sizeof(addr);
+ if (getsockname(fd, (struct sockaddr *) &addr, &alen) < 0) {
+ isns_error("getsockname: %m\n");
+ goto out;
+ }
+
+ if (!isns_portal_from_sockaddr(portal, &addr))
+ goto out;
+ if (sock->is_type == SOCK_STREAM)
+ portal->proto = IPPROTO_TCP;
+ else
+ portal->proto = IPPROTO_UDP;
+
+ debug_verbose("socket_get_portal: %s\n", isns_portal_string(portal));
+ success = 1;
+
+out:
+ /* If we used a temp UDP socket, close it */
+ if (fd >= 0 && fd != sock->is_desc)
+ close(fd);
+ return success;
+}
+
+isns_socket_t *
+isns_socket_find_server(const isns_portal_info_t *portal)
+{
+ struct sockaddr_storage bound_addr;
+ int sock_type, addr_len;
+ isns_list_t *pos, *next;
+
+ addr_len = isns_portal_to_sockaddr(portal, &bound_addr);
+ if ((sock_type = isns_socket_type_from_portal(portal)) < 0)
+ return NULL;
+
+ isns_list_foreach(&all_sockets, pos, next) {
+ isns_socket_t *sock = isns_list_item(isns_socket_t, is_list, pos);
+
+ if (!sock->is_client
+ && sock->is_type == sock_type
+ && sock->is_dst.addrlen == addr_len
+ && !memcmp(&sock->is_dst.addr, &bound_addr, addr_len)) {
+ sock->is_users++;
+ return sock;
+ }
+ }
+
+ return NULL;
+}
+
+int
+isns_addr_get_port(const struct sockaddr *addr)
+{
+ const struct sockaddr_in *sin;
+ const struct sockaddr_in6 *six;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ sin = (const struct sockaddr_in *) addr;
+ return ntohs(sin->sin_port);
+
+ case AF_INET6:
+ six = (const struct sockaddr_in6 *) addr;
+ return ntohs(six->sin6_port);
+ }
+ return 0;
+}
+
+void
+isns_addr_set_port(struct sockaddr *addr, unsigned int port)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *six;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *) addr;
+ sin->sin_port = htons(port);
+ break;
+
+ case AF_INET6:
+ six = (struct sockaddr_in6 *) addr;
+ six->sin6_port = htons(port);
+ break;
+ }
+}
diff --git a/utils/open-isns/socket.h b/utils/open-isns/socket.h
new file mode 100644
index 0000000..cc63d23
--- /dev/null
+++ b/utils/open-isns/socket.h
@@ -0,0 +1,95 @@
+/*
+ * iSNS network code
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_SOCKET_H
+#define ISNS_SOCKET_H
+
+#include "isns.h"
+#include "buffer.h"
+#include "message.h"
+
+struct isns_partial_msg {
+ isns_message_t imp_base;
+ uint32_t imp_flags;
+ uint32_t imp_first_seq;
+ uint32_t imp_last_seq;
+ unsigned int imp_pdu_count;
+ unsigned int imp_msg_size;
+ buf_t * imp_chain;
+
+ struct ucred imp_credbuf;
+};
+
+#define imp_users imp_base.im_users
+#define imp_list imp_base.im_list
+#define imp_xid imp_base.im_xid
+#define imp_header imp_base.im_header
+#define imp_addr imp_base.im_addr
+#define imp_addrlen imp_base.im_addrlen
+#define imp_header imp_base.im_header
+#define imp_payload imp_base.im_payload
+#define imp_security imp_base.im_security
+#define imp_creds imp_base.im_creds
+
+enum {
+ ISNS_SOCK_LISTENING,
+ ISNS_SOCK_CONNECTING,
+ ISNS_SOCK_IDLE,
+ ISNS_SOCK_FAILED,
+ ISNS_SOCK_DISCONNECTED,
+ ISNS_SOCK_DEAD,
+};
+
+/* Helper class */
+struct __isns_socket_addr {
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ struct addrinfo * list;
+};
+
+struct isns_socket {
+ isns_list_t is_list;
+ int is_desc;
+ int is_type;
+ unsigned int is_client : 1,
+ is_autoclose : 1,
+ is_disconnect_fatal : 1,
+ is_report_failure : 1,
+ is_destroy : 1;
+ unsigned int is_users;
+ int is_poll_mask;
+ int is_state;
+
+ isns_security_t * is_security;
+
+ struct __isns_socket_addr is_src, is_dst;
+
+ unsigned int is_retrans_timeout;
+
+ /* If we're past this time, is_timeout() is called. */
+ struct timeval is_deadline;
+
+ buf_t * is_recv_buf;
+ buf_t * is_xmit_buf;
+
+ size_t is_queue_size;
+ isns_message_queue_t is_partial;
+ isns_message_queue_t is_complete;
+ isns_message_queue_t is_pending;
+
+ void (*is_poll_in)(isns_socket_t *);
+ void (*is_poll_out)(isns_socket_t *);
+ void (*is_poll_hup)(isns_socket_t *);
+ void (*is_poll_err)(isns_socket_t *);
+ void (*is_timeout)(isns_socket_t *);
+ void (*is_error)(isns_socket_t *, int);
+};
+
+extern int isns_socket_submit(isns_socket_t *,
+ isns_message_t *,
+ long);
+
+#endif /* ISNS_SOCKET_H */
diff --git a/utils/open-isns/source.h b/utils/open-isns/source.h
new file mode 100644
index 0000000..59fb662
--- /dev/null
+++ b/utils/open-isns/source.h
@@ -0,0 +1,32 @@
+/*
+ * iSNS source attribute handling
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_SOURCE_H
+#define ISNS_SOURCE_H
+
+#include "attrs.h"
+
+struct isns_source {
+ unsigned int is_users;
+ isns_attr_t * is_attr;
+ unsigned int is_untrusted : 1;
+
+ isns_object_t * is_node;
+ unsigned int is_node_type;
+
+ isns_object_t * is_entity;
+};
+
+extern int isns_source_encode(buf_t *, const isns_source_t *);
+extern int isns_source_decode(buf_t *, isns_source_t **);
+extern int isns_source_set_node(isns_source_t *, isns_db_t *);
+extern void isns_source_set_entity(isns_source_t *, isns_object_t *);
+extern isns_source_t * isns_source_dummy(void);
+
+extern char * isns_build_source_pattern(const char *);
+extern int isns_source_pattern_match(const char *, const char *);
+
+#endif /* ISNS_SOURCE_H */
diff --git a/utils/open-isns/storage-node.c b/utils/open-isns/storage-node.c
new file mode 100644
index 0000000..97e54d1
--- /dev/null
+++ b/utils/open-isns/storage-node.c
@@ -0,0 +1,202 @@
+/*
+ * iSNS object model - storage node
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "isns.h"
+#include "objects.h"
+#include "util.h"
+
+isns_object_t *
+isns_create_storage_node(const char *name, uint32_t type,
+ isns_object_t *parent)
+{
+ isns_object_t *obj;
+
+ if (parent && !ISNS_IS_ENTITY(parent)) {
+ isns_warning("Invalid container type \"%s\" for storage node: "
+ "should be \"%s\"\n",
+ parent->ie_template->iot_name,
+ isns_entity_template.iot_name);
+ return NULL;
+ }
+
+ obj = isns_create_object(&isns_iscsi_node_template, NULL, parent);
+ isns_object_set_string(obj,
+ ISNS_TAG_ISCSI_NAME, name);
+ isns_object_set_uint32(obj,
+ ISNS_TAG_ISCSI_NODE_TYPE, type);
+
+ return obj;
+}
+
+isns_object_t *
+isns_create_storage_node2(const isns_source_t *source,
+ uint32_t type,
+ isns_object_t *parent)
+{
+ isns_attr_t *name_attr;
+ isns_object_t *obj;
+
+ if (parent && !ISNS_IS_ENTITY(parent)) {
+ isns_warning("Invalid container type \"%s\" for storage node: "
+ "should be \"%s\"\n",
+ parent->ie_template->iot_name,
+ isns_entity_template.iot_name);
+ return NULL;
+ }
+ if ((name_attr = isns_source_attr(source)) == NULL) {
+ isns_warning("No source attribute\n");
+ return NULL;
+ }
+
+ if (name_attr->ia_tag_id == ISNS_TAG_ISCSI_NAME) {
+ obj = isns_create_object(&isns_iscsi_node_template, NULL, parent);
+ isns_attr_list_update_attr(&obj->ie_attrs, name_attr);
+ isns_object_set_uint32(obj,
+ ISNS_TAG_ISCSI_NODE_TYPE, type);
+ } else {
+ /* No iFCP yet, sorry */
+ isns_warning("%s: source tag type %u not supported\n",
+ __FUNCTION__);
+ return NULL;
+ }
+
+ return obj;
+}
+
+isns_object_t *
+isns_create_iscsi_initiator(const char *name,
+ isns_object_t *parent)
+{
+ return isns_create_storage_node(name,
+ 1 << ISNS_ISCSI_NODE_TYPE_INITIATOR,
+ parent);
+}
+
+isns_object_t *
+isns_create_iscsi_target(const char *name,
+ isns_object_t *parent)
+{
+ return isns_create_storage_node(name,
+ 1 << ISNS_ISCSI_NODE_TYPE_TARGET,
+ parent);
+}
+
+const char *
+isns_storage_node_name(const isns_object_t *node)
+{
+ const isns_attr_t *attr;
+
+ if (node->ie_attrs.ial_count == 0)
+ return NULL;
+ attr = node->ie_attrs.ial_data[0];
+ if (attr->ia_value.iv_type != &isns_attr_type_string)
+ return NULL;
+
+ switch (attr->ia_tag_id) {
+ case ISNS_TAG_ISCSI_NAME:
+ case ISNS_TAG_FC_PORT_NAME_WWPN:
+ return attr->ia_value.iv_string;
+ }
+
+ return 0;
+
+}
+
+isns_attr_t *
+isns_storage_node_key_attr(const isns_object_t *node)
+{
+ if (node->ie_attrs.ial_count == 0)
+ return NULL;
+ return node->ie_attrs.ial_data[0];
+}
+
+static uint32_t iscsi_node_attrs[] = {
+ ISNS_TAG_ISCSI_NAME,
+ ISNS_TAG_ISCSI_NODE_TYPE,
+ ISNS_TAG_ISCSI_ALIAS,
+ ISNS_TAG_ISCSI_SCN_BITMAP,
+ ISNS_TAG_ISCSI_NODE_INDEX,
+ ISNS_TAG_WWNN_TOKEN,
+ ISNS_TAG_ISCSI_AUTHMETHOD,
+ /* RFC 4171 lists a "iSCSI node certificate"
+ * as an option attribute of an iSCSI
+ * storage node, but doesn't define it anywhere
+ * in the spec.
+ */
+};
+
+static uint32_t iscsi_node_key_attrs[] = {
+ ISNS_TAG_ISCSI_NAME,
+};
+
+isns_object_template_t isns_iscsi_node_template = {
+ .iot_name = "iSCSI Storage Node",
+ .iot_handle = ISNS_OBJECT_TYPE_NODE,
+ .iot_attrs = iscsi_node_attrs,
+ .iot_num_attrs = array_num_elements(iscsi_node_attrs),
+ .iot_keys = iscsi_node_key_attrs,
+ .iot_num_keys = array_num_elements(iscsi_node_key_attrs),
+ .iot_index = ISNS_TAG_ISCSI_NODE_INDEX,
+ .iot_next_index = ISNS_TAG_ISCSI_NODE_NEXT_INDEX,
+ .iot_container = &isns_entity_template,
+};
+
+static uint32_t fc_port_attrs[] = {
+ ISNS_TAG_FC_PORT_NAME_WWPN,
+ ISNS_TAG_PORT_ID,
+ ISNS_TAG_FC_PORT_TYPE,
+ ISNS_TAG_SYMBOLIC_PORT_NAME,
+ ISNS_TAG_FABRIC_PORT_NAME,
+ ISNS_TAG_HARD_ADDRESS,
+ ISNS_TAG_PORT_IP_ADDRESS,
+ ISNS_TAG_CLASS_OF_SERVICE,
+ ISNS_TAG_FC4_TYPES,
+ ISNS_TAG_FC4_DESCRIPTOR,
+ ISNS_TAG_FC4_FEATURES,
+ ISNS_TAG_IFCP_SCN_BITMAP,
+ ISNS_TAG_PORT_ROLE,
+ ISNS_TAG_PERMANENT_PORT_NAME,
+};
+
+static uint32_t fc_port_key_attrs[] = {
+ ISNS_TAG_FC_PORT_NAME_WWPN,
+};
+
+isns_object_template_t isns_fc_port_template = {
+ .iot_name = "iFCP Port",
+ .iot_handle = ISNS_OBJECT_TYPE_FC_PORT,
+ .iot_attrs = fc_port_attrs,
+ .iot_num_attrs = array_num_elements(fc_port_attrs),
+ .iot_keys = fc_port_key_attrs,
+ .iot_num_keys = array_num_elements(fc_port_key_attrs),
+ .iot_container = &isns_entity_template,
+};
+
+static uint32_t fc_node_attrs[] = {
+ ISNS_TAG_FC_NODE_NAME_WWNN,
+ ISNS_TAG_SYMBOLIC_NODE_NAME,
+ ISNS_TAG_NODE_IP_ADDRESS,
+ ISNS_TAG_NODE_IPA,
+ ISNS_TAG_PROXY_ISCSI_NAME,
+};
+
+static uint32_t fc_node_key_attrs[] = {
+ ISNS_TAG_FC_NODE_NAME_WWNN,
+};
+
+isns_object_template_t isns_fc_node_template = {
+ .iot_name = "iFCP Device Node",
+ .iot_handle = ISNS_OBJECT_TYPE_FC_NODE,
+ .iot_attrs = fc_node_attrs,
+ .iot_num_attrs = array_num_elements(fc_node_attrs),
+ .iot_keys = fc_node_key_attrs,
+ .iot_num_keys = array_num_elements(fc_node_key_attrs),
+ .iot_container = &isns_fc_port_template,
+};
+
diff --git a/utils/open-isns/sysdep-unix.c b/utils/open-isns/sysdep-unix.c
new file mode 100644
index 0000000..8c601a7
--- /dev/null
+++ b/utils/open-isns/sysdep-unix.c
@@ -0,0 +1,132 @@
+/*
+ * System dependent stuff
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <unistd.h>
+#include "isns.h"
+#include "util.h"
+
+int
+isns_enumerate_portals(isns_portal_info_t *result, unsigned int max)
+{
+ char buffer[8192], *end, *ptr;
+ struct ifconf ifc;
+ unsigned int nportals = 0;
+ int fd = -1;
+
+ if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ isns_error("%s: no socket - %m\n", __FUNCTION__);
+ return 0;
+ }
+
+ ifc.ifc_buf = buffer;
+ ifc.ifc_len = sizeof(buffer);
+ if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
+ isns_error("ioctl(SIOCGIFCONF): %m\n");
+ goto out;
+ }
+
+ ptr = buffer;
+ end = buffer + ifc.ifc_len;
+ while (ptr < end) {
+ struct ifreq ifr;
+ struct sockaddr_storage ifaddr;
+ isns_portal_info_t portal;
+ int ifflags;
+
+ memcpy(&ifr, ptr, sizeof(ifr));
+ ptr += sizeof(ifr);
+
+ /* Get the interface addr */
+ memcpy(&ifaddr, &ifr.ifr_addr, sizeof(ifr.ifr_addr));
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+ isns_error("ioctl(%s, SIOCGIFFLAGS): %m\n",
+ ifr.ifr_name);
+ continue;
+ }
+ ifflags = ifr.ifr_flags;
+
+ if ((ifflags & IFF_UP) == 0)
+ continue;
+ if ((ifflags & IFF_LOOPBACK) != 0)
+ continue;
+
+ if (!isns_portal_from_sockaddr(&portal, &ifaddr))
+ continue;
+
+ isns_debug_socket("Got interface %u: %s %s\n",
+ nportals, ifr.ifr_name,
+ isns_portal_string(&portal));
+ if (nportals < max)
+ result[nportals++] = portal;
+ }
+
+out:
+ if (fd >= 0)
+ close(fd);
+ return nportals;
+}
+
+int
+isns_portal_from_sockaddr(isns_portal_info_t *portal,
+ const struct sockaddr_storage *addr)
+{
+ struct sockaddr_in6 *six;
+ struct sockaddr_in *sin;
+
+ memset(portal, 0, sizeof(*portal));
+
+ /* May have to convert AF_INET to AF_INET6 */
+ six = &portal->addr;
+ switch (addr->ss_family) {
+ case AF_INET6:
+ memcpy(six, addr, sizeof(*six));
+ break;
+
+ case AF_INET:
+ sin = (struct sockaddr_in *) addr;
+ six->sin6_family = AF_INET6;
+ six->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
+ six->sin6_port = sin->sin_port;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+isns_portal_to_sockaddr(const isns_portal_info_t *portal,
+ struct sockaddr_storage *addr)
+{
+ const struct sockaddr_in6 *six = &portal->addr;
+ struct sockaddr_in *sin;
+
+ /* Check if this is really a v4 address is disguise.
+ * If so, explicitly use an AF_INET socket - the
+ * stack may not support IPv6.
+ */
+ if (IN6_IS_ADDR_V4MAPPED(&six->sin6_addr)
+ || IN6_IS_ADDR_V4COMPAT(&six->sin6_addr)) {
+ sin = (struct sockaddr_in *) addr;
+
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = six->sin6_addr.s6_addr32[3];
+ sin->sin_port = six->sin6_port;
+
+ return sizeof(*sin);
+ }
+
+ /* This is the genuine article */
+ memcpy(addr, six, sizeof(*six));
+ return sizeof(*six);
+}
diff --git a/utils/open-isns/tags.c b/utils/open-isns/tags.c
new file mode 100644
index 0000000..7413cee
--- /dev/null
+++ b/utils/open-isns/tags.c
@@ -0,0 +1,740 @@
+/*
+ * Define all iSNS tags with their types, etc.
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include "isns-proto.h"
+#include "vendor.h"
+#include "attrs.h"
+#include "security.h"
+#include "objects.h"
+#include "util.h"
+
+#define ISNS_MAX_BUILTIN_TAG 4096
+
+
+static void print_bitfield(unsigned long, char **, char *, size_t);
+static int parse_bitfield( char **, const char *, uint32_t *);
+static const char *help_bitfield(char **);
+
+#define DECLARE_VALIDATOR(name) \
+static int isns_##name##_validate(const isns_value_t *, const isns_policy_t *);
+#define DECLARE_ACCESSORS(name) \
+static int isns_##name##_parse(isns_value_t *, const char *buf); \
+static void isns_##name##_print(const isns_value_t *, char *buf, size_t size); \
+static const char * isns_##name##_help(void)
+#define USE_VALIDATOR(name) \
+ .it_validate = isns_##name##_validate
+#define USE_ACCESSORS(name) \
+ .it_parse = isns_##name##_parse, \
+ .it_print = isns_##name##_print, \
+ .it_help = isns_##name##_help
+
+DECLARE_VALIDATOR(entity_protocol);
+DECLARE_ACCESSORS(entity_protocol);
+DECLARE_ACCESSORS(tcpudp_port);
+DECLARE_VALIDATOR(iscsi_node_type);
+DECLARE_ACCESSORS(iscsi_node_type);
+DECLARE_ACCESSORS(timestamp);
+DECLARE_ACCESSORS(portal_secbitmap);
+DECLARE_ACCESSORS(scn_bitmap);
+DECLARE_ACCESSORS(dd_features);
+DECLARE_ACCESSORS(policy_object_type);
+DECLARE_ACCESSORS(policy_function);
+
+static const char *isns_authmethod_help(void);
+
+#define TAG(ID, name, type, args...) \
+[ISNS_TAG_##ID] = { \
+ .it_id = ISNS_TAG_##ID, \
+ .it_name = name, \
+ .it_type = &isns_attr_type_##type, \
+ args \
+}
+
+static isns_tag_type_t isns_tags[ISNS_MAX_BUILTIN_TAG] = {
+TAG(DELIMITER, "Delimiter", nil),
+TAG(ENTITY_IDENTIFIER, "Entity identifier", string),
+TAG(ENTITY_PROTOCOL, "Entity protocol", uint32,
+ USE_VALIDATOR(entity_protocol),
+ USE_ACCESSORS(entity_protocol)),
+TAG(MGMT_IP_ADDRESS, "Mgmt IP address", ipaddr),
+TAG(TIMESTAMP, "Timestamp", uint64,
+ USE_ACCESSORS(timestamp),
+ .it_readonly = 1),
+TAG(PROTOCOL_VERSION_RANGE, "Protocol version range", range16),
+TAG(REGISTRATION_PERIOD, "Registration Period", uint32),
+TAG(ENTITY_INDEX, "Entity index", uint32,
+ .it_readonly = 1),
+TAG(ENTITY_NEXT_INDEX, "Entity next index", uint32,
+ .it_readonly = 1),
+TAG(PORTAL_IP_ADDRESS, "Portal IP address", ipaddr),
+TAG(PORTAL_TCP_UDP_PORT, "Portal TCP/UDP port", uint32,
+ USE_ACCESSORS(tcpudp_port)),
+TAG(ESI_INTERVAL, "ESI interval", uint32),
+TAG(ESI_PORT, "ESI port", uint32,
+ USE_ACCESSORS(tcpudp_port)),
+TAG(PORTAL_SYMBOLIC_NAME, "Portal name", string),
+TAG(PORTAL_INDEX, "Portal index", uint32),
+TAG(SCN_PORT, "SCN port", uint32,
+ USE_ACCESSORS(tcpudp_port)),
+TAG(PORTAL_SECURITY_BITMAP, "Portal security bitmap", uint32,
+ USE_ACCESSORS(portal_secbitmap)),
+TAG(PORTAL_NEXT_INDEX, "Portal next index", uint32,
+ .it_readonly = 1),
+
+TAG(ISCSI_NAME, "iSCSI name", string),
+TAG(ISCSI_NODE_TYPE, "iSCSI node type", uint32,
+ USE_VALIDATOR(iscsi_node_type),
+ USE_ACCESSORS(iscsi_node_type)),
+TAG(ISCSI_ALIAS, "iSCSI alias", string),
+TAG(ISCSI_SCN_BITMAP, "iSCSI SCN bitmap", uint32,
+ USE_ACCESSORS(scn_bitmap)),
+TAG(ISCSI_NODE_INDEX, "iSCSI node index", uint32,
+ .it_readonly = 1),
+TAG(WWNN_TOKEN, "WWNN token", uint64),
+TAG(ISCSI_NODE_NEXT_INDEX, "iSCSI node next index",uint32,
+ .it_readonly = 1),
+TAG(ISCSI_AUTHMETHOD, "iSCSI auth method", string,
+ .it_help = isns_authmethod_help),
+
+TAG(PG_ISCSI_NAME, "Portal group name", string),
+TAG(PG_PORTAL_IP_ADDR, "Portal group address", ipaddr),
+TAG(PG_PORTAL_TCP_UDP_PORT, "Portal group port", uint32,
+ USE_ACCESSORS(tcpudp_port)),
+TAG(PG_TAG, "Portal group tag", uint32),
+TAG(PG_INDEX, "Portal group index", uint32,
+ .it_readonly = 1),
+TAG(PG_NEXT_INDEX, "Portal group next index",uint32,
+ .it_readonly = 1),
+
+/* FC Port */
+TAG(FC_PORT_NAME_WWPN, "FC port name WWPN", uint64),
+TAG(PORT_ID, "FC port ID", uint32),
+TAG(FC_PORT_TYPE, "FC port type", uint32),
+TAG(SYMBOLIC_PORT_NAME, "FC symbolic port name",string),
+TAG(FABRIC_PORT_NAME, "FC fabric port name", uint64),
+TAG(HARD_ADDRESS, "FC hard", uint32),
+TAG(PORT_IP_ADDRESS, "FC Port IP address", ipaddr),
+TAG(CLASS_OF_SERVICE, "FC service class", uint32),
+TAG(FC4_TYPES, "FC4 types", opaque),
+TAG(FC4_DESCRIPTOR, "FC4 descriptor", string),
+TAG(FC4_FEATURES, "FC4 features", opaque),
+TAG(IFCP_SCN_BITMAP, "iFCP SCN bitmap", uint32,
+ USE_ACCESSORS(scn_bitmap)),
+TAG(PORT_ROLE, "FC port role", uint32),
+TAG(PERMANENT_PORT_NAME, "FC permanent port name",uint64),
+TAG(FC4_TYPE_CODE, "FC4 type code", uint32),
+
+/* FC Node */
+TAG(FC_NODE_NAME_WWNN, "FC node name", uint64),
+TAG(SYMBOLIC_NODE_NAME, "FC symbolic node name",string),
+TAG(NODE_IP_ADDRESS, "FC node IP address", ipaddr),
+TAG(NODE_IPA, "FC node IPA", uint64),
+TAG(PROXY_ISCSI_NAME, "FC node proxy iSCSI name",string),
+
+/* Other FC tags to go here */
+
+/* Discovery domain set */
+TAG(DD_SET_ID, "DD set ID", uint32),
+TAG(DD_SET_SYMBOLIC_NAME, "DD set name", string),
+TAG(DD_SET_STATUS, "DD set status", uint32),
+TAG(DD_SET_NEXT_ID, "DD set next ID", uint32,
+ .it_readonly = 1),
+
+/* Discovery domain */
+TAG(DD_ID, "DD ID", uint32),
+TAG(DD_SYMBOLIC_NAME, "DD name", string),
+TAG(DD_MEMBER_ISCSI_INDEX, "DD member iSCSI index",uint32,
+ .it_multiple = 1),
+TAG(DD_MEMBER_ISCSI_NAME, "DD member iSCSI name", string,
+ .it_multiple = 1),
+TAG(DD_MEMBER_FC_PORT_NAME, "DD member FC WWPN", string,
+ .it_multiple = 1),
+TAG(DD_MEMBER_PORTAL_INDEX, "DD member portal index",uint32,
+ .it_multiple = 1),
+TAG(DD_MEMBER_PORTAL_IP_ADDR, "DD member portal addr",ipaddr,
+ .it_multiple = 1),
+TAG(DD_MEMBER_PORTAL_TCP_UDP_PORT,"DD member portal port",uint32,
+ USE_ACCESSORS(tcpudp_port),
+ .it_multiple = 1),
+TAG(DD_FEATURES, "DD features", uint32,
+ USE_ACCESSORS(dd_features)),
+TAG(DD_NEXT_ID, "DD next ID", uint32,
+ .it_readonly = 1),
+};
+
+/*
+ * End of RFC defined tags
+ */
+#undef TAG
+
+/*
+ * Open-iSNS vendor specific tags
+ */
+#define TAG(ID, name, type, args...) \
+{ \
+ .it_id = OPENISNS_TAG_##ID, \
+ .it_name = name, \
+ .it_type = &isns_attr_type_##type, \
+ args \
+}
+
+static isns_tag_type_t isns_vendor_tags[] = {
+TAG(POLICY_SPI, "Security Policy Index", string),
+TAG(POLICY_KEY, "DSA security key", opaque),
+TAG(POLICY_ENTITY, "Policy allowed entity name", string),
+TAG(POLICY_OBJECT_TYPE, "Policy allowed object types", uint32,
+ USE_ACCESSORS(policy_object_type)),
+TAG(POLICY_NODE_NAME, "Policy allowed node name", string,
+ .it_multiple = 1),
+TAG(POLICY_NODE_TYPE, "Policy allowed node type", uint32,
+ USE_VALIDATOR(iscsi_node_type),
+ USE_ACCESSORS(iscsi_node_type)),
+TAG(POLICY_FUNCTIONS, "Policy allowed functions", uint32,
+ USE_ACCESSORS(policy_function)),
+TAG(POLICY_VISIBLE_DD, "Visible Discovery Domain", string,
+ .it_multiple = 1),
+TAG(POLICY_DEFAULT_DD, "Default Discovery Domain", string),
+
+{ 0 }
+};
+
+/*
+ * End of vendor-specific tags
+ */
+
+static isns_tag_type_t isns_unknown_tag = {
+ .it_id = 0xffff,
+ .it_name = "unknown",
+ .it_type = &isns_attr_type_opaque,
+};
+
+/*
+ * Map iSNS attribute tag to its data type
+ */
+const isns_tag_type_t *
+isns_tag_type_by_id(uint32_t id)
+{
+ isns_tag_type_t *tag;
+
+ if (id < ISNS_MAX_BUILTIN_TAG) {
+ tag = &isns_tags[id];
+ if (tag->it_type == NULL) {
+ *tag = isns_unknown_tag;
+ tag->it_id = id;
+ }
+ return tag;
+ }
+
+ for (tag = isns_vendor_tags; tag->it_name; ++tag) {
+ if (tag->it_id == id)
+ return tag;
+ }
+
+ return &isns_unknown_tag;
+}
+
+/*
+ * Specific validators/pretty printers
+ */
+int
+isns_entity_protocol_validate(const isns_value_t *value, const isns_policy_t *policy)
+{
+ enum isns_entity_protocol protocol = value->iv_uint32;
+
+ switch (protocol) {
+ case ISNS_ENTITY_PROTOCOL_NONE:
+ case ISNS_ENTITY_PROTOCOL_ISCSI:
+ case ISNS_ENTITY_PROTOCOL_IFCP:
+ return 1;
+ }
+ return 0;
+}
+
+int
+isns_entity_protocol_parse(isns_value_t *value, const char *string)
+{
+ uint32_t prot;
+
+ if (!strcasecmp(string, "none"))
+ prot = ISNS_ENTITY_PROTOCOL_NONE;
+ else if (!strcasecmp(string, "iscsi"))
+ prot = ISNS_ENTITY_PROTOCOL_ISCSI;
+ else if (!strcasecmp(string, "ifcp"))
+ prot = ISNS_ENTITY_PROTOCOL_IFCP;
+ else
+ return 0;
+ value->iv_uint32 = prot;
+ return 1;
+}
+
+void
+isns_entity_protocol_print(const isns_value_t *value, char *buf, size_t size)
+{
+ enum isns_entity_protocol protocol = value->iv_uint32;
+ const char *prot_name;
+
+ switch (protocol) {
+ case ISNS_ENTITY_PROTOCOL_NONE:
+ prot_name = "None";
+ break;
+
+ case ISNS_ENTITY_PROTOCOL_ISCSI:
+ prot_name = "iSCSI";
+ break;
+
+ case ISNS_ENTITY_PROTOCOL_IFCP:
+ prot_name = "iFCP";
+ break;
+
+ default:
+ prot_name = "Unknown";
+ }
+ snprintf(buf, size, "%s (%u)", prot_name, protocol);
+}
+
+const char *
+isns_entity_protocol_help(void)
+{
+ return "one of None, iSCSI, iFCP";
+}
+
+/*
+ * TCP/UDP port
+ */
+int
+isns_tcpudp_port_parse(isns_value_t *value, const char *string)
+{
+ uint32_t num;
+ const char *ep;
+
+ num = strtoul(string, (char **) &ep, 0);
+ if (ep && *ep) {
+ if (!strcasecmp(ep, "/udp"))
+ num |= ISNS_PORTAL_PORT_UDP_MASK;
+ else
+ if (!strcasecmp(ep, "/tcp"))
+ /* nothing */;
+ else {
+ isns_error("Cannot parse port spec \"%s\"\n",
+ string);
+ return 0;
+ }
+ }
+ value->iv_uint32 = num;
+ return 1;
+}
+
+void
+isns_tcpudp_port_print(const isns_value_t *value, char *buf, size_t size)
+{
+ uint32_t portspec = value->iv_uint32, num;
+
+ if (portspec == 0) {
+ snprintf(buf, size, "[default]");
+ } else {
+ num = portspec & 0xffff;
+ if (portspec & ISNS_PORTAL_PORT_UDP_MASK) {
+ snprintf(buf, size, "%u/udp", num);
+ } else {
+ snprintf(buf, size, "%u/tcp", num);
+ }
+ }
+}
+
+const char *
+isns_tcpudp_port_help(void)
+{
+ return "<port>/tcp, <port>/udp, or <port> (defaults to TCP)";
+}
+
+int
+isns_timestamp_parse(isns_value_t *value, const char *string)
+{
+ isns_error("Timestamp parsing not implemented\n");
+ return 0;
+}
+
+void
+isns_timestamp_print(const isns_value_t *value, char *buf, size_t size)
+{
+ time_t timestamp = value->iv_uint64;
+ char *str, *s;
+
+ str = ctime(&timestamp);
+ if ((s = strchr(str, '\n')) != NULL)
+ *s = '\0';
+
+ snprintf(buf, size, "%s", str);
+}
+
+const char *
+isns_timestamp_help(void)
+{
+ return NULL;
+}
+
+/*
+ * Helper macros to implement the off-the-shelf bitfield
+ * accessors.
+ */
+#define IMPLEMENT_BITFIELD_ACCESSORS(name) \
+int isns_##name##_parse(isns_value_t *value, const char *string) \
+{ \
+ return parse_bitfield(name##_bit_names, string, \
+ &value->iv_uint32); \
+} \
+ \
+void \
+isns_##name##_print(const isns_value_t *value, char *buf, size_t size) \
+{ \
+ print_bitfield(value->iv_uint32, name##_bit_names, \
+ buf, size); \
+} \
+ \
+const char * \
+isns_##name##_help(void) \
+{ \
+ return help_bitfield(name##_bit_names); \
+}
+
+
+static char * iscsi_node_type_bit_names[32] = {
+[ISNS_ISCSI_NODE_TYPE_TARGET] = "Target",
+[ISNS_ISCSI_NODE_TYPE_INITIATOR] = "Initiator",
+[ISNS_ISCSI_NODE_TYPE_CONTROL] = "Control",
+};
+
+int
+isns_iscsi_node_type_validate(const isns_value_t *value, const isns_policy_t *policy)
+{
+ uint32_t bits = value->iv_uint32, permitted;
+
+ permitted = ISNS_ISCSI_INITIATOR_MASK |
+ ISNS_ISCSI_TARGET_MASK |
+ ISNS_ISCSI_CONTROL_MASK;
+ if (bits & ~permitted)
+ return 0;
+
+ if (policy && !isns_policy_validate_node_type(policy, bits))
+ return 0;
+
+ return 1;
+}
+
+IMPLEMENT_BITFIELD_ACCESSORS(iscsi_node_type);
+
+/*
+ * Portal Security Bitmap
+ */
+static char * portal_secbitmap_bit_names[32] = {
+[ISNS_PORTAL_SEC_BITMAP_VALID] = "bitmap valid",
+[ISNS_PORTAL_SEC_IPSEC_ENABLED] = "ipsec enabled",
+[ISNS_PORTAL_SEC_MAIN_MODE_ENABLED] = "main mode enabled",
+[ISNS_PORTAL_SEC_AGGR_MODE_ENABLED] = "aggressive mode enabled",
+[ISNS_PORTAL_SEC_PFS_ENABLED] = "pfs enabled",
+[ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED] = "transport mode preferred",
+[ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED] = "tunnel mode preferred",
+};
+
+IMPLEMENT_BITFIELD_ACCESSORS(portal_secbitmap);
+
+/*
+ * SCN bitmap
+ */
+static char * scn_bitmap_bit_names[32] = {
+[ISNS_SCN_DD_MEMBER_ADDED] = "DD/DDS member added",
+[ISNS_SCN_DD_MEMBER_REMOVED] = "DD/DDS member removed",
+[ISNS_SCN_OBJECT_UPDATED] = "object updated",
+[ISNS_SCN_OBJECT_ADDED] = "object added",
+[ISNS_SCN_OBJECT_REMOVED] = "object removed",
+[ISNS_SCN_MANAGEMENT_REGISTRATION] = "management registration",
+[ISNS_SCN_TARGET_AND_SELF_ONLY] = "target and self information only",
+[ISNS_SCN_INITIATOR_AND_SELF_ONLY] = "initiator and self information only",
+};
+
+IMPLEMENT_BITFIELD_ACCESSORS(scn_bitmap);
+
+/*
+ * DD features bitmap
+ */
+static char * dd_features_bit_names[32] = {
+[ISNS_DD_BOOT_LIST_ENABLED] = "Boot list enabled",
+};
+
+IMPLEMENT_BITFIELD_ACCESSORS(dd_features);
+
+/*
+ * Policy: list of allowed functions
+ */
+static char * policy_function_bit_names[32] = {
+[ISNS_DEVICE_ATTRIBUTE_REGISTER]= "DevAttrReg",
+[ISNS_DEVICE_ATTRIBUTE_QUERY] = "DevAttrQry",
+[ISNS_DEVICE_GET_NEXT] = "DevGetNext",
+[ISNS_DEVICE_DEREGISTER] = "DevDereg",
+[ISNS_SCN_REGISTER] = "SCNReg",
+[ISNS_SCN_DEREGISTER] = "SCNDereg",
+[ISNS_SCN_EVENT] = "SCNEvent",
+[ISNS_STATE_CHANGE_NOTIFICATION]= "SCN",
+[ISNS_DD_REGISTER] = "DDReg",
+[ISNS_DD_DEREGISTER] = "DDDereg",
+[ISNS_DDS_REGISTER] = "DDSReg",
+[ISNS_DDS_DEREGISTER] = "DDSDereg",
+[ISNS_ENTITY_STATUS_INQUIRY] = "ESI",
+[ISNS_HEARTBEAT] = "Heartbeat",
+};
+
+IMPLEMENT_BITFIELD_ACCESSORS(policy_function);
+
+/*
+ * Policy: list of allowed node types
+ */
+static char * policy_object_type_bit_names[32] = {
+[ISNS_OBJECT_TYPE_ENTITY] = "entity",
+[ISNS_OBJECT_TYPE_NODE] = "iscsi-node",
+[ISNS_OBJECT_TYPE_PORTAL] = "portal",
+[ISNS_OBJECT_TYPE_PG] = "portal-group",
+[ISNS_OBJECT_TYPE_DD] = "dd",
+[ISNS_OBJECT_TYPE_DDSET] = "ddset",
+[ISNS_OBJECT_TYPE_POLICY] = "policy",
+};
+
+static int
+isns_policy_object_type_parse(isns_value_t *vp, const char *buf)
+{
+ char *copy, *s, *next;
+ int rv = 0;
+
+ if (!strcasecmp(buf, "ALL")) {
+ vp->iv_uint32 = ~0;
+ return 1;
+ }
+ if (!strcasecmp(buf, "DEFAULT")) {
+ vp->iv_uint32 = ISNS_DEFAULT_OBJECT_ACCESS;
+ return 1;
+ }
+
+ vp->iv_uint32 = 0;
+ copy = isns_strdup(buf);
+ for (s = copy; s; s = next) {
+ char *perm;
+ int bit, mask = 0;
+
+ while (1) {
+ unsigned int n;
+
+ n = strcspn(s, ",+;|");
+ if (n) {
+ next = s + n;
+ if (*next)
+ *next++ = '\0';
+ break;
+ }
+ ++n;
+ }
+
+ mask = ISNS_PERMISSION_READ;
+ if ((perm = strchr(s, ':')) != NULL) {
+ *perm++ = '\0';
+ mask = 0;
+ while (*perm) {
+ switch (*perm++) {
+ case 'R': case 'r':
+ mask = ISNS_PERMISSION_READ;
+ break;
+ case 'W': case 'w':
+ mask = ISNS_PERMISSION_READ;
+ break;
+ default:
+ goto failed;
+ }
+ }
+ }
+
+ for (bit = 0; bit < 32; ++bit) {
+ if (policy_object_type_bit_names[bit]
+ && !strcasecmp(policy_object_type_bit_names[bit], s))
+ goto found;
+ }
+ goto failed;
+
+found: vp->iv_uint32 |= ISNS_ACCESS(bit, mask);
+ }
+ rv = 1;
+
+failed:
+ isns_free(copy);
+ return rv;
+}
+
+static void
+isns_policy_object_type_print(const isns_value_t *vp, char *buf, size_t size)
+{
+ unsigned int i, pos = 0;
+ uint32_t mask;
+ const char *sepa = "";
+
+ mask = vp->iv_uint32;
+ if (mask == 0) {
+ snprintf(buf, size, "<empty>");
+ return;
+ }
+
+ for (i = 0; i < 32; ++i, mask >>= 2) {
+ const char *name;
+
+ if (!(mask & 3))
+ continue;
+
+ name = policy_object_type_bit_names[i];
+ if (name)
+ snprintf(buf + pos, size - pos, "%s%s:%s%s", sepa, name,
+ (mask & ISNS_PERMISSION_READ)? "r" : "",
+ (mask & ISNS_PERMISSION_WRITE)? "w" : "");
+ else
+ snprintf(buf + pos, size - pos, "%sbit%u:%s%s",sepa, i,
+ (mask & ISNS_PERMISSION_READ)? "r" : "",
+ (mask & ISNS_PERMISSION_WRITE)? "w" : "");
+ sepa = ", ";
+ pos = strlen(buf);
+ }
+}
+
+static const char *
+isns_policy_object_type_help(void)
+{
+ static char buffer[256];
+ unsigned int i, n;
+ char *sepa = "";
+
+ strcpy(buffer, "bitfield (type:perm): perm=R, W, or RW; type=");
+ n = strlen(buffer);
+
+ for (i = 0; i < 32; ++i) {
+ if (policy_object_type_bit_names[i]) {
+ snprintf(buffer + n, sizeof(buffer) - n,
+ "%s%s", sepa,
+ policy_object_type_bit_names[i]);
+ sepa = ", ";
+ }
+ }
+ return buffer;
+}
+
+/*
+ * Help message for AuthMethod
+ */
+const char *
+isns_authmethod_help(void)
+{
+ return "comma separated list, including of KRB5, SPKM1, SPKM2, SRP, CHAP, none";
+}
+
+/*
+ * Helper functions to deal with bitfields
+ */
+static void
+print_bitfield(unsigned long value, char **bit_names,
+ char *buf, size_t size)
+{
+ unsigned int bit, mask;
+ const char *sepa = "";
+ char *buf_end;
+
+ if (value == 0) {
+ snprintf(buf, size, "<NIL>");
+ return;
+ }
+
+ buf_end = buf + size;
+ for (bit = 0, mask = 1; mask; ++bit, mask <<= 1) {
+ char namebuf[16], *name;
+
+ if (!(value & mask))
+ continue;
+
+ if ((name = bit_names[bit]) == NULL) {
+ sprintf(namebuf, "bit%u", bit);
+ name = namebuf;
+ }
+
+ snprintf(buf, buf_end - buf, "%s%s", sepa, name);
+ buf += strlen(buf);
+ sepa = ", ";
+ }
+}
+
+static int
+parse_bitfield(char **bit_names,
+ const char *string,
+ uint32_t *result)
+{
+ *result = 0;
+
+ if (!strcasecmp(string, "ALL")) {
+ unsigned int bit;
+
+ for (bit = 0; bit < 32; ++bit) {
+ if (bit_names[bit])
+ *result |= 1 << bit;
+ }
+ return 1;
+ }
+
+ if (!strcasecmp(string, "NONE"))
+ return 1;
+
+ while (*string) {
+ unsigned int n, bit, match = 0;
+
+ n = strcspn(string, ",+;|");
+ if (n == 0)
+ goto next;
+
+ for (bit = 0; bit < 32; ++bit) {
+ if (!bit_names[bit])
+ continue;
+ if (!strncasecmp(bit_names[bit], string, n)) {
+ *result |= 1 << bit;
+ match++;
+ }
+ }
+ if (!match)
+ return 0;
+
+next:
+ string += n;
+ string += strspn(string, ",+;|");
+ }
+
+ return 1;
+}
+
+static const char *
+help_bitfield(char **bit_names)
+{
+ static char buffer[1024];
+ char *pos, sepa = ':';
+ unsigned int bit;
+
+ strcpy(buffer, "bitfield");
+ pos = strchr(buffer, '\0');
+
+ for (bit = 0; bit < 32; ++bit) {
+ if (bit_names[bit] == NULL)
+ continue;
+
+ snprintf(pos, sizeof(buffer) - (pos - buffer),
+ "%c %s", sepa, bit_names[bit]);
+
+ pos += strlen(pos);
+ sepa = ',';
+ }
+ return buffer;
+}
+
diff --git a/utils/open-isns/tests/.cvsignore b/utils/open-isns/tests/.cvsignore
new file mode 100644
index 0000000..fa1eb3c
--- /dev/null
+++ b/utils/open-isns/tests/.cvsignore
@@ -0,0 +1,2 @@
+*.swp
+pauw[1-9]
diff --git a/utils/open-isns/tests/Makefile b/utils/open-isns/tests/Makefile
new file mode 100644
index 0000000..778195a
--- /dev/null
+++ b/utils/open-isns/tests/Makefile
@@ -0,0 +1,40 @@
+#
+# Simple makefile to run regression tests, and to
+# document how to run them.
+
+#
+# Each test case is a perl script, testXX.pl. Run as
+# perl testXX.pl
+# Optionally followed by
+# -q quiet - just print a header line, and the overall result
+# -v verbose - display more detailed information, including the
+# commands being run
+# -f fast - skip tests that take more than 15 seconds
+#
+# The default is to be slightly verbose, and display a comment
+# about each stage of the test.
+
+# All test related data is kept in /tmp/isns-test, with a
+# subdirectory for each test.
+# For instance, test01 will create
+# /tmp/isns-test/test01/server0
+# /tmp/isns-test/test01/client1
+# /tmp/isns-test/test01/dump
+#
+# The server and client directories will contain configuration
+# data, logfiles, and (for the server) the Unix socket, the
+# PID file, and the database.
+#
+# The dump directory contains snapshots of the on-disk database
+# for each test stage (if the test stage involves a verification
+# of the database).
+
+tests:
+ @for test in test*.pl; do \
+ perl $$test -q; \
+ done
+
+quick:
+ @for test in test*.pl; do \
+ perl $$test -q --fast; \
+ done
diff --git a/utils/open-isns/tests/client.conf b/utils/open-isns/tests/client.conf
new file mode 100644
index 0000000..034a739
--- /dev/null
+++ b/utils/open-isns/tests/client.conf
@@ -0,0 +1,8 @@
+SourceName = @NOT_SET@
+AuthName = @NOT_SET@
+ServerAddress = @NOT_SET@
+BindAddress = @NOT_SET@
+Security = @NOT_SET@
+AuthKeyFile = @NOT_SET@
+ServerKeyFile = @NOT_SET@
+ControlSocket = @NOT_SET@
diff --git a/utils/open-isns/tests/data/test01/01-enroll b/utils/open-isns/tests/data/test01/01-enroll
new file mode 100644
index 0000000..f59329b
--- /dev/null
+++ b/utils/open-isns/tests/data/test01/01-enroll
@@ -0,0 +1,18 @@
+Dumping database contents
+Backend: /tmp/isns-test/test01/server0/database
+Last EID: 1
+Last Index: 4
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test01/02-registration b/utils/open-isns/tests/data/test01/02-registration
new file mode 100644
index 0000000..fd26f3c
--- /dev/null
+++ b/utils/open-isns/tests/data/test01/02-registration
@@ -0,0 +1,42 @@
+Dumping database contents
+Backend: /tmp/isns-test/test01/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 12:40:58 2007
+ 0007 uint32 : Entity index = 4
+--------------
+Object: index=5 type=<iSCSI Storage Node> state=mature parent=4
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=4
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 6
+--------------
+Object: index=7 type=<iSCSI Portal Group> state=mature parent=4
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 7
diff --git a/utils/open-isns/tests/data/test01/03-query b/utils/open-isns/tests/data/test01/03-query
new file mode 100644
index 0000000..8208f83
--- /dev/null
+++ b/utils/open-isns/tests/data/test01/03-query
@@ -0,0 +1,20 @@
+object[0] = <Network Entity>
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 12:41:41 2007
+ 0007 uint32 : Entity index = 4
+object[1] = <iSCSI Storage Node>
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 5
+object[2] = <Portal>
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 6
+object[3] = <iSCSI Portal Group>
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 7
diff --git a/utils/open-isns/tests/data/test01/03-registration b/utils/open-isns/tests/data/test01/03-registration
new file mode 100644
index 0000000..affd69a
--- /dev/null
+++ b/utils/open-isns/tests/data/test01/03-registration
@@ -0,0 +1,20 @@
+Dumping database contents
+Backend: /tmp/isns-test/test01/server0/database
+Last EID: 1
+Last Index: 4
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "isns.client1"
+ 0602v string : Policy allowed source name = "isns.client1"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "isns.client2"
+ 0602v string : Policy allowed source name = "isns.client2"
+ 0607v string : Policy allowed node name = "isns.client2"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
diff --git a/utils/open-isns/tests/data/test01/99-unregistration b/utils/open-isns/tests/data/test01/99-unregistration
new file mode 100644
index 0000000..c7518ff
--- /dev/null
+++ b/utils/open-isns/tests/data/test01/99-unregistration
@@ -0,0 +1,18 @@
+Dumping database contents
+Backend: /tmp/isns-test/test01/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test02/01-enroll b/utils/open-isns/tests/data/test02/01-enroll
new file mode 100644
index 0000000..e91fa0b
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/01-enroll
@@ -0,0 +1,18 @@
+Dumping database contents
+Backend: /tmp/isns-test/test02/server0/database
+Last EID: 1
+Last Index: 4
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test02/02-enroll b/utils/open-isns/tests/data/test02/02-enroll
new file mode 100644
index 0000000..dbcb735
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/02-enroll
@@ -0,0 +1,24 @@
+Dumping database contents
+Backend: /tmp/isns-test/test02/server0/database
+Last EID: 1
+Last Index: 5
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client2.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client2"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+ 0608v uint32 : Policy allowed node type = Target
diff --git a/utils/open-isns/tests/data/test02/03-registration b/utils/open-isns/tests/data/test02/03-registration
new file mode 100644
index 0000000..ec607e6
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/03-registration
@@ -0,0 +1,72 @@
+Dumping database contents
+Backend: /tmp/isns-test/test02/server0/database
+Last EID: 1
+Last Index: 13
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client2.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client2"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+ 0608v uint32 : Policy allowed node type = Target
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:53 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 6
+--------------
+Object: index=7 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 127.1.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 7
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.1.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
+--------------
+Object: index=9 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:53 2007
+ 0007 uint32 : Entity index = 9
+--------------
+Object: index=10 type=<iSCSI Storage Node> state=mature parent=9
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+--------------
+Object: index=11 type=<Portal> state=mature parent=9
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+--------------
+Object: index=12 type=<iSCSI Portal Group> state=mature parent=9
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
diff --git a/utils/open-isns/tests/data/test02/04-query b/utils/open-isns/tests/data/test02/04-query
new file mode 100644
index 0000000..fbdb0c0
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/04-query
@@ -0,0 +1,20 @@
+object[0] = <Network Entity>
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 5
+object[1] = <iSCSI Storage Node>
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 6
+object[2] = <Portal>
+ 0010 ipaddr : Portal IP address = 127.1.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 7
+object[3] = <iSCSI Portal Group>
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.1.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
diff --git a/utils/open-isns/tests/data/test02/05-query b/utils/open-isns/tests/data/test02/05-query
new file mode 100644
index 0000000..a35db9e
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/05-query
@@ -0,0 +1,20 @@
+object[0] = <Network Entity>
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 9
+object[1] = <iSCSI Storage Node>
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+object[2] = <Portal>
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+object[3] = <iSCSI Portal Group>
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
diff --git a/utils/open-isns/tests/data/test02/06-dd-registration b/utils/open-isns/tests/data/test02/06-dd-registration
new file mode 100644
index 0000000..833f62a
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/06-dd-registration
@@ -0,0 +1,81 @@
+Dumping database contents
+Backend: /tmp/isns-test/test02/server0/database
+Last EID: 1
+Last Index: 14
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client2.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client2"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+ 0608v uint32 : Policy allowed node type = Target
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 6
+--------------
+Object: index=7 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 127.1.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 7
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.1.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
+--------------
+Object: index=9 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 9
+--------------
+Object: index=10 type=<iSCSI Storage Node> state=mature parent=9
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+--------------
+Object: index=11 type=<Portal> state=mature parent=9
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+--------------
+Object: index=12 type=<iSCSI Portal Group> state=mature parent=9
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
+--------------
+Object: index=13 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 6
+ 0814 string : DD member iSCSI name = "isns.client1"
+ 0813 uint32 : DD member iSCSI index = 10
+ 0814 string : DD member iSCSI name = "isns.client2"
diff --git a/utils/open-isns/tests/data/test02/07-query b/utils/open-isns/tests/data/test02/07-query
new file mode 100644
index 0000000..de13226
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/07-query
@@ -0,0 +1,40 @@
+object[0] = <Network Entity>
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 5
+object[1] = <iSCSI Storage Node>
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 6
+object[2] = <Portal>
+ 0010 ipaddr : Portal IP address = 127.1.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 7
+object[3] = <iSCSI Portal Group>
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.1.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
+object[4] = <Network Entity>
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 9
+object[5] = <iSCSI Storage Node>
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+object[6] = <Portal>
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+object[7] = <iSCSI Portal Group>
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
diff --git a/utils/open-isns/tests/data/test02/08-query b/utils/open-isns/tests/data/test02/08-query
new file mode 100644
index 0000000..de13226
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/08-query
@@ -0,0 +1,40 @@
+object[0] = <Network Entity>
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 5
+object[1] = <iSCSI Storage Node>
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 6
+object[2] = <Portal>
+ 0010 ipaddr : Portal IP address = 127.1.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 7
+object[3] = <iSCSI Portal Group>
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.1.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
+object[4] = <Network Entity>
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 9
+object[5] = <iSCSI Storage Node>
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+object[6] = <Portal>
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+object[7] = <iSCSI Portal Group>
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
diff --git a/utils/open-isns/tests/data/test02/09-query b/utils/open-isns/tests/data/test02/09-query
new file mode 100644
index 0000000..a35db9e
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/09-query
@@ -0,0 +1,20 @@
+object[0] = <Network Entity>
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 9
+object[1] = <iSCSI Storage Node>
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+object[2] = <Portal>
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+object[3] = <iSCSI Portal Group>
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
diff --git a/utils/open-isns/tests/data/test02/10-dd-registration b/utils/open-isns/tests/data/test02/10-dd-registration
new file mode 100644
index 0000000..69bf9f6
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/10-dd-registration
@@ -0,0 +1,87 @@
+Dumping database contents
+Backend: /tmp/isns-test/test02/server0/database
+Last EID: 1
+Last Index: 15
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client2.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client2"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+ 0608v uint32 : Policy allowed node type = Target
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 6
+--------------
+Object: index=7 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 127.1.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 7
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.1.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
+--------------
+Object: index=9 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007
+ 0007 uint32 : Entity index = 9
+--------------
+Object: index=10 type=<iSCSI Storage Node> state=mature parent=9
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+--------------
+Object: index=11 type=<Portal> state=mature parent=9
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+--------------
+Object: index=12 type=<iSCSI Portal Group> state=mature parent=9
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
+--------------
+Object: index=13 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 6
+ 0814 string : DD member iSCSI name = "isns.client1"
+ 0813 uint32 : DD member iSCSI index = 10
+ 0814 string : DD member iSCSI name = "isns.client2"
+ 0813 uint32 : DD member iSCSI index = 14
+ 0814 string : DD member iSCSI name = "iqn.com.foobar:disk1"
+--------------
+Object: index=14 type=<iSCSI Storage Node> state=limbo
+ 0020 string : iSCSI name = "iqn.com.foobar:disk1"
+ 0024 uint32 : iSCSI node index = 14
diff --git a/utils/open-isns/tests/data/test02/11-query b/utils/open-isns/tests/data/test02/11-query
new file mode 100644
index 0000000..5b4c49d
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/11-query
@@ -0,0 +1,10 @@
+object[0] = <Discovery Domain>
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 6
+ 0814 string : DD member iSCSI name = "isns.client1"
+ 0813 uint32 : DD member iSCSI index = 10
+ 0814 string : DD member iSCSI name = "isns.client2"
+ 0813 uint32 : DD member iSCSI index = 14
+ 0814 string : DD member iSCSI name = "iqn.com.foobar:disk1"
diff --git a/utils/open-isns/tests/data/test02/12-dd-deregistration b/utils/open-isns/tests/data/test02/12-dd-deregistration
new file mode 100644
index 0000000..d330b2a
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/12-dd-deregistration
@@ -0,0 +1,85 @@
+Dumping database contents
+Backend: /tmp/isns-test/test02/server0/database
+Last EID: 1
+Last Index: 15
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client2.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client2"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+ 0608v uint32 : Policy allowed node type = Target
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:20:33 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 6
+--------------
+Object: index=7 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 127.1.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 7
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.1.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
+--------------
+Object: index=9 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:20:33 2007
+ 0007 uint32 : Entity index = 9
+--------------
+Object: index=10 type=<iSCSI Storage Node> state=mature parent=9
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+--------------
+Object: index=11 type=<Portal> state=mature parent=9
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+--------------
+Object: index=12 type=<iSCSI Portal Group> state=mature parent=9
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
+--------------
+Object: index=13 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 6
+ 0814 string : DD member iSCSI name = "isns.client1"
+ 0813 uint32 : DD member iSCSI index = 14
+ 0814 string : DD member iSCSI name = "iqn.com.foobar:disk1"
+--------------
+Object: index=14 type=<iSCSI Storage Node> state=limbo
+ 0020 string : iSCSI name = "iqn.com.foobar:disk1"
+ 0024 uint32 : iSCSI node index = 14
diff --git a/utils/open-isns/tests/data/test02/13-dd-deregistration b/utils/open-isns/tests/data/test02/13-dd-deregistration
new file mode 100644
index 0000000..4175e8e
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/13-dd-deregistration
@@ -0,0 +1,83 @@
+Dumping database contents
+Backend: /tmp/isns-test/test02/server0/database
+Last EID: 1
+Last Index: 15
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client2.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client2"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+ 0608v uint32 : Policy allowed node type = Target
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 6
+--------------
+Object: index=7 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 127.1.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 7
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.1.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
+--------------
+Object: index=9 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007
+ 0007 uint32 : Entity index = 9
+--------------
+Object: index=10 type=<iSCSI Storage Node> state=mature parent=9
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+--------------
+Object: index=11 type=<Portal> state=mature parent=9
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+--------------
+Object: index=12 type=<iSCSI Portal Group> state=mature parent=9
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
+--------------
+Object: index=13 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 6
+ 0814 string : DD member iSCSI name = "isns.client1"
+--------------
+Object: index=14 type=<iSCSI Storage Node> state=limbo
+ 0020 string : iSCSI name = "iqn.com.foobar:disk1"
+ 0024 uint32 : iSCSI node index = 14
diff --git a/utils/open-isns/tests/data/test02/14-dd-registration b/utils/open-isns/tests/data/test02/14-dd-registration
new file mode 100644
index 0000000..56cfac3
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/14-dd-registration
@@ -0,0 +1,85 @@
+Dumping database contents
+Backend: /tmp/isns-test/test02/server0/database
+Last EID: 1
+Last Index: 15
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client2.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client2"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+ 0608v uint32 : Policy allowed node type = Target
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 6
+--------------
+Object: index=7 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 127.1.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 7
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.1.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
+--------------
+Object: index=9 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007
+ 0007 uint32 : Entity index = 9
+--------------
+Object: index=10 type=<iSCSI Storage Node> state=mature parent=9
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+--------------
+Object: index=11 type=<Portal> state=mature parent=9
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+--------------
+Object: index=12 type=<iSCSI Portal Group> state=mature parent=9
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
+--------------
+Object: index=13 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 6
+ 0814 string : DD member iSCSI name = "isns.client1"
+ 0813 uint32 : DD member iSCSI index = 10
+ 0814 string : DD member iSCSI name = "isns.client2"
+--------------
+Object: index=14 type=<iSCSI Storage Node> state=limbo
+ 0020 string : iSCSI name = "iqn.com.foobar:disk1"
+ 0024 uint32 : iSCSI node index = 14
diff --git a/utils/open-isns/tests/data/test02/15-dd-deregistration b/utils/open-isns/tests/data/test02/15-dd-deregistration
new file mode 100644
index 0000000..d9b420f
--- /dev/null
+++ b/utils/open-isns/tests/data/test02/15-dd-deregistration
@@ -0,0 +1,76 @@
+Dumping database contents
+Backend: /tmp/isns-test/test02/server0/database
+Last EID: 1
+Last Index: 15
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client2.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client2"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+ 0608v uint32 : Policy allowed node type = Target
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 6
+--------------
+Object: index=7 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 127.1.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 7
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.1.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
+--------------
+Object: index=9 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client2.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007
+ 0007 uint32 : Entity index = 9
+--------------
+Object: index=10 type=<iSCSI Storage Node> state=mature parent=9
+ 0020 string : iSCSI name = "isns.client2"
+ 0021 uint32 : iSCSI node type = Target
+ 0024 uint32 : iSCSI node index = 10
+--------------
+Object: index=11 type=<Portal> state=mature parent=9
+ 0010 ipaddr : Portal IP address = 127.1.0.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 11
+--------------
+Object: index=12 type=<iSCSI Portal Group> state=mature parent=9
+ 0030 string : Portal group name = "isns.client2"
+ 0031 ipaddr : Portal group address = 127.1.0.2
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
+--------------
+Object: index=14 type=<iSCSI Storage Node> state=limbo
+ 0020 string : iSCSI name = "iqn.com.foobar:disk1"
+ 0024 uint32 : iSCSI node index = 14
diff --git a/utils/open-isns/tests/data/test03/01-enroll b/utils/open-isns/tests/data/test03/01-enroll
new file mode 100644
index 0000000..0046b41
--- /dev/null
+++ b/utils/open-isns/tests/data/test03/01-enroll
@@ -0,0 +1,18 @@
+Dumping database contents
+Backend: /tmp/isns-test/test03/server0/database
+Last EID: 1
+Last Index: 4
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test03/02-registration b/utils/open-isns/tests/data/test03/02-registration
new file mode 100644
index 0000000..11062a6
--- /dev/null
+++ b/utils/open-isns/tests/data/test03/02-registration
@@ -0,0 +1,42 @@
+Dumping database contents
+Backend: /tmp/isns-test/test03/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:36:35 2007
+ 0007 uint32 : Entity index = 4
+--------------
+Object: index=5 type=<iSCSI Storage Node> state=mature parent=4
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=4
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 6
+--------------
+Object: index=7 type=<iSCSI Portal Group> state=mature parent=4
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 7
diff --git a/utils/open-isns/tests/data/test03/03-unregistration b/utils/open-isns/tests/data/test03/03-unregistration
new file mode 100644
index 0000000..874235b
--- /dev/null
+++ b/utils/open-isns/tests/data/test03/03-unregistration
@@ -0,0 +1,42 @@
+Dumping database contents
+Backend: /tmp/isns-test/test03/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:25:47 2007
+ 0007 uint32 : Entity index = 4
+--------------
+Object: index=5 type=<iSCSI Storage Node> state=mature parent=4
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 5
+--------------
+Object: index=6 type=<Portal> state=limbo
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 6
+--------------
+Object: index=7 type=<iSCSI Portal Group> state=mature parent=4
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 7
diff --git a/utils/open-isns/tests/data/test03/04-unregistration b/utils/open-isns/tests/data/test03/04-unregistration
new file mode 100644
index 0000000..efe63e4
--- /dev/null
+++ b/utils/open-isns/tests/data/test03/04-unregistration
@@ -0,0 +1,18 @@
+Dumping database contents
+Backend: /tmp/isns-test/test03/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test03/99-unregistration b/utils/open-isns/tests/data/test03/99-unregistration
new file mode 100644
index 0000000..9ea63b1
--- /dev/null
+++ b/utils/open-isns/tests/data/test03/99-unregistration
@@ -0,0 +1,13 @@
+Dumping database contents
+Backend: /tmp/isns-test/test03/server0/database
+Last EID: 1
+Last Index: 7
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
diff --git a/utils/open-isns/tests/data/test04/01-enroll b/utils/open-isns/tests/data/test04/01-enroll
new file mode 100644
index 0000000..3caf7a2
--- /dev/null
+++ b/utils/open-isns/tests/data/test04/01-enroll
@@ -0,0 +1,18 @@
+Dumping database contents
+Backend: /tmp/isns-test/test04/server0/database
+Last EID: 1
+Last Index: 4
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test04/02-registration b/utils/open-isns/tests/data/test04/02-registration
new file mode 100644
index 0000000..0780d23
--- /dev/null
+++ b/utils/open-isns/tests/data/test04/02-registration
@@ -0,0 +1,42 @@
+Dumping database contents
+Backend: /tmp/isns-test/test04/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:38:41 2007
+ 0007 uint32 : Entity index = 4
+--------------
+Object: index=5 type=<iSCSI Storage Node> state=mature parent=4
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=4
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 6
+--------------
+Object: index=7 type=<iSCSI Portal Group> state=mature parent=4
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 7
diff --git a/utils/open-isns/tests/data/test04/03-restart b/utils/open-isns/tests/data/test04/03-restart
new file mode 100644
index 0000000..0780d23
--- /dev/null
+++ b/utils/open-isns/tests/data/test04/03-restart
@@ -0,0 +1,42 @@
+Dumping database contents
+Backend: /tmp/isns-test/test04/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:38:41 2007
+ 0007 uint32 : Entity index = 4
+--------------
+Object: index=5 type=<iSCSI Storage Node> state=mature parent=4
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=4
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 6
+--------------
+Object: index=7 type=<iSCSI Portal Group> state=mature parent=4
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 7
diff --git a/utils/open-isns/tests/data/test04/04-query b/utils/open-isns/tests/data/test04/04-query
new file mode 100644
index 0000000..3320a8c
--- /dev/null
+++ b/utils/open-isns/tests/data/test04/04-query
@@ -0,0 +1,20 @@
+object[0] = <Network Entity>
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:38:42 2007
+ 0007 uint32 : Entity index = 4
+object[1] = <iSCSI Storage Node>
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 5
+object[2] = <Portal>
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 6
+object[3] = <iSCSI Portal Group>
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 7
diff --git a/utils/open-isns/tests/data/test05/01-enroll b/utils/open-isns/tests/data/test05/01-enroll
new file mode 100644
index 0000000..421d125
--- /dev/null
+++ b/utils/open-isns/tests/data/test05/01-enroll
@@ -0,0 +1,18 @@
+Dumping database contents
+Backend: /tmp/isns-test/test05/server0/database
+Last EID: 1
+Last Index: 4
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test05/02-registration b/utils/open-isns/tests/data/test05/02-registration
new file mode 100644
index 0000000..8be9f56
--- /dev/null
+++ b/utils/open-isns/tests/data/test05/02-registration
@@ -0,0 +1,42 @@
+Dumping database contents
+Backend: /tmp/isns-test/test05/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 20
+ 0004 uint64 : Timestamp = Fri Sep 14 13:40:40 2007
+ 0007 uint32 : Entity index = 4
+--------------
+Object: index=5 type=<iSCSI Storage Node> state=mature parent=4
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=4
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 6
+--------------
+Object: index=7 type=<iSCSI Portal Group> state=mature parent=4
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 7
diff --git a/utils/open-isns/tests/data/test05/03-expired b/utils/open-isns/tests/data/test05/03-expired
new file mode 100644
index 0000000..1c8b6ea
--- /dev/null
+++ b/utils/open-isns/tests/data/test05/03-expired
@@ -0,0 +1,18 @@
+Dumping database contents
+Backend: /tmp/isns-test/test05/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test06/01-enroll b/utils/open-isns/tests/data/test06/01-enroll
new file mode 100644
index 0000000..5ededae
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/01-enroll
@@ -0,0 +1,18 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 4
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test06/02-registration b/utils/open-isns/tests/data/test06/02-registration
new file mode 100644
index 0000000..5b6ab13
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/02-registration
@@ -0,0 +1,42 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:41:51 2007
+ 0007 uint32 : Entity index = 4
+--------------
+Object: index=5 type=<iSCSI Storage Node> state=mature parent=4
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=4
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 6
+--------------
+Object: index=7 type=<iSCSI Portal Group> state=mature parent=4
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 7
diff --git a/utils/open-isns/tests/data/test06/03-registration b/utils/open-isns/tests/data/test06/03-registration
new file mode 100644
index 0000000..ad555dc
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/03-registration
@@ -0,0 +1,42 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 12
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=8 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:41:51 2007
+ 0007 uint32 : Entity index = 8
+--------------
+Object: index=9 type=<iSCSI Storage Node> state=mature parent=8
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 9
+--------------
+Object: index=10 type=<Portal> state=mature parent=8
+ 0010 ipaddr : Portal IP address = 192.168.1.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 10
+--------------
+Object: index=11 type=<iSCSI Portal Group> state=mature parent=8
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 192.168.1.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 11
diff --git a/utils/open-isns/tests/data/test06/04-registration b/utils/open-isns/tests/data/test06/04-registration
new file mode 100644
index 0000000..477248d
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/04-registration
@@ -0,0 +1,42 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 16
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=12 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:41:51 2007
+ 0007 uint32 : Entity index = 12
+--------------
+Object: index=13 type=<iSCSI Storage Node> state=mature parent=12
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 13
+--------------
+Object: index=14 type=<Portal> state=mature parent=12
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 14
+--------------
+Object: index=15 type=<iSCSI Portal Group> state=mature parent=12
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 192.168.1.2
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 15
diff --git a/utils/open-isns/tests/data/test06/05-dd-registration b/utils/open-isns/tests/data/test06/05-dd-registration
new file mode 100644
index 0000000..01776db
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/05-dd-registration
@@ -0,0 +1,49 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 17
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=12 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:41:52 2007
+ 0007 uint32 : Entity index = 12
+--------------
+Object: index=13 type=<iSCSI Storage Node> state=mature parent=12
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 13
+--------------
+Object: index=14 type=<Portal> state=mature parent=12
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 14
+--------------
+Object: index=15 type=<iSCSI Portal Group> state=mature parent=12
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 192.168.1.2
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 15
+--------------
+Object: index=16 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 13
+ 0814 string : DD member iSCSI name = "isns.client1"
diff --git a/utils/open-isns/tests/data/test06/06-registration b/utils/open-isns/tests/data/test06/06-registration
new file mode 100644
index 0000000..6da36e2
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/06-registration
@@ -0,0 +1,49 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 20
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=17 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:41:52 2007
+ 0007 uint32 : Entity index = 17
+--------------
+Object: index=13 type=<iSCSI Storage Node> state=mature parent=17
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 13
+--------------
+Object: index=18 type=<Portal> state=mature parent=17
+ 0010 ipaddr : Portal IP address = 192.168.1.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 18
+--------------
+Object: index=19 type=<iSCSI Portal Group> state=mature parent=17
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 192.168.1.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 19
+--------------
+Object: index=16 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 13
+ 0814 string : DD member iSCSI name = "isns.client1"
diff --git a/utils/open-isns/tests/data/test06/07-dd-registration b/utils/open-isns/tests/data/test06/07-dd-registration
new file mode 100644
index 0000000..b3201d2
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/07-dd-registration
@@ -0,0 +1,52 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 20
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=17 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:41:52 2007
+ 0007 uint32 : Entity index = 17
+--------------
+Object: index=13 type=<iSCSI Storage Node> state=mature parent=17
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 13
+--------------
+Object: index=18 type=<Portal> state=mature parent=17
+ 0010 ipaddr : Portal IP address = 192.168.1.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 18
+--------------
+Object: index=19 type=<iSCSI Portal Group> state=mature parent=17
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 192.168.1.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 19
+--------------
+Object: index=16 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 13
+ 0814 string : DD member iSCSI name = "isns.client1"
+ 0816 uint32 : DD member portal index = 18
+ 0817 ipaddr : DD member portal addr = 192.168.1.1
+ 0818 uint32 : DD member portal port = 860/tcp
diff --git a/utils/open-isns/tests/data/test06/08-registration b/utils/open-isns/tests/data/test06/08-registration
new file mode 100644
index 0000000..f965777
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/08-registration
@@ -0,0 +1,64 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 22
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=17 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:41:52 2007
+ 0007 uint32 : Entity index = 17
+--------------
+Object: index=13 type=<iSCSI Storage Node> state=mature parent=17
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 13
+--------------
+Object: index=18 type=<Portal> state=limbo
+ 0010 ipaddr : Portal IP address = 192.168.1.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 18
+--------------
+Object: index=19 type=<iSCSI Portal Group> state=mature parent=17
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 192.168.1.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 19
+--------------
+Object: index=16 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 13
+ 0814 string : DD member iSCSI name = "isns.client1"
+ 0816 uint32 : DD member portal index = 18
+ 0817 ipaddr : DD member portal addr = 192.168.1.1
+ 0818 uint32 : DD member portal port = 860/tcp
+--------------
+Object: index=20 type=<Portal> state=mature parent=17
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 20
+--------------
+Object: index=21 type=<iSCSI Portal Group> state=mature parent=17
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 192.168.1.2
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 21
diff --git a/utils/open-isns/tests/data/test06/09-registration b/utils/open-isns/tests/data/test06/09-registration
new file mode 100644
index 0000000..1308d3c
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/09-registration
@@ -0,0 +1,64 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 22
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=17 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:41:52 2007
+ 0007 uint32 : Entity index = 17
+--------------
+Object: index=13 type=<iSCSI Storage Node> state=mature parent=17
+ 0020 string : iSCSI name = "isns.client1"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0024 uint32 : iSCSI node index = 13
+--------------
+Object: index=18 type=<Portal> state=mature parent=17
+ 0010 ipaddr : Portal IP address = 192.168.1.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 18
+--------------
+Object: index=19 type=<iSCSI Portal Group> state=mature parent=17
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 192.168.1.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 19
+--------------
+Object: index=16 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 13
+ 0814 string : DD member iSCSI name = "isns.client1"
+ 0816 uint32 : DD member portal index = 18
+ 0817 ipaddr : DD member portal addr = 192.168.1.1
+ 0818 uint32 : DD member portal port = 860/tcp
+--------------
+Object: index=20 type=<Portal> state=limbo
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 20
+--------------
+Object: index=21 type=<iSCSI Portal Group> state=mature parent=17
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 192.168.1.2
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 21
diff --git a/utils/open-isns/tests/data/test06/10-unregistration b/utils/open-isns/tests/data/test06/10-unregistration
new file mode 100644
index 0000000..0c42d1c
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/10-unregistration
@@ -0,0 +1,37 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 22
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=13 type=<iSCSI Storage Node> state=limbo
+ 0020 string : iSCSI name = "isns.client1"
+ 0024 uint32 : iSCSI node index = 13
+--------------
+Object: index=18 type=<Portal> state=limbo
+ 0010 ipaddr : Portal IP address = 192.168.1.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 18
+--------------
+Object: index=16 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 13
+ 0814 string : DD member iSCSI name = "isns.client1"
+ 0816 uint32 : DD member portal index = 18
+ 0817 ipaddr : DD member portal addr = 192.168.1.1
+ 0818 uint32 : DD member portal port = 860/tcp
diff --git a/utils/open-isns/tests/data/test06/11-registration b/utils/open-isns/tests/data/test06/11-registration
new file mode 100644
index 0000000..d8d03f8
--- /dev/null
+++ b/utils/open-isns/tests/data/test06/11-registration
@@ -0,0 +1,52 @@
+Dumping database contents
+Backend: /tmp/isns-test/test06/server0/database
+Last EID: 1
+Last Index: 24
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=22 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 21 11:23:54 2007
+ 0007 uint32 : Entity index = 22
+--------------
+Object: index=13 type=<iSCSI Storage Node> state=mature parent=22
+ 0020 string : iSCSI name = "isns.client1"
+ 0024 uint32 : iSCSI node index = 13
+ 0021 uint32 : iSCSI node type = Initiator
+--------------
+Object: index=18 type=<Portal> state=mature parent=22
+ 0010 ipaddr : Portal IP address = 192.168.1.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 18
+--------------
+Object: index=23 type=<iSCSI Portal Group> state=mature parent=22
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 192.168.1.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 23
+--------------
+Object: index=16 type=<Discovery Domain> state=mature
+ 0811 uint32 : DD ID = 1
+ 0812 string : DD name = "isns.dd1"
+ 081e uint32 : DD features = <NIL>
+ 0813 uint32 : DD member iSCSI index = 13
+ 0814 string : DD member iSCSI name = "isns.client1"
+ 0816 uint32 : DD member portal index = 18
+ 0817 ipaddr : DD member portal addr = 192.168.1.1
+ 0818 uint32 : DD member portal port = 860/tcp
diff --git a/utils/open-isns/tests/data/test07/01-enroll b/utils/open-isns/tests/data/test07/01-enroll
new file mode 100644
index 0000000..ad2eaf4
--- /dev/null
+++ b/utils/open-isns/tests/data/test07/01-enroll
@@ -0,0 +1,19 @@
+Dumping database contents
+Backend: /tmp/isns-test/test07/server0/database
+Last EID: 1
+Last Index: 4
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+ 0004 uint64 : Timestamp = Fri Sep 14 13:42:57 2007
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test07/02-registration b/utils/open-isns/tests/data/test07/02-registration
new file mode 100644
index 0000000..da8962a
--- /dev/null
+++ b/utils/open-isns/tests/data/test07/02-registration
@@ -0,0 +1,45 @@
+Dumping database contents
+Backend: /tmp/isns-test/test07/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+ 0004 uint64 : Timestamp = Fri Sep 14 13:42:57 2007
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=4 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:42:57 2007
+ 0007 uint32 : Entity index = 4
+--------------
+Object: index=5 type=<iSCSI Storage Node> state=mature parent=4
+ 0020 string : iSCSI name = "isns.client1"
+ 0024 uint32 : iSCSI node index = 5
+ 0021 uint32 : iSCSI node type = Initiator
+--------------
+Object: index=6 type=<Portal> state=mature parent=4
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 6
+ 0014 uint32 : ESI port = 65535/tcp
+ 0013 uint32 : ESI interval = 5
+--------------
+Object: index=7 type=<iSCSI Portal Group> state=mature parent=4
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 7
diff --git a/utils/open-isns/tests/data/test07/03-expired b/utils/open-isns/tests/data/test07/03-expired
new file mode 100644
index 0000000..edb9ea4
--- /dev/null
+++ b/utils/open-isns/tests/data/test07/03-expired
@@ -0,0 +1,19 @@
+Dumping database contents
+Backend: /tmp/isns-test/test07/server0/database
+Last EID: 1
+Last Index: 8
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+ 0004 uint64 : Timestamp = Fri Sep 14 13:42:57 2007
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test07/04-registration b/utils/open-isns/tests/data/test07/04-registration
new file mode 100644
index 0000000..de57cbd
--- /dev/null
+++ b/utils/open-isns/tests/data/test07/04-registration
@@ -0,0 +1,57 @@
+Dumping database contents
+Backend: /tmp/isns-test/test07/server0/database
+Last EID: 1
+Last Index: 14
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+ 0004 uint64 : Timestamp = Fri Sep 14 13:43:12 2007
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
+--------------
+Object: index=8 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "client1.isns-test.eu"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 14 13:43:12 2007
+ 0007 uint32 : Entity index = 8
+--------------
+Object: index=9 type=<iSCSI Storage Node> state=mature parent=8
+ 0020 string : iSCSI name = "isns.client1"
+ 0024 uint32 : iSCSI node index = 9
+ 0021 uint32 : iSCSI node type = Initiator
+--------------
+Object: index=10 type=<Portal> state=mature parent=8
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 860/tcp
+ 0016 uint32 : Portal index = 10
+ 0014 uint32 : ESI port = 65535/tcp
+ 0013 uint32 : ESI interval = 5
+--------------
+Object: index=11 type=<Portal> state=mature parent=8
+ 0010 ipaddr : Portal IP address = 127.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 1/tcp
+ 0016 uint32 : Portal index = 11
+--------------
+Object: index=12 type=<iSCSI Portal Group> state=mature parent=8
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 860/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 12
+--------------
+Object: index=13 type=<iSCSI Portal Group> state=mature parent=8
+ 0030 string : Portal group name = "isns.client1"
+ 0031 ipaddr : Portal group address = 127.0.0.1
+ 0032 uint32 : Portal group port = 1/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 13
diff --git a/utils/open-isns/tests/data/test07/05-expired b/utils/open-isns/tests/data/test07/05-expired
new file mode 100644
index 0000000..fd51f78
--- /dev/null
+++ b/utils/open-isns/tests/data/test07/05-expired
@@ -0,0 +1,19 @@
+Dumping database contents
+Backend: /tmp/isns-test/test07/server0/database
+Last EID: 1
+Last Index: 14
+--------------
+Object: index=1 type=<Network Entity> state=mature PRIVATE
+ 0001 string : Entity identifier = "CONTROL"
+ 0007 uint32 : Entity index = 1
+ 0004 uint64 : Timestamp = Fri Sep 14 13:43:12 2007
+--------------
+Object: index=2 type=<Policy> state=mature parent=1 PRIVATE
+ 0601v string : Security Policy Index = "client1.isns-test.eu"
+ 0607v string : Policy allowed node name = "isns.client1"
+ 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
+ 0020 string : iSCSI name = "isns.control"
+ 0021 uint32 : iSCSI node type = <NIL>
+ 0024 uint32 : iSCSI node index = 3
diff --git a/utils/open-isns/tests/data/test08/01-pauw1 b/utils/open-isns/tests/data/test08/01-pauw1
new file mode 100644
index 0000000..3de54da
--- /dev/null
+++ b/utils/open-isns/tests/data/test08/01-pauw1
@@ -0,0 +1,100 @@
+Dumping database contents
+Backend: /tmp/isns-test/test08/server0/database
+Last EID: 1
+Last Index: 15
+--------------
+Object: index=1 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "cyan.pauw.homeunix.net"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Mon Sep 17 15:15:41 2007
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<iSCSI Storage Node> state=mature parent=1
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"
+ 0024 uint32 : iSCSI node index = 2
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "Test (10 GB)"
+ 002a string : iSCSI auth method = "None"
+--------------
+Object: index=3 type=<iSCSI Storage Node> state=mature parent=1
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1"
+ 0024 uint32 : iSCSI node index = 3
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "160 GB disk (ntfs)"
+ 002a string : iSCSI auth method = "None"
+--------------
+Object: index=4 type=<iSCSI Storage Node> state=mature parent=1
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2"
+ 0024 uint32 : iSCSI node index = 4
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "160 GB disk (ext3)"
+ 002a string : iSCSI auth method = "CHAP"
+--------------
+Object: index=5 type=<iSCSI Storage Node> state=mature parent=1
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3"
+ 0024 uint32 : iSCSI node index = 5
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "Test (1 GB)"
+ 002a string : iSCSI auth method = "None"
+--------------
+Object: index=6 type=<iSCSI Storage Node> state=mature parent=1
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4"
+ 0024 uint32 : iSCSI node index = 6
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "Test (40 GB)"
+ 002a string : iSCSI auth method = "CHAP"
+--------------
+Object: index=7 type=<iSCSI Storage Node> state=mature parent=1
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5"
+ 0024 uint32 : iSCSI node index = 7
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "test"
+ 002a string : iSCSI auth method = "None"
+--------------
+Object: index=8 type=<Portal> state=mature parent=1
+ 0010 ipaddr : Portal IP address = 10.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0016 uint32 : Portal index = 8
+--------------
+Object: index=9 type=<iSCSI Portal Group> state=mature parent=1
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"
+ 0031 ipaddr : Portal group address = 10.0.0.1
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0034 uint32 : Portal group index = 9
+ 0033 uint32 : Portal group tag = 1
+--------------
+Object: index=10 type=<iSCSI Portal Group> state=mature parent=1
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1"
+ 0031 ipaddr : Portal group address = 10.0.0.1
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0034 uint32 : Portal group index = 10
+ 0033 uint32 : Portal group tag = 1
+--------------
+Object: index=11 type=<iSCSI Portal Group> state=mature parent=1
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2"
+ 0031 ipaddr : Portal group address = 10.0.0.1
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0034 uint32 : Portal group index = 11
+ 0033 uint32 : Portal group tag = 1
+--------------
+Object: index=12 type=<iSCSI Portal Group> state=mature parent=1
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3"
+ 0031 ipaddr : Portal group address = 10.0.0.1
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0034 uint32 : Portal group index = 12
+ 0033 uint32 : Portal group tag = 1
+--------------
+Object: index=13 type=<iSCSI Portal Group> state=mature parent=1
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4"
+ 0031 ipaddr : Portal group address = 10.0.0.1
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0034 uint32 : Portal group index = 13
+ 0033 uint32 : Portal group tag = 1
+--------------
+Object: index=14 type=<iSCSI Portal Group> state=mature parent=1
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5"
+ 0031 ipaddr : Portal group address = 10.0.0.1
+ 0032 uint32 : Portal group port = 3260/tcp
+ 0034 uint32 : Portal group index = 14
+ 0033 uint32 : Portal group tag = 1
diff --git a/utils/open-isns/tests/data/test09/01-pauw2 b/utils/open-isns/tests/data/test09/01-pauw2
new file mode 100644
index 0000000..9b0a814
--- /dev/null
+++ b/utils/open-isns/tests/data/test09/01-pauw2
@@ -0,0 +1,31 @@
+Dumping database contents
+Backend: /tmp/isns-test/test09/server0/database
+Last EID: 1
+Last Index: 9
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Mon Sep 17 15:18:04 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 33849/tcp
+ 0016 uint32 : Portal index = 6
+ 0014 uint32 : ESI port = 56288/tcp
+ 0013 uint32 : ESI interval = 300
+--------------
+Object: index=7 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
+ 0024 uint32 : iSCSI node index = 7
+ 0021 uint32 : iSCSI node type = Initiator
+ 0022 string : iSCSI alias = "blue.pauw.homeunix.net"
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "iqn.2005-03.org.open-iscsi:blue"
+ 0031 ipaddr : Portal group address = 192.168.1.2
+ 0032 uint32 : Portal group port = 33849/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
diff --git a/utils/open-isns/tests/data/test10/01-pauw3 b/utils/open-isns/tests/data/test10/01-pauw3
new file mode 100644
index 0000000..b7f3b10
--- /dev/null
+++ b/utils/open-isns/tests/data/test10/01-pauw3
@@ -0,0 +1,31 @@
+Dumping database contents
+Backend: /tmp/isns-test/test10/server0/database
+Last EID: 1
+Last Index: 9
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Mon Sep 17 16:34:30 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 33849/tcp
+ 0016 uint32 : Portal index = 6
+ 0014 uint32 : ESI port = 56288/tcp
+ 0013 uint32 : ESI interval = 10
+--------------
+Object: index=7 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
+ 0024 uint32 : iSCSI node index = 7
+ 0021 uint32 : iSCSI node type = Initiator
+ 0022 string : iSCSI alias = "blue.pauw.homeunix.net"
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "iqn.2005-03.org.open-iscsi:blue"
+ 0031 ipaddr : Portal group address = 192.168.1.2
+ 0032 uint32 : Portal group port = 33849/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
diff --git a/utils/open-isns/tests/data/test10/02-expired b/utils/open-isns/tests/data/test10/02-expired
new file mode 100644
index 0000000..b7f3b10
--- /dev/null
+++ b/utils/open-isns/tests/data/test10/02-expired
@@ -0,0 +1,31 @@
+Dumping database contents
+Backend: /tmp/isns-test/test10/server0/database
+Last EID: 1
+Last Index: 9
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Mon Sep 17 16:34:30 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 33849/tcp
+ 0016 uint32 : Portal index = 6
+ 0014 uint32 : ESI port = 56288/tcp
+ 0013 uint32 : ESI interval = 10
+--------------
+Object: index=7 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
+ 0024 uint32 : iSCSI node index = 7
+ 0021 uint32 : iSCSI node type = Initiator
+ 0022 string : iSCSI alias = "blue.pauw.homeunix.net"
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "iqn.2005-03.org.open-iscsi:blue"
+ 0031 ipaddr : Portal group address = 192.168.1.2
+ 0032 uint32 : Portal group port = 33849/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
diff --git a/utils/open-isns/tests/data/test10/03-pauw3 b/utils/open-isns/tests/data/test10/03-pauw3
new file mode 100644
index 0000000..412c5b5
--- /dev/null
+++ b/utils/open-isns/tests/data/test10/03-pauw3
@@ -0,0 +1,31 @@
+Dumping database contents
+Backend: /tmp/isns-test/test10/server0/database
+Last EID: 1
+Last Index: 9
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Mon Sep 17 16:34:51 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 33849/tcp
+ 0016 uint32 : Portal index = 6
+ 0014 uint32 : ESI port = 56288/tcp
+ 0013 uint32 : ESI interval = 10
+--------------
+Object: index=7 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
+ 0024 uint32 : iSCSI node index = 7
+ 0021 uint32 : iSCSI node type = Initiator
+ 0022 string : iSCSI alias = "blue.pauw.homeunix.net"
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "iqn.2005-03.org.open-iscsi:blue"
+ 0031 ipaddr : Portal group address = 192.168.1.2
+ 0032 uint32 : Portal group port = 33849/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
diff --git a/utils/open-isns/tests/data/test10/04-expired b/utils/open-isns/tests/data/test10/04-expired
new file mode 100644
index 0000000..412c5b5
--- /dev/null
+++ b/utils/open-isns/tests/data/test10/04-expired
@@ -0,0 +1,31 @@
+Dumping database contents
+Backend: /tmp/isns-test/test10/server0/database
+Last EID: 1
+Last Index: 9
+--------------
+Object: index=5 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Mon Sep 17 16:34:51 2007
+ 0007 uint32 : Entity index = 5
+--------------
+Object: index=6 type=<Portal> state=mature parent=5
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 33849/tcp
+ 0016 uint32 : Portal index = 6
+ 0014 uint32 : ESI port = 56288/tcp
+ 0013 uint32 : ESI interval = 10
+--------------
+Object: index=7 type=<iSCSI Storage Node> state=mature parent=5
+ 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
+ 0024 uint32 : iSCSI node index = 7
+ 0021 uint32 : iSCSI node type = Initiator
+ 0022 string : iSCSI alias = "blue.pauw.homeunix.net"
+--------------
+Object: index=8 type=<iSCSI Portal Group> state=mature parent=5
+ 0030 string : Portal group name = "iqn.2005-03.org.open-iscsi:blue"
+ 0031 ipaddr : Portal group address = 192.168.1.2
+ 0032 uint32 : Portal group port = 33849/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 8
diff --git a/utils/open-isns/tests/data/test11/01-pauw4 b/utils/open-isns/tests/data/test11/01-pauw4
new file mode 100644
index 0000000..50c6b92
--- /dev/null
+++ b/utils/open-isns/tests/data/test11/01-pauw4
@@ -0,0 +1,32 @@
+Dumping database contents
+Backend: /tmp/isns-test/test11/server0/database
+Last EID: 1
+Last Index: 5
+--------------
+Object: index=1 type=<Network Entity> state=mature
+ 0001 string : Entity identifier = "troopa.nki.nl"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 7200
+ 0004 uint64 : Timestamp = Fri Sep 21 09:37:10 2007
+ 0007 uint32 : Entity index = 1
+--------------
+Object: index=2 type=<Portal> state=mature parent=1
+ 0010 ipaddr : Portal IP address = 192.168.1.40
+ 0011 uint32 : Portal TCP/UDP port = 3229/tcp
+ 0017 uint32 : SCN port = 3230/tcp
+ 0014 uint32 : ESI port = 3230/tcp
+ 0013 uint32 : ESI interval = 300
+ 0016 uint32 : Portal index = 2
+--------------
+Object: index=3 type=<iSCSI Portal Group> state=mature parent=1
+ 0030 string : Portal group name = "iqn.1991-05.com.microsoft:orange"
+ 0031 ipaddr : Portal group address = 192.168.1.40
+ 0032 uint32 : Portal group port = 3229/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0034 uint32 : Portal group index = 3
+--------------
+Object: index=4 type=<iSCSI Storage Node> state=mature parent=1
+ 0020 string : iSCSI name = "iqn.1991-05.com.microsoft:orange"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0022 string : iSCSI alias = "<MS SW iSCSI Initiator>"
+ 0024 uint32 : iSCSI node index = 4
diff --git a/utils/open-isns/tests/genkey b/utils/open-isns/tests/genkey
new file mode 100644
index 0000000..36c5eee
--- /dev/null
+++ b/utils/open-isns/tests/genkey
@@ -0,0 +1,175 @@
+#!/bin/bash
+#
+# This is a very simple script to generate a DSA
+# key pair for authenticated iSNS.
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This script is supposed to be run on the iSNS server.
+# For the first time, run as
+# isnsgenkey -s 1024
+# This will generate a DSA params file, and a DSA private
+# and public key for the server.
+#
+# For each client, generate a key using
+# isnsgenkey <clientname>
+# where <clientname> is the fully qualified domain name.
+# This script will convert the FQDN to a valid iSNS
+# source name (isns.com.foobar.host)
+
+myname=`basename $0`
+etcdir=/etc/isns
+keystore=$etcdir/keystore
+dsa_parms=$etcdir/dsa.params
+dsa_bits=1024
+opt_force=0
+opt_server=0
+
+function usage {
+ cat <<-EOF >&2
+ $*
+ Usage:
+ $myname -s [-f] bits
+ $myname clientname
+ EOF
+ exit 1
+}
+
+function make_isns_name {
+ OFS="$IFS"
+ IFS=.
+ set -- $*
+
+ __result=$1; shift
+ for part; do
+ __result=$part.$__result
+ done
+ echo "isns.$__result"
+ IFS="$OFS"
+}
+
+set -- `getopt b:fk:s $*`
+while [ $# -gt 0 ]; do
+ opt=$1; shift
+ case $opt in
+ --) break;;
+ -b) dsa_bits=$1; shift;;
+ -f) opt_force=1;;
+ -k) dsa_priv=$1; shift;;
+ -s) opt_server=1;;
+ *) usage "Unknown option $opt";;
+ esac
+done
+
+if [ `id -un` != "root" -a $opt_force -eq 0 ]; then
+ echo "$myname: should be run by super user only" >&2
+ exit 1
+fi
+
+# All newly generated files should have restricted
+# access by default.
+umask 077
+
+tmpdir=`mktemp -d /tmp/isnsgenkey.XXXXXX`
+trap "rm -rf $tmpdir" 0 1 2 15
+
+if [ $opt_server -ne 0 ]; then
+ [ $# -eq 1 ] || usage "Expected DSA key length"
+ dsa_bits=$1
+
+ install -m 755 -d $etcdir
+ if [ -z $dsa_priv ]; then
+ dsa_priv=$etcdir/auth_key
+ fi
+ dsa_pub=$dsa_priv.pub
+ dsa_copy=
+else
+ [ $# -eq 1 ] || usage "Expected client name"
+ client=`make_isns_name $1`
+
+ mkdir -p $tmpdir$etcdir
+ # build_client_conf $client > $tmpdir$etcdir/client.conf
+
+ if [ -z $dsa_priv ]; then
+ dsa_priv=$tmpdir$etcdir/auth_key
+ fi
+ dsa_pub=$dsa_priv.pub
+ dsa_copy=$keystore/$client
+fi
+
+if [ -f $dsa_priv -a $opt_force -eq 0 ]; then
+ cat <<-EOF
+
+ ------------------------------------------------------------------
+ | There is already a DSA key installed in $dsa_priv. In order to
+ | generate a new key, please specify the -f [force] option.
+ ------------------------------------------------------------------
+ EOF
+ exit 1
+fi
+
+if [ ! -r $dsa_parms ]; then
+ if [ $opt_server -eq 0 ]; then
+ echo "Please run $myname in server-initialization mode first" >&2
+ exit 1
+ fi
+
+ cat <<-EOF
+
+ ------------------------------------------------------------------
+ | I will now try to generate a set of DSA parameters. This can be
+ | a slow process, so please be patient.
+ ------------------------------------------------------------------
+ EOF
+
+ mkdir -p `dirname $dsa_parms`
+ openssl dsaparam $dsa_bits -out $dsa_parms ||
+ exit 1
+
+ # DSA parameters are public
+ chmod 644 $dsa_parms
+fi
+
+cat <<EOF
+------------------------------------------------------------------
+| I will now try to generate a DSA private key and store it in
+| $dsa_priv.
+|
+| The key will not be protected by a passphrase.
+------------------------------------------------------------------
+EOF
+openssl gendsa -out $dsa_priv $dsa_parms
+openssl dsa -pubout -in $dsa_priv -out $dsa_pub
+chmod 644 $dsa_pub
+
+cat <<EOF
+------------------------------------------------------------------
+| Testing new DSA key
+------------------------------------------------------------------
+EOF
+if ! openssl dgst -dss1 -sign $dsa_priv -out $tmpdir/test-sig /etc/hosts; then
+ echo "DSA signature failed - aborting!" >&2
+ exit 1
+fi
+if ! openssl dgst -dss1 -verify $dsa_pub -signature $tmpdir/test-sig /etc/hosts; then
+ echo "DSA verification failed - aborting!" >&2
+ exit 1
+fi
+od -tx1 $tmpdir/test-sig
+
+if [ $opt_server -eq 0 ]; then
+ echo "Installing DSA public key as $dsa_copy"
+ install -d -m 755 $keystore
+ install -m 644 $dsa_pub $dsa_copy
+ install -m 644 $etcdir/auth_key.pub $tmpdir$etcdir/server.pub
+
+ tarball=auth-$client.tar.gz
+ tar -C $tmpdir -czf $tarball .$etcdir
+
+ cat <<-EOF
+ ------------------------------------------------------------------
+ | Successfully packaged $tarball
+ | Please copy this file to client $client and install
+ ------------------------------------------------------------------
+ EOF
+fi
diff --git a/utils/open-isns/tests/harness.pl b/utils/open-isns/tests/harness.pl
new file mode 100644
index 0000000..d7ce025
--- /dev/null
+++ b/utils/open-isns/tests/harness.pl
@@ -0,0 +1,929 @@
+#!/usr/bin/perl
+
+use Getopt::Long;
+
+$__isns_verbose = 1;
+$__isns_security = 1;
+
+$__isns_bin = "../";
+$__isns_seq = 0;
+$__isns_test_base = '/tmp/isns-test';
+$__isns_test_dir = '/tmp/isns-test/test';
+$__isns_stage = 1;
+$__isns_test_data = '';
+$__isns_test_dump = '';
+$__isns_passed = 0;
+$__isns_failed = 0;
+$__isns_warned = 0;
+@__isns_servers = ();
+
+%__isns_ignore_tag = (
+ "0004" => 1, # Timestamp
+ "0603v" => 1, # DSA public key
+);
+
+sub isns_fail {
+
+ print "*** FAILURE ***\n";
+ $__isns_failed++;
+
+ my $line;
+ foreach $line (@_) {
+ print "*** $line ***\n";
+ }
+}
+
+sub isns_pass {
+
+ print "*** SUCCESS ***\n" if ($__isns_verbose > 1);
+ $__isns_passed++;
+}
+
+sub isns_warn {
+
+ printf "*** WARNING: %s ***\n", join(' ', @_);
+ $__isns_warned++;
+}
+
+sub isns_die {
+
+ printf "*** TERMINAL FAILURE: %s ***\n", join(' ', @_);
+ $__isns_failed++;
+
+ &isns_finish;
+ die "Test aborted\n";
+}
+
+sub isns_finish {
+
+ my $pid;
+ foreach $pid (@__isns_servers) {
+ kill 15, $pid or &isns_warn("Cannot kill server process (pid=$pid): $!\n");
+ }
+
+ &isns_report;
+}
+
+sub isns_report {
+
+ print "*** Test $__isns_test_name complete.";
+ print " PASSED: $__isns_passed" if ($__isns_passed);
+ print " FAILED: $__isns_failed" if ($__isns_failed);
+ print " WARNINGS: $__isns_warned" if ($__isns_warned);
+ print " ***\n";
+}
+
+sub isns_info {
+
+ print @_ if ($__isns_verbose > 1);
+}
+
+sub isns_notice {
+
+ print @_ if ($__isns_verbose > 0);
+}
+
+sub isns_stage {
+
+ local($name, @msg) = @_;
+
+ if ($name =~ m/^[0-9]/o) {
+ $__isns_stage_name = $name;
+ } else {
+ $__isns_stage_name = sprintf "%02d-%s",
+ $__isns_stage++, $name;
+ }
+ &isns_notice("*** $__isns_stage_name: ", @msg, " ***\n");
+}
+
+sub build_config {
+
+ local($src_file, $dst_file, *__subst) = @_;
+ my $key;
+ my $okey;
+ my $value;
+ my $sepa;
+ my %subst;
+
+ &isns_info("*** Building $src_file -> $dst_file\n");
+
+ # Translate all keys to lower case.
+ foreach $key (keys(%__subst)) {
+ $value = $__subst{$key};
+ $key =~ tr/A-Z/a-z/;
+ $subst{$key} = $value;
+ }
+# foreach $key (keys(%subst)) {
+# printf " %s -> %s\n", $key, $subst{$key};
+# }
+
+ open IN, "<$src_file" or die "$src_file: $!\n";
+ open OUT, ">$dst_file" or die "$dst_file: $!\n";
+
+ while (<IN>) {
+ $line = $_;
+ if (m:(\S+)(\s*=\s*)(.*):o) {
+ ($okey, $sepa, $value) = ($1, $2, $3);
+
+ $key = $okey;
+ $key =~ tr/A-Z/a-z/;
+
+ if ($subst{$key}) {
+ $line = "$okey$sepa$subst{$key}\n";
+ }
+ }
+
+ # Ignore unconfigured lines.
+ next if ($line =~ m/\@[A-Z_]*\@/o);
+ print OUT $line;
+ }
+ close OUT;
+ close IN;
+}
+
+sub get_config_value {
+ local($cfg_file, $item_name) = @_;
+ my $result;
+ my $name;
+ my $value;
+
+ $item_name =~ tr/A-Z/a-z/;
+
+ open IN, "<$cfg_file" or die "$cfg_file: $!\n";
+ while (<IN>) {
+ chop;
+ ($name, $value) = split(/\s+=\s+/, $_);
+
+ $name =~ tr/A-Z/a-z/;
+ if ($name eq $item_name) {
+ $result = $value;
+ last;
+ }
+ }
+ close IN;
+
+ return $result;
+}
+
+sub create_key {
+
+ local($keyfile) = @_;
+
+ if ($__isns_security) {
+ &isns_info("*** Creating key at $keyfile\n");
+ system "./genkey -fsk $keyfile 2048 >${keyfile}.log 2>&1";
+ }
+ return $keyfile;
+}
+
+sub create_server {
+
+ local(*override) = @_;
+ my %local_config;
+ my $my_dir;
+ my $handle;
+ my $config;
+
+ $handle = sprintf "server%d", $__isns_seq++;
+ $my_dir = "$__isns_test_dir/${handle}";
+
+ mkdir $my_dir, 0700 or die "Cannot create $my_dir: $!\n";
+
+ $server_addr = "127.0.0.1:7770" unless ($server_addr);
+
+ $config = "$my_dir/config";
+
+ $local_config{"SourceName"} = "isns.$handle";
+ $local_config{"Database"} = "$my_dir/database";
+ $local_config{"BindAddress"} = "$server_addr";
+ $local_config{"PIDFile"} = "$my_dir/pid";
+ $local_config{"ControlSocket"} = "$my_dir/control";
+ $local_config{"Security"} = $__isns_security;
+ $local_config{"AuthKeyFile"} = &create_key("$my_dir/auth_key");
+
+ foreach $key (keys(%override)) {
+ $local_config{$key} = $override{$key};
+ }
+
+ &build_config('server.conf', $config, \%local_config);
+ return $config;
+}
+
+sub create_client {
+
+ local($server_config, $client_address) = @_;
+ my %local_config;
+ my $server_key;
+ my $control_socket;
+ my $server_addr;
+ my $my_dir;
+ my $handle;
+ my $config;
+
+ $handle = sprintf "client%d", $__isns_seq++;
+ $my_dir = "$__isns_test_dir/${handle}";
+
+ mkdir $my_dir, 0700 or die "Cannot create $my_dir: $!\n";
+
+ $control_socket = &get_config_value($server_config, "ControlSocket");
+ $server_addr = &get_config_value($server_config, "BindAddress");
+ $server_addr = "127.0.0.1" unless ($server_addr);
+
+ $config = "$my_dir/config";
+
+ $local_config{"SourceName"} = "isns.$handle";
+ $local_config{"AuthName"} = "$handle.isns-test.eu";
+ $local_config{"ServerAddress"} = $server_addr;
+ $local_config{"ControlSocket"} = $control_socket;
+ $local_config{"BindAddress"} = $client_address if ($client_address);
+ $local_config{"server_config"} = $server_config;
+ $local_config{"Security"} = $__isns_security;
+ $local_config{"AuthKeyFile"} = &create_key("$my_dir/auth_key");
+ $local_config{"ServerKeyFile"} =
+ &get_config_value($server_config, "AuthKeyFile") . ".pub";
+
+ &build_config('client.conf', $config, \%local_config);
+
+ $__isns_data{$config,"server_config"} = $server_config;
+ $__isns_data{$config} = %local_config;
+ return $config;
+}
+
+sub get_logfile {
+
+ local($config) = @_;
+ my $dir;
+
+ $dir = $config;
+ $dir =~ s|/+[^/]+$||o;
+
+ return "$dir/logfile";
+}
+
+sub run_command {
+
+ local(@cmd) = @_;
+ my $status;
+ my $cmd;
+
+ $cmd = join(' ', @cmd);
+ &isns_info("$cmd\n");
+
+ system "$cmd";
+
+ $status = $?;
+ if ($status) {
+ &isns_warn("Command failed, exit status $status");
+ print "*** Command was: $cmd ***\n";
+ return undef;
+ }
+
+ return 1;
+}
+
+sub isns_start_server {
+
+ local($server_config) = @_;
+ my $logfile;
+ my $pidfile;
+ my $pid;
+
+ die "restart_server: missing server config argument!\n"
+ unless(-f $server_config);
+ $logfile = &get_logfile($server_config);
+ $pidfile = &get_config_value($server_config, "PIDFile");
+
+ &isns_info("*** Starting server (logging to $logfile)\n");
+
+ $pid = fork();
+ if ($pid) {
+ my $retry;
+
+ if ($pidfile) {
+ for ($retry = 0; $retry < 5; $retry++) {
+ last if (-f $pidfile);
+ sleep 1;
+ }
+ $pid = `cat $pidfile` if ($pidfile);
+ chop($pid);
+ }
+ &isns_info("*** Started server (pid=$pid) ***\n");
+ push(@__isns_servers, $pid);
+ return $pid;
+ }
+
+ &isns_info("${__isns_bin}isnsd -c $server_config -f -d all\n");
+ exec "${__isns_bin}isnsd -c $server_config -f -d all >$logfile 2>&1 &"
+ or die "Unable to run isnsd: $!\n";
+}
+
+sub isns_stop_server {
+
+ local($pid) = @_;
+ my @list;
+ my $p;
+
+ kill 15, $pid or &isns_warn("Cannot kill server process (pid=$pid): $!\n");
+ foreach $p (@__isns_servers) {
+ append(@list, $p) unless ($p == $pid);
+ }
+ @__isns_servers = @list;
+}
+
+sub isns_restart_server {
+
+ local($pid, $server_config);
+
+ if ($_[0] =~ m:^\d+$:o) {
+ $pid = shift(@_);
+ } else {
+ if ($#__isns_servers < 0) {
+ &isns_warn("isns_restart_server: no server running\n");
+ return 0;
+ }
+ $pid = $__isns_servers[0];
+ }
+ $server_config = shift(@_);
+
+ &isns_stop_server($pid);
+ return &isns_start_server($server_config);
+}
+
+sub isns_verify_db {
+
+ local($stage, $server_config);
+ my $dump_file;
+ my $data_file;
+
+ if ($_[0] =~ m/^\d/o) {
+ $stage = shift(@_);
+ } else {
+ $stage = $__isns_stage_name;
+ }
+ $server_config = shift(@_);
+
+ die "Test case forgot to call test_prep" unless($__isns_test_data);
+
+ $dump_file = "$__isns_test_dump/$stage";
+ unless (&run_command("${__isns_bin}/isnsd -c $server_config --dump-db > $dump_file")) {
+ &isns_fail;
+ return 0;
+ }
+
+ # See if the reference data file exists. If it
+ # doesn't, this means we're priming the test case.
+ # Just copy the dump file.
+ $data_file = "$__isns_test_data/$stage";
+ unless (-f $data_file) {
+ print "*** Saving database dump for stage $stage ***\n";
+ mkdir $__isns_test_data, 0755;
+ system "cp $dump_file $data_file";
+ return 1;
+ }
+
+ &isns_info("*** Verifying database dump for stage $stage ***\n");
+ if (&verify_dump($stage, $data_file, $dump_file)) {
+ &isns_pass;
+ } else {
+ if ($__isns_verbose > 1) {
+ system("diff -u -ITimestamp -I'DSA security key' $data_file $dump_file");
+ }
+ &isns_fail;
+ }
+
+ return 1;
+}
+
+sub verify_db {
+
+ &isns_verify_db(@_);
+}
+
+sub verify_response {
+
+ local($stage, $client_config) = @_;
+ my $dump_file;
+ my $data_file;
+
+ die "Test case forgot to call test_prep" unless($__isns_test_data);
+
+ $dump_file = &get_logfile($client_config);
+
+ # See if the reference data file exists. If it
+ # doesn't, this means we're priming the test case.
+ # Just copy the dump file.
+ $data_file = "$__isns_test_data/$stage";
+ unless (-f $data_file) {
+ print "*** Saving data for stage $stage ***\n";
+ mkdir $__isns_test_data, 0755;
+ system "cp $dump_file $data_file";
+ return 1;
+ }
+
+ &isns_info("*** Verifying data for stage $stage ***\n");
+ if (&verify_query($stage, $data_file, $dump_file)) {
+ &isns_pass;
+ } else {
+ &isns_fail("Query response returns unexpected data");
+ system "cp $dump_file $__isns_test_dump/$stage";
+ print "*** Saved dump as $__isns_test_dump/$stage\n";
+ print "*** Reference data in $data_file\n";
+ if ($__isns_verbose > 1) {
+ system("diff -u -ITimestamp -I'DSA security key' $data_file $dump_file");
+ }
+ }
+
+ return 1;
+}
+
+sub verify_dump {
+
+ local($stage, $data_file, $dump_file) = @_;
+ my $line;
+ my @dump;
+ my @data;
+ my @obj1;
+ my @obj2;
+
+ @dump = &load_dump($dump_file);
+ @data = &load_dump($data_file);
+
+ &skip_header(\@dump);
+ &skip_header(\@data);
+
+ while (1) {
+ $line++;
+
+ @obj1 = &get_next_object(\@dump);
+ @obj2 = &get_next_object(\@data);
+
+ last unless(@obj1 || @obj2);
+
+ unless (@obj1 && @obj2) {
+ print STDERR "*** $stage: Excess data at end of dump\n";
+ return 0;
+ }
+
+ unless (&compare_objects(\@obj1, \@obj2)) {
+ print STDERR "*** Object mismatch (object $line):\n";
+ print STDERR "Expected:\n ";
+ print STDERR join("\n ", @obj2), "\n";
+ print STDERR "Got:\n ";
+ print STDERR join("\n ", @obj1), "\n";
+ return 0;
+ }
+ }
+
+ if (@data) {
+ print STDERR "*** $stage: Unexpected end of dump at line $line\n";
+ return 0;
+ }
+
+ return 1;
+}
+
+sub skip_header {
+
+ local(*list) = @_;
+ local($_);
+
+ while ($_ = shift(@list)) {
+ last if (/^-/o);
+ }
+}
+
+sub get_next_object {
+
+ local(*list) = @_;
+ local($_, $header, @result);
+ my @tags;
+
+ while ($_ = shift(@list)) {
+ next if (/^-/o);
+ if (/^\s+([0-9a-fv]+)\s+/o) {
+ next if ($__isns_ignore_tag{$1});
+ push(@tags, $_);
+ } else {
+ if (@result) {
+ unshift(@list, $_);
+ last;
+ }
+ push(@result, $_);
+ }
+ #print "### $_\n";
+ }
+
+ if (@tags) {
+ push(@result, sort(@tags));
+ }
+ return @result;
+}
+
+sub compare_objects {
+
+ local(*a, *b) = @_;
+ local($i);
+
+ return 0 unless ($#a == $#b);
+ for ($i = 0; $i <= $#a; $i++) {
+ return 0 unless ($a[$i] eq $b[$i]);
+ }
+
+ return 1;
+}
+
+
+sub verify_query {
+
+ local($stage, $data_file, $dump_file) = @_;
+ my $line;
+ my @dump;
+ my @data;
+
+ @dump = &load_dump($dump_file);
+ @data = &load_dump($data_file);
+
+ while (@dump) {
+ $line++;
+ unless (@data) {
+ print STDERR "*** $stage: Excess data in dump at line $line\n";
+ return 0;
+ }
+
+ $a = shift(@dump);
+ $b = shift(@data);
+ if ($a =~ /^\S/o) {
+ next if ($a eq $b);
+ print STDERR "*** $stage: Mismatch at line $line ***\n";
+ print STDERR "*** Found: $a\n";
+ print STDERR "*** Expected: $b\n";
+ return 0;
+ }
+
+ ($nix, $a_tag, $a_value) = split(/\s+/, $a, 3);
+ ($nix, $b_tag, $b_value) = split(/\s+/, $b, 3);
+ if ($a_tag ne $b_tag) {
+ print STDERR "*** $stage: Tag mismatch at line $line\n";
+ print STDERR "*** Found: $a\n";
+ print STDERR "*** Expected: $b\n";
+ return 0;
+ }
+
+ next if ($__isns_ignore_tag{$a_tag});
+ if ($a_value ne $b_value) {
+ print STDERR "*** $stage: Value mismatch at line $line (tag $a_tag)\n";
+ print STDERR "*** Found: $a\n";
+ print STDERR "*** Expected: $b\n";
+ return 0;
+ }
+ }
+
+ if (@data) {
+ print STDERR "*** $stage: Unexpected end of dump at line $line\n";
+ return 0;
+ }
+
+ return 1;
+}
+
+sub load_dump {
+
+ local($filename) = @_;
+ my @result;
+
+ open IN, $filename or die "Unable to open $filename: $!\n";
+ while (<IN>) {
+ chop;
+ push(@result, $_);
+ }
+ close IN;
+ return @result;
+}
+
+
+sub run_client {
+
+ local($config, @args) = @_;
+ my $logfile;
+ my $cmd;
+
+ $logfile = &get_logfile($config);
+
+ $cmd = "${__isns_bin}/isnsadm -c $client_config " . join(' ', @args);
+ if (&run_command("$cmd >$logfile")) {
+ return $logfile;
+ }
+ return undef;
+}
+
+sub __isns_enroll_client {
+
+ local($client_config, @extra_args) = @_;
+ my $source_name;
+ my $auth_name;
+ my $auth_key;
+ my @args;
+
+ $source_name = &get_config_value($client_config, "SourceName");
+ $auth_name = &get_config_value($client_config, "AuthName");
+ $auth_key = &get_config_value($client_config, "AuthKeyFile");
+
+ push(@args, "--local --enroll $auth_name node-name=$source_name");
+ push(@args, " key=${auth_key}.pub") if ($auth_key);
+ push(@args, @extra_args) if (@extra_args);
+
+ &run_client($client_config, @args);
+}
+
+sub isns_enroll_client {
+
+ local($client, @args) = @_;
+ my $server;
+
+ $server = $__isns_data{$client,"server_config"};
+ &isns_stage("enroll", "Enrolling client");
+ &__isns_enroll_client($client, @args);
+ &verify_db($__isns_stage_name, $server);
+}
+
+sub enroll_client {
+
+ print "*** Enrolling client ***\n";
+ &__isns_enroll_client(@_);
+}
+
+sub __isns_register_client {
+
+ local($client_config, @extra_args) = @_;
+ my @args;
+
+ push(@args, "--register");
+ push(@args, @extra_args) if (@extra_args);
+
+ &run_client($client_config, @args);
+}
+
+sub isns_register_client {
+
+ local($client, @args) = @_;
+ my $server;
+
+ $server = $__isns_data{$client,"server_config"};
+ &isns_stage("registration", "Registering client " . join(' ', @args));
+ &__isns_register_client($client, @args);
+ &verify_db($__isns_stage_name, $server);
+}
+
+sub register_client {
+
+ print "*** Registering client ***\n";
+ &__isns_register_client(@_);
+}
+
+sub __isns_query_objects {
+
+ local($client_config, @extra_args) = @_;
+ my @args;
+
+ push(@args, "--query");
+ push(@args, @extra_args) if (@extra_args);
+
+ return &run_client($client_config, @args);
+}
+
+sub isns_query_objects {
+
+ local($client, @args) = @_;
+
+ &isns_stage("query", "Querying " . join(' ', @args));
+ &__isns_query_objects($client, @args);
+ &verify_response($__isns_stage_name, $client);
+}
+
+sub query_objects {
+
+ print "*** Querying objects ***\n";
+ __isns_query_objects(@_);
+}
+
+sub isns_query_eid {
+
+ local($client_config, @extra_args) = @_;
+ my $logfile;
+ my @args;
+ local($eid);
+
+ push(@args, "--query-eid");
+ push(@args, @extra_args) if (@extra_args);
+
+ &isns_info("*** Querying for EID ***\n");
+ $logfile = &run_client($client_config, @args);
+
+ if ($logfile) {
+ $eid = `cat $logfile`;
+ unless ($eid) {
+ &isns_fail("Server reports empty EID");
+ }
+ chop($eid);
+ }
+
+ return $eid;
+}
+
+sub __isns_unregister_client {
+
+ local($client_config, @extra_args) = @_;
+ my @args;
+
+ push(@args, "--deregister");
+ push(@args, @extra_args) if (@extra_args);
+
+ &run_client($client_config, @args);
+}
+
+sub isns_unregister_client {
+
+ my $stage = 0;
+ my $client;
+ my $server;
+ my $eid;
+
+ if ($_[0] =~ m/^\d/o) {
+ &isns_stage(shift(@_), "Unregister client");
+ } else {
+ &isns_stage("unregistration", "Unregister client");
+ }
+
+ $client = shift(@_);
+
+ unless (@_) {
+ $eid = &isns_query_eid($client);
+ push(@_, "eid=$eid");
+ }
+
+ &__isns_unregister_client($client, @_);
+
+ $server = $__isns_data{$client,"server_config"};
+ &verify_db($__isns_stage_name, $server);
+}
+
+sub unregister_client {
+
+ &isns_info("*** Unregistering client ***\n");
+ &__isns_unregister_client(@_);
+}
+
+sub __isns_register_domain {
+
+ local($client_config, @extra_args) = @_;
+ my @args;
+
+ push(@args, "--local --dd-register");
+ push(@args, @extra_args) if (@extra_args);
+
+ &run_client($client_config, @args);
+}
+
+sub isns_register_domain {
+
+ local($client, @args) = @_;
+ my $server;
+
+ &isns_stage("dd-registration", "Registering DD " . join(' ', @args));
+ &__isns_register_domain($client, @args);
+
+ $server = $__isns_data{$client,"server_config"};
+ &isns_verify_db($server);
+}
+
+sub register_domain {
+
+ &isns_info("*** Registering DD ***\n");
+ &__isns_register_domain(@_);
+}
+
+sub __isns_deregister_domain {
+
+ local($client_config, @extra_args) = @_;
+ my @args;
+
+ push(@args, "--local --dd-deregister");
+ push(@args, @extra_args) if (@extra_args);
+
+ &run_client($client_config, @args);
+}
+
+sub isns_deregister_domain {
+
+ local($client, @args) = @_;
+ my $server;
+
+ &isns_stage("dd-deregistration", "Deregistering DD (members)" . join(' ', @args));
+ &__isns_deregister_domain($client, @args);
+
+ $server = $__isns_data{$client,"server_config"};
+ &isns_verify_db($server);
+}
+
+sub isns_external_test {
+
+ local($client, @args) = @_;
+ my $logfile;
+ my $stage;
+ my $cmd;
+
+ $logfile = &get_logfile($client);
+
+ $cmd = shift(@args);
+ $stage = $cmd;
+ $stage =~ s:.*/::o;
+
+ $cmd = "${__isns_bin}/$cmd -c $client " . join(' ', @args);
+
+ &isns_stage($stage, "Running external $cmd " . join(' ', @args));
+ unless (&run_command("$cmd >$logfile")) {
+ return undef;
+ }
+
+ $server = $__isns_data{$client,"server_config"};
+ &isns_verify_db($server);
+}
+
+sub __isns_prep_test {
+
+ local($name, $duration, @ARGV) = @_;
+
+ GetOptions('verbose+' => \$__isns_verbose,
+ "quiet" => \$__isns_quiet,
+ "fast" => \$__isns_quick,
+ "insecure" => \$__isns_insecure);
+ $__isns_verbose = 0 if ($__isns_quiet);
+ $__isns_security = 0 if ($__isns_insecure);
+
+ if ($__isns_quick && $duration > 15) {
+ print "*** Skipping $name (duration ~ $duration seconds) ***\n";
+ exit(0);
+ }
+
+ print "*** Starting $name ***\n";
+ printf "*** This test case will take about %u sec ***\n", $duration
+ if ($duration);
+ $__isns_test_name = $name;
+ $__isns_test_dir = "$__isns_test_base/$name";
+ $__isns_test_dump = "$__isns_test_dir/dump";
+ $__isns_test_data = "data/$name";
+
+ # Be careful when removing test dir
+ system "rm -rf $__isns_test_dir" if ($__isns_test_dir =~ m:/tmp/:o);
+
+ mkdir $__isns_test_base, 0700;
+ mkdir $__isns_test_dir, 0700;
+ mkdir $__isns_test_dump, 0700;
+}
+
+sub test_prep {
+
+ local($name, @args) = @_;
+
+ __isns_prep_test($name, 0, @args);
+}
+
+sub isns_prep_slow_test {
+
+ __isns_prep_test(@_);
+}
+
+# Sleep for a few seconds, giving the user some dots to keep
+# him occupied.
+sub isns_idle {
+
+ local($time) = @_;
+
+ if ($__isns_verbose == 0) {
+ sleep $time;
+ return;
+ }
+
+ $| = 1;
+ print "Snooze";
+ while ($time--) {
+ print ".";
+ sleep 1;
+ }
+ print "\n";
+ $| = 0;
+}
+
+sub main {
+
+ my $server_config;
+ my $client_config;
+
+ &test_prep;
+
+ $server_config = &create_server;
+ $client_config = &create_client($server_config);
+}
+
+#&main;
+1;
diff --git a/utils/open-isns/tests/pauw1.c b/utils/open-isns/tests/pauw1.c
new file mode 100644
index 0000000..c3e66f7
--- /dev/null
+++ b/utils/open-isns/tests/pauw1.c
@@ -0,0 +1,179 @@
+/*
+ * Test case, captured from a Wasabi Storage Builder
+ * registering itself.
+ */
+#include <getopt.h>
+#include <isns.h>
+#include <paths.h>
+#include <util.h>
+#include <message.h>
+
+int
+main(int argc, char **argv)
+{
+ const char *opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG;
+ isns_client_t *clnt;
+ isns_attr_list_t *attrs;
+ isns_simple_t *reg;
+ isns_portal_info_t portal_info;
+ uint32_t status;
+ int c;
+
+ while ((c = getopt(argc, argv, "c:d:")) != -1) {
+ switch (c) {
+ case 'c':
+ opt_configfile = optarg;
+ break;
+
+ case 'd':
+ isns_enable_debugging(optarg);
+ break;
+
+ default:
+ isns_fatal("Unknown option\n");
+ }
+ }
+
+ isns_read_config(opt_configfile);
+ isns_assign_string(&isns_config.ic_source_name,
+ "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0");
+
+ clnt = isns_create_default_client(NULL);
+
+ reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER,
+ clnt->ic_source, NULL);
+
+ attrs = &reg->is_operating_attrs;
+
+#define ADD(type, tag, value) \
+ isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value)
+#define STR(tag, value) ADD(string, tag, value)
+#define U32(tag, value) ADD(uint32, tag, value)
+#define NIL(tag) isns_attr_list_append_nil(attrs, ISNS_TAG_##tag)
+#define TARGET(name, alias, auth) \
+ STR(ISCSI_NAME, name); \
+ U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); \
+ STR(ISCSI_ALIAS, alias); \
+ STR(ISCSI_AUTHMETHOD, auth)
+
+ STR(ENTITY_IDENTIFIER, "cyan.pauw.homeunix.net");
+ U32(ENTITY_PROTOCOL, 2);
+ U32(REGISTRATION_PERIOD, 31536000);
+
+ TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0",
+ "Test (10 GB)",
+ "None");
+ TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1",
+ "160 GB disk (ntfs)",
+ "None");
+ TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2",
+ "160 GB disk (ext3)",
+ "CHAP");
+ TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3",
+ "Test (1 GB)",
+ "None");
+ TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4",
+ "Test (40 GB)",
+ "CHAP");
+ TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5",
+ "test",
+ "None");
+
+ isns_portal_parse(&portal_info, "10.0.0.1:3260/tcp", NULL);
+ isns_portal_to_attr_list(&portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ attrs);
+
+ /* Mumbo jumbo encoding of portal groups */
+ U32(PG_TAG, 1);
+ STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0");
+ STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1");
+ STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2");
+ STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3");
+ STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4");
+ STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5");
+
+ /* Strictly speaking, a PGT not followed by any data is invalid.
+ *
+ * 5.6.5.1.
+ * When a Portal is registered, the Portal attributes MAY
+ * immediately be followed by a PGT attribute. The PGT attribute
+ * SHALL be followed by the set of PG iSCSI Names representing
+ * nodes that will be associated to the Portal using the indicated
+ * PGT value.
+ */
+ NIL(PG_TAG);
+
+ isns_simple_print(reg, isns_print_stdout);
+
+ status = isns_client_call(clnt, &reg);
+
+ if (status != ISNS_SUCCESS)
+ isns_fatal("Unable to register object: %s\n",
+ isns_strerror(status));
+
+ printf("Successfully registered object(s)\n");
+ isns_simple_print(reg, isns_print_stdout);
+
+ return 0;
+}
+
+/*
+ Creating file DB backend (/var/lib/isns)
+ DB: loading all objects from /var/lib/isns
+ Next ESI message in 3600 seconds
+ Incoming PDU xid=0001 seq=0 len=1208 func=DevAttrReg client first last
+ Next message xid=0001
+ Received message
+ ---DevAttrReg---
+ Source:
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"
+ Message attributes: <empty list>
+ Operating attributes:
+ 0001 string : Entity identifier = "cyan.pauw.homeunix.net"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0006 uint32 : Registration Period = 31536000
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "Test (10 GB)"
+ 002a string : iSCSI auth method = "None"
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1"
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "160 GB disk (ntfs)"
+ 002a string : iSCSI auth method = "None"
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2"
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "160 GB disk (ext3)"
+ 002a string : iSCSI auth method = "CHAP"
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3"
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "Test (1 GB)"
+ 002a string : iSCSI auth method = "None"
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4"
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "Test (40 GB)"
+ 002a string : iSCSI auth method = "CHAP"
+ 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5"
+ 0021 uint32 : iSCSI node type = Target
+ 0022 string : iSCSI alias = "test"
+ 002a string : iSCSI auth method = "None"
+ 0010 ipaddr : Portal IP address = 10.0.0.1
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0033 uint32 : Portal group tag = 1
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1"
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2"
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3"
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4"
+ 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5"
+ 0033 nil : Portal group tag = <empty>
+ :: policy insecure function DevAttrReg (0001) permitted
+ :: policy insecure source
+iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0 permitted
+ :: policy insecure operation DevAttrReg on Network Entity object
+permitted
+ DB: Storing object 00000001 -> /var/lib/isns/00000001
+ DB: added object 1 (Network Entity) state 1
+Segmentation fault
+ */
diff --git a/utils/open-isns/tests/pauw2.c b/utils/open-isns/tests/pauw2.c
new file mode 100644
index 0000000..29084b3
--- /dev/null
+++ b/utils/open-isns/tests/pauw2.c
@@ -0,0 +1,212 @@
+/*
+ * Test case, captured from iscsi-target
+ * registering itself.
+ */
+#include <getopt.h>
+#include <isns.h>
+#include <paths.h>
+#include <util.h>
+#include <message.h>
+
+#define ADD(type, tag, value) \
+ isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value)
+#define STR(tag, value) ADD(string, tag, value)
+#define U32(tag, value) ADD(uint32, tag, value)
+#define NIL(tag) isns_attr_list_append_nil(attrs, ISNS_TAG_##tag)
+#define TARGET(name, alias, auth) \
+ STR(ISCSI_NAME, name); \
+ U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); \
+ STR(ISCSI_ALIAS, alias); \
+ STR(ISCSI_AUTHMETHOD, auth)
+
+int
+main(int argc, char **argv)
+{
+ const char *opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG;
+ isns_client_t *clnt;
+ isns_attr_list_t *attrs;
+ isns_simple_t *reg;
+ isns_portal_info_t portal_info;
+ uint32_t status;
+ int c;
+
+ while ((c = getopt(argc, argv, "c:d:")) != -1) {
+ switch (c) {
+ case 'c':
+ opt_configfile = optarg;
+ break;
+
+ case 'd':
+ isns_enable_debugging(optarg);
+ break;
+
+ default:
+ isns_fatal("Unknown option\n");
+ }
+ }
+
+ isns_read_config(opt_configfile);
+
+ /*
+ ---DevAttrReg[REPLACE]---
+ Source:
+ 0020 string : iSCSI name = "iqn.2007-03.com.example:stgt.disk"
+ Message attributes:
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ Operating attributes:
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 3260/tcp
+ 0017 uint32 : SCN port = 42138/tcp
+ 0020 string : iSCSI name = "iqn.2007-03.com.example:stgt.disk"
+ 0021 uint32 : iSCSI node type = Target
+ */
+ isns_assign_string(&isns_config.ic_source_name,
+ "iqn.2007-03.com.example:stgt.disk");
+
+ clnt = isns_create_default_client(NULL);
+ reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER,
+ clnt->ic_source, NULL);
+ reg->is_replace = 1;
+
+ /* Message attributes */
+ attrs = &reg->is_message_attrs;
+ STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net");
+
+ /* Operating attributes */
+ attrs = &reg->is_operating_attrs;
+
+ STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net");
+ U32(ENTITY_PROTOCOL, 2);
+
+ isns_portal_parse(&portal_info, "192.168.1.2:3260/tcp", NULL);
+ isns_portal_to_attr_list(&portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ attrs);
+
+ U32(SCN_PORT, 42138);
+ STR(ISCSI_NAME, "iqn.2007-03.com.example:stgt.disk");
+ U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK);
+ isns_simple_print(reg, isns_print_stdout);
+
+ status = isns_client_call(clnt, &reg);
+
+ if (status != ISNS_SUCCESS)
+ isns_fatal("Unable to register object: %s\n",
+ isns_strerror(status));
+
+ printf("Successfully registered object #1\n");
+ // isns_simple_print(reg, isns_print_stdout);
+ isns_simple_free(reg);
+ isns_client_destroy(clnt);
+
+ /*
+ ---DevAttrReg[REPLACE]---
+ Source:
+ 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
+ Message attributes:
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ Operating attributes:
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 33849/tcp
+ 0014 uint32 : ESI port = 56288/tcp
+ 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0022 string : iSCSI alias = "blue.pauw.homeunix.net"
+
+ [...]
+ response status 0x0003 (Invalid registration)
+
+ This would fail because we got confused about EID in
+ the replace case.
+ */
+ isns_assign_string(&isns_config.ic_source_name,
+ "iqn.2005-03.org.open-iscsi:blue");
+
+ clnt = isns_create_default_client(NULL);
+ reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER,
+ clnt->ic_source, NULL);
+ reg->is_replace = 1;
+
+ /* Message attributes */
+ attrs = &reg->is_message_attrs;
+ STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net");
+
+ /* Operating attributes */
+ attrs = &reg->is_operating_attrs;
+
+ STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net");
+ U32(ENTITY_PROTOCOL, 2);
+
+ isns_portal_parse(&portal_info, "192.168.1.2:33849/tcp", NULL);
+ isns_portal_to_attr_list(&portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ attrs);
+
+ U32(ESI_PORT, 56288);
+ STR(ISCSI_NAME, "iqn.2005-03.org.open-iscsi:blue");
+ U32(ISCSI_NODE_TYPE, ISNS_ISCSI_INITIATOR_MASK);
+ STR(ISCSI_ALIAS, "blue.pauw.homeunix.net");
+ isns_simple_print(reg, isns_print_stdout);
+
+ status = isns_client_call(clnt, &reg);
+
+ if (status != ISNS_SUCCESS)
+ isns_fatal("Unable to register object: %s\n",
+ isns_strerror(status));
+
+ printf("Successfully registered object #2\n");
+ // isns_simple_print(reg, isns_print_stdout);
+ isns_simple_free(reg);
+ isns_client_destroy(clnt);
+
+ return 0;
+}
+
+/*
+ Creating file DB backend (/var/lib/isns)
+ DB: loading all objects from /var/lib/isns
+ Next ESI message in 3600 seconds
+ Incoming PDU xid=0001 seq=0 len=232 func=DevAttrReg client first last
+ Next message xid=0001
+ Received message
+
+ :: policy insecure function DevAttrReg (0001) permitted
+ :: policy insecure source iqn.2005-03.org.open-iscsi:blue permitted
+ :: policy insecure operation DevAttrReg on object 00000001 (Network
+Entity) permitted
+ Replacing Network Entity (id 1)
+ DB: removed object 2 (Portal)
+ DB: removed object 4 (iSCSI Portal Group)
+ DB: removed object 3 (iSCSI Storage Node)
+ DB: removed object 1 (Network Entity)
+ DB: destroying object 2 (Portal)
+ DB: Purging object 2 (/var/lib/isns/00000002)
+ DB: destroying object 1 (Network Entity)
+ DB: Purging object 1 (/var/lib/isns/00000001)
+ DB: destroying object 3 (iSCSI Storage Node)
+ DB: Purging object 3 (/var/lib/isns/00000003)
+ DB: destroying object 4 (iSCSI Portal Group)
+ DB: Purging object 4 (/var/lib/isns/00000004)
+ :: policy insecure entity ID blue.pauw.homeunix.net permitted
+ :: policy insecure operation DevAttrReg on Network Entity object
+permitted
+ DB: Storing object 5 -> /var/lib/isns/00000005
+ DB: added object 5 (Network Entity) state 1
+ DB: Storing object 5 -> /var/lib/isns/00000005
+ isns_esi_callback(0x9dee788, 0x10)
+ Deleting SCN registration for iqn.2007-03.com.example:stgt.disk
+ isns_esi_callback(0x9deeae0, 0x10)
+ isns_esi_callback(0x9deea30, 0x10)
+ isns_esi_callback(0x9deec80, 0x10)
+ SCN multicast <iSCSI Storage Node 3, removed>
+ isns_scn_callback(0x9deec80, 0x10)
+ isns_esi_callback(0x9def4b0, 0xc)
+ Enable ESI monitoring for entity 5
+
+ */
diff --git a/utils/open-isns/tests/pauw3.c b/utils/open-isns/tests/pauw3.c
new file mode 100644
index 0000000..3be0baa
--- /dev/null
+++ b/utils/open-isns/tests/pauw3.c
@@ -0,0 +1,139 @@
+/*
+ * This tests another problem reported by Albert, where a
+ * re-registration shortly before ESI expiry would fail
+ * to resurrect the registration properly.
+ *
+ * Usage:
+ * pauw3 [options] timeout
+ *
+ * Where timeout is the delay until we try to re-register
+ */
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include <isns.h>
+#include <paths.h>
+#include <util.h>
+#include <message.h>
+
+#define ADD(type, tag, value) \
+ isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value)
+#define STR(tag, value) ADD(string, tag, value)
+#define U32(tag, value) ADD(uint32, tag, value)
+#define NIL(tag) isns_attr_list_append_nil(attrs, ISNS_TAG_##tag)
+#define TARGET(name, alias, auth) \
+ STR(ISCSI_NAME, name); \
+ U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); \
+ STR(ISCSI_ALIAS, alias); \
+ STR(ISCSI_AUTHMETHOD, auth)
+
+int
+main(int argc, char **argv)
+{
+ const char *opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG;
+ isns_client_t *clnt;
+ isns_attr_list_t *attrs;
+ isns_simple_t *reg;
+ isns_portal_info_t portal_info;
+ uint32_t status;
+ int opt_replace = 1;
+ int c, n, timeout;
+
+ while ((c = getopt(argc, argv, "c:d:n")) != -1) {
+ switch (c) {
+ case 'c':
+ opt_configfile = optarg;
+ break;
+
+ case 'd':
+ isns_enable_debugging(optarg);
+ break;
+
+ case 'n':
+ opt_replace = 0;
+ break;
+
+ default:
+ isns_fatal("Unknown option\n");
+ }
+ }
+
+ if (optind != argc - 1)
+ isns_fatal("Need timeout argument\n");
+ timeout = parse_timeout(argv[optind]);
+
+ isns_read_config(opt_configfile);
+
+ /*
+ ---DevAttrReg[REPLACE]---
+ Source:
+ 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
+ Message attributes:
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ Operating attributes:
+ 0001 string : Entity identifier = "blue.pauw.homeunix.net"
+ 0002 uint32 : Entity protocol = iSCSI (2)
+ 0010 ipaddr : Portal IP address = 192.168.1.2
+ 0011 uint32 : Portal TCP/UDP port = 33849/tcp
+ 0014 uint32 : ESI port = 56288/tcp
+ 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
+ 0021 uint32 : iSCSI node type = Initiator
+ 0022 string : iSCSI alias = "blue.pauw.homeunix.net"
+
+ [...]
+ response status 0x0003 (Invalid registration)
+
+ This would fail because we got confused about EID in
+ the replace case.
+ */
+ isns_assign_string(&isns_config.ic_source_name,
+ "iqn.2005-03.org.open-iscsi:blue");
+
+ for (n = 0; n < 2; ++n) {
+ clnt = isns_create_default_client(NULL);
+ reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER,
+ clnt->ic_source, NULL);
+ reg->is_replace = opt_replace;
+
+ /* Message attributes */
+ attrs = &reg->is_message_attrs;
+ STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net");
+
+ /* Operating attributes */
+ attrs = &reg->is_operating_attrs;
+
+ STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net");
+ U32(ENTITY_PROTOCOL, 2);
+
+ isns_portal_parse(&portal_info, "192.168.1.2:33849/tcp", NULL);
+ isns_portal_to_attr_list(&portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ attrs);
+
+ U32(ESI_PORT, 56288);
+ STR(ISCSI_NAME, "iqn.2005-03.org.open-iscsi:blue");
+ U32(ISCSI_NODE_TYPE, ISNS_ISCSI_INITIATOR_MASK);
+ STR(ISCSI_ALIAS, "blue.pauw.homeunix.net");
+ isns_simple_print(reg, isns_print_stdout);
+
+ status = isns_client_call(clnt, &reg);
+
+ if (status != ISNS_SUCCESS)
+ isns_fatal("Unable to register object: %s\n",
+ isns_strerror(status));
+
+ printf("Successfully registered object\n");
+ // isns_simple_print(reg, isns_print_stdout);
+ isns_simple_free(reg);
+ isns_client_destroy(clnt);
+
+ if (n == 0) {
+ printf("Sleeping for %d seconds\n", timeout);
+ sleep(timeout);
+ }
+ }
+
+ return 0;
+}
diff --git a/utils/open-isns/tests/pauw4.c b/utils/open-isns/tests/pauw4.c
new file mode 100644
index 0000000..9510ddd
--- /dev/null
+++ b/utils/open-isns/tests/pauw4.c
@@ -0,0 +1,137 @@
+/*
+ * Test MS initiator registration.
+ * The oddity about this is that the PG object precedes the
+ * initiator object in the message.
+ */
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include <isns.h>
+#include <paths.h>
+#include <util.h>
+#include <message.h>
+
+#define ADD(type, tag, value) \
+ isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value)
+#define STR(tag, value) ADD(string, tag, value)
+#define U32(tag, value) ADD(uint32, tag, value)
+#define NIL(tag) isns_attr_list_append_nil(attrs, ISNS_TAG_##tag)
+#define TARGET(name, alias, auth) \
+ STR(ISCSI_NAME, name); \
+ U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); \
+ STR(ISCSI_ALIAS, alias); \
+ STR(ISCSI_AUTHMETHOD, auth)
+
+int
+main(int argc, char **argv)
+{
+ const char *opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG;
+ isns_client_t *clnt;
+ isns_attr_list_t *attrs;
+ isns_simple_t *reg;
+ isns_portal_info_t portal_info;
+ uint32_t status;
+ int opt_replace = 1;
+ int c;
+
+ while ((c = getopt(argc, argv, "c:d:n")) != -1) {
+ switch (c) {
+ case 'c':
+ opt_configfile = optarg;
+ break;
+
+ case 'd':
+ isns_enable_debugging(optarg);
+ break;
+
+ case 'n':
+ opt_replace = 0;
+ break;
+
+ default:
+ isns_fatal("Unknown option\n");
+ }
+ }
+
+ isns_read_config(opt_configfile);
+
+ isns_assign_string(&isns_config.ic_source_name,
+ "iqn.1991-05.com.microsoft:orange");
+
+ clnt = isns_create_default_client(NULL);
+
+ reg = isns_simple_create(ISNS_SCN_DEREGISTER, clnt->ic_source, NULL);
+
+ /* Message attributes */
+ attrs = &reg->is_message_attrs;
+ STR(ISCSI_NAME, "iqn.1991-05.com.microsoft:orange");
+
+ status = isns_client_call(clnt, &reg);
+ if (status != ISNS_SUCCESS)
+ isns_error("SCNDereg failed: %s\n", isns_strerror(status));
+ isns_simple_free(reg);
+
+
+ reg = isns_simple_create(ISNS_DEVICE_DEREGISTER, clnt->ic_source, NULL);
+
+ attrs = &reg->is_operating_attrs;
+ STR(ENTITY_IDENTIFIER, "troopa.nki.nl");
+ U32(ENTITY_PROTOCOL, 2);
+
+ isns_portal_parse(&portal_info, "192.168.1.40:3229/tcp", NULL);
+ isns_portal_to_attr_list(&portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ attrs);
+
+ STR(ISCSI_NAME, "iqn.1991-05.com.microsoft:orange");
+
+ status = isns_client_call(clnt, &reg);
+ if (status != ISNS_SUCCESS)
+ isns_fatal("DevDereg failed: %s\n", isns_strerror(status));
+ isns_simple_free(reg);
+
+ reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, clnt->ic_source, NULL);
+ reg->is_replace = opt_replace;
+
+ attrs = &reg->is_operating_attrs;
+ STR(ENTITY_IDENTIFIER, "troopa.nki.nl");
+ U32(ENTITY_PROTOCOL, 2);
+
+ isns_portal_parse(&portal_info, "192.168.1.40:3229/tcp", NULL);
+ isns_portal_to_attr_list(&portal_info,
+ ISNS_TAG_PORTAL_IP_ADDRESS,
+ ISNS_TAG_PORTAL_TCP_UDP_PORT,
+ attrs);
+
+ U32(SCN_PORT, 3230);
+ U32(ESI_PORT, 3230);
+
+ U32(PG_TAG, 1);
+ STR(PG_ISCSI_NAME, "iqn.1991-05.com.microsoft:orange");
+
+ STR(ISCSI_NAME, "iqn.1991-05.com.microsoft:orange");
+ U32(ISCSI_NODE_TYPE, ISNS_ISCSI_INITIATOR_MASK);
+ STR(ISCSI_ALIAS, "<MS SW iSCSI Initiator>");
+
+ status = isns_client_call(clnt, &reg);
+ if (status != ISNS_SUCCESS)
+ isns_fatal("DevAttrReg failed: %s\n", isns_strerror(status));
+ isns_simple_free(reg);
+
+ reg = isns_simple_create(ISNS_DEVICE_GET_NEXT, clnt->ic_source, NULL);
+ attrs = &reg->is_message_attrs;
+ NIL(ISCSI_NAME);
+
+ attrs = &reg->is_operating_attrs;
+ U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK);
+ NIL(ISCSI_NODE_TYPE);
+
+ status = isns_client_call(clnt, &reg);
+ if (status != ISNS_SUCCESS)
+ isns_fatal("DevGetNext failed: %s\n", isns_strerror(status));
+ isns_simple_free(reg);
+
+ return 0;
+}
diff --git a/utils/open-isns/tests/server.conf b/utils/open-isns/tests/server.conf
new file mode 100644
index 0000000..fc0bb5a
--- /dev/null
+++ b/utils/open-isns/tests/server.conf
@@ -0,0 +1,11 @@
+BindAddress = @SERVER_ADDRESS@
+SourceName = @SOURCE_NAME@
+Database = @DB_PATH@
+RegistrationPeriod = 2h
+ESIMinInterval = 1m
+ESIMinInterval = 5m
+Security = @NOT_SET@
+AuthKeyFile = @AUTH_KEY@
+ClientKeyStore = DB:
+PIDFile = @MYDIR@/pid
+ControlSocket = @MYDIR@/control
diff --git a/utils/open-isns/tests/test01.pl b/utils/open-isns/tests/test01.pl
new file mode 100644
index 0000000..258acff
--- /dev/null
+++ b/utils/open-isns/tests/test01.pl
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case validates registration and simple query of
+# single client.
+
+push(@INC, ".");
+require "harness.pl";
+
+&test_prep("test01", @ARGV);
+
+$server = &create_server;
+$client = &create_client($server);
+
+&isns_start_server($server);
+
+# 1: Enroll the test client
+&isns_enroll_client($client);
+
+# 2: Register an initiator with default portal
+&isns_register_client($client, "initiator portal");
+
+# 3: Run a simple query
+&isns_query_objects($client, "eid");
+
+# 99: Unregister client
+&isns_unregister_client("99-unregistration", $client);
+
+&isns_finish;
diff --git a/utils/open-isns/tests/test02.pl b/utils/open-isns/tests/test02.pl
new file mode 100644
index 0000000..208bed5
--- /dev/null
+++ b/utils/open-isns/tests/test02.pl
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case validates registration and simple query of
+# two clients, and simple DD functionality.
+
+push(@INC, ".");
+require "harness.pl";
+
+&test_prep("test02", @ARGV);
+
+$server = &create_server;
+$client1 = &create_client($server, "127.1.0.1");
+$client2 = &create_client($server, "127.1.0.2");
+
+&isns_start_server($server);
+
+# 1: Enroll the client1
+&isns_enroll_client($client1);
+
+# 2: Enroll the client1
+&isns_enroll_client($client2, "node-type=target");
+
+&isns_stage("registration", "Registering both clients");
+&__isns_register_client($client1, "initiator portal");
+&__isns_register_client($client2, "target portal");
+&isns_verify_db($server);
+
+# Now each of the two clients should just see
+# itself
+&isns_query_objects($client1, "eid");
+&isns_query_objects($client2, "eid");
+
+# Register a DD linking the two nodes
+&isns_register_domain($client1, "member-name=isns.client1", "member-name=isns.client2");
+
+# Now the clients should see each other
+&isns_query_objects($client1, "eid");
+&isns_query_objects($client2, "eid");
+
+# Initiator querying for target:
+&isns_query_objects($client1, "iscsi-node-type=Target");
+
+# Add another member to this DD, and re-add client2 (making
+# sure the server doesn't generate dupes)
+&isns_register_domain($client1, "dd-id=1", "member-name=isns.client2", "member-name=iqn.com.foobar:disk1");
+
+# Query the list of DDs we're a member of
+&isns_query_objects($client1, "dd-id");
+
+# Remove some entries from the DD
+&isns_deregister_domain($client1, "1", "member-iscsi-idx=10");
+&isns_deregister_domain($client1, "1", "member-name=iqn.com.foobar:disk1");
+&isns_register_domain($client1, "dd-id=1", "member-name=isns.client2");
+&isns_deregister_domain($client1, "1");
+
+&isns_finish;
diff --git a/utils/open-isns/tests/test03.pl b/utils/open-isns/tests/test03.pl
new file mode 100644
index 0000000..3cc0d71
--- /dev/null
+++ b/utils/open-isns/tests/test03.pl
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case validates registration and unregistration.
+
+push(@INC, ".");
+require "harness.pl";
+
+&test_prep("test03", @ARGV);
+
+$server = &create_server;
+$client = &create_client($server);
+
+&isns_start_server($server);
+
+&isns_enroll_client($client);
+&isns_register_client($client, "initiator portal");
+
+# Unregistering the portal should leave the iscsi node and
+# portal group active, and move the portal to state limbo.
+&isns_unregister_client($client, "portal=127.0.0.1:860");
+
+# As the iscsi node goes away, so should the whole entity
+&isns_unregister_client($client, "iscsi-name=isns.client1");
+
+&isns_finish;
diff --git a/utils/open-isns/tests/test04.pl b/utils/open-isns/tests/test04.pl
new file mode 100644
index 0000000..8181a4e
--- /dev/null
+++ b/utils/open-isns/tests/test04.pl
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case verifies that the database remains intact
+# across server restarts.
+
+push(@INC, ".");
+require "harness.pl";
+
+&test_prep("test04", @ARGV);
+
+$server = &create_server;
+$client = &create_client($server);
+
+&isns_start_server($server);
+
+&isns_enroll_client($client);
+&isns_register_client($client, "initiator portal");
+
+# Restart the server, and make sure it still displays
+# the database properly
+&isns_stage("restart", "Restarting server process");
+&isns_restart_server($server);
+&isns_verify_db($server);
+
+# Run a simple query
+&isns_query_objects($client, "iscsi-name");
+
+&isns_finish;
diff --git a/utils/open-isns/tests/test05.pl b/utils/open-isns/tests/test05.pl
new file mode 100644
index 0000000..694d7c3
--- /dev/null
+++ b/utils/open-isns/tests/test05.pl
@@ -0,0 +1,25 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case verifies entity expiry
+
+push(@INC, ".");
+require "harness.pl";
+
+&isns_prep_slow_test("test05", 30, @ARGV);
+
+$server = &create_server({ "RegistrationPeriod" => "20s" });
+$client = &create_client($server);
+
+&isns_start_server($server);
+
+&isns_enroll_client($client);
+&isns_register_client($client, "initiator portal");
+
+&isns_stage("expired", "Waiting for registration period to expire (25s)");
+&isns_idle(25);
+&isns_verify_db($server);
+
+&isns_finish;
+
diff --git a/utils/open-isns/tests/test06.pl b/utils/open-isns/tests/test06.pl
new file mode 100644
index 0000000..6b6aa05
--- /dev/null
+++ b/utils/open-isns/tests/test06.pl
@@ -0,0 +1,50 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case validates DevAttrReg replace mode.
+
+push(@INC, ".");
+require "harness.pl";
+
+&test_prep("test06", @ARGV);
+
+$server = &create_server;
+$client = &create_client($server);
+
+&isns_start_server($server);
+
+# 1: Enroll the client
+&isns_enroll_client($client);
+
+# 2: Register a simple initiator with one portal
+&isns_register_client($client, "initiator portal");
+
+$eid = &isns_query_eid($client);
+unless ($eid) {
+ &isns_die("Cannot obtain entity ID");
+}
+
+# Now replace the portal with different values
+&isns_register_client($client, "--replace entity=$eid initiator portal=192.168.1.1:iscsi");
+&isns_register_client($client, "--replace entity=$eid initiator portal=192.168.1.2:iscsi");
+
+&isns_register_domain($client, "member-name=isns.client1");
+
+# Replace our registration once more. Now the object index of the
+# initiator should not change, since it's a domain member now.
+&isns_register_client($client, "--replace entity=$eid initiator portal=192.168.1.1:iscsi");
+
+# Make the portal a domain member too. Now even the portal index should stay
+# the same. Note that we do not replace the whole entity now, but just the
+# portal
+&isns_register_domain($client, "dd-id=1 member-addr=192.168.1.1 member-port=860");
+&isns_register_client($client, "--replace --key portal=192.168.1.1:iscsi portal=192.168.1.2:iscsi");
+&isns_register_client($client, "--replace --key portal=192.168.1.2:iscsi portal=192.168.1.1:iscsi");
+
+# Now unregister the whole client, and re-register.
+# Portal and client index should remain the same
+&isns_unregister_client($client, "eid=$eid");
+&isns_register_client($client, "initiator portal=192.168.1.1:iscsi");
+
+&isns_finish;
diff --git a/utils/open-isns/tests/test07.pl b/utils/open-isns/tests/test07.pl
new file mode 100644
index 0000000..c13df11
--- /dev/null
+++ b/utils/open-isns/tests/test07.pl
@@ -0,0 +1,37 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case validates that the server discards portals
+# that do not respond to ESI messages
+
+push(@INC, ".");
+require "harness.pl";
+
+&isns_prep_slow_test("test07", 30, @ARGV);
+
+$server = &create_server({ "ESIMinInterval" => "5s" });
+$client = &create_client($server);
+
+&isns_start_server($server);
+
+# 1: Enroll the client
+&isns_enroll_client($client);
+
+# 2: Register a simple initiator with one portal
+&isns_register_client($client, "initiator portal,esi-port=65535,esi-interval=5");
+
+&isns_stage("expired", "Waiting for ESI to expire (~15 sec)");
+&isns_idle(15);
+&isns_verify_db($server);
+
+# 3: Register a simple initiator with two portals, one with ESI and one without.
+# When the ESI monitored portal expires, this should still take down
+# the whole network entity.
+&isns_register_client($client, "initiator portal,esi-port=65535,esi-interval=5 portal=127.0.0.1:1");
+
+&isns_stage("expired", "Waiting for ESI to expire (~15 sec)");
+&isns_idle(15);
+&isns_verify_db($server);
+
+&isns_finish;
diff --git a/utils/open-isns/tests/test08.pl b/utils/open-isns/tests/test08.pl
new file mode 100644
index 0000000..1487532
--- /dev/null
+++ b/utils/open-isns/tests/test08.pl
@@ -0,0 +1,23 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case validates registration and simple query of
+# single client.
+
+push(@INC, ".");
+require "harness.pl";
+
+# For now, this one will run w/o security only
+push(@ARGV, '-i');
+
+&test_prep("test08", @ARGV);
+
+$server = &create_server;
+$client = &create_client($server);
+
+&isns_start_server($server);
+
+&isns_external_test($client, "tests/pauw1");
+
+&isns_finish;
diff --git a/utils/open-isns/tests/test09.pl b/utils/open-isns/tests/test09.pl
new file mode 100644
index 0000000..bd2bd7f
--- /dev/null
+++ b/utils/open-isns/tests/test09.pl
@@ -0,0 +1,23 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case validates registration and simple query of
+# single client.
+
+push(@INC, ".");
+require "harness.pl";
+
+# For now, this one will run w/o security only
+push(@ARGV, '-i');
+
+&test_prep("test09", @ARGV);
+
+$server = &create_server;
+$client = &create_client($server);
+
+&isns_start_server($server);
+
+&isns_external_test($client, "tests/pauw2");
+
+&isns_finish;
diff --git a/utils/open-isns/tests/test10.pl b/utils/open-isns/tests/test10.pl
new file mode 100644
index 0000000..7286521
--- /dev/null
+++ b/utils/open-isns/tests/test10.pl
@@ -0,0 +1,33 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case validates registration and simple query of
+# single client.
+
+push(@INC, ".");
+require "harness.pl";
+
+# For now, this one will run w/o security only
+push(@ARGV, '-i');
+
+&isns_prep_slow_test("test10", 20, @ARGV);
+
+$server = &create_server({ "ESIMinInterval" => "10s" });
+$client = &create_client($server);
+
+&isns_start_server($server);
+
+&isns_external_test($client, "tests/pauw3", "16");
+
+&isns_stage("expired", "Waiting for ESI to come around");
+&isns_idle(5);
+&isns_verify_db($server);
+
+&isns_external_test($client, "tests/pauw3", "-n", "16");
+
+&isns_stage("expired", "Waiting for ESI to come around");
+&isns_idle(5);
+&isns_verify_db($server);
+
+&isns_finish;
diff --git a/utils/open-isns/tests/test11.pl b/utils/open-isns/tests/test11.pl
new file mode 100644
index 0000000..2745955
--- /dev/null
+++ b/utils/open-isns/tests/test11.pl
@@ -0,0 +1,23 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+#
+# This test case validates registration and simple query of
+# single client.
+
+push(@INC, ".");
+require "harness.pl";
+
+# For now, this one will run w/o security only
+push(@ARGV, '-i');
+
+&test_prep("test11", @ARGV);
+
+$server = &create_server;
+$client = &create_client($server);
+
+&isns_start_server($server);
+
+&isns_external_test($client, "tests/pauw4");
+
+&isns_finish;
diff --git a/utils/open-isns/timer.c b/utils/open-isns/timer.c
new file mode 100644
index 0000000..ed8a23f
--- /dev/null
+++ b/utils/open-isns/timer.c
@@ -0,0 +1,126 @@
+/*
+ * Timers (one-short and periodic)
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include "isns.h"
+#include "util.h"
+
+typedef struct isns_timer isns_timer_t;
+struct isns_timer {
+ isns_list_t it_list;
+ time_t it_when;
+ unsigned int it_period;
+ isns_timer_callback_t * it_func;
+ void * it_data;
+};
+
+
+static ISNS_LIST_DECLARE(timers);
+
+static void
+__isns_arm_timer(isns_timer_t *tm)
+{
+ isns_list_t *pos, *next;
+ time_t when = tm->it_when;
+
+ isns_list_foreach(&timers, pos, next) {
+ isns_timer_t *cur = isns_list_item(isns_timer_t, it_list, pos);
+
+ if (when < cur->it_when)
+ break;
+ }
+ isns_item_insert_before(pos, &tm->it_list);
+}
+
+static isns_timer_t *
+__isns_create_timer(time_t when,
+ unsigned int period,
+ isns_timer_callback_t *fn,
+ void *data)
+{
+ isns_timer_t *tm;
+
+ tm = isns_calloc(1, sizeof(*tm));
+ tm->it_when = when;
+ tm->it_period = period;
+ tm->it_func = fn;
+ tm->it_data = data;
+ return tm;
+}
+
+void
+isns_add_timer(unsigned int period,
+ isns_timer_callback_t *fn,
+ void *data)
+{
+ isns_timer_t *tm;
+
+ isns_assert(period);
+ tm = __isns_create_timer(time(NULL) + period, period, fn, data);
+ __isns_arm_timer(tm);
+}
+
+void
+isns_add_oneshot_timer(unsigned int expires,
+ isns_timer_callback_t *fn,
+ void *data)
+{
+ isns_timer_t *tm;
+
+ tm = __isns_create_timer(time(NULL) + expires, 0, fn, data);
+ __isns_arm_timer(tm);
+}
+
+void
+isns_cancel_timer(isns_timer_callback_t *fn, void *data)
+{
+ isns_list_t *pos, *next;
+
+ isns_list_foreach(&timers, pos, next) {
+ isns_timer_t *tm = isns_list_item(isns_timer_t, it_list, pos);
+
+ if (tm->it_func == fn
+ && (data == NULL || tm->it_data == data)) {
+ isns_list_del(pos);
+ isns_free(tm);
+ }
+ }
+}
+
+time_t
+isns_run_timers(void)
+{
+
+ while (!isns_list_empty(&timers)) {
+ isns_timer_t *tm = isns_list_item(isns_timer_t, it_list, timers.next);
+ isns_timer_callback_t *func;
+ time_t expire;
+ void *data;
+
+ expire = tm->it_when;
+ if (time(NULL) < expire)
+ return expire;
+
+ isns_list_del(&tm->it_list);
+ func = tm->it_func;
+ data = tm->it_data;
+ expire = 0;
+
+ /* If it's a periodic timer, rearm it now. This allows
+ * the timer callback to cancel the timer. */
+ if (tm->it_period) {
+ tm->it_when = time(NULL) + tm->it_period;
+ __isns_arm_timer(tm);
+ } else {
+ isns_free(tm);
+ }
+
+ func(data);
+ }
+
+ return 0;
+}
diff --git a/utils/open-isns/types.h b/utils/open-isns/types.h
new file mode 100644
index 0000000..ddd153f
--- /dev/null
+++ b/utils/open-isns/types.h
@@ -0,0 +1,57 @@
+/*
+ * Open-iSNS types
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_TYPES_H
+#define ISNS_TYPES_H
+
+typedef struct isns_simple isns_simple_t;
+typedef struct isns_source isns_source_t;
+typedef struct isns_object isns_object_t;
+typedef struct isns_relation isns_relation_t;
+typedef struct isns_attr isns_attr_t;
+typedef struct isns_attr_list isns_attr_list_t;
+typedef struct isns_message isns_message_t;
+typedef struct isns_socket isns_socket_t;
+typedef struct isns_db isns_db_t;
+typedef struct isns_tag_type isns_tag_type_t;
+typedef const struct isns_object_template isns_object_template_t;
+typedef struct isns_authdata isns_authdata_t;
+typedef struct isns_security isns_security_t;
+typedef struct isns_principal isns_principal_t;
+typedef struct isns_policy isns_policy_t;
+typedef struct isns_keystore isns_keystore_t;
+typedef struct isns_scope isns_scope_t;
+typedef struct isns_portal_info isns_portal_info_t;
+typedef struct isns_server isns_server_t;
+typedef struct isns_db_event isns_db_event_t;
+typedef struct isns_bitvector isns_bitvector_t;
+
+typedef struct isns_object_list {
+ unsigned int iol_count;
+ isns_object_t ** iol_data;
+} isns_object_list_t;
+
+#define ISNS_OBJECT_LIST_INIT { .iol_count = 0, .iol_data = NULL }
+
+/*
+ * An attribute list
+ */
+struct isns_attr_list {
+ unsigned int ial_count;
+ isns_attr_t ** ial_data;
+};
+#define ISNS_ATTR_LIST_INIT { .ial_count = 0, .ial_data = NULL }
+
+/*
+ * Function types.
+ */
+typedef void isns_print_fn_t(const char *, ...);
+typedef void isns_timer_callback_t(void *);
+
+
+#endif /* ISNS_TYPES_H */
+
+
diff --git a/utils/open-isns/util.c b/utils/open-isns/util.c
new file mode 100644
index 0000000..4c0a7b2
--- /dev/null
+++ b/utils/open-isns/util.c
@@ -0,0 +1,263 @@
+/*
+ * util.c
+ *
+ * Misc utility functions
+ *
+ * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include "util.h"
+
+unsigned long
+parse_size(const char *arg)
+{
+ unsigned long mult = 1, ret;
+ char *s;
+
+ ret = strtol(arg, &s, 0);
+
+ switch (*s++) {
+ case 'g':
+ case 'G':
+ mult = 1024 * 1024 * 1024;
+ break;
+ case 'm':
+ case 'M':
+ mult = 1024 * 1024;
+ break;
+ case 'k':
+ case 'K':
+ mult = 1024;
+ break;
+
+ case '\0':
+ return ret;
+
+ default:
+ bad:
+ err(1, "parse_size: unknown unit in \"%s\"\n", arg);
+ }
+
+ if (*s != '\0')
+ goto bad;
+
+ return mult * ret;
+}
+
+char *
+print_size(unsigned long size)
+{
+ static char unit[] = "-kMG";
+ static char buffer[64];
+ unsigned int power = 0;
+
+ while (size && !(size % 1024) && power < sizeof(unit)) {
+ size /= 1024;
+ power++;
+ }
+
+ if (!power) {
+ snprintf(buffer, sizeof(buffer), "%lu", size);
+ } else {
+ snprintf(buffer, sizeof(buffer), "%lu%c",
+ size, unit[power]);
+ }
+ return buffer;
+}
+
+unsigned int
+parse_count(const char *arg)
+{
+ unsigned long ret;
+ char *s;
+
+ ret = strtoul(arg, &s, 0);
+ if (*s != '\0')
+ err(1, "parse_count: unexpected character in \"%s\"\n", arg);
+
+ return ret;
+}
+
+int
+parse_int(const char *arg)
+{
+ long ret;
+ char *s;
+
+ ret = strtol(arg, &s, 0);
+ if (*s != '\0')
+ err(1, "parse_count: unexpected character in \"%s\"\n", arg);
+
+ return ret;
+}
+
+long long
+parse_longlong(const char *arg)
+{
+ long long ret;
+ char *s;
+
+ ret = strtoll(arg, &s, 0);
+ if (*s != '\0')
+ err(1, "parse_count: unexpected character in \"%s\"\n", arg);
+
+ return ret;
+}
+
+double
+parse_double(const char *arg)
+{
+ double ret;
+ char *s;
+
+ ret = strtod(arg, &s);
+ if (*s != '\0')
+ err(1, "parse_count: unexpected character in \"%s\"\n", arg);
+
+ return ret;
+}
+
+unsigned int
+parse_timeout(const char *arg)
+{
+ unsigned int v, ret = 0;
+ char *s;
+
+ do {
+ v = strtoul(arg, &s, 10);
+ switch (*s) {
+ case '\0':
+ ret += v;
+ break;
+ case 'd':
+ v *= 24;
+ case 'h':
+ v *= 60;
+ case 'm':
+ v *= 60;
+ case 's':
+ ret += v;
+ ++s;
+ break;
+
+ default:
+ errx(1, "parse_timeout: unexpected character in \"%s\"\n",
+ arg);
+ }
+
+ arg = s;
+ } while (*arg);
+
+ return ret;
+}
+
+void
+isns_string_array_append(struct string_array *array, const char *val)
+{
+ if (!(array->count % 32)) {
+ array->list = isns_realloc(array->list,
+ (array->count + 32) * sizeof(val));
+ }
+ array->list[array->count++] = val? isns_strdup(val) : NULL;
+}
+
+void
+isns_string_array_destroy(struct string_array *array)
+{
+ unsigned int i;
+
+ for (i = 0; i < array->count; ++i)
+ isns_free(array->list[i]);
+ isns_free(array->list);
+ memset(array, 0, sizeof(*array));
+}
+
+void
+isns_assign_string(char **var, const char *val)
+{
+ char *s = NULL;
+
+ if (val && !(s = isns_strdup(val)))
+ errx(1, "out of memory");
+
+ if (*var)
+ isns_free(*var);
+ *var = s;
+}
+
+/*
+ * Recursively create a directory
+ */
+int
+isns_mkdir_recursive(const char *pathname)
+{
+ const char *orig_pathname = pathname;
+ char *squirrel[64];
+ char *copy = NULL, *s;
+ int ns = 0;
+
+ if (!pathname || !strcmp(pathname, "."))
+ return 0;
+ while (1) {
+ if (mkdir(pathname, 0755) >= 0) {
+ if (ns == 0)
+ break;
+ *squirrel[--ns] = '/';
+ continue;
+ }
+
+ if (errno == EEXIST)
+ goto good;
+ if (errno != ENOENT)
+ goto bad;
+
+ if (copy == NULL) {
+ copy = isns_strdup(pathname);
+ pathname = copy;
+ }
+
+ s = strrchr(copy, '/');
+ while (s > copy && s[-1] == '/')
+ --s;
+ *s = '\0';
+
+ isns_assert(ns < 64);
+ squirrel[ns++] = s;
+
+ if (s == copy)
+ goto bad;
+ }
+
+good: if (copy)
+ isns_free(copy);
+ errno = 0;
+ return 0;
+
+bad: if (copy)
+ isns_free(copy);
+ perror(orig_pathname);
+ return -1;
+}
+
+/*
+ * This one differs from POSIX dirname; it does not
+ * modify its argument
+ */
+const char *
+isns_dirname(const char *pathname)
+{
+ static char buffer[4096];
+ char *s;
+
+ strcpy(buffer, pathname);
+ if ((s = strrchr(buffer, '/')) != NULL) {
+ *s = '\0';
+ return buffer;
+ }
+ return ".";
+}
diff --git a/utils/open-isns/util.h b/utils/open-isns/util.h
new file mode 100644
index 0000000..bd6b979
--- /dev/null
+++ b/utils/open-isns/util.h
@@ -0,0 +1,288 @@
+/*
+ * Utility functions
+ *
+ * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h> // for strdup
+#include "types.h"
+
+#define array_num_elements(a) (sizeof(a) / sizeof((a)[0]))
+
+const char * isns_dirname(const char *);
+int isns_mkdir_recursive(const char *);
+
+extern const char *parser_separators;
+char * parser_get_next_line(FILE *);
+char * parser_get_next_word(char **);
+char * parser_get_rest_of_line(char **);
+int parser_split_line(char *, unsigned int, char **);
+
+unsigned long parse_size(const char *);
+unsigned int parse_count(const char *);
+int parse_int(const char *);
+long long parse_longlong(const char *);
+double parse_double(const char *);
+unsigned int parse_timeout(const char *);
+
+char * print_size(unsigned long);
+
+/*
+ * Very simple and stupid string array.
+ */
+struct string_array {
+ unsigned int count;
+ char ** list;
+};
+
+void isns_string_array_append(struct string_array *, const char *);
+void isns_string_array_destroy(struct string_array *);
+
+void isns_assign_string(char **, const char *);
+
+void isns_write_pidfile(const char *);
+void isns_update_pidfile(const char *);
+void isns_remove_pidfile(const char *);
+
+extern void isns_log_background(void);
+extern void isns_assert_failed(const char *,
+ const char *, unsigned int);
+extern void isns_fatal(const char *, ...);
+extern void isns_warning(const char *, ...);
+extern void isns_error(const char *, ...);
+extern void isns_notice(const char *, ...);
+extern void isns_debug_general(const char *, ...);
+extern void isns_debug_socket(const char *, ...);
+extern void isns_debug_protocol(const char *, ...);
+extern void isns_debug_message(const char *, ...);
+extern void isns_debug_state(const char *, ...);
+extern void isns_debug_auth(const char *, ...);
+extern void isns_debug_scn(const char *, ...);
+extern void isns_debug_esi(const char *, ...);
+extern void isns_enable_debugging(const char *);
+extern int isns_debug_enabled(int);
+
+enum {
+ DBG_GENERAL = 0,
+ DBG_SOCKET,
+ DBG_PROTOCOL,
+ DBG_MESSAGE,
+ DBG_STATE,
+ DBG_AUTH,
+ DBG_SCN,
+ DBG_ESI,
+};
+
+/*
+ * There's no htonll yet
+ */
+#ifndef htonll
+# include <endian.h>
+# include <byteswap.h>
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define htonll(x) (x)
+# define ntohll(x) (x)
+# elif __BYTE_ORDER == __LITTLE_ENDIAN
+# define htonll(x) __bswap_64(x)
+# define ntohll(x) __bswap_64(x)
+# endif
+#endif
+
+/*
+ * One of the those eternal staples of C coding:
+ */
+#ifndef MIN
+# define MIN(a, b) ((a) < (b)? (a) : (b))
+# define MAX(a, b) ((a) > (b)? (a) : (b))
+#endif
+
+#define DECLARE_BITMAP(name, NBITS) \
+ uint32_t name[(NBITS+31) >> 5] = { 0 }
+
+#define __BIT_INDEX(nr) (nr >> 5)
+#define __BIT_MASK(nr) (1 << (nr & 31))
+
+static inline void
+set_bit(uint32_t *map, unsigned int nr)
+{
+ map[__BIT_INDEX(nr)] |= __BIT_MASK(nr);
+}
+
+static inline void
+clear_bit(uint32_t *map, unsigned int nr)
+{
+ map[__BIT_INDEX(nr)] &= ~__BIT_MASK(nr);
+}
+
+static inline int
+test_bit(const uint32_t *map, unsigned int nr)
+{
+ return !!(map[__BIT_INDEX(nr)] & __BIT_MASK(nr));
+}
+
+/*
+ * Dynamically sized bit vector
+ */
+extern isns_bitvector_t *isns_bitvector_alloc(void);
+extern void isns_bitvector_init(isns_bitvector_t *);
+extern void isns_bitvector_destroy(isns_bitvector_t *);
+extern void isns_bitvector_free(isns_bitvector_t *);
+extern int isns_bitvector_test_bit(const isns_bitvector_t *, unsigned int);
+extern int isns_bitvector_set_bit(isns_bitvector_t *, unsigned int);
+extern int isns_bitvector_clear_bit(isns_bitvector_t *, unsigned int);
+extern int isns_bitvector_is_empty(const isns_bitvector_t *);
+extern int isns_bitvector_intersect(const isns_bitvector_t *a,
+ const isns_bitvector_t *b,
+ isns_bitvector_t *result);
+extern void isns_bitvector_print(const isns_bitvector_t *,
+ isns_print_fn_t *);
+extern void isns_bitvector_foreach(const isns_bitvector_t *bv,
+ int (*cb)(uint32_t, void *),
+ void *user_data);
+
+/*
+ * List manipulation primites
+ */
+typedef struct isns_list isns_list_t;
+struct isns_list {
+ isns_list_t * next;
+ isns_list_t * prev;
+};
+
+#define ISNS_LIST_DECLARE(list) \
+ isns_list_t list = { &list, &list }
+
+static inline void
+isns_list_init(isns_list_t *head)
+{
+ head->next = head->prev = head;
+}
+
+static inline void
+__isns_list_insert(isns_list_t *prev, isns_list_t *item, isns_list_t *next)
+{
+ item->next = next;
+ item->prev = prev;
+ next->prev = item;
+ prev->next = item;
+}
+
+static inline void
+isns_list_append(isns_list_t *head, isns_list_t *item)
+{
+ __isns_list_insert(head->prev, item, head);
+}
+
+static inline void
+isns_list_insert(isns_list_t *head, isns_list_t *item)
+{
+ __isns_list_insert(head, item, head->next);
+}
+
+static inline void
+isns_item_insert_before(isns_list_t *where, isns_list_t *item)
+{
+ __isns_list_insert(where->prev, item, where);
+}
+
+static inline void
+isns_item_insert_after(isns_list_t *where, isns_list_t *item)
+{
+ __isns_list_insert(where, item, where->next);
+}
+
+static inline void
+isns_list_del(isns_list_t *item)
+{
+ isns_list_t *prev = item->prev;
+ isns_list_t *next = item->next;
+
+ prev->next = next;
+ next->prev = prev;
+ item->next = item->prev = item;
+}
+
+static inline int
+isns_list_empty(const isns_list_t *head)
+{
+ return head == head->next;
+}
+
+static inline void
+isns_list_move(isns_list_t *dst, isns_list_t *src)
+{
+ isns_list_t *prev, *next;
+ isns_list_t *head, *tail;
+
+ if (isns_list_empty(src))
+ return;
+
+ prev = dst->prev;
+ next = dst;
+
+ head = src->next;
+ tail = src->prev;
+
+ next->prev = tail;
+ prev->next = head;
+ head->prev = prev;
+ tail->next = next;
+
+ src->next = src->prev = src;
+}
+
+#define isns_list_item(type, member, ptr) \
+ container_of(type, member, ptr)
+
+#define isns_list_foreach(list, __pos, __next) \
+ for (__pos = (list)->next; \
+ (__pos != list) && (__next = __pos->next, 1); \
+ __pos = __next)
+
+#if 0
+/* This is defined in stddef */
+#define offsetof(type, member) ((unsigned long) &(((type *) 0)->member))
+#endif
+#define container_of(type, member, ptr) \
+ ((type *) (((unsigned char *) ptr) - offsetof(type, member)))
+
+/*
+ * Use isns_assert instead of libc's assert, so that the
+ * message can be captured and sent to syslog.
+ */
+#define isns_assert(condition) do { \
+ if (!(condition)) \
+ isns_assert_failed(#condition, \
+ __FILE__, __LINE__); \
+} while (0)
+
+#ifndef MDEBUG
+# define isns_malloc(size) malloc(size)
+# define isns_calloc(n, size) calloc(n, size)
+# define isns_realloc(p, size) realloc(p, size)
+# define isns_strdup(s) strdup(s)
+# define isns_free(p) free(p)
+#else
+# define isns_malloc(size) isns_malloc_fn(size, __FILE__, __LINE__)
+# define isns_calloc(n, size) isns_calloc_fn(n, size, __FILE__, __LINE__)
+# define isns_realloc(p, size) isns_realloc_fn(p, size, __FILE__, __LINE__)
+# define isns_strdup(s) isns_strdup_fn(s, __FILE__, __LINE__)
+# define isns_free(p) isns_free_fn(p, __FILE__, __LINE__)
+
+extern void * (*isns_malloc_fn)(size_t, const char *, unsigned int);
+extern void * (*isns_calloc_fn)(unsigned int, size_t,
+ const char *, unsigned int);
+extern void * (*isns_realloc_fn)(void *, size_t,
+ const char *, unsigned int);
+extern char * (*isns_strdup_fn)(const char *, const char *, unsigned int);
+extern void (*isns_free_fn)(void *, const char *, unsigned int);
+#endif
+
+#endif /* UTIL_H */
diff --git a/utils/open-isns/vendor.c b/utils/open-isns/vendor.c
new file mode 100644
index 0000000..e24164d
--- /dev/null
+++ b/utils/open-isns/vendor.c
@@ -0,0 +1,41 @@
+/*
+ * iSNS vendor specific objects
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "isns.h"
+#include "objects.h"
+#include "attrs.h"
+#include "vendor.h"
+#include "util.h"
+
+static uint32_t policy_attrs[] = {
+ OPENISNS_TAG_POLICY_SPI,
+ OPENISNS_TAG_POLICY_KEY,
+ OPENISNS_TAG_POLICY_ENTITY,
+ OPENISNS_TAG_POLICY_OBJECT_TYPE,
+ OPENISNS_TAG_POLICY_NODE_NAME,
+ OPENISNS_TAG_POLICY_NODE_TYPE,
+ OPENISNS_TAG_POLICY_FUNCTIONS,
+ OPENISNS_TAG_POLICY_VISIBLE_DD,
+ OPENISNS_TAG_POLICY_DEFAULT_DD,
+};
+
+static uint32_t policy_key_attrs[] = {
+ OPENISNS_TAG_POLICY_SPI,
+};
+
+isns_object_template_t isns_policy_template = {
+ .iot_name = "Policy",
+ .iot_handle = ISNS_OBJECT_TYPE_POLICY,
+ .iot_attrs = policy_attrs,
+ .iot_num_attrs = array_num_elements(policy_attrs),
+ .iot_keys = policy_key_attrs,
+ .iot_num_keys = array_num_elements(policy_key_attrs),
+ .iot_container = &isns_entity_template,
+ .iot_vendor_specific = 1,
+};
+
diff --git a/utils/open-isns/vendor.h b/utils/open-isns/vendor.h
new file mode 100644
index 0000000..49c6132
--- /dev/null
+++ b/utils/open-isns/vendor.h
@@ -0,0 +1,56 @@
+/*
+ * iSNS "vendor-specific" protocol definitions
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ */
+
+#ifndef ISNS_VENDOR_H
+#define ISNS_VENDOR_H
+
+#include "isns-proto.h"
+
+/*
+ * We're poor, we don't own a OUI. Let's fake one.
+ */
+#define OPENISNS_VENDOR_OUI 0xFFFF00
+#define OPENISNS_VENDOR_PREFIX (OPENISNS_VENDOR_OUI << 8)
+#define OPENISNS_IS_PRIVATE_ATTR(tag) (((tag) >> 16) == 0xFFFF)
+
+enum openisns_vendor_tag {
+ /* Security Policy Identifier */
+ OPENISNS_TAG_POLICY_SPI = OPENISNS_VENDOR_PREFIX + ISNS_VENDOR_SPECIFIC_OTHER_BASE,
+
+ __OPENISNS_TAG_POLICY_RESERVED,
+
+ /* DSA signature key (public) */
+ OPENISNS_TAG_POLICY_KEY,
+
+ /* Entity name to use */
+ OPENISNS_TAG_POLICY_ENTITY,
+
+ /* Functions the client is permitted to invoke */
+ OPENISNS_TAG_POLICY_FUNCTIONS,
+
+ /* Object types the client is permitted to see. */
+ OPENISNS_TAG_POLICY_OBJECT_TYPE,
+
+ /* iSCSI node name the client is permitted to register.
+ * This attribute may occur multiple times.
+ * If absent, it defaults to POLICY_SOURCE_NAME
+ */
+ OPENISNS_TAG_POLICY_NODE_NAME,
+
+ /* Node type bitmap the client is permitted to register */
+ OPENISNS_TAG_POLICY_NODE_TYPE,
+
+ /* Default discovery domain the client will be
+ * placed in.
+ * Not used yet.
+ */
+ OPENISNS_TAG_POLICY_DEFAULT_DD,
+ OPENISNS_TAG_POLICY_VISIBLE_DD,
+};
+
+extern const struct isns_object_template isns_policy_template;
+
+#endif /* ISNS_VENDOR_H */