Bring in ISCSI initiator support.
authorMatthew Dillon <dillon@apollo.backplane.com>
Mon, 24 Aug 2009 15:59:40 +0000 (08:59 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Mon, 24 Aug 2009 15:59:40 +0000 (08:59 -0700)
* Port ISCSI initiator support from FreeBSD.

* Additional work to help stabilize it.

Submitted-by: Antonio Huete Jimenez <tuxillo@quantumachine.net>
Taken-from: FreeBSD

30 files changed:
sbin/Makefile
sbin/iscontrol/Makefile [new file with mode: 0644]
sbin/iscontrol/auth_subr.c [new file with mode: 0644]
sbin/iscontrol/config.c [new file with mode: 0644]
sbin/iscontrol/fsm.c [new file with mode: 0644]
sbin/iscontrol/iscontrol.8 [new file with mode: 0644]
sbin/iscontrol/iscontrol.c [new file with mode: 0644]
sbin/iscontrol/iscontrol.h [new file with mode: 0644]
sbin/iscontrol/iscsi.conf.5 [new file with mode: 0644]
sbin/iscontrol/login.c [new file with mode: 0644]
sbin/iscontrol/misc.c [new file with mode: 0644]
sbin/iscontrol/misc.h [new file with mode: 0644]
sbin/iscontrol/pdu.c [new file with mode: 0644]
sbin/iscontrol/pdu.h [new file with mode: 0644]
share/man/man4/iscsi_initiator.4 [new file with mode: 0644]
sys/conf/files
sys/conf/options
sys/config/LINT
sys/config/VKERNEL
sys/dev/disk/Makefile
sys/dev/disk/iscsi/Makefile [new file with mode: 0644]
sys/dev/disk/iscsi/initiator/Makefile [new file with mode: 0644]
sys/dev/disk/iscsi/initiator/isc_cam.c [new file with mode: 0644]
sys/dev/disk/iscsi/initiator/isc_sm.c [new file with mode: 0644]
sys/dev/disk/iscsi/initiator/isc_soc.c [new file with mode: 0644]
sys/dev/disk/iscsi/initiator/isc_subr.c [new file with mode: 0644]
sys/dev/disk/iscsi/initiator/iscsi.c [new file with mode: 0644]
sys/dev/disk/iscsi/initiator/iscsi.h [new file with mode: 0644]
sys/dev/disk/iscsi/initiator/iscsi_subr.c [new file with mode: 0644]
sys/dev/disk/iscsi/initiator/iscsivar.h [new file with mode: 0644]

index b95bc9b..0086450 100644 (file)
@@ -35,6 +35,7 @@ SUBDIR=       adjkerntz \
        init \
        ip6fw \
        ipfw \
+       iscontrol \
        jscan \
        kldconfig \
        kldload \
diff --git a/sbin/iscontrol/Makefile b/sbin/iscontrol/Makefile
new file mode 100644 (file)
index 0000000..c9b606e
--- /dev/null
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+SRCS= iscontrol.c pdu.c fsm.c config.c login.c auth_subr.c misc.c
+PROG= iscontrol
+DPADD= ${LIBCAM} ${LIBMD}
+LDADD= -lcam -lmd
+
+CFLAGS += -I${.CURDIR}/../../sys/dev/disk/iscsi/initiator
+#CFLAGS += -g -DDEBUG
+
+MAN= iscsi.conf.5 iscontrol.8
+
+WARNS?= 3
+.include <bsd.prog.mk>
diff --git a/sbin/iscontrol/auth_subr.c b/sbin/iscontrol/auth_subr.c
new file mode 100644 (file)
index 0000000..f861e13
--- /dev/null
@@ -0,0 +1,206 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ | $Id: auth_subr.c,v 2.2 2007/06/01 08:09:37 danny Exp $
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#if __FreeBSD_version < 500000
+#include <sys/time.h>
+#endif
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <md5.h>
+#include <sha.h>
+
+#include "iscsi.h"
+#include "iscontrol.h"
+
+static int
+chapMD5(char id, char *cp, char *chapSecret, unsigned char *digest)
+{
+     MD5_CTX   ctx;
+     char      *tmp;
+     int       len;
+
+     debug_called(3);
+
+     MD5Init(&ctx);
+
+     MD5Update(&ctx, &id, 1);
+
+     if((len = str2bin(chapSecret, &tmp)) == 0) {
+         // print error
+         return -1;
+     }
+     MD5Update(&ctx, tmp, len);
+     free(tmp);
+
+     if((len = str2bin(cp, &tmp)) == 0) {
+         // print error
+         return -1;
+     }
+     MD5Update(&ctx, tmp, len);
+     free(tmp);
+
+     MD5Final(digest, &ctx);
+
+
+     return 0;
+}
+
+static int
+chapSHA1(char id, char *cp, char *chapSecret, unsigned char *digest)
+{
+     SHA1_CTX  ctx;
+     char      *tmp;
+     int       len;
+
+     debug_called(3);
+
+     SHA1_Init(&ctx);
+
+     SHA1_Update(&ctx, &id, 1);
+
+     if((len = str2bin(chapSecret, &tmp)) == 0) {
+         // print error
+         return -1;
+     }
+     SHA1_Update(&ctx, tmp, len);
+     free(tmp);
+
+     if((len = str2bin(cp, &tmp)) == 0) {
+         // print error
+         return -1;
+     }
+     SHA1_Update(&ctx, tmp, len);
+     free(tmp);
+
+     SHA1_Final(digest, &ctx);
+
+     return 0;
+
+}
+/*
+ | the input text format can be anything that the rfc3270 defines
+ | (see section 5.1 and str2bin)
+ | digest length for md5 is 128bits, and for sha1 is 160bits.
+ | digest is an ASCII string which represents the bits in
+ | hexadecimal or base64 according to the challenge(cp) format
+ */
+char *
+chapDigest(char *ap, char id, char *cp, char *chapSecret)
+{
+     int       len;
+     unsigned  char digest[20];
+     char      encoding[3];
+
+     debug_called(3);
+
+     len = 0;
+     if(strcmp(ap, "5") == 0 && chapMD5(id, cp, chapSecret, digest) == 0)
+         len = 16;
+     else
+     if(strcmp(ap, "7") == 0 && chapSHA1(id, cp, chapSecret, digest) == 0)
+         len = 20;
+
+     if(len) {
+         sprintf(encoding, "%.2s", cp);
+         return bin2str(encoding, digest, len);
+     }
+
+     return NULL;
+}
+
+char *
+genChapChallenge(char *encoding, size_t len)
+{
+     int       fd;
+     unsigned  char tmp[1024];
+
+     if(len > sizeof(tmp))
+         return NULL;
+
+     if((fd = open("/dev/random", O_RDONLY)) != -1) {
+         read(fd, tmp, len);
+         close(fd);
+         return bin2str(encoding, tmp, len);
+     }
+     perror("/dev/random");
+     // make up something ...
+     return NULL;
+}
+
+#ifdef TEST_AUTH
+static void
+puke(char *str, unsigned char *dg, int len)
+{
+     printf("%3d] %s\n     0x", len, str);
+     while(len-- > 0)
+         printf("%02x", *dg++);
+     printf("\n");
+}
+
+main(int cc, char **vv)
+{
+     char *p, *ap, *ip, *cp, *chapSecret, *digest;
+     int len;
+
+#if 0
+     ap = "5";
+     chapSecret = "0xa5aff013dd839b1edd31ee73a1df0b1b";
+//     chapSecret = "abcdefghijklmnop";
+     len = str2bin(chapSecret, &cp);
+     puke(chapSecret, cp, len);
+
+     ip = "238";
+     cp = "0xbd456029";
+
+
+     if((digest = chapDigest(ap, ip, cp, chapSecret)) != NULL) {
+         len = str2bin(digest, &cp);
+         puke(digest, cp, len);
+     }
+#else
+     printf("%d] %s\n", 24, genChallenge("0X", 24));
+#endif
+}
+#endif
diff --git a/sbin/iscontrol/config.c b/sbin/iscontrol/config.c
new file mode 100644 (file)
index 0000000..2ae7cb0
--- /dev/null
@@ -0,0 +1,375 @@
+ /*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ | $Id: config.c,v 2.1 2006/11/12 08:06:51 danny Exp danny $
+ */
+
+#include <sys/cdefs.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+#include <camlib.h>
+
+#include "iscsi.h"
+#include "iscontrol.h"
+
+/*
+ | ints
+ */
+#define OPT_port                       1
+#define OPT_tags                       2
+
+#define OPT_maxConnections             3
+#define OPT_maxRecvDataSegmentLength   4
+#define OPT_maxXmitDataSegmentLength   5
+#define OPT_maxBurstLength             6
+#define OPT_firstBurstLength           7
+#define OPT_defaultTime2Wait           8
+#define OPT_defaultTime2Retain         9
+#define OPT_maxOutstandingR2T          10
+#define OPT_errorRecoveryLevel         11
+#define OPT_targetPortalGroupTag       12
+#define OPT_headerDigest               13
+#define OPT_dataDigest                 14
+/*
+ | Booleans
+ */
+#define OPT_initialR2T                 16
+#define OPT_immediateData              17
+#define OPT_dataPDUInOrder             18
+#define OPT_dataSequenceInOrder                19
+/*
+ | strings
+ */
+#define OPT_sessionType                        15
+
+#define OPT_targetAddress              21
+#define OPT_targetAlias                        22
+#define OPT_targetName                 23
+#define OPT_initiatorName              24
+#define OPT_initiatorAlias             25
+#define OPT_authMethod                 26
+
+#define OPT_chapSecret                 27
+#define OPT_chapIName                  28
+#define OPT_chapDigest                 29
+#define OPT_tgtChapName                        30
+#define OPT_tgtChapSecret              31
+#define OPT_tgtChallengeLen            32
+/*
+ | private
+ */
+#define OPT_maxluns                    33
+#define OPT_iqn                                34
+#define OPT_sockbufsize                        35
+
+#define _OFF(v)        ((int)&((isc_opt_t *)NULL)->v)
+#define _E(u, s, v) {.usage=u, .scope=s, .name=#v, .tokenID=OPT_##v}
+
+textkey_t keyMap[] = {
+     _E(U_PR, S_PR, port),
+     _E(U_PR, S_PR, tags),
+     _E(U_PR, S_PR, maxluns),
+     _E(U_PR, S_PR, sockbufsize),
+
+     _E(U_PR, S_PR, iqn),
+     _E(U_PR, S_PR, chapSecret),
+     _E(U_PR, S_PR, chapIName),
+     _E(U_PR, S_PR, chapDigest),
+     _E(U_PR, S_PR, tgtChapName),
+     _E(U_PR, S_PR, tgtChapSecret),
+     _E(U_PR, S_PR, tgtChallengeLen),
+
+     _E(U_IO, S_CO, headerDigest),
+     _E(U_IO, S_CO, dataDigest),
+
+     _E(U_IO, S_CO, authMethod),
+
+     _E(U_LO, S_SW, maxConnections),
+     _E(U_IO, S_SW, targetName),
+
+     _E(U_IO, S_SW, initiatorName),
+     _E(U_ALL,S_SW, targetAlias),
+     _E(U_ALL,S_SW, initiatorAlias),
+     _E(U_ALL,S_SW, targetAddress),
+
+     _E(U_ALL,S_SW, targetPortalGroupTag),
+
+     _E(U_LO, S_SW, initialR2T),
+     _E(U_LO, S_SW, immediateData),
+
+     _E(U_ALL,S_CO, maxRecvDataSegmentLength),
+     _E(U_ALL,S_CO, maxXmitDataSegmentLength),
+
+     _E(U_LO, S_SW, maxBurstLength),
+     _E(U_LO, S_SW, firstBurstLength),
+     _E(U_LO, S_SW, defaultTime2Wait),
+     _E(U_LO, S_SW, defaultTime2Retain),
+
+     _E(U_LO, S_SW, maxOutstandingR2T),
+     _E(U_LO, S_SW, dataPDUInOrder),
+     _E(U_LO, S_SW, dataSequenceInOrder),
+
+     _E(U_LO, S_SW, errorRecoveryLevel),
+
+     _E(U_LO, S_SW, sessionType),
+
+     {0, 0, 0, 0}
+};
+
+#define _OPT_INT(w)    strtol((char *)w, NULL, 0)
+#define _OPT_STR(w)    (char *)(w)
+
+static __inline  int
+_OPT_BOOL(char *w)
+{
+     if(isalpha(*w))
+         return strcasecmp(w, "TRUE") == 0;
+     else
+         return _OPT_INT(w);
+}
+
+#define _CASE(k, v)    case OPT_##k: op->k = v; break
+static void
+setOption(isc_opt_t *op, int which, void *rval)
+{
+     switch(which) {
+         _CASE(port, _OPT_INT(rval));
+         _CASE(tags, _OPT_INT(rval));
+         _CASE(maxluns, _OPT_INT(rval));
+         _CASE(iqn, _OPT_STR(rval));
+         _CASE(sockbufsize, _OPT_INT(rval));
+
+         _CASE(maxConnections, _OPT_INT(rval));
+         _CASE(maxRecvDataSegmentLength, _OPT_INT(rval));
+         _CASE(maxXmitDataSegmentLength, _OPT_INT(rval));
+         _CASE(maxBurstLength, _OPT_INT(rval));
+         _CASE(firstBurstLength, _OPT_INT(rval));
+         _CASE(defaultTime2Wait, _OPT_INT(rval));
+         _CASE(defaultTime2Retain, _OPT_INT(rval));
+         _CASE(maxOutstandingR2T, _OPT_INT(rval));
+         _CASE(errorRecoveryLevel, _OPT_INT(rval));
+         _CASE(targetPortalGroupTag, _OPT_INT(rval));
+         _CASE(headerDigest, _OPT_STR(rval));
+         _CASE(dataDigest, _OPT_STR(rval));
+
+         _CASE(targetAddress, _OPT_STR(rval));
+         _CASE(targetAlias, _OPT_STR(rval));
+         _CASE(targetName, _OPT_STR(rval));
+         _CASE(initiatorName, _OPT_STR(rval));
+         _CASE(initiatorAlias, _OPT_STR(rval));
+         _CASE(authMethod, _OPT_STR(rval));
+         _CASE(chapSecret, _OPT_STR(rval));
+         _CASE(chapIName, _OPT_STR(rval));
+         _CASE(chapDigest, _OPT_STR(rval));
+
+         _CASE(tgtChapName, _OPT_STR(rval));
+         _CASE(tgtChapSecret, _OPT_STR(rval));
+
+         _CASE(initialR2T, _OPT_BOOL(rval));
+         _CASE(immediateData, _OPT_BOOL(rval));
+         _CASE(dataPDUInOrder, _OPT_BOOL(rval));
+         _CASE(dataSequenceInOrder, _OPT_BOOL(rval));
+     }
+}
+
+static char *
+getline(FILE *fd)
+{
+     static char       *sp, line[BUFSIZ];
+     char              *lp, *p;
+
+     do {
+         if(sp == NULL)
+              sp = fgets(line, sizeof line, fd);
+
+         if((lp = sp) == NULL)
+              break;
+         if((p = strchr(lp, '\n')) != NULL)
+              *p = 0;
+         if((p = strchr(lp, '#')) != NULL)
+              *p = 0;
+         if((p = strchr(lp, ';')) != NULL) {
+              *p++ = 0;
+              sp = p;
+         } else
+              sp = NULL;
+         if(*lp)
+              return lp;
+     } while (feof(fd) == 0);
+     return NULL;
+}
+
+static int
+getConfig(FILE *fd, char *key, char **Ar, int *nargs)
+{
+     char      *lp, *p, **ar;
+     int       state, len, n;
+
+     ar = Ar;
+     if(key)
+         len = strlen(key);
+     else
+         len = 0;
+     state = 0;
+     while((lp = getline(fd)) != NULL) {
+         for(; isspace(*lp); lp++)
+              ;
+         switch(state) {
+         case 0:
+              if((p = strchr(lp, '{')) != NULL) {
+                   n = 0;
+                   while((--p > lp) && *p && isspace(*p));
+                   n = p - lp;
+                   if(len && strncmp(lp, key, MAX(n, len)) == 0)
+                        state = 2;
+                   else
+                        state = 1;
+                   continue;
+              }
+              break;
+
+         case 1:
+              if(*lp == '}')
+                   state = 0;
+              continue;
+
+         case 2:
+              if(*lp == '}')
+                   goto done;
+
+              break;
+         }
+
+
+         for(p = &lp[strlen(lp)-1]; isspace(*p); p--)
+              *p = 0;
+         if((*nargs)-- > 0)
+              *ar++ = strdup(lp);
+     }
+
+ done:
+     if(*nargs > 0)
+         *ar = 0;
+     *nargs =  ar - Ar;
+     return ar - Ar;
+}
+
+static textkey_t *
+keyLookup(char *key)
+{
+     textkey_t *tk;
+
+     for(tk = keyMap; tk->name; tk++) {
+         if(strcasecmp(key, tk->name) == 0)
+              return tk;
+     }
+     return NULL;
+}
+
+static void
+puke(isc_opt_t *op)
+{
+     printf("%24s = %d\n", "port", op->port);
+     printf("%24s = %d\n", "tags", op->tags);
+     printf("%24s = %d\n", "maxluns", op->maxluns);
+     printf("%24s = %s\n", "iqn", op->iqn);
+
+     printf("%24s = %d\n", "maxConnections", op->maxConnections);
+     printf("%24s = %d\n", "maxRecvDataSegmentLength", op->maxRecvDataSegmentLength);
+     printf("%24s = %d\n", "maxXmitDataSegmentLength", op->maxRecvDataSegmentLength);
+     printf("%24s = %d\n", "maxBurstLength", op->maxBurstLength);
+     printf("%24s = %d\n", "firstBurstLength", op->firstBurstLength);
+     printf("%24s = %d\n", "defaultTime2Wait", op->defaultTime2Wait);
+     printf("%24s = %d\n", "defaultTime2Retain", op->defaultTime2Retain);
+     printf("%24s = %d\n", "maxOutstandingR2T", op->maxOutstandingR2T);
+     printf("%24s = %d\n", "errorRecoveryLevel", op->errorRecoveryLevel);
+     printf("%24s = %d\n", "targetPortalGroupTag", op->targetPortalGroupTag);
+
+     printf("%24s = %s\n", "headerDigest", op->headerDigest);
+     printf("%24s = %s\n", "dataDigest", op->dataDigest);
+
+     printf("%24s = %d\n", "initialR2T", op->initialR2T);
+     printf("%24s = %d\n", "immediateData", op->immediateData);
+     printf("%24s = %d\n", "dataPDUInOrder", op->dataPDUInOrder);
+     printf("%24s = %d\n", "dataSequenceInOrder", op->dataSequenceInOrder);
+
+     printf("%24s = %s\n", "sessionType", op->sessionType);
+     printf("%24s = %s\n", "targetAddress", op->targetAddress);
+     printf("%24s = %s\n", "targetAlias", op->targetAlias);
+     printf("%24s = %s\n", "targetName", op->targetName);
+     printf("%24s = %s\n", "initiatorName", op->initiatorName);
+     printf("%24s = %s\n", "initiatorAlias", op->initiatorAlias);
+     printf("%24s = %s\n", "authMethod", op->authMethod);
+     printf("%24s = %s\n", "chapSecret", op->chapSecret);
+     printf("%24s = %s\n", "chapIName", op->chapIName);
+     printf("%24s = %s\n", "tgtChapName", op->tgtChapName);
+     printf("%24s = %s\n", "tgtChapSecret", op->tgtChapSecret);
+     printf("%24s = %d\n", "tgttgtChallengeLen", op->tgtChallengeLen);
+}
+
+void
+parseArgs(int nargs, char **args, isc_opt_t *op)
+{
+     char      **ar;
+     char      *p, *v;
+     textkey_t *tk;
+
+     for(ar = args; nargs > 0; nargs--, ar++) {
+         p = strchr(*ar, '=');
+         if(p == NULL)
+              continue;
+         *p = 0;
+         v = p + 1;
+         while(isspace(*--p))
+              *p = 0;
+         while(isspace(*v))
+              v++;
+         if((tk = keyLookup(*ar)) == NULL)
+              continue;
+         setOption(op, tk->tokenID, v);
+     }
+}
+
+void
+parseConfig(FILE *fd, char *key, isc_opt_t *op)
+{
+     char      *Ar[256];
+     int       cc;
+
+     cc = 256;
+     if(getConfig(fd, key, Ar, &cc))
+         parseArgs(cc, Ar, op);
+     if(vflag)
+         puke(op);
+}
diff --git a/sbin/iscontrol/fsm.c b/sbin/iscontrol/fsm.c
new file mode 100644 (file)
index 0000000..711704f
--- /dev/null
@@ -0,0 +1,748 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ | $Id: fsm.c,v 2.8 2007/05/19 16:34:21 danny Exp danny $
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#if __FreeBSD_version < 500000
+#include <sys/time.h>
+#endif
+#include <sys/ioctl.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <camlib.h>
+
+#include "iscsi.h"
+#include "iscontrol.h"
+
+typedef enum {
+     T1 = 1,
+     T2, /*T3,*/ T4, T5, /*T6,*/ T7, T8, T9,
+     T10, T11, T12, T13, T14, T15, T16, T18
+} trans_t;
+
+/*
+ | now supports IPV6
+ | thanks to:
+ |     Hajimu UMEMOTO @ Internet Mutual Aid Society Yokohama, Japan
+ |     ume@mahoroba.org  ume@{,jp.}FreeBSD.org
+ |     http://www.imasy.org/~ume/
+ */
+static trans_t
+tcpConnect(isess_t *sess)
+{
+     isc_opt_t *op = sess->op;
+     int       val, sv_errno, soc;
+     struct     addrinfo *res, *res0, hints;
+     char      pbuf[10];
+
+     debug_called(3);
+     if(sess->flags & (SESS_RECONNECT|SESS_REDIRECT)) {
+         syslog(LOG_INFO, "%s", (sess->flags & SESS_RECONNECT)
+                ? "Reconnect": "Redirected");
+
+         debug(1, "%s", (sess->flags & SESS_RECONNECT) ? "Reconnect": "Redirected");
+         shutdown(sess->soc, SHUT_RDWR);
+         //close(sess->soc);
+         sess->soc = -1;
+
+         sess->flags &= ~SESS_CONNECTED;
+         if(sess->flags & SESS_REDIRECT) {
+              sess->redirect_cnt++;
+              sess->flags |= SESS_RECONNECT;
+         } else
+              sleep(2); // XXX: actually should be ?
+#ifdef notyet
+         {
+              time_t   sec;
+         // make sure we are not in a loop
+         // XXX: this code has to be tested
+         sec = time(0) - sess->reconnect_time;
+         if(sec > (5*60)) {
+              // if we've been connected for more that 5 minutes
+              // then just reconnect
+              sess->reconnect_time = sec;
+              sess->reconnect_cnt1 = 0;
+         }
+         else {
+              //
+              sess->reconnect_cnt1++;
+              if((sec / sess->reconnect_cnt1) < 2) {
+                   // if less that 2 seconds from the last reconnect
+                   // we are most probably looping
+                   syslog(LOG_CRIT, "too many reconnects %d", sess->reconnect_cnt1);
+                   return 0;
+              }
+         }
+     }
+#endif
+         sess->reconnect_cnt++;
+     }
+
+     snprintf(pbuf, sizeof(pbuf), "%d", op->port);
+     memset(&hints, 0, sizeof(hints));
+     hints.ai_family   = PF_UNSPEC;
+     hints.ai_socktype = SOCK_STREAM;
+     debug(1, "targetAddress=%s port=%d", op->targetAddress, op->port);
+     if((val = getaddrinfo(op->targetAddress, pbuf, &hints, &res0)) != 0) {
+          fprintf(stderr, "getaddrinfo(%s): %s\n", op->targetAddress, gai_strerror(val));
+          return 0;
+     }
+     sess->flags &= ~SESS_CONNECTED;
+     sv_errno = 0;
+     soc = -1;
+     for(res = res0; res; res = res->ai_next) {
+         soc = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+         if (soc == -1)
+              continue;
+
+     // from Patrick.Guelat@imp.ch:
+     // iscontrol can be called without waiting for the socket entry to time out
+         val = 1;
+         if(setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &val, (socklen_t)sizeof(val)) < 0) {
+                 fprintf(stderr, "Cannot set socket SO_REUSEADDR %d: %s\n\n",
+                                 errno, strerror(errno));
+         }
+         if(connect(soc, res->ai_addr, res->ai_addrlen) == 0)
+            break;
+
+         sv_errno = errno;
+         close(soc);
+         soc = -1;
+     }
+     freeaddrinfo(res0);
+
+     if(soc != -1) {
+         sess->soc = soc;
+
+         /* Default to TCP_NODELAY to improve transfers */
+         if(setsockopt(sess->soc, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) < 0)
+                 fprintf(stderr, "Cannot set socket NO delay option err=%d %s\n",
+                                 errno, strerror(errno));
+
+#if 0
+         struct        timeval timeout;
+
+         val = 1;
+         if(setsockopt(sess->soc, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0)
+              fprintf(stderr, "Cannot set socket KEEPALIVE option err=%d %s\n",
+                      errno, strerror(errno));
+
+         if(setsockopt(sess->soc, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) < 0)
+              fprintf(stderr, "Cannot set socket NO delay option err=%d %s\n",
+                      errno, strerror(errno));
+
+         timeout.tv_sec = 10;
+         timeout.tv_usec = 0;
+         if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0)
+            || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)) {
+              fprintf(stderr, "Cannot set socket timeout to %ld err=%d %s\n",
+                      timeout.tv_sec, errno, strerror(errno));
+         }
+#endif
+#ifdef CURIOUS
+         {
+              int len = sizeof(val);
+              if(getsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, &len) == 0)
+                   fprintf(stderr, "was: SO_SNDBUF=%dK\n", val/1024);
+         }
+#endif
+         if(sess->op->sockbufsize) {
+              val = sess->op->sockbufsize * 1024;
+              if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0)
+                 || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0)) {
+                   fprintf(stderr, "Cannot set socket sndbuf & rcvbuf to %d err=%d %s\n",
+                           val, errno, strerror(errno));
+                   return 0;
+              }
+         }
+         sess->flags |= SESS_CONNECTED;
+         return T1;
+     }
+
+     fprintf(stderr, "errno=%d\n", sv_errno);
+     perror("connect");
+     switch(sv_errno) {
+     case ECONNREFUSED:
+     case ENETUNREACH:
+     case ETIMEDOUT:
+         if((sess->flags & SESS_REDIRECT) == 0) {
+              if(strcmp(op->targetAddress, sess->target.address) != 0) {
+                   syslog(LOG_INFO, "reconnecting to original target address");
+                   free(op->targetAddress);
+                   op->targetAddress           = sess->target.address;
+                   op->port                    = sess->target.port;
+                   op->targetPortalGroupTag    = sess->target.pgt;
+                   return T1;
+              }
+         }
+         sleep(5); // for now ...
+         return T1;
+     default:
+         return 0; // terminal error
+     }
+}
+
+int
+setOptions(isess_t *sess, int flag)
+{
+     isc_opt_t oop;
+     char      *sep;
+
+     debug_called(3);
+
+     bzero(&oop, sizeof(isc_opt_t));
+
+     if((flag & SESS_FULLFEATURE) == 0) {
+         oop.initiatorName     = sess->op->initiatorName;
+         oop.targetAddress     = sess->op->targetAddress;
+         if(sess->op->targetName != 0)
+              oop.targetName = sess->op->targetName;
+
+         oop.maxRecvDataSegmentLength = sess->op->maxRecvDataSegmentLength;
+         oop.maxXmitDataSegmentLength = sess->op->maxXmitDataSegmentLength; // XXX:
+         oop.maxBurstLength = sess->op->maxBurstLength;
+         oop.maxluns = sess->op->maxluns;
+     }
+     else {
+         /*
+          | turn on digestion only after login
+          */
+         if(sess->op->headerDigest != NULL) {
+              sep = strchr(sess->op->headerDigest, ',');
+              if(sep == NULL)
+                   oop.headerDigest = sess->op->headerDigest;
+              debug(1, "oop.headerDigest=%s", oop.headerDigest);
+         }
+         if(sess->op->dataDigest != NULL) {
+              sep = strchr(sess->op->dataDigest, ',');
+              if(sep == NULL)
+                   oop.dataDigest = sess->op->dataDigest;
+              debug(1, "oop.dataDigest=%s", oop.dataDigest);
+         }
+     }
+
+     if(ioctl(sess->fd, ISCSISETOPT, &oop)) {
+         perror("ISCSISETOPT");
+         return -1;
+     }
+     return 0;
+}
+
+static trans_t
+startSession(isess_t *sess)
+{
+
+     int       n, fd, nfd;
+     char      *dev;
+
+     debug_called(3);
+
+     if((sess->flags & SESS_CONNECTED) == 0) {
+         return T2;
+     }
+     if(sess->fd == -1) {
+         fd = open(iscsidev, O_RDWR);
+         if(fd < 0) {
+              perror(iscsidev);
+              return 0;
+         }
+         {
+              // XXX: this has to go
+              size_t   n;
+              n = sizeof(sess->isid);
+              if(sysctlbyname("net.iscsi.isid", (void *)sess->isid, (size_t *)&n, 0, 0) != 0)
+                   perror("sysctlbyname");
+         }
+         if(ioctl(fd, ISCSISETSES, &n)) {
+              perror("ISCSISETSES");
+              return 0;
+         }
+         sleep(1);     /* XXX temporary */
+         asprintf(&dev, "%s%d", iscsidev, n);
+         nfd = open(dev, O_RDWR);
+         if(nfd < 0) {
+              perror(dev);
+              free(dev);
+              return 0;
+         }
+         free(dev);
+         close(fd);
+         sess->fd = nfd;
+
+         if(setOptions(sess, 0) != 0)
+              return -1;
+     }
+
+     if(ioctl(sess->fd, ISCSISETSOC, &sess->soc)) {
+         perror("ISCSISETSOC");
+         return 0;
+     }
+
+     return T4;
+}
+
+isess_t *currsess;
+
+static void
+trap(int sig)
+{
+     syslog(LOG_NOTICE, "trapped signal %d", sig);
+     fprintf(stderr, "trapped signal %d\n", sig);
+
+     switch(sig) {
+     case SIGHUP:
+         currsess->flags |= SESS_DISCONNECT;
+         break;
+
+     case SIGUSR1:
+         currsess->flags |= SESS_RECONNECT;
+         break;
+
+     case SIGINT:
+     case SIGTERM:
+     default:
+         return; // ignore
+     }
+}
+
+static void
+doCAM(isess_t *sess)
+{
+     char      pathstr[1024];
+     union ccb *ccb;
+     int       i;
+
+     if(ioctl(sess->fd, ISCSIGETCAM, &sess->cam) != 0) {
+         syslog(LOG_WARNING, "ISCSIGETCAM failed: %d", errno);
+         return;
+     }
+     debug(2, "nluns=%d", sess->cam.target_nluns);
+     /*
+      | for now will do this for each lun ...
+      */
+     for(i = 0; i < sess->cam.target_nluns; i++) {
+         debug(2, "CAM path_id=%d target_id=%d target_lun=%d",
+               sess->cam.path_id, sess->cam.target_id, sess->cam.target_lun[i]);
+
+         sess->camdev = cam_open_btl(sess->cam.path_id, sess->cam.target_id,
+                                     sess->cam.target_lun[i], O_RDWR, NULL);
+         if(sess->camdev == NULL) {
+              syslog(LOG_WARNING, "%s", cam_errbuf);
+              debug(3, "%s", cam_errbuf);
+              continue;
+         }
+
+         cam_path_string(sess->camdev, pathstr, sizeof(pathstr));
+         debug(2, "pathstr=%s", pathstr);
+
+         ccb = cam_getccb(sess->camdev);
+         bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_relsim) - sizeof(struct ccb_hdr));
+         ccb->ccb_h.func_code = XPT_REL_SIMQ;
+         ccb->crs.release_flags = RELSIM_ADJUST_OPENINGS;
+         ccb->crs.openings = sess->op->tags;
+
+         if(cam_send_ccb(sess->camdev, ccb) < 0)
+              syslog(LOG_WARNING, "%s", cam_errbuf);
+         else
+         if((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+              syslog(LOG_WARNING, "XPT_REL_SIMQ CCB failed");
+              // cam_error_print(sess->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
+         }
+         else
+              syslog(LOG_INFO, "%s tagged openings now %d\n", pathstr, ccb->crs.openings);
+
+         cam_freeccb(ccb);
+         cam_close_device(sess->camdev);
+     }
+}
+
+static trans_t
+supervise(isess_t *sess)
+{
+     int       sig, val;
+
+     debug_called(3);
+
+     if(strcmp(sess->op->sessionType, "Discovery") == 0) {
+         sess->flags |= SESS_DISCONNECT;
+         return T9;
+     }
+
+     if(vflag)
+         printf("ready to go scsi\n");
+
+     if(setOptions(sess, SESS_FULLFEATURE) != 0)
+         return 0; // failure
+
+     if((sess->flags & SESS_FULLFEATURE) == 0) {
+         if(daemon(0, 1) != 0) {
+              perror("daemon");
+              exit(1);
+         }
+
+         openlog("iscontrol", LOG_CONS|LOG_PERROR|LOG_PID|LOG_NDELAY, LOG_KERN);
+         syslog(LOG_INFO, "running");
+
+         currsess = sess;
+         if(ioctl(sess->fd, ISCSISTART)) {
+              perror("ISCSISTART");
+              return -1;
+         }
+         doCAM(sess);
+
+     }
+     else {
+         if(ioctl(sess->fd, ISCSIRESTART)) {
+              perror("ISCSIRESTART");
+              return -1;
+         }
+     }
+
+     signal(SIGINT, trap);
+     signal(SIGHUP, trap);
+     signal(SIGTERM, trap);
+
+     sig = SIGUSR1;
+     signal(sig, trap);
+     if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
+         perror("ISCSISIGNAL");
+         return -1;
+     }
+     sess->flags |= SESS_FULLFEATURE;
+
+     sess->flags &= ~(SESS_REDIRECT | SESS_RECONNECT);
+     printf("iscontrol: supervise starting main loop\n");
+     /*
+      | the main loop - actually do nothing
+      | all the work is done inside the kernel
+      */
+     while((sess->flags & (SESS_REDIRECT|SESS_RECONNECT|SESS_DISCONNECT)) == 0) {
+         // do something?
+         // like sending a nop_out?
+         sleep(60);
+     }
+     printf("iscontrol: supervise going down\n");
+     syslog(LOG_INFO, "sess flags=%x", sess->flags);
+
+     sig = 0;
+     if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
+         perror("ISCSISIGNAL");
+     }
+
+     if(sess->flags & SESS_DISCONNECT) {
+         val = 0;
+         if(ioctl(sess->fd, ISCSISTOP, &val)) {
+              perror("ISCSISTOP");
+         }
+         sess->flags &= ~SESS_FULLFEATURE;
+         return T9;
+     }
+     else {
+         sess->flags |= SESS_INITIALLOGIN1;
+     }
+     return T8;
+}
+
+static int
+handledDiscoveryResp(isess_t *sess, pdu_t *pp)
+{
+     u_char    *ptr;
+     int       len, n;
+
+     debug_called(3);
+
+     len = pp->ds_len;
+     ptr = pp->ds;
+     while(len > 0) {
+         if(*ptr != 0)
+              printf("%s\n", ptr);
+         n = strlen((char *)ptr) + 1;
+         len -= n;
+         ptr += n;
+     }
+     return 0;
+}
+
+static int
+doDiscovery(isess_t *sess)
+{
+     pdu_t     spp;
+     text_req_t        *tp = (text_req_t *)&spp.ipdu.bhs;
+
+     debug_called(3);
+
+     bzero(&spp, sizeof(pdu_t));
+     tp->cmd = ISCSI_TEXT_CMD /*| 0x40 */; // because of a bug in openiscsi-target
+     tp->F = 1;
+     tp->ttt = 0xffffffff;
+     addText(&spp, "SendTargets=All");
+     return sendPDU(sess, &spp, handledDiscoveryResp);
+}
+
+static trans_t
+doLogin(isess_t *sess)
+{
+     isc_opt_t *op = sess->op;
+     int       status, count;
+
+     debug_called(3);
+
+     if(op->chapSecret == NULL && op->tgtChapSecret == NULL)
+         /*
+          | don't need any security negotiation
+          | or in other words: we don't have any secrets to exchange
+          */
+         sess->csg = LON_PHASE;
+     else
+         sess->csg = SN_PHASE;
+
+     if(sess->tsih) {
+         sess->tsih = 0;       // XXX: no 'reconnect' yet
+         sess->flags &= ~SESS_NEGODONE; // XXX: KLUDGE
+     }
+     count = 10; // should be more than enough
+     do {
+         debug(3, "count=%d csg=%d", count, sess->csg);
+         status = loginPhase(sess);
+         if(count-- == 0)
+              // just in case we get into a loop
+              status = -1;
+     } while(status == 0 && (sess->csg != FF_PHASE));
+
+     sess->flags &= ~SESS_INITIALLOGIN;
+     debug(3, "status=%d", status);
+
+     switch(status) {
+     case 0: // all is ok ...
+         sess->flags |= SESS_LOGGEDIN;
+         if(strcmp(sess->op->sessionType, "Discovery") == 0)
+              doDiscovery(sess);
+         return T5;
+
+     case 1:   // redirect - temporary/permanent
+         /*
+          | start from scratch?
+          */
+         sess->flags &= ~SESS_NEGODONE;
+         sess->flags |= (SESS_REDIRECT | SESS_INITIALLOGIN1);
+         syslog(LOG_DEBUG, "target sent REDIRECT");
+         return T7;
+
+     case 2: // initiator terminal error
+         return 0;
+     case 3: // target terminal error -- could retry ...
+         sleep(5);
+         return T7; // lets try
+     default:
+         return 0;
+     }
+}
+
+static int
+handleLogoutResp(isess_t *sess, pdu_t *pp)
+{
+     if(sess->flags & SESS_DISCONNECT)
+         return 0;
+     return T13;
+}
+
+static trans_t
+startLogout(isess_t *sess)
+{
+     pdu_t     spp;
+     logout_req_t *p = (logout_req_t *)&spp.ipdu.bhs;
+
+     bzero(&spp, sizeof(pdu_t));
+     p->cmd = ISCSI_LOGOUT_CMD| 0x40;
+     p->reason = BIT(7) | 0;
+     p->CID = htons(1);
+
+     return sendPDU(sess, &spp, handleLogoutResp);
+}
+
+static trans_t
+inLogout(isess_t *sess)
+{
+     if(sess->flags & SESS_RECONNECT)
+         return T18;
+     return 0;
+}
+
+typedef enum {
+     S1=1, S2, S3, S4, S5, S6, S7, S8
+} state_t;
+\f
+#if 0
+      S1: FREE
+      S2: XPT_WAIT
+      S4: IN_LOGIN
+      S5: LOGGED_IN
+      S6: IN_LOGOUT
+      S7: LOGOUT_REQUESTED
+      S8: CLEANUP_WAIT
+
+                     -------<-------------+
+         +--------->/ 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
+         +---------------------------+
+#endif
+
+int
+fsm(isc_opt_t *op)
+{
+     state_t   state;
+     isess_t   *sess;
+
+     if((sess = calloc(1, sizeof(isess_t))) == NULL) {
+         // boy, is this a bad start ...
+         fprintf(stderr, "no memory!\n");
+         return -1;
+     }
+
+     state = S1;
+     sess->op = op;
+     sess->fd = -1;
+     sess->soc = -1;
+     sess->target.address = strdup(op->targetAddress);
+     sess->target.port = op->port;
+     sess->target.pgt = op->targetPortalGroupTag;
+
+     sess->flags = SESS_INITIALLOGIN | SESS_INITIALLOGIN1;
+
+     do {
+         switch(state) {
+         case S1:
+              switch(tcpConnect(sess)) {
+              case T1: state = S2; break;
+              default: state = S8; break;
+              }
+              break;
+
+         case S2:
+              switch(startSession(sess)) {
+              case T2: state = S1; break;
+              case T4: state = S4; break;
+              default: state = S8; break;
+              }
+              break;
+
+         case S4:
+              switch(doLogin(sess)) {
+              case T7:  state = S1; break;
+              case T5:  state = S5; break;
+              default: state = S8; break;
+              }
+              break;
+
+         case S5:
+              switch(supervise(sess)) {
+              case T8:  state = S1; break;
+              case T9:  state = S6; break;
+              case T11: state = S7; break;
+              case T15: state = S8; break;
+              default: state = S8; break;
+              }
+              break;
+
+         case S6:
+              switch(startLogout(sess)) {
+              case T13: state = S1; break;
+              case T14: state = S6; break;
+              case T16: state = S8; break;
+              default: state = S8; break;
+              }
+              break;
+
+         case S7:
+              switch(inLogout(sess)) {
+              case T18: state = S1; break;
+              case T10: state = S6; break;
+              case T12: state = S7; break;
+              case T16: state = S8; break;
+              default: state = S8; break;
+              }
+              break;
+
+         case S8:
+              // maybe do some clean up?
+              syslog(LOG_INFO, "terminated");
+              return 0;
+         default:
+              syslog(LOG_INFO, "unknown state %d", state);
+              return 0;
+         }
+     } while(1);
+}
diff --git a/sbin/iscontrol/iscontrol.8 b/sbin/iscontrol/iscontrol.8
new file mode 100644 (file)
index 0000000..3ca789e
--- /dev/null
@@ -0,0 +1,123 @@
+.\" Copyright (c) 2007-2008 Daniel Braniss <danny@cs.huji.ac.il>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 22, 2007
+.Dt ISCONTROL 8
+.Os
+.Sh NAME
+.Nm iscontrol
+.Nd login/negotiator/control for an iSCSI initiator session
+.Sh SYNOPSIS
+.Nm
+.Op Fl vd
+.Oo
+.Op Fl Ar file
+.Op Fl n Ar nickname
+.Oc
+.Op Fl t Ar target
+.Op Ar variable Ns = Ns Ar value
+.Sh DESCRIPTION
+Internet SCSI (iSCSI) is a network protocol standard, that allows the
+use of the SCSI protocol over TCP/IP networks,
+the
+.Nm
+program is the userland side of an iSCSI session, see
+.Xr iscsi_initiator 4 .
+It has 2 modes of operation, if -d (discovery session) is specified,
+it will print out the
+.Em target names
+returned by the target and exit.
+In the second mode, it will, after a successful login/negotiation, run
+in daemon mode, monitoring the connection, and will try to reconnect
+in case of a network/target failure.
+It will terminate/logout the session
+when a SIGHUP signal is received.
+The flags are as follows:
+.Bl -tag -width variable=value
+.It Fl v
+verbose mode.
+.It Fl d
+do a
+.Em discovery session
+and exit.
+.It Fl c Ar file
+a file containing configuration
+.Em key-options ,
+see
+.Xr iscsi.conf 5
+.It Fl n Ar nickname
+if
+.Sy -c file
+is specified, then search for the block named
+.Em nickname
+in that file, see
+.Xr iscsi.conf 5
+.It Fl t Ar target
+is the target's IP address or name
+.It Ar variable Ns = Ns Ar value
+see
+.Xr iscsi.conf 5
+for the complete list of variables/options and their
+possible values.
+.El
+.Sh EXAMPLES
+.Dl iscontrol -dt myiscsitarget
+.Pp
+will start a
+.Em discovery session
+with the target and
+print to stdout the list of available targetnames/targetadresses.
+Note: this listing does not necessarily mean availability, since
+depending on the target configuration, a discovery session might
+not need login/access permission, but a
+.Em full session
+certainly does.
+.sp
+.Dl iscontrol -c /etc/iscsi.conf -n myiscsi
+.Pp
+will read options from
+.Pa /etc/iscsi.conf ,
+use the targetaddress
+found in the block nicknamed myiscsi, login and negotiate
+whatever options are specified, and start an iscsi-session.
+.Sh SEE ALSO
+.Xr da 4 ,
+.Xr iscsi_initiator 4 ,
+.Xr sa 4 ,
+.Xr iscsi.conf 5 ,
+.Xr camcontrol 8
+.Sh STANDARDS
+RFC 3720
+.\"Sh HISTORY
+.Sh BUGS
+.Nm
+should probably load the iscsi_initiator module if needed.
+.br
+Not all functions/specifications have been implemented yet, noticeably
+missing are the Task Management Functions.
+The error recovery, though not
+.Em fully compliant
+does a brave effort to recover from network disconnects.
diff --git a/sbin/iscontrol/iscontrol.c b/sbin/iscontrol/iscontrol.c
new file mode 100644 (file)
index 0000000..07f557a
--- /dev/null
@@ -0,0 +1,231 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ | $Id: iscontrol.c,v 2.2 2006/12/01 09:11:56 danny Exp danny $
+ */
+/*
+ | the user level initiator (client)
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <camlib.h>
+
+#include "iscsi.h"
+#include "iscontrol.h"
+
+#define USAGE "[-v] [-d] [-c config] [-n name] [-t target] "
+#define OPTIONS        "vdc:t:n:"
+
+#ifndef DEBUG
+//int  vflag;
+#endif
+
+token_t AuthMethods[] = {
+     {"None",  NONE},
+     {"KRB5",  KRB5},
+     {"SPKM1", SPKM1},
+     {"SPKM2", SPKM2},
+     {"SRP",   SRP},
+     {"CHAP",  CHAP},
+     {0, 0}
+};
+
+token_t        DigestMethods[] = {
+     {"None",  0},
+     {"CRC32", 1},
+     {"CRC32C",        1},
+     {0, 0}
+};
+
+u_char isid[6 + 6];
+/*
+ | Default values
+ */
+isc_opt_t opvals = {
+     .port                     = 3260,
+     .sockbufsize              = 128,
+     .iqn                      = "iqn.2005-01.il.ac.huji.cs:",
+
+     .sessionType              = "Normal",
+     .targetAddress            = 0,
+     .targetName               = 0,
+     .initiatorName            = 0,
+     .authMethod               = "None",
+     .headerDigest             = "None,CRC32C",
+     .dataDigest               = "None,CRC32C",
+     .maxConnections           = 1,
+     .maxRecvDataSegmentLength = 64 * 1024,
+     .maxXmitDataSegmentLength = 8 * 1024, // 64 * 1024,
+     .maxBurstLength           = 128 * 1024,
+     .firstBurstLength         = 64 * 1024, // must be less than maxBurstLength
+     .defaultTime2Wait         = 0,
+     .defaultTime2Retain       = 0,
+     .maxOutstandingR2T                = 1,
+     .errorRecoveryLevel       = 0,
+
+     .dataPDUInOrder           = TRUE,
+     .dataSequenceInOrder      = TRUE,
+
+     .initialR2T               = TRUE,
+     .immediateData            = TRUE,
+};
+
+int
+lookup(token_t *tbl, char *m)
+{
+     token_t   *tp;
+
+     for(tp = tbl; tp->name != NULL; tp++)
+         if(strcasecmp(tp->name, m) == 0)
+              return tp->val;
+     return 0;
+}
+
+int
+main(int cc, char **vv)
+{
+     int       ch, disco;
+     char      *pname, *p, *q, *ta, *kw;
+     isc_opt_t *op;
+     FILE      *fd;
+
+     op = &opvals;
+     iscsidev = "/dev/"ISCSIDEV;
+     fd = NULL;
+     pname = vv[0];
+     if((p = strrchr(pname, '/')) != NULL)
+         pname = p + 1;
+
+     kw = ta = 0;
+     disco = 0;
+
+     while((ch = getopt(cc, vv, OPTIONS)) != -1) {
+         switch(ch) {
+         case 'v':
+              vflag++;
+              break;
+         case 'c':
+              fd = fopen(optarg, "r");
+              if(fd == NULL) {
+                   perror(optarg);
+                   exit(1);
+              }
+              break;
+         case 'd':
+              disco = 1;
+              break;
+         case 't':
+              ta = optarg;
+              break;
+         case 'n':
+              kw = optarg;
+              break;
+         default:
+         badu:
+              fprintf(stderr, "Usage: %s %s\n", pname, USAGE);
+              exit(1);
+         }
+     }
+     if(fd == NULL)
+         fd = fopen("/etc/iscsi.conf", "r");
+
+     if(fd != NULL) {
+         parseConfig(fd, kw, op);
+         fclose(fd);
+     }
+     cc -= optind;
+     vv += optind;
+     if(cc > 0) {
+         if(vflag)
+              printf("adding '%s'\n", *vv);
+         parseArgs(cc, vv, op);
+     }
+     if(ta)
+         op->targetAddress = ta;
+
+     if(op->targetAddress == NULL) {
+         fprintf(stderr, "No target!\n");
+         goto badu;
+     }
+     q = op->targetAddress;
+     if(*q == '[' && (q = strchr(q, ']')) != NULL) {
+         *q++ = '\0';
+         op->targetAddress++;
+     } else
+         q = op->targetAddress;
+     if((p = strchr(q, ':')) != NULL) {
+         *p++ = 0;
+         op->port = atoi(p);
+         p = strchr(p, ',');
+     }
+     if(p || ((p = strchr(q, ',')) != NULL)) {
+         *p++ = 0;
+         op->targetPortalGroupTag = atoi(p);
+     }
+     if(op->initiatorName == 0) {
+         char  hostname[256];
+
+         if(op->iqn) {
+              if(gethostname(hostname, sizeof(hostname)) == 0)
+                   asprintf(&op->initiatorName, "%s:%s", op->iqn, hostname);
+              else
+                   asprintf(&op->initiatorName, "%s:%d", op->iqn, (int)time(0) & 0xff); // XXX:
+         }
+         else {
+              if(gethostname(hostname, sizeof(hostname)) == 0)
+                   asprintf(&op->initiatorName, "%s", hostname);
+              else
+                   asprintf(&op->initiatorName, "%d", (int)time(0) & 0xff); // XXX:
+         }
+     }
+     if(disco) {
+         op->sessionType = "Discovery";
+         op->targetName = 0;
+     }
+
+     fsm(op);
+
+     exit(0);
+}
diff --git a/sbin/iscontrol/iscontrol.h b/sbin/iscontrol/iscontrol.h
new file mode 100644 (file)
index 0000000..31d9e73
--- /dev/null
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ | $Id: iscontrol.h,v 2.3 2007/04/27 08:36:49 danny Exp danny $
+ */
+#ifdef DEBUG
+int vflag;
+
+# define debug(level, fmt, args...)    do {if (level <= vflag) printf("%s: " fmt "\n", __func__ , ##args);} while(0)
+# define debug_called(level)           do {if (level <= vflag) printf("%s: called\n", __func__);} while(0)
+#else
+# define debug(level, fmt, args...)
+# define debug_called(level)
+#endif // DEBUG
+#define xdebug(fmt, args...)   printf("%s: " fmt "\n", __func__ , ##args)
+
+#define BIT(n) (1 <<(n))
+
+#define MAXREDIRECTS   2
+
+typedef int auth_t(void *sess);
+
+typedef struct {
+     char      *address;
+     int       port;
+     int       pgt;
+} target_t;
+
+typedef struct isess {
+     int       flags;
+#define SESS_CONNECTED         BIT(0)
+#define SESS_DISCONNECT                BIT(1)
+#define SESS_LOGGEDIN          BIT(2)
+#define SESS_RECONNECT         BIT(3)
+#define SESS_REDIRECT          BIT(4)
+
+#define SESS_NEGODONE          BIT(10) // XXX: kludge
+
+#define SESS_FULLFEATURE       BIT(29)
+#define SESS_INITIALLOGIN1     BIT(30)
+#define SESS_INITIALLOGIN      BIT(31)
+
+
+     isc_opt_t *op;            // operational values
+     target_t  target;         // the Original target address
+     int       fd;             // the session fd
+     int       soc;            // the socket
+     iscsi_cam_t       cam;
+     struct cam_device *camdev;
+
+     time_t    open_time;
+     int       redirect_cnt;
+     time_t    redirect_time;
+     int       reconnect_cnt;
+     int       reconnect_cnt1;
+     time_t    reconnect_time;
+     char      isid[6+1];
+     int       csg;            // current stage
+     int       nsg;            // next stage
+     // Phases/Stages
+#define        SN_PHASE        0       // Security Negotiation
+#define LON_PHASE      1       // Login Operational Negotiation
+#define FF_PHASE       3       // FuLL-Feature
+     uint      tsih;
+     sn_t      sn;
+} isess_t;
+
+typedef struct token {
+     char      *name;
+     int       val;
+} token_t;
+
+typedef enum {
+     NONE      = 0,
+     KRB5,
+     SPKM1,
+     SPKM2,
+     SRP,
+     CHAP
+} authm_t;
+
+extern token_t AuthMethods[];
+extern token_t DigestMethods[];
+
+typedef enum {
+     SET,
+     GET
+} oper_t;
+
+typedef enum {
+     U_PR,     // private
+     U_IO,     // Initialize Only -- during login
+     U_LO,     // Leading Only -- when TSIH is zero
+     U_FFPO,   // Full Feature Phase Only
+     U_ALL     // in any phase
+} usage_t;
+
+typedef enum {
+     S_PR,
+     S_CO,     // Connect only
+     S_SW      // Session Wide
+} scope_t;
+
+typedef void keyfun_t(isess_t *, oper_t);
+
+typedef struct {
+     usage_t   usage;
+     scope_t   scope;
+     char      *name;
+     int       tokenID;
+} textkey_t;
+
+typedef int handler_t(isess_t *sess, pdu_t *pp);
+
+int    authenticateLogin(isess_t *sess);
+int    fsm(isc_opt_t *op);
+int    sendPDU(isess_t *sess, pdu_t *pp, handler_t *hdlr);
+int    addText(pdu_t *pp, char *fmt, ...);
+void   freePDU(pdu_t *pp);
+int    xmitpdu(isess_t *sess, pdu_t *pp);
+int    recvpdu(isess_t *sess, pdu_t *pp);
+void   pukeText(char *it, pdu_t *pp);
+
+int    lookup(token_t *tbl, char *m);
+
+int    vflag;
+char   *iscsidev;
+
+void   parseArgs(int nargs, char **args, isc_opt_t *op);
+void   parseConfig(FILE *fd, char *key, isc_opt_t *op);
+
+char   *chapDigest(char *ap, char id, char *cp, char *chapSecret);
+char   *genChapChallenge(char *encoding, size_t len);
+
+int    str2bin(char *str, char **rsp);
+char   *bin2str(char *fmt, unsigned char *md, int blen);
+
+int    negotiateOPV(isess_t *sess);
+int    setOptions(isess_t *sess, int flag);
+
+int    loginPhase(isess_t *sess);
diff --git a/sbin/iscontrol/iscsi.conf.5 b/sbin/iscontrol/iscsi.conf.5
new file mode 100644 (file)
index 0000000..fc6eadf
--- /dev/null
@@ -0,0 +1,210 @@
+.\" Copyright (c) 2007-2008 Daniel Braniss <danny@cs.huji.ac.il>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 5, 2007
+.Os
+.Dt ISCSI.CONF 5
+.Sh NAME
+.Nm iscsi.conf
+.Nd key options to be negotiated in an iSCSI session
+.Sh DESCRIPTION
+The file
+.Nm ,
+is read by the
+.Xr iscontrol 8
+program.
+It contains declarations and parameter/key-options.
+The syntax is very simple,
+.D1 Li variable = value;
+and they can be grouped via a
+.Em block
+declaration:
+.Bf Li
+.Bd -literal
+       # this is a comment
+       target_1 { # nickname
+          variable = value;
+          ...
+       } # this must be on a line by itself.
+.Ed
+.Ef
+.Pp
+The following are specified in the iSCSI RFC 3720,
+for a full description see sections 11/12 of the RFC.
+.Bl -tag -width MaxConnections
+.It Cm AuthMethod
+currently only supported authentication method is CHAP, with
+digest either MD5 or SHA.
+Default is none.
+.It Cm HeaderDigest
+a
+.Em digest
+is calculated on the header of all iSCSI PDUs, and
+checked.
+Only CRC32C is implemented.
+Default is none.
+.It Cm DataDigest
+same as for HeaderDigest, but on the data part of the iSCSI PDU.
+.It Cm MaxConnections
+is the number of simultaneous connections per session,
+currently only 1.
+.It Cm TargetName
+is the name by which the target is known, not to be confused with
+target address, either obtained via the target administrator, or
+from a
+.Em discovery session.
+.It Cm InitiatorName
+if not specified, defaults to
+.Sy iqn.2005-01.il.ac.huji.cs:
+.Aq hostname .
+.It Cm TargetAlias / InitiatorAlias
+not implemented.
+.It Cm TargetAddress
+is of the form
+.Sy domainname[:port][,portal-group-tag]
+to quote the RFC:
+.Bd -ragged -compact
+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].
+.Ed
+Note: portal-group-tag is unused at the moment.
+.It Cm TargetPortalGroupTag
+.Em not implemented yet.
+.It Cm InitialR2T
+.Em not implemented yet.
+.It Cm ImmediateData
+.Em not implemented yet.
+.It Cm MaxRecvDataSegmentLength
+the maximum data segment length in
+bytes it can receive in an iSCSI PDU, default is 8192.
+.It Cm MaxBurstLength
+.Em not implemented yet.
+.It Cm FirstBurstLength
+.Em not implemented yet.
+.It Cm DefaultTime2Wait
+.Em not implemented yet.
+.It Cm DefaultTime2Retain
+.Em not implemented yet.
+.It Cm MaxOutstandingR2T
+is used to calculate/negotiate the
+.Em tag opening ,
+can be overriden by the
+.Sy tag
+option.
+.It Cm DataPDUInOrder
+.Em not implemented yet.
+.It Cm DataSequenceInOrder
+.Em not implemented yet.
+.It Cm ErrorRecoveryLevel
+Only level 0 is supported.
+.It Cm SessionType
+either Discovery or Normal, default is Normal, see the
+.Fl d
+flag of
+.Cm iscontrol .
+.El
+.sp
+The following are not specified in the
+.Sy RFC 3720
+.Bl -tag -width sockbufsize
+.It Cm port
+The iSCSI port used by the iSCSI protocol, defaults to 3260.
+.It Cm tags
+Sets the
+.Em tag opening
+to the value specified.
+.It Cm maxluns
+overrides the compiled value of
+.Sy luns ,
+see
+.Xr iscsi_initiator 4 .
+This value can only be reduced.
+.It Cm sockbufsize
+sets the receiver and transmitter socket buffer size to
+.Em size,
+in kilobytes.
+The default is 128.
+.El
+.sp
+If
+.Em AuthMethod
+is set to
+.Cm CHAP ,
+then the following must also be set:
+.Bl -tag -width chapSecret
+.It Cm chapSecret
+this
+.Em shared-secret .
+Can be either an ASCII string (e.g. hello world), a hex string (e.g
+0xababcd0987654321...), or base64 string (eg 0b...)
+.It Cm chapIName
+the chap-name, defaults to
+.Em hostname .
+.It Cm chapDigest
+can be MD5 or SHA1.
+.It Cm tgtChapSecret/tgtChapName
+same as the none
+.Em tgt
+counterpart, but to authenticate the target.
+.El
+.Sh FILES
+.Pa /etc/iscsi.conf
+.Sh EXAMPLES
+.Bd -literal
+#
+# Globals
+#
+port = 3260
+#
+myiscsi { # nickname
+   targetaddress       = iscsi1
+   targetname          = iqn.1900.com.com:sn.123456
+}
+chaptest {
+   targetaddress= 10.0.0.1;
+   targetname  = iqn.1900.com.com:sn.123456
+   initiatorname= iqn.2005-01.il.ac.huji.cs:nobody
+   authmethod = CHAP; chapDigest = SHA1;
+   chapsecret = 0x3713c3336d9a224c2791c873d3d2b174
+   tags                = 256
+}
+.Ed
+.Sh ERRORS
+The parsing is very primitive, so do not expect - at the moment - any
+error messages.
+.Sh SEE ALSO
+.Xr iscsi_initiator 4 ,
+.Xr iscontrol 8
+.Sh STANDARDS
+ISCSI RFC 3720
+.\"Sh HISTORY
+.\"Sh AUTHORS
+.Sh BUGS
+Some options have not been implemented, either they were found
+to be unecessary, or not understood, this can change in the future.
+.br
+The tags opening value is difficult to calculate, use wisely.
diff --git a/sbin/iscontrol/login.c b/sbin/iscontrol/login.c
new file mode 100644 (file)
index 0000000..cf8f1e9
--- /dev/null
@@ -0,0 +1,442 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ | $Id: login.c,v 1.4 2007/04/27 07:40:40 danny Exp danny $
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#if __FreeBSD_version < 500000
+#include <sys/time.h>
+#endif
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "iscsi.h"
+#include "iscontrol.h"
+
+static char *status_class1[] = {
+     "Initiator error",
+     "Authentication failure",
+     "Authorization failure",
+     "Not found",
+     "Target removed",
+     "Unsupported version",
+     "Too many connections",
+     "Missing parameter",
+     "Can't include in session",
+     "Session type not suported",
+     "Session does not exist",
+     "Invalid during login",
+};
+#define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
+
+static char *status_class3[] = {
+     "Target error",
+     "Service unavailable",
+     "Out of resources"
+};
+#define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
+
+static char *
+selectFrom(char *str, token_t *list)
+{
+     char      *sep, *sp;
+     token_t   *lp;
+     int       n;
+
+     sp = str;
+     do {
+         sep = strchr(sp, ',');
+         if(sep != NULL)
+              n = sep - sp;
+         else
+              n = strlen(sp);
+
+         for(lp = list; lp->name != NULL; lp++) {
+              if(strncasecmp(lp->name, sp, n) == 0)
+                   return strdup(lp->name);
+         }
+         sp = sep + 1;
+     } while(sep != NULL);
+
+     return NULL;
+}
+
+static char *
+getkeyval(char *key, pdu_t *pp)
+{
+    char       *ptr;
+    int        klen, len, n;
+
+    debug_called(3);
+
+    len = pp->ds_len;
+    ptr = (char *)pp->ds;
+    klen = strlen(key);
+    while(len > klen) {
+        if(strncmp(key, ptr, klen) == 0)
+             return ptr+klen;
+        n = strlen(ptr) + 1;
+        len -= n;
+        ptr += n;
+    }
+    return 0;
+}
+
+static int
+handleTgtResp(isess_t *sess, pdu_t *pp)
+{
+     isc_opt_t *op = sess->op;
+     char      *np, *rp, *d1, *d2;
+     int       res, l1, l2;
+
+     res = -1;
+     if(((np = getkeyval("CHAP_N=", pp)) == NULL) ||
+       ((rp = getkeyval("CHAP_R=", pp)) == NULL))
+         goto out;
+     if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) {
+         fprintf(stderr, "%s does not match\n", np);
+         goto out;
+     }
+     l1 = str2bin(op->tgtChapDigest, &d1);
+     l2 = str2bin(rp, &d2);
+
+     debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp);
+     if(l1 == l2 && memcmp(d1, d2, l1) == 0)
+       res = 0;
+     if(l1)
+         free(d1);
+     if(l2)
+         free(d2);
+ out:
+     free(op->tgtChapDigest);
+     op->tgtChapDigest = NULL;
+
+     debug(3, "res=%d", res);
+
+     return res;
+}
+
+static void
+processParams(isess_t *sess, pdu_t *pp)
+{
+     isc_opt_t         *op = sess->op;
+     int               len, klen, n;
+     char              *eq, *ptr;
+
+     debug_called(3);
+
+     len = pp->ds_len;
+     ptr = (char *)pp->ds;
+     while(len > 0) {
+         if(vflag > 1)
+              printf("got: len=%d %s\n", len, ptr);
+         klen = 0;
+         if((eq = strchr(ptr, '=')) != NULL)
+              klen = eq - ptr;
+         if(klen > 0) {
+              if(strncmp(ptr, "TargetAddress", klen) == 0) {
+                   char        *p, *q, *ta = NULL;
+
+                   // TargetAddress=domainname[:port][,portal-group-tag]
+                   // XXX: if(op->targetAddress) free(op->targetAddress);
+                   q = op->targetAddress = strdup(eq+1);
+                   if(*q == '[') {
+                        // bracketed IPv6
+                        if((q = strchr(q, ']')) != NULL) {
+                             *q++ = '\0';
+                             ta = op->targetAddress;
+                             op->targetAddress = strdup(ta+1);
+                        } else
+                             q = op->targetAddress;
+                   }
+                   if((p = strchr(q, ',')) != NULL) {
+                        *p++ = 0;
+                        op->targetPortalGroupTag = atoi(p);
+                   }
+                   if((p = strchr(q, ':')) != NULL) {
+                        *p++ = 0;
+                        op->port = atoi(p);
+                   }
+                   if(ta)
+                        free(ta);
+              } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) {
+                   // danny's RFC
+                   op->maxXmitDataSegmentLength = strtol(eq+1, (char **)NULL, 0);
+              } else  if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) {
+                   op->targetPortalGroupTag = strtol(eq+1, (char **)NULL, 0);
+              } else if(strncmp(ptr, "HeaderDigest", klen) == 0) {
+                   op->headerDigest = selectFrom(eq+1, DigestMethods);
+              } else if(strncmp(ptr, "DataDigest", klen) == 0) {
+                   op->dataDigest = selectFrom(eq+1, DigestMethods);
+              } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0)
+                   op->maxOutstandingR2T = strtol(eq+1, (char **)NULL, 0);
+#if 0
+              else
+              for(kp = keyMap; kp->name; kp++) {
+                   if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=')
+                        mp->func(sess, ptr+kp->len+1, GET);
+              }
+#endif
+         }
+         n = strlen(ptr) + 1;
+         len -= n;
+         ptr += n;
+     }
+
+}
+
+static int
+handleLoginResp(isess_t *sess, pdu_t *pp)
+{
+     login_rsp_t *lp = (login_rsp_t *)pp;
+     uint      st_class, status = ntohs(lp->status);
+
+     debug_called(3);
+     debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status);
+
+     st_class  = status >> 8;
+     if(status) {
+         unsigned int st_detail = status & 0xff;
+
+         switch(st_class) {
+         case 1: // Redirect
+              switch(st_detail) {
+                   // the ITN (iSCSI target Name) requests a:
+              case 1: // temporary address change
+              case 2: // permanent address change
+                   status = 0;
+              }
+              break;
+
+         case 2: // Initiator Error
+              if(st_detail < CLASS1_ERRS)
+                   printf("0x%04x: %s\n", status, status_class1[st_detail]);
+              break;
+
+         case 3:
+              if(st_detail < CLASS3_ERRS)
+                   printf("0x%04x: %s\n", status, status_class3[st_detail]);
+              break;
+         }
+     }
+
+     if(status == 0) {
+         processParams(sess, pp);
+         setOptions(sess, 0); // XXX: just in case ...
+
+         if(lp->T) {
+              isc_opt_t        *op = sess->op;
+
+              if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL))
+                   if(handleTgtResp(sess, pp) != 0)
+                        return 1; // XXX: Authentication failure ...
+              sess->csg = lp->NSG;
+              if(sess->csg == FF_PHASE) {
+                   // XXX: will need this when implementing reconnect.
+                   sess->tsih = lp->tsih;
+                   debug(2, "TSIH=%x", sess->tsih);
+              }
+         }
+     }
+
+     return st_class;
+}
+
+static int
+handleChap(isess_t *sess, pdu_t *pp)
+{
+     pdu_t             spp;
+     login_req_t       *lp;
+     isc_opt_t         *op = sess->op;
+     char              *ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits
+
+     debug_called(3);
+
+     bzero(&spp, sizeof(pdu_t));
+     lp = (login_req_t *)&spp.ipdu.bhs;
+     lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
+     memcpy(lp->isid, sess->isid, 6);
+     lp->tsih = sess->tsih;    // MUST be zero the first time!
+     lp->CID = htons(1);
+     lp->CSG = SN_PHASE;       // Security Negotiation
+     lp->NSG = LON_PHASE;
+     lp->T = 1;
+
+     if(((ap = getkeyval("CHAP_A=", pp)) == NULL) ||
+       ((ip = getkeyval("CHAP_I=", pp)) == NULL) ||
+       ((cp = getkeyval("CHAP_C=", pp)) == NULL))
+         return -1;
+
+     if((digest = chapDigest(ap, (char)strtol(ip, (char **)NULL, 0), cp, op->chapSecret)) == NULL)
+         return -1;
+
+     addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName);
+     addText(&spp, "CHAP_R=%s", digest);
+     free(digest);
+
+     if(op->tgtChapSecret != NULL) {
+         op->tgtChapID = (random() >> 24) % 255; // should be random enough ...
+         addText(&spp, "CHAP_I=%d", op->tgtChapID);
+         cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8);
+         addText(&spp, "CHAP_C=%s", cp);
+         op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret);
+     }
+
+     return sendPDU(sess, &spp, handleLoginResp);
+}
+
+static int
+authenticate(isess_t *sess)
+{
+     pdu_t             spp;
+     login_req_t       *lp;
+     isc_opt_t *op = sess->op;
+
+     bzero(&spp, sizeof(pdu_t));
+     lp = (login_req_t *)&spp.ipdu.bhs;
+     lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
+     memcpy(lp->isid, sess->isid, 6);
+     lp->tsih = sess->tsih;    // MUST be zero the first time!
+     lp->CID = htons(1);
+     lp->CSG = SN_PHASE;       // Security Negotiation
+     lp->NSG = SN_PHASE;
+     lp->T = 0;
+
+     switch((authm_t)lookup(AuthMethods, op->authMethod)) {
+     case NONE:
+         return 0;
+
+     case KRB5:
+     case SPKM1:
+     case SPKM2:
+     case SRP:
+         return 2;
+
+     case CHAP:
+         if(op->chapDigest == 0)
+              addText(&spp, "CHAP_A=5");
+         else
+         if(strcmp(op->chapDigest, "MD5") == 0)
+              addText(&spp, "CHAP_A=5");
+         else
+         if(strcmp(op->chapDigest, "SHA1") == 0)
+              addText(&spp, "CHAP_A=7");
+         else
+              addText(&spp, "CHAP_A=5,7");
+         return sendPDU(sess, &spp, handleChap);
+     }
+     return 1;
+}
+
+int
+loginPhase(isess_t *sess)
+{
+     pdu_t             spp, *sp = &spp;
+     isc_opt_t         *op = sess->op;
+     login_req_t       *lp;
+     int               status = 1;
+
+     debug_called(3);
+
+     bzero(sp, sizeof(pdu_t));
+     lp = (login_req_t *)&spp.ipdu.bhs;
+     lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
+     memcpy(lp->isid, sess->isid, 6);
+     lp->tsih = sess->tsih;    // MUST be zero the first time!
+     lp->CID = htons(1);       // sess->cid?
+
+     if((lp->CSG = sess->csg) == LON_PHASE)
+         lp->NSG = FF_PHASE;   // lets try and go full feature ...
+     else
+         lp->NSG = LON_PHASE;
+     lp->T = 1;                        // transit to next login stage
+
+     if(sess->flags & SESS_INITIALLOGIN1) {
+         sess->flags &= ~SESS_INITIALLOGIN1;
+
+         addText(sp, "SessionType=%s", op->sessionType);
+         addText(sp, "InitiatorName=%s", op->initiatorName);
+         if(strcmp(op->sessionType, "Discovery") != 0) {
+              addText(sp, "TargetName=%s", op->targetName);
+         }
+     }
+     switch(sess->csg) {
+     case SN_PHASE:    // Security Negotiation
+         addText(sp, "AuthMethod=%s", op->authMethod);
+         break;
+
+     case LON_PHASE:   // Login Operational Negotiation
+         if((sess->flags & SESS_NEGODONE) == 0) {
+              sess->flags |= SESS_NEGODONE;
+              addText(sp, "MaxBurstLength=%d", op->maxBurstLength);
+              addText(sp, "HeaderDigest=%s", op->headerDigest);
+              addText(sp, "DataDigest=%s", op->dataDigest);
+              addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength);
+              addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel);
+              addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait);
+              addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain);
+              addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No");
+              addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No");
+              addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T);
+
+              if(strcmp(op->sessionType, "Discovery") != 0) {
+                   addText(sp, "MaxConnections=%d", op->maxConnections);
+                   addText(sp, "FirstBurstLength=%d", op->firstBurstLength);
+                   addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No");
+                   addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No");
+              }
+         }
+
+         break;
+     }
+
+     status = sendPDU(sess, &spp, handleLoginResp);
+
+     switch(status) {
+     case 0: // all is ok ...
+         if(sess->csg == SN_PHASE)
+              /*
+               | if we are still here, then we need
+               | to exchange some secrets ...
+               */
+              status = authenticate(sess);
+     }
+
+     return status;
+}
diff --git a/sbin/iscontrol/misc.c b/sbin/iscontrol/misc.c
new file mode 100644 (file)
index 0000000..60f26a3
--- /dev/null
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ | $Id: misc.c,v 2.1 2006/11/12 08:06:51 danny Exp $
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#if __FreeBSD_version < 500000
+#include <sys/time.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "misc.h"
+
+static inline char
+c2b(unsigned char c)
+{
+     switch(c) {
+     case '0' ... '9':
+         return c - '0';
+     case 'a' ... 'f':
+         return c - 'a' + 10;
+     case 'A' ... 'F':
+         return c - 'A' + 10;
+     }
+     return 0;
+}
+
+static char    base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                          "abcdefghijklmnopqrstuvwxyz"
+                          "0123456789+/";
+
+static __inline unsigned char
+c64tobin(unsigned char c64)
+{
+     int       i;
+     for(i = 0; i < 64; i++)
+         if(base64[i] == c64)
+              break;
+     return i;
+}
+/*
+ | according to rfc3720, the binary string
+ | cannot be larger than 1024 - but i can't find it :-) XXX
+ | not enforced yet.
+ */
+int
+str2bin(char *str, char **rsp)
+{
+     char      *src, *dst, *tmp;
+     int       i, len = 0;
+
+     src = str;
+     tmp = NULL;
+     if(strncasecmp("0x", src, 2) == 0) {
+         src += 2;
+         len = strlen(src);
+
+         if((tmp = malloc((len+1)/2)) == NULL) {
+              // XXX: print some error?
+              return 0;
+         }
+         dst = tmp;
+         if(len & 1)
+              *dst++ = c2b(*src++);
+         while(*src) {
+              *dst = c2b(*src++) << 4;
+              *dst++ |= c2b(*src++);
+         }
+         len = dst - tmp;
+     } else
+     if(strncasecmp("0b", src , 2) == 0) {
+         // base64
+         unsigned char b6;
+
+         src += 2;
+         len = strlen(src) / 4 * 3;
+         if((tmp = malloc(len)) == NULL) {
+              // XXX: print some error?
+              return 0;
+         }
+         dst = tmp;
+         i = 0;
+         while(*src && ((b6 = c64tobin(*src++)) != 64)) {
+              switch(i % 4) {
+              case 0:
+                   *dst = b6 << 2;
+                   break;
+              case 1:
+                   *dst++ |= b6 >> 4;
+                   *dst = b6 << 4;
+                   break;
+              case 2:
+                   *dst++ |= b6 >> 2;
+                   *dst = b6 << 6;
+                   break;
+              case 3:
+                   *dst++ |= b6;
+                   break;
+              }
+              i++;
+         }
+         len = dst - tmp;
+     }
+     else {
+         /*
+          | assume it to be an ascii string, so just copy it
+          */
+         len = strlen(str);
+         if((tmp = malloc(len)) == NULL)
+              return 0;
+         dst = tmp;
+         src = str;
+         while(*src)
+              *dst++ = *src++;
+     }
+
+     *rsp = tmp;
+     return len;
+}
+
+char *
+bin2str(char *encoding, unsigned char *md, int blen)
+{
+     int       len;
+     char      *dst, *ds;
+     unsigned char *cp;
+
+     if(strncasecmp(encoding, "0x", 2) == 0) {
+         char  ofmt[5];
+
+         len = blen * 2;
+         dst = malloc(len + 3);
+         strcpy(dst, encoding);
+         ds = dst + 2;
+         cp = md;
+         sprintf(ofmt, "%%02%c", encoding[1]);
+         while(blen-- > 0) {
+              sprintf(ds, ofmt, *cp++);
+              ds += 2;
+         }
+         *ds = 0;
+         return dst;
+     }
+     if(strncasecmp(encoding, "0b", 2) == 0) {
+         int i, b6;
+
+         len = (blen + 2) * 4 / 3;
+         dst = malloc(len + 3);
+         strcpy(dst, encoding);
+         ds = dst + 2;
+         cp = md;
+         b6 = 0; // to keep compiler happy.
+         for(i = 0; i < blen; i++) {
+              switch(i % 3) {
+              case 0:
+                   *ds++ = base64[*cp >> 2];
+                   b6 = (*cp & 0x3) << 4;
+                   break;
+              case 1:
+                   b6 += (*cp >> 4);
+                   *ds++ = base64[b6];
+                   b6 = (*cp & 0xf) << 2;
+                   break;
+              case 2:
+                   b6 += (*cp >> 6);
+                   *ds++ = base64[b6];
+                   *ds++ = base64[*cp & 0x3f];
+              }
+              cp++;
+         }
+         switch(blen % 3) {
+         case 0:
+              break;
+         case 1:
+              *ds++ = base64[b6];
+              *ds++ = '=';
+              *ds++ = '=';
+              break;
+         case 2:
+              *ds++ = base64[b6];
+              *ds++ = '=';
+              break;
+         }
+
+         *ds = 0;
+         return dst;
+     }
+
+     return NULL;
+}
diff --git a/sbin/iscontrol/misc.h b/sbin/iscontrol/misc.h
new file mode 100644 (file)
index 0000000..c33a151
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Antonio Huete Jimenez <tuxillo@quantumachine.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+*/
+
+/* Miscellaneous functions */
+int str2bin(char *str, char **rsp);
+char *bin2str(char *encoding, unsigned char *md, int blen);
diff --git a/sbin/iscontrol/pdu.c b/sbin/iscontrol/pdu.c
new file mode 100644 (file)
index 0000000..9dc08f1
--- /dev/null
@@ -0,0 +1,173 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ | $Id: pdu.c,v 2.2 2006/12/01 09:11:56 danny Exp danny $
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <camlib.h>
+
+#include "iscsi.h"
+#include "iscontrol.h"
+
+int
+xmitpdu(isess_t *sess, pdu_t *pp)
+{
+     if(ioctl(sess->fd, ISCSISEND, pp)) {
+         perror("xmitpdu");
+         return -1;
+     }
+     if(vflag)
+         pukeText("I-", pp);
+
+     return 0;
+}
+
+int
+recvpdu(isess_t *sess, pdu_t *pp)
+{
+     if(ioctl(sess->fd, ISCSIRECV, pp)) {
+         perror("recvpdu");
+         return -1;
+     }
+     // XXX: return error if truncated via
+     // the FUDGE factor.
+     if(vflag)
+         pukeText("T-", pp);
+
+     return 0;
+}
+
+int
+sendPDU(isess_t *sess, pdu_t *pp, handler_t *hdlr)
+{
+     if(xmitpdu(sess, pp))
+         return 0;
+     if(hdlr) {
+         int res;
+
+         pp->ahs_size = 8 * 1024;
+         if((pp->ahs = malloc(pp->ahs_size)) == NULL) {
+              fprintf(stderr, "out of mem!");
+              return -1;
+         }
+         pp->ds_size = 0;
+         if((res = recvpdu(sess, pp)) != 0) {
+              fprintf(stderr, "recvpdu failed\n");
+              return res;
+         }
+         res = hdlr(sess, pp);
+         freePDU(pp);
+         return res;
+     }
+     return 1;
+}
+
+
+#define FUDGE (512 * 8)
+/*
+ | We use the same memory for the response
+ | so make enough room ...
+ | XXX: must find a better way.
+ */
+int
+addText(pdu_t *pp, char *fmt, ...)
+{
+     u_int     len;
+     char      *str;
+     va_list   ap;
+
+     va_start(ap, fmt);
+     len = vasprintf(&str, fmt, ap) + 1;
+     if((pp->ds_len + len) > 0xffffff) {
+         printf("ds overflow\n");
+         free(str);
+         return 0;
+     }
+
+     if((pp->ds_len + len) > pp->ds_size) {
+         u_char        *np;
+
+         np = realloc(pp->ds, pp->ds_size + len + FUDGE);
+         if(np == NULL) {
+              free(str);
+              //XXX: out of memory!
+              return -1;
+         }
+         pp->ds = np;
+         pp->ds_size += len + FUDGE;
+     }
+     memcpy(pp->ds + pp->ds_len, str, len);
+     pp->ds_len += len;
+     free(str);
+     return len;
+}
+
+void
+freePDU(pdu_t *pp)
+{
+     if(pp->ahs_size)
+         free(pp->ahs);
+     if(pp->ds_size)
+         free(pp->ds);
+     bzero(&pp->ipdu, sizeof(union ipdu_u));
+     pp->ahs = NULL;
+     pp->ds = NULL;
+     pp->ahs_size = 0;
+     pp->ds_size = pp->ds_len = 0;
+}
+
+void
+pukeText(char *it, pdu_t *pp)
+{
+     char      *ptr;
+     int       cmd;
+     size_t    len, n;
+
+     len = pp->ds_len;
+     ptr = (char *)pp->ds;
+     cmd = pp->ipdu.bhs.opcode;
+
+     printf("%s: cmd=0x%x len=%d\n", it, cmd, (int)len);
+     while(len > 0) {
+         printf("\t%s\n", ptr);
+         n = strlen(ptr) + 1;
+         len -= n;
+         ptr += n;
+     }
+}
diff --git a/sbin/iscontrol/pdu.h b/sbin/iscontrol/pdu.h
new file mode 100644 (file)
index 0000000..d300667
--- /dev/null
@@ -0,0 +1,134 @@
+/*-
+ * Copyright (c) 2005 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ | $Id: pdu.h,v 2.1 2006/11/12 08:06:51 danny Exp $
+ */
+
+/*
+ | keep in BIG endian order (network byte order).
+ */
+
+typedef struct login_req {
+     char      cmd;    // 0x03
+
+     u_char    NSG:2;
+     u_char    CSG:2;
+     u_char    _:2;
+     u_char    C:1;
+     u_char    T:1;
+
+     char      v_max;
+     char      v_min;
+
+     int       len;    // remapped via standard bhs
+     char      isid[6];
+     short     tsih;
+     int       itt;    // Initiator Task Tag;
+
+     int       CID:16;
+     int       rsv:16;
+
+     int       cmdSN;
+     int       expStatSN;
+     int       unused[4];
+} login_req_t;
+
+typedef struct login_rsp {
+     char      cmd;    // 0x23
+     u_char    NSG:2;
+     u_char    CSG:2;
+     u_char    _1:2;
+     u_char    C:1;
+     u_char    T:1;
+
+     char      v_max;
+     char      v_act;
+
+     int       len;    // remapped via standard bhs
+     char      isid[6];
+     short     tsih;
+     int       itt;    // Initiator Task Tag;
+     int       _2;
+     rsp_sn_t  sn;
+     int       status:16;
+     int       _3:16;
+     int       _4[2];
+} login_rsp_t;
+
+typedef struct text_req {
+     char      cmd;    // 0x04
+
+     u_char    _1:6;
+     u_char    C:1;    // Continuation
+     u_char    F:1;    // Final
+     char      _2[2];
+
+     int       len;
+     int       itt;            // Initiator Task Tag
+     int       LUN[2];
+     int       ttt;            // Target Transfer Tag
+     int       cmdSN;
+     int       expStatSN;
+     int       unused[4];
+} text_req_t;
+
+/*
+ | Responses
+ */
+typedef struct logout_req {
+     char      cmd;    // 0x06
+     char      reason; // 0 - close session
+                       // 1 - close connection
+                       // 2 - remove the connection for recovery
+     char      _2[2];
+
+     int       len;
+     int       _r[2];
+     int       itt;    // Initiator Task Tag;
+
+     u_int     CID:16;
+     u_int     rsv:16;
+
+     int       cmdSN;
+     int       expStatSN;
+     int       unused[4];
+} logout_req_t;
+
+typedef struct logout_rsp {
+     char      cmd;    // 0x26
+     char      cbits;
+     char      _1[2];
+     int       len;
+     int       _2[2];
+     int       itt;
+     int       _3;
+     rsp_sn_t  sn;
+     short     time2wait;
+     short     time2retain;
+     int       _4;
+} logout_rsp_t;
diff --git a/share/man/man4/iscsi_initiator.4 b/share/man/man4/iscsi_initiator.4
new file mode 100644 (file)
index 0000000..7831cf7
--- /dev/null
@@ -0,0 +1,105 @@
+.\" Copyright (c) 2007 Daniel Braniss <danny@cs.huji.ac.il>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 23, 2007
+.Os
+.Dt ISCSI_INITIATOR 4
+.Sh NAME
+.Nm iscsi_initiator
+.Nd kernel driver for the iSCSI protocol
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device iscsi_initiator"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+iscsi_initiator_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+implements the kernel side of the Internet SCSI (iSCSI) network
+protocol standard, the user land companion is
+.Xr iscontrol 8 ,
+and permits access to remote
+.Em virtual
+SCSI devices via
+.Xr cam 4 .
+.Sh SYSCTL VARIABLES
+.Bl -tag -width ".Va net.iscsi.n.targeaddress"
+.It Va debug.iscsi_initiator
+set the debug-level, 0 means no debugging, 9 for maximum.
+.It Va net.iscsi.isid
+the initiator part of the Session Identifier.
+.It "the following are informative only:"
+.It Va net.iscsi.driver_version
+the current version of the driver.
+.It Va net.iscsi.sessions
+the number of current active sessions.
+.It Va net.iscsi.n.targetname
+is the targe name of session
+.Em n .
+.It Va net.iscsi.n.targeaddress
+is the IP address of the target of session
+.Em n .
+.It Va net.iscsi.n.stats
+are some statistics for session
+.EM n
+.It Va net.iscsi.n.pid
+is the
+.Em "process id"
+of the userland side of session
+.Em n ,
+see
+.Xr iscontrol 8 .
+.El
+.Sh FILES
+The
+.Nm
+driver creates the following:
+.Bl -tag -width ".Pa /dev/iscsi%dxx" -compact
+.Pp
+.It Pa /dev/iscsi
+used to create new sessions.
+.It Pa /dev/iscsi%d
+for each new session.
+.El
+.Sh SEE ALSO
+.Xr cam 4 ,
+.Xr camcontrol 8 ,
+.Xr iscontrol 8
+.Sh STANDARDS
+iSCSI RFC 3720
+.\" .Sh HISTORY
+.\" .Sh AUTHORS
+.Sh BUGS
+The lun discovery method is old-fashioned.
index cf0cf40..386fe45 100644 (file)
@@ -61,6 +61,13 @@ bus/cam/scsi/scsi_ses.c                      optional ses
 bus/cam/scsi/scsi_target.c             optional targ
 bus/cam/scsi/scsi_targ_bh.c            optional targbh
 
+dev/disk/iscsi/initiator/isc_cam.c     optional iscsi_initiator scbus
+dev/disk/iscsi/initiator/isc_sm.c      optional iscsi_initiator scbus
+dev/disk/iscsi/initiator/isc_soc.c     optional iscsi_initiator scbus
+dev/disk/iscsi/initiator/isc_subr.c    optional iscsi_initiator scbus
+dev/disk/iscsi/initiator/iscsi.c       optional iscsi_initiator scbus
+dev/disk/iscsi/initiator/iscsi_subr.c  optional iscsi_initiator scbus
+
 contrib/ipfilter/netinet/fil.c         optional ipfilter inet
 contrib/ipfilter/netinet/ip_auth.c     optional ipfilter inet
 contrib/ipfilter/netinet/ip_fil.c      optional ipfilter inet
index d29e9ac..2197acd 100644 (file)
@@ -209,6 +209,9 @@ SCSI_DELAY          opt_scsi.h
 SCSI_NO_SENSE_STRINGS  opt_scsi.h
 SCSI_NO_OP_STRINGS     opt_scsi.h
 
+# iSCSI options
+ISCSI_INITIATOR                opt_iscsi_initiator.h
+
 # Options used only in cam/scsi/scsi_cd.c
 CHANGER_MIN_BUSY_SECONDS       opt_cd.h
 CHANGER_MAX_BUSY_SECONDS       opt_cd.h
index 087d9d5..ef75663 100644 (file)
@@ -920,6 +920,10 @@ device             pass                    #CAM passthrough driver
 device         pt                      #SCSI processor type
 device         ses                     #SCSI SES/SAF-TE driver
 
+# Options for iSCSI
+device         iscsi_initiator
+
+
 # CAM OPTIONS:
 # debugging options:
 # -- NOTE --  If you specify one of the bus/target/lun options, you must
index d808b0d..b9976e9 100644 (file)
@@ -94,6 +94,13 @@ pseudo-device        faith   1       # IPv6-to-IPv4 relaying (translation)
 
 #pseudo-device carp
 
+# SCSI peripherals
+device          scbus           # SCSI bus (required)
+device          da              # Direct Access (disks)
+device          sa              # Sequential Access (tape etc)
+device          cd              # CD
+device          pass            # Passthrough device (direct SCSI access)
+
 # The `bpf' pseudo-device enables the Berkeley Packet Filter.
 # Be aware of the administrative consequences of enabling this!
 pseudo-device  bpf             #Berkeley packet filter
index 166c3ca..92d7fdc 100644 (file)
@@ -3,7 +3,8 @@
 
 .include "${.CURDIR}/../../platform/${MACHINE_PLATFORM}/Makefile.inc"
 
-SUBDIR= aha ahci aic aic7xxx ccd ispfw md mpt ncv nsp sbp sili stg trm vn vpo
+SUBDIR= aha ahci aic aic7xxx ccd iscsi ispfw md mpt ncv nsp sbp
+SUBDIR+= sili stg trm vn vpo
 
 .for dir in ${SUBDIR}
 .if empty(DEV_SUPPORT:Mdisk) && \
diff --git a/sys/dev/disk/iscsi/Makefile b/sys/dev/disk/iscsi/Makefile
new file mode 100644 (file)
index 0000000..625e3be
--- /dev/null
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= initiator
+
+.include <bsd.subdir.mk>
diff --git a/sys/dev/disk/iscsi/initiator/Makefile b/sys/dev/disk/iscsi/initiator/Makefile
new file mode 100644 (file)
index 0000000..043f96e
--- /dev/null
@@ -0,0 +1,13 @@
+# $FreeBSD: src/sys/modules/iscsi/initiator/Makefile,v 1.1.6.1 2008/11/25 02:59:29 kensmith Exp $
+
+KMOD=iscsi_initiator
+
+SRCS=  iscsi.h iscsivar.h
+SRCS+= iscsi.c isc_cam.c isc_soc.c isc_sm.c isc_subr.c iscsi_subr.c
+SRCS+= opt_cam.h opt_iscsi_initiator.h
+SRCS+= bus_if.h device_if.h
+
+# Debugging
+#CFLAGS+= -g -DISCSI_INITIATOR_DEBUG=9
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/disk/iscsi/initiator/isc_cam.c b/sys/dev/disk/iscsi/initiator/isc_cam.c
new file mode 100644 (file)
index 0000000..6cd3c53
--- /dev/null
@@ -0,0 +1,399 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/iscsi/initiator/isc_cam.c,v 1.3 2008/11/25 07:17:11 scottl Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/callout.h>
+#include <sys/lock.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/uio.h>
+#include <sys/sysctl.h>
+#include <sys/eventhandler.h>
+#include <sys/mutex.h>
+#include <sys/mutex2.h>
+
+#include <bus/cam/cam.h>
+#include <bus/cam/cam_ccb.h>
+#include <bus/cam/cam_sim.h>
+#include <bus/cam/cam_xpt_sim.h>
+#include <bus/cam/cam_periph.h>
+
+#include <dev/disk/iscsi/initiator/iscsi.h>
+#include <dev/disk/iscsi/initiator/iscsivar.h>
+
+// XXX: untested/incomplete
+void
+ic_freeze(isc_session_t *sp)
+{
+     debug_called(8);
+#if 0
+     sdebug(2, "freezing path=%p", sp->cam_path == NULL? 0: sp->cam_path);
+     if((sp->cam_path != NULL) && !(sp->flags & ISC_FROZEN)) {
+         xpt_freeze_devq(sp->cam_path, 1);
+     }
+#endif
+     sp->flags |= ISC_FROZEN;
+}
+
+// XXX: untested/incomplete
+void
+ic_release(isc_session_t *sp)
+{
+     debug_called(8);
+#if 0
+     sdebug(2, "release path=%p", sp->cam_path == NULL? 0: sp->cam_path);
+     if((sp->cam_path != NULL) && (sp->flags & ISC_FROZEN)) {
+         xpt_release_devq(sp->cam_path, 1, TRUE);
+     }
+#endif
+     sp->flags &= ~ISC_FROZEN;
+}
+
+void
+ic_lost_target(isc_session_t *sp, int target)
+{
+     struct isc_softc   *isp = sp->isc;
+
+     debug_called(8);
+     sdebug(2, "target=%d", target);
+     if(sp->cam_path != NULL) {
+         lockmgr(&isp->cam_lock, LK_EXCLUSIVE);
+         xpt_async(AC_LOST_DEVICE, sp->cam_path, NULL);
+         xpt_free_path(sp->cam_path);
+         lockmgr(&isp->cam_lock, LK_RELEASE);
+         sp->cam_path = 0; // XXX
+     }
+}
+
+static void
+_scan_callback(struct cam_periph *periph, union ccb *ccb)
+{
+     isc_session_t *sp = (isc_session_t *)ccb->ccb_h.spriv_ptr0;
+
+     debug_called(8);
+
+     kfree(ccb, M_TEMP);
+
+     if(sp->flags & ISC_FFPWAIT) {
+         sp->flags &= ~ISC_FFPWAIT;
+         wakeup(sp);
+     }
+}
+
+static void
+_scan_target(isc_session_t *sp, int target)
+{
+     union ccb         *ccb;
+
+     debug_called(8);
+     sdebug(2, "target=%d", target);
+
+     if((ccb = kmalloc(sizeof(union ccb), M_TEMP, M_WAITOK | M_ZERO)) == NULL) {
+         xdebug("scan failed (can't allocate CCB)");
+         return;
+     }
+     CAM_LOCK(sp->isc);
+     xpt_setup_ccb(&ccb->ccb_h, sp->cam_path, 5/*priority (low)*/);
+     ccb->ccb_h.func_code      = XPT_SCAN_BUS;
+     ccb->ccb_h.cbfcnp         = _scan_callback;
+     ccb->crcn.flags           = CAM_FLAG_NONE;
+     ccb->ccb_h.spriv_ptr0     = sp;
+
+     xpt_action(ccb);
+     CAM_UNLOCK(sp->isc);
+}
+
+int
+ic_fullfeature(struct cdev *dev)
+{
+     struct isc_softc  *isp = dev->si_drv1;
+     isc_session_t     *sp = (isc_session_t *)dev->si_drv2;
+
+     debug_called(8);
+     sdebug(3, "dev=%d sc=%p", minor(dev), isp);
+
+     sp->flags &= ~ISC_FFPHASE;
+     sp->flags |= ISC_FFPWAIT;
+
+     CAM_LOCK(isp);
+     if(xpt_create_path(&sp->cam_path, xpt_periph, cam_sim_path(sp->isc->cam_sim),
+                       sp->sid, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+         xdebug("can't create cam path");
+         CAM_UNLOCK(isp);
+         return ENODEV; // XXX
+     }
+     CAM_UNLOCK(isp);
+
+     _scan_target(sp, sp->sid);
+
+     while(sp->flags & ISC_FFPWAIT)
+         tsleep(sp, 0, "ffp", 5*hz); // timeout should be configurable
+     if(sp->target_nluns > 0) {
+         sp->flags |= ISC_FFPHASE;
+         return 0;
+     }
+
+     return ENODEV;
+}
+
+static void
+_inq(struct cam_sim *sim, union ccb *ccb, int maxluns)
+{
+     struct ccb_pathinq *cpi = &ccb->cpi;
+
+     debug_called(4);
+
+     cpi->version_num = 1; /* XXX??? */
+     cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_32;
+     cpi->target_sprt = 0;
+     cpi->hba_misc = 0;
+     cpi->hba_eng_cnt = 0;
+     cpi->max_target = ISCSI_MAX_TARGETS - 1;
+     cpi->initiator_id = ISCSI_MAX_TARGETS;
+     cpi->max_lun = maxluns;
+     cpi->bus_id = cam_sim_bus(sim);
+     cpi->base_transfer_speed = 3300;
+     strncpy(cpi->sim_vid, "DragonFly", SIM_IDLEN);
+     strncpy(cpi->hba_vid, "iSCSI", HBA_IDLEN);
+     strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+     cpi->unit_number = cam_sim_unit(sim);
+     cpi->ccb_h.status = CAM_REQ_CMP;
+}
+
+static __inline int
+_scsi_encap(struct cam_sim *sim, union ccb *ccb)
+{
+     int               ret;
+     struct isc_softc  *isp = (struct isc_softc *)cam_sim_softc(sim);
+
+     lockmgr(&isp->cam_lock, LK_EXCLUSIVE);
+     ret = scsi_encap(sim, ccb);
+     lockmgr(&isp->cam_lock, LK_RELEASE);
+     return ret;
+}
+
+static void
+ic_action(struct cam_sim *sim, union ccb *ccb)
+{
+     struct ccb_hdr    *ccb_h = &ccb->ccb_h;
+     struct isc_softc  *isp = (struct isc_softc *)cam_sim_softc(sim);
+     isc_session_t     *sp;
+
+     debug_called(8);
+
+     if((ccb_h->target_id != CAM_TARGET_WILDCARD) && (ccb_h->target_id < MAX_SESSIONS))
+         sp = isp->sessions[ccb_h->target_id];
+     else
+         sp = NULL;
+
+     ccb_h->spriv_ptr0 = sp;
+
+     debug(4, "func_code=0x%x flags=0x%x status=0x%x target=%d lun=%d retry_count=%d timeout=%d",
+          ccb_h->func_code, ccb->ccb_h.flags, ccb->ccb_h.status,
+          ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+          ccb->ccb_h.retry_count, ccb_h->timeout);
+     /*
+      | first quick check
+      */
+     switch(ccb_h->func_code) {
+     default:
+         // XXX: maybe check something else?
+         break;
+
+     case XPT_SCSI_IO:
+     case XPT_RESET_DEV:
+     case XPT_GET_TRAN_SETTINGS:
+     case XPT_SET_TRAN_SETTINGS:
+     case XPT_CALC_GEOMETRY:
+         if(sp == NULL) {
+              ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+              xpt_done(ccb);
+              return;
+         }
+         break;
+
+     case XPT_PATH_INQ:
+     case XPT_NOOP:
+         if(sp == NULL && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+              ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+              xpt_done(ccb);
+              debug(4, "status = CAM_DEV_NOT_THERE");
+              return;
+         }
+     }
+
+     switch(ccb_h->func_code) {
+
+     case XPT_PATH_INQ:
+         _inq(sim, ccb, (sp? sp->opt.maxluns: ISCSI_MAX_LUNS) - 1);
+         break;
+
+     case XPT_RESET_BUS: // (can just be a stub that does nothing and completes)
+     {
+         struct ccb_pathinq *cpi = &ccb->cpi;
+
+         debug(3, "XPT_RESET_BUS");
+         cpi->ccb_h.status = CAM_REQ_CMP;
+         break;
+     }
+
+     case XPT_SCSI_IO:
+     {
+         struct ccb_scsiio* csio = &ccb->csio;
+
+         debug(4, "XPT_SCSI_IO cmd=0x%x", csio->cdb_io.cdb_bytes[0]);
+         if(sp == NULL) {
+              ccb_h->status = CAM_REQ_INVALID; //CAM_NO_NEXUS;
+              debug(4, "xpt_done.status=%d", ccb_h->status);
+              break;
+         }
+         if(ccb_h->target_lun == CAM_LUN_WILDCARD) {
+              debug(3, "target=%d: bad lun (-1)", ccb_h->target_id);
+              ccb_h->status = CAM_LUN_INVALID;
+              break;
+         }
+         if(_scsi_encap(sim, ccb) != 0)
+              return;
+         break;
+     }
+
+     case XPT_CALC_GEOMETRY:
+     {
+         struct        ccb_calc_geometry *ccg;
+
+         ccg = &ccb->ccg;
+         debug(6, "XPT_CALC_GEOMETRY vsize=%jd bsize=%d", ccg->volume_size, ccg->block_size);
+         if(ccg->block_size == 0 ||
+            (ccg->volume_size < ccg->block_size)) {
+              // print error message  ...
+              /* XXX: what error is appropiate? */
+              break;
+         } else
+              cam_calc_geometry(ccg, /*extended*/1);
+         break;
+     }
+
+     case XPT_GET_TRAN_SETTINGS:
+     default:
+         ccb_h->status = CAM_REQ_INVALID;
+         break;
+     }
+     xpt_done(ccb);
+     return;
+}
+
+static void
+ic_poll(struct cam_sim *sim)
+{
+     debug_called(8);
+
+}
+
+int
+ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp)
+{
+     int       i;
+
+     debug_called(8);
+
+     if(sp && sp->isc->cam_sim) {
+         cp->path_id = cam_sim_path(sp->isc->cam_sim);
+         cp->target_id = sp->sid;
+         cp->target_nluns = sp->target_nluns; // XXX: -1?
+         for(i = 0; i < cp->target_nluns; i++)
+              cp->target_lun[i] = sp->target_lun[i];
+         return 0;
+     }
+     return ENXIO;
+}
+
+void
+ic_destroy(struct isc_softc *isp)
+{
+     debug_called(8);
+
+     CAM_LOCK(isp); // can't harm :-)
+
+     xpt_async(AC_LOST_DEVICE, isp->cam_path, NULL);
+     xpt_free_path(isp->cam_path);
+     xpt_bus_deregister(cam_sim_path(isp->cam_sim));
+     cam_sim_free(isp->cam_sim);
+
+     CAM_UNLOCK(isp);
+}
+
+int
+ic_init(struct isc_softc *isp)
+{
+     struct cam_sim    *sim;
+     struct cam_devq   *devq;
+     struct cam_path   *path;
+
+     if((devq = cam_simq_alloc(256)) == NULL)
+         return ENOMEM;
+
+     lockinit(&isp->cam_lock, "isc-cam", 0, LK_CANRECURSE);
+     sim = cam_sim_alloc(ic_action, ic_poll,
+                        "iscsi", isp, 0, /* unit */
+                        &sim_mplock,
+                        1,             /* max_dev_transactions */
+                        100,           /* max_tagged_dev_transactions */
+                        devq);
+     if(sim == NULL) {
+         cam_simq_release(devq);
+         lockuninit(&isp->cam_lock);
+         return ENXIO;
+     }
+     CAM_LOCK(isp);
+     if(xpt_bus_register(sim,
+                        0/*bus_number*/) != CAM_SUCCESS)
+         goto bad;
+
+     if(xpt_create_path(&path, xpt_periph, cam_sim_path(sim),
+                       CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+         xpt_bus_deregister(cam_sim_path(sim));
+         goto bad;
+     }
+
+     CAM_UNLOCK(isp);
+
+     isp->cam_sim = sim;
+     isp->cam_path = path;
+
+     debug(2, "cam subsystem initialized"); // XXX: add dev ...
+     debug(4, "sim=%p path=%p", sim, path);
+     return 0;
+
+ bad:
+     cam_sim_free(sim);
+     CAM_UNLOCK(isp);
+     return ENXIO;
+}
diff --git a/sys/dev/disk/iscsi/initiator/isc_sm.c b/sys/dev/disk/iscsi/initiator/isc_sm.c
new file mode 100644 (file)
index 0000000..4ab7beb
--- /dev/null
@@ -0,0 +1,807 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/iscsi/initiator/isc_sm.c,v 1.3 2008/11/25 07:17:11 scottl Exp $
+ */
+/*
+ | iSCSI - Session Manager
+ | $Id: isc_sm.c,v 1.30 2007/04/22 09:53:09 danny Exp danny $
+ */
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/socketvar.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/syslog.h>
+#include <sys/mbuf.h>
+#include <sys/bus.h>
+#include <sys/eventhandler.h>
+#include <sys/mutex.h>
+#include <sys/mutex2.h>
+
+#include <bus/cam/cam.h>
+#include <bus/cam/cam_ccb.h>
+#include <bus/cam/cam_sim.h>
+#include <bus/cam/cam_xpt_sim.h>
+#include <bus/cam/cam_periph.h>
+
+#include <dev/disk/iscsi/initiator/iscsi.h>
+#include <dev/disk/iscsi/initiator/iscsivar.h>
+
+static void
+_async(isc_session_t *sp, pduq_t *pq)
+{
+     debug_called(8);
+
+     iscsi_async(sp, pq);
+
+     pdu_free(sp->isc, pq);
+}
+
+static void
+_reject(isc_session_t *sp, pduq_t *pq)
+{
+     pduq_t    *opq;
+     pdu_t     *pdu;
+     reject_t  *reject;
+     int       itt;
+
+     debug_called(8);
+     pdu = mtod(pq->mp, pdu_t *);
+     itt = pdu->ipdu.bhs.itt;
+     reject = &pq->pdu.ipdu.reject;
+     sdebug(2, "itt=%x reason=0x%x", ntohl(itt), reject->reason);
+     opq = i_search_hld(sp, itt, 0);
+     if(opq != NULL)
+         iscsi_reject(sp, opq, pq);
+     else {
+         switch(pq->pdu.ipdu.bhs.opcode) {
+         case ISCSI_LOGOUT_CMD: // XXX: wasabi does this - can't figure out why
+              sdebug(2, "ISCSI_LOGOUT_CMD ...");
+              break;
+         default:
+              xdebug("%d] we lost something itt=%x",
+                     sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
+         }
+     }
+     pdu_free(sp->isc, pq);
+}
+
+static void
+_r2t(isc_session_t *sp, pduq_t *pq)
+{
+     pduq_t    *opq;
+
+     debug_called(8);
+     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
+     if(opq != NULL) {
+         iscsi_r2t(sp, opq, pq);
+     }
+     else {
+         r2t_t         *r2t = &pq->pdu.ipdu.r2t;
+
+         xdebug("%d] we lost something itt=%x r2tSN=%d bo=%x ddtl=%x",
+                sp->sid, ntohl(pq->pdu.ipdu.bhs.itt),
+                ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl));
+     }
+     pdu_free(sp->isc, pq);
+}
+
+static void
+_scsi_rsp(isc_session_t *sp, pduq_t *pq)
+{
+     pduq_t    *opq;
+
+     debug_called(8);
+     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 0);
+     debug(5, "itt=%x pq=%p opq=%p", ntohl(pq->pdu.ipdu.bhs.itt), pq, opq);
+     if(opq != NULL)
+         iscsi_done(sp, opq, pq);
+     else
+         xdebug("%d] we lost something itt=%x",
+                sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
+     pdu_free(sp->isc, pq);
+}
+
+static void
+_read_data(isc_session_t *sp, pduq_t *pq)
+{
+     pduq_t            *opq;
+
+     debug_called(8);
+     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
+     if(opq != NULL) {
+         if(scsi_decap(sp, opq, pq) != 1) {
+              i_remove_hld(sp, opq); // done
+              pdu_free(sp->isc, opq);
+         }
+     }
+     else
+         xdebug("%d] we lost something itt=%x",
+                sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
+     pdu_free(sp->isc, pq);
+}
+/*
+ | this is a kludge,
+ | the jury is not back with a veredict, user or kernel
+ */
+static void
+_nop_out(isc_session_t *sp)
+{
+     pduq_t    *pq;
+     nop_out_t *nop_out;
+
+     debug_called(8);
+
+     sdebug(4, "cws=%d", sp->cws);
+     if(sp->cws == 0) {
+         /*
+          | only send a nop if window is closed.
+          */
+         if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL)
+              // I guess we ran out of resources
+              return;
+         nop_out = &pq->pdu.ipdu.nop_out;
+         nop_out->opcode = ISCSI_NOP_OUT;
+         nop_out->itt = htonl(sp->sn.itt);
+         nop_out->ttt = -1;
+         nop_out->I = 1;
+         nop_out->F = 1;
+         if(isc_qout(sp, pq) != 0) {
+              sdebug(1, "failed");
+              pdu_free(sp->isc, pq);
+         }
+     }
+}
+
+static void
+_nop_in(isc_session_t *sp, pduq_t *pq)
+{
+     pdu_t     *pp = &pq->pdu;
+     nop_in_t  *nop_in = &pp->ipdu.nop_in;
+     bhs_t     *bhs = &pp->ipdu.bhs;
+
+     debug_called(8);
+
+     sdebug(5, "itt=%x ttt=%x", htonl(nop_in->itt), htonl(nop_in->ttt));
+     if(nop_in->itt == -1) {
+         if(pp->ds_len != 0) {
+              /*
+               | according to RFC 3720 this should be zero
+               | what to do if not?
+               */
+              xdebug("%d] dslen not zero", sp->sid);
+         }
+         if(nop_in->ttt != -1) {
+              nop_out_t        *nop_out;
+              /*
+               | target wants a nop_out
+               */
+              bhs->opcode = ISCSI_NOP_OUT;
+              bhs->I = 1;
+              bhs->F = 1;
+              /*
+               | we are reusing the pdu, so bhs->ttt == nop_in->ttt;
+               | and need to zero out 'Reserved'
+               | small cludge here.
+               */
+              nop_out = &pp->ipdu.nop_out;
+              nop_out->sn.maxcmd = 0;
+              memset(nop_out->mbz, 0, sizeof(nop_out->mbz));
+              (void)isc_qout(sp, pq); //XXX: should check return?
+              return;
+         }
+         //else {
+              // just making noise?
+              // see 10.9.1: target does not want and answer.
+         //}
+
+     } else
+     if(nop_in->ttt == -1) {
+         /*
+          | it is an answer to a nop_in from us
+          */
+         if(nop_in->itt != -1) {
+#ifdef ISC_WAIT4PING
+              // XXX: MUTEX please
+              if(sp->flags & ISC_WAIT4PING) {
+                   i_nqueue_rsp(sp, pq);
+                   wakeup(&sp->rsp);
+                   return;
+              }
+#endif
+         }
+     }
+     /*
+      | drop it
+      */
+     pdu_free(sp->isc, pq);
+     return;
+}
+
+int
+i_prepPDU(isc_session_t *sp, pduq_t *pq)
+{
+     size_t    len, n;
+     pdu_t     *pp = &pq->pdu;
+     bhs_t     *bhp = &pp->ipdu.bhs;
+
+     len = sizeof(bhs_t);
+     if(pp->ahs_len) {
+         len += pp->ahs_len;
+         bhp->AHSLength =  pp->ahs_len / 4;
+     }
+     if(sp->hdrDigest)
+         len += 4;
+     if(pp->ds_len) {
+         n = pp->ds_len;
+         len += n;
+#if BYTE_ORDER == LITTLE_ENDIAN
+         bhp->DSLength = ((n & 0x00ff0000) >> 16)
+              | (n & 0x0000ff00)
+              | ((n & 0x000000ff) << 16);
+#else
+         bhp->DSLength = n;
+#endif
+         if(len & 03) {
+              n = 4 - (len & 03);
+              len += n;
+         }
+         if(sp->dataDigest)
+              len += 4;
+     }
+
+     pq->len = len;
+     len -= sizeof(bhs_t);
+     if(sp->opt.maxBurstLength && (len > sp->opt.maxBurstLength)) {
+         xdebug("%d] pdu len=%zd > %d",
+                sp->sid, len, sp->opt.maxBurstLength);
+         // XXX: when this happens it used to hang ...
+         return E2BIG;
+     }
+     return 0;
+}
+
+int
+isc_qout(isc_session_t *sp, pduq_t *pq)
+{
+     int error = 0;
+
+     debug_called(8);
+
+     if(pq->len == 0 && (error = i_prepPDU(sp, pq)))
+         return error;
+
+     if(pq->pdu.ipdu.bhs.I)
+         i_nqueue_isnd(sp, pq);
+     else
+     if(pq->pdu.ipdu.data_out.opcode == ISCSI_WRITE_DATA)
+         i_nqueue_wsnd(sp, pq);
+     else
+         i_nqueue_csnd(sp, pq);
+
+     sdebug(5, "enqued: pq=%p", pq);
+
+     iscsi_lock_ex(&sp->io_mtx);
+     sp->flags |= ISC_OQNOTEMPTY;
+     if(sp->flags & ISC_OWAITING)
+     wakeup(&sp->flags);
+     iscsi_unlock_ex(&sp->io_mtx);
+
+     return error;
+}
+/*
+ | called when a fullPhase is restarted
+ */
+static void
+ism_restart(isc_session_t *sp)
+{
+     int lastcmd;
+
+     sdebug(2, "restart ...");
+     lastcmd = iscsi_requeue(sp);
+#if 0
+     if(lastcmd != sp->sn.cmd) {
+         sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd, sp->sn.cmd);
+         sp->sn.cmd = lastcmd;
+     }
+#endif
+     iscsi_lock_ex(&sp->io_mtx);
+     if(sp->flags & ISC_OWAITING) {
+         wakeup(&sp->flags);
+     }
+     iscsi_unlock_ex(&sp->io_mtx);
+
+     sdebug(2, "restarted lastcmd=0x%x", lastcmd);
+}
+
+int
+ism_fullfeature(struct cdev *dev, int flag)
+{
+     isc_session_t *sp = (isc_session_t *)dev->si_drv2;
+     int       error;
+
+     sdebug(2, "flag=%d", flag);
+
+     error = 0;
+     switch(flag) {
+     case 0: // stop
+         sp->flags &= ~ISC_FFPHASE;
+         break;
+     case 1: // start
+         error = ic_fullfeature(dev);
+         break;
+     case 2: // restart
+         ism_restart(sp);
+         break;
+     }
+     return error;
+}
+
+void
+ism_recv(isc_session_t *sp, pduq_t *pq)
+{
+     bhs_t     *bhs;
+     int       statSN;
+
+     debug_called(8);
+
+     bhs = &pq->pdu.ipdu.bhs;
+     statSN = ntohl(bhs->OpcodeSpecificFields[1]);
+#if 0
+     {
+         /*
+          | this code is only for debugging.
+          */
+         sn_t  *sn = &sp->sn;
+         if(sp->cws == 0) {
+              if((sp->flags & ISC_STALLED) == 0) {
+                   sdebug(4, "window closed: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
+                          sn->maxCmd, sn->expCmd, bhs->opcode, sn->cmd, sp->cws);
+                   sp->flags |= ISC_STALLED;
+              } else
+              if(sp->flags & ISC_STALLED) {
+                   sdebug(4, "window opened: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
+                          sn->maxCmd, sn->expCmd, bhs->opcode, sn->cmd, sp->cws);
+                   sp->flags &= ~ISC_STALLED;;
+              }
+         }
+     }
+#endif
+
+#ifdef notyet
+     if(sp->sn.expCmd != sn->cmd) {
+         sdebug(1, "we lost something ... exp=0x%x cmd=0x%x",
+                sn->expCmd, sn->cmd);
+     }
+#endif
+     sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x",
+           bhs->opcode, ntohl(bhs->itt), statSN, sp->sn.maxCmd);
+
+     switch(bhs->opcode) {
+     case ISCSI_READ_DATA: {
+         data_in_t     *cmd = &pq->pdu.ipdu.data_in;
+
+         if(cmd->S == 0)
+              break;
+     }
+
+     default:
+         if(statSN > (sp->sn.stat + 1)) {
+              sdebug(1, "we lost some rec=0x%x exp=0x%x",
+                     statSN, sp->sn.stat);
+              // XXX: must do some error recovery here.
+         }
+         sp->sn.stat = statSN;
+     }
+
+     switch(bhs->opcode) {
+     case ISCSI_LOGIN_RSP:
+     case ISCSI_TEXT_RSP:
+     case ISCSI_LOGOUT_RSP:
+         i_nqueue_rsp(sp, pq);
+         wakeup(&sp->rsp);
+         sdebug(3, "wakeup rsp");
+         break;
+
+     case ISCSI_NOP_IN:                _nop_in(sp, pq);        break;
+     case ISCSI_SCSI_RSP:      _scsi_rsp(sp, pq);      break;
+     case ISCSI_READ_DATA:     _read_data(sp, pq);     break;
+     case ISCSI_R2T:           _r2t(sp, pq);           break;
+     case ISCSI_REJECT:                _reject(sp, pq);        break;
+     case ISCSI_ASYNC:         _async(sp, pq);         break;
+
+     case ISCSI_TASK_RSP:
+     default:
+         sdebug(1, "opcode=0x%x itt=0x%x not implemented yet",
+                bhs->opcode, ntohl(bhs->itt));
+         break;
+     }
+}
+\f
+/*
+ | go through the out queues looking for work
+ | if either nothing to do, or window is closed
+ | return.
+ */
+static int
+proc_out(isc_session_t *sp)
+{
+     sn_t      *sn = &sp->sn;
+     pduq_t    *pq;
+     int       error, ndone;
+     int       which;
+
+     debug_called(8);
+     error = ndone = 0;
+
+     while(sp->flags & ISC_LINK_UP) {
+         pdu_t *pp;
+         bhs_t *bhs;
+         /*
+          | check if there is outstanding work in:
+          | 1- the Immediate queue
+          | 2- the R2T queue
+          | 3- the cmd queue, only if the command window allows it.
+          */
+         which = BIT(0) | BIT(1);
+         if(SNA_GT(sn->cmd, sn->maxCmd) == 0) // if(sn->maxCmd - sn->smc + 1) > 0
+              which |= BIT(2);
+
+         sdebug(4, "which=%d sn->maxCmd=%d sn->cmd=%d", which, sn->maxCmd, sn->cmd);
+
+         if((pq = i_dqueue_snd(sp, which)) == NULL)
+              break;
+         sdebug(4, "pq=%p", pq);
+
+         pp = &pq->pdu;
+         bhs = &pp->ipdu.bhs;
+         switch(bhs->opcode) {
+         case ISCSI_SCSI_CMD:
+              sn->itt++;
+              bhs->itt = htonl(sn->itt);
+
+         case ISCSI_LOGIN_CMD:
+         case ISCSI_TEXT_CMD:
+         case ISCSI_LOGOUT_CMD:
+         case ISCSI_SNACK:
+         case ISCSI_NOP_OUT:
+         case ISCSI_TASK_CMD:
+              bhs->CmdSN = htonl(sn->cmd);
+              if(bhs->I == 0)
+                   sn->cmd++;
+
+         case ISCSI_WRITE_DATA:
+              bhs->ExpStSN = htonl(sn->stat);
+              break;
+
+         default:
+              // XXX: can this happen?
+              xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
+                     bhs->opcode,
+                     sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
+              // XXX: and now?
+         }
+
+         sdebug(4, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
+               bhs->opcode,
+               sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
+
+         if(pq->ccb)
+              i_nqueue_hld(sp, pq);
+
+         if((error = isc_sendPDU(sp, pq)) == 0) {
+              ndone++;
+              if(pq->ccb == NULL)
+                   pdu_free(sp->isc, pq);
+         }
+         else {
+              xdebug("error=%d ndone=%d opcode=0x%x ccb=%p itt=%x",
+                     error, ndone, bhs->opcode, pq->ccb, ntohl(bhs->itt));
+              if(pq->ccb)
+                   i_remove_hld(sp, pq);
+              switch(error) {
+              case EPIPE:
+                   sp->flags &= ~ISC_LINK_UP;
+
+              case EAGAIN:
+                   xdebug("requed");
+                   i_rqueue_pdu(sp, pq);
+                   break;
+
+              default:
+              if(pq->ccb) {
+                        xdebug("back to cam");
+                        pq->ccb->ccb_h.status |= CAM_REQUEUE_REQ; // some better error?
+                        XPT_DONE(sp->isc, pq->ccb);
+                        pdu_free(sp->isc, pq);
+              }
+                   else
+                        xdebug("we lost it!");
+              }
+         }
+     }
+     return error;
+}
+\f
+/*
+ | survives link breakdowns.
+ */
+static void
+ism_proc(void *vp)
+{
+     isc_session_t     *sp = (isc_session_t *)vp;
+     int               error;
+
+     debug_called(8);
+
+     sdebug(3, "started sp->flags=%x", sp->flags);
+     do {
+         if((sp->flags & ISC_HOLD) == 0) {
+              error = proc_out(sp);
+              if(error) {
+                   sdebug(3, "error=%d", error);
+              }
+         }
+         iscsi_lock_ex(&sp->io_mtx);
+         if((sp->flags & ISC_LINK_UP) == 0) {
+              wakeup(&sp->soc);
+         }
+
+         if((sp->flags & (ISC_OQNOTEMPTY | ISC_SM_RUN)) == ISC_SM_RUN) {
+              sp->flags |= ISC_OWAITING;
+              if(issleep(&sp->flags, &sp->io_mtx, 0, "iscproc", hz*30) == EWOULDBLOCK) {
+                   if(sp->flags & ISC_CON_RUNNING)
+                   _nop_out(sp);
+              }
+              sp->flags &= ~ISC_OWAITING;
+         }
+         sp->flags &= ~ISC_OQNOTEMPTY;
+         iscsi_unlock_ex(&sp->io_mtx);
+     } while(sp->flags & ISC_SM_RUN);
+
+     sp->flags &= ~ISC_SM_RUNNING;
+     sdebug(3, "dropped ISC_SM_RUNNING");
+
+     wakeup(sp);
+
+     debug(3, "terminated sp=%p sp->sid=%d", sp, sp->sid);
+
+     kthread_exit();
+}
+
+#if 0
+static int
+isc_dump_options(SYSCTL_HANDLER_ARGS)
+{
+     int error;
+     isc_session_t *sp;
+     char      buf[1024], *bp;
+
+     sp = (isc_session_t *)arg1;
+     bp = buf;
+     ksprintf(bp, "targetname='%s'", sp->opt.targetName);
+     bp += strlen(bp);
+     ksprintf(bp, " targetname='%s'", sp->opt.targetAddress);
+     error = SYSCTL_OUT(req, buf, strlen(buf));
+     return error;
+}
+#endif
+
+static int
+isc_dump_stats(SYSCTL_HANDLER_ARGS)
+{
+     isc_session_t     *sp;
+     struct isc_softc  *sc;
+     char      buf[1024], *bp;
+     int       error, n;
+
+     sp = (isc_session_t *)arg1;
+     sc = sp->isc;
+
+     bp = buf;
+     n = sizeof(buf);
+     ksnprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent);
+     bp += strlen(bp);
+     n -= strlen(bp);
+     ksnprintf(bp, n, " flags=0x%08x pdus-alloc=%d pdus-max=%d",
+                 sp->flags, sc->npdu_alloc, sc->npdu_max);
+     bp += strlen(bp);
+     n -= strlen(bp);
+     ksnprintf(bp, n, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x",
+                 sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
+     error = SYSCTL_OUT(req, buf, strlen(buf));
+     return error;
+}
+
+static int
+isc_sysctl_targetName(SYSCTL_HANDLER_ARGS)
+{
+     char      buf[128], **cp;
+     int       error;
+
+     cp = (char **)arg1;
+     ksnprintf(buf, sizeof(buf), "%s", *cp);
+     error = SYSCTL_OUT(req, buf, strlen(buf));
+     return error;
+}
+static int
+isc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS)
+{
+     char      buf[128], **cp;
+     int       error;
+
+     cp = (char **)arg1;
+     ksnprintf(buf, sizeof(buf), "%s", *cp);
+     error = SYSCTL_OUT(req, buf, strlen(buf));
+     return error;
+}
+static void
+isc_add_sysctls(isc_session_t *sp)
+{
+     debug_called(8);
+     sdebug(6, "sid=%d %s", sp->sid, sp->dev->si_name);
+
+     sysctl_ctx_init(&sp->clist);
+     sp->oid = SYSCTL_ADD_NODE(&sp->clist,
+                              SYSCTL_CHILDREN(sp->isc->oid),
+                              OID_AUTO,
+                              sp->dev->si_name+5, // iscsi0
+                              CTLFLAG_RD,
+                              0,
+                              "initiator");
+     SYSCTL_ADD_PROC(&sp->clist,
+                    SYSCTL_CHILDREN(sp->oid),
+                    OID_AUTO,
+                    "targetname",
+                    CTLFLAG_RD,
+                    (void *)&sp->opt.targetName, 0,
+                    isc_sysctl_targetName, "A", "target name");
+
+     SYSCTL_ADD_PROC(&sp->clist,
+                    SYSCTL_CHILDREN(sp->oid),
+                    OID_AUTO,
+                    "targeaddress",
+                    CTLFLAG_RD,
+                    (void *)&sp->opt.targetAddress, 0,
+                    isc_sysctl_targetAddress, "A", "target address");
+
+     SYSCTL_ADD_PROC(&sp->clist,
+                    SYSCTL_CHILDREN(sp->oid),
+                    OID_AUTO,
+                    "stats",
+                    CTLFLAG_RD,
+                    (void *)sp, 0,
+                    isc_dump_stats, "A", "statistics");
+
+     SYSCTL_ADD_INT(&sp->clist,
+                    SYSCTL_CHILDREN(sp->oid),
+                    OID_AUTO,
+                    "douio",
+                    CTLFLAG_RW,
+                    &sp->douio, 0, "enable uio on read");
+}
+
+void
+ism_stop(isc_session_t *sp)
+{
+     struct isc_softc *sc = sp->isc;
+     cdev_t    dev;
+
+     debug_called(8);
+     sdebug(2, "terminating");
+     /*
+      | first stop the receiver
+      */
+     isc_stop_receiver(sp);
+
+     /*
+      | now stop the xmitter
+      */
+     sp->flags &= ~ISC_SM_RUN;
+     while(sp->flags & ISC_SM_RUNNING) {
+         sdebug(2, "waiting for ism to stop");
+         wakeup(&sp->flags);
+         tsleep(sp, 0, "-", hz);
+     }
+     sdebug(2, "ism stopped");
+     sp->flags &= ~ISC_FFPHASE;
+
+     iscsi_cleanup(sp);
+
+     (void)i_pdu_flush(sp);
+
+     ic_lost_target(sp, sp->sid);
+
+     lockmgr(&sc->lock, LK_EXCLUSIVE);
+     TAILQ_REMOVE(&sc->isc_sess, sp, sp_link);
+     sc->nsess--;
+     lockmgr(&sc->lock, LK_RELEASE);
+
+     dev = sp->dev;
+     sp->dev = NULL;
+
+     release_dev(dev);
+     destroy_dev(dev);
+
+     mtx_uninit(&sp->rsp_mtx);
+     mtx_uninit(&sp->rsv_mtx);
+     mtx_uninit(&sp->hld_mtx);
+     mtx_uninit(&sp->snd_mtx);
+     mtx_uninit(&sp->io_mtx);
+
+     i_freeopt(&sp->opt);
+     sc->sessions[sp->sid] = NULL;
+
+     if(sysctl_ctx_free(&sp->clist))
+         xdebug("sysctl_ctx_free failed");
+
+     kfree(sp, M_ISCSI);
+}
+
+int
+ism_start(isc_session_t *sp)
+{
+     debug_called(8);
+    /*
+     | now is a good time to do some initialization
+     */
+     TAILQ_INIT(&sp->rsp);
+     TAILQ_INIT(&sp->rsv);
+     TAILQ_INIT(&sp->csnd);
+     TAILQ_INIT(&sp->isnd);
+     TAILQ_INIT(&sp->wsnd);
+     TAILQ_INIT(&sp->hld);
+
+     mtx_init(&sp->rsv_mtx);
+     mtx_init(&sp->rsp_mtx);
+     mtx_init(&sp->snd_mtx);
+     mtx_init(&sp->hld_mtx);
+
+     mtx_init(&sp->io_mtx);
+
+     isc_add_sysctls(sp);
+
+     sp->flags |= ISC_SM_RUN;
+     sp->flags |= ISC_SM_RUNNING;
+
+     debug(4, "starting ism_proc: sp->sid=%d", sp->sid);
+     return kthread_create(ism_proc, sp, &sp->stp, "ism_%d", sp->sid);
+}
diff --git a/sys/dev/disk/iscsi/initiator/isc_soc.c b/sys/dev/disk/iscsi/initiator/isc_soc.c
new file mode 100644 (file)
index 0000000..369a6ab
--- /dev/null
@@ -0,0 +1,677 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/iscsi/initiator/isc_soc.c,v 1.6 2009/06/25 18:46:30 kib Exp $
+ */
+/*
+ | iSCSI
+ | $Id: isc_soc.c,v 1.26 2007/05/19 06:09:01 danny Exp danny $
+ */
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/socketvar.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/syslog.h>
+#include <sys/mbuf.h>
+#include <sys/user.h>
+#include <signal.h>
+#include <sys/eventhandler.h>
+#include <sys/mutex.h>
+#include <sys/mutex2.h>
+#include <sys/socketops.h>
+
+#include <bus/cam/cam.h>
+#include <bus/cam/cam_ccb.h>
+
+#include <dev/disk/iscsi/initiator/iscsi.h>
+#include <dev/disk/iscsi/initiator/iscsivar.h>
+
+#ifndef NO_USE_MBUF
+#define USE_MBUF
+#endif
+
+#ifdef USE_MBUF
+
+static int ou_refcnt = 0;
+
+/*
+ | function for counting refs on external storage for mbuf
+ */
+static void
+ext_ref(void *arg)
+{
+     pduq_t *a = arg;
+
+     debug(3, "ou_refcnt=%d arg=%p b=%p", ou_refcnt, a, a->buf);
+     atomic_add_int(&a->refcnt, 1);
+}
+
+/*
+ | function for freeing external storage for mbuf
+ */
+static void
+ext_free(void *arg)
+{
+     pduq_t *a = arg;
+
+     if (atomic_fetchadd_int(&a->refcnt, -1) == 1)
+         if (a->buf != NULL) {
+              debug(3, "ou_refcnt=%d a=%p b=%p", ou_refcnt, a, a->buf);
+              kfree(a->buf, M_ISCSI);
+              a->buf = NULL;
+         }
+}
+
+int
+isc_sendPDU(isc_session_t *sp, pduq_t *pq)
+{
+     struct mbuf *mh, **mp;
+     pdu_t             *pp = &pq->pdu;
+     int               len, error;
+
+     debug_called(8);
+     /*
+      | mbuf for the iSCSI header
+      */
+     MGETHDR(mh, MB_TRYWAIT, MT_DATA);
+     mh->m_len = mh->m_pkthdr.len = sizeof(union ipdu_u);
+     mh->m_pkthdr.rcvif = NULL;
+     MH_ALIGN(mh, sizeof(union ipdu_u));
+     bcopy(&pp->ipdu, mh->m_data, sizeof(union ipdu_u));
+     mh->m_next = NULL;
+
+     if(sp->hdrDigest)
+         pq->pdu.hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0);
+     if(pp->ahs_len) {
+          /*
+          | Add any AHS to the iSCSI hdr mbuf
+           |  XXX Assert: (mh->m_pkthdr.len + pp->ahs_len) < MHLEN
+          */
+          bcopy(pp->ahs, (mh->m_data + mh->m_len), pp->ahs_len);
+          mh->m_len += pp->ahs_len;
+          mh->m_pkthdr.len += pp->ahs_len;
+
+         if(sp->hdrDigest)
+              pq->pdu.hdr_dig = sp->hdrDigest(&pp->ahs, pp->ahs_len, pq->pdu.hdr_dig);
+     }
+     if(sp->hdrDigest) {
+         debug(2, "hdr_dig=%x", pq->pdu.hdr_dig);
+          /*
+          | Add header digest to the iSCSI hdr mbuf
+          | XXX Assert: (mh->m_pkthdr.len + 4) < MHLEN
+          */
+          bcopy(&pp->hdr_dig, (mh->m_data + mh->m_len), sizeof(int));
+          mh->m_len += sizeof(int);
+          mh->m_pkthdr.len += sizeof(int);
+     }
+     mp = &mh->m_next;
+     if(pq->pdu.ds) {
+          struct mbuf   *md;
+          int           off = 0;
+
+          len = pp->ds_len;
+         while(len & 03) // the specs say it must be int alligned
+              len++;
+          while(len > 0) {
+                int       l;
+
+              MGET(md, MB_TRYWAIT, MT_DATA);
+              pq->refcnt++;
+
+                l = min(MCLBYTES, len);
+              debug(5, "setting ext_free(arg=%p len/l=%d/%d)", pq->buf, len, l);
+              md->m_ext.ext_buf = pq->buf;
+              md->m_ext.ext_free = ext_free;
+              md->m_ext.ext_ref = ext_ref;
+              md->m_ext.ext_arg = pq;
+              md->m_ext.ext_size = l;
+              md->m_flags |= M_EXT;
+              md->m_data = pp->ds + off;
+              md->m_len = l;
+              md->m_next = NULL;
+              mh->m_pkthdr.len += l;
+              *mp = md;
+              mp = &md->m_next;
+              len -= l;
+              off += l;
+          }
+     }
+     if(sp->dataDigest) {
+          struct mbuf   *me;
+
+         pp->ds_dig = sp->dataDigest(pp->ds, pp->ds_len, 0);
+
+         MGET(me, MB_TRYWAIT, MT_DATA);
+          me->m_len = sizeof(int);
+          MH_ALIGN(mh, sizeof(int));
+          bcopy(&pp->ds_dig, me->m_data, sizeof(int));
+          me->m_next = NULL;
+          mh->m_pkthdr.len += sizeof(int);
+          *mp = me;
+     }
+     if((error = sosend(sp->soc, NULL, NULL, mh, 0, 0, sp->td)) != 0) {
+         sdebug(3, "error=%d", error);
+         return error;
+     }
+     sp->stats.nsent++;
+     getmicrouptime(&sp->stats.t_sent);
+     return 0;
+}
+#else /* NO_USE_MBUF */
+int
+isc_sendPDU(isc_session_t *sp, pduq_t *pq)
+{
+     struct uio *uio = &pq->uio;
+     struct iovec *iv;
+     pdu_t     *pp = &pq->pdu;
+     int       len, error;
+
+     debug_called(8);
+
+     bzero(uio, sizeof(struct uio));
+     uio->uio_rw = UIO_WRITE;
+     uio->uio_segflg = UIO_SYSSPACE;
+     uio->uio_td = sp->td;
+     uio->uio_iov = iv = pq->iov;
+
+     iv->iov_base = &pp->ipdu;
+     iv->iov_len = sizeof(union ipdu_u);
+     uio->uio_resid = pq->len;
+     iv++;
+     if(sp->hdrDigest)
+         pq->pdu.hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0);
+     if(pp->ahs_len) {
+         iv->iov_base = pp->ahs;
+         iv->iov_len = pp->ahs_len;
+         iv++;
+
+         if(sp->hdrDigest)
+              pq->pdu.hdr_dig = sp->hdrDigest(&pp->ahs, pp->ahs_len, pq->pdu.hdr_dig);
+     }
+     if(sp->hdrDigest) {
+         debug(2, "hdr_dig=%x", pq->pdu.hdr_dig);
+         iv->iov_base = &pp->hdr_dig;
+         iv->iov_len = sizeof(int);
+         iv++;
+     }
+     if(pq->pdu.ds) {
+         iv->iov_base = pp->ds;
+         iv->iov_len = pp->ds_len;
+         while(iv->iov_len & 03) // the specs say it must be int alligned
+              iv->iov_len++;
+         iv++;
+     }
+     if(sp->dataDigest) {
+         pp->ds_dig = sp->dataDigest(pp->ds, pp->ds_len, 0);
+         iv->iov_base = &pp->ds_dig;
+         iv->iov_len = sizeof(int);
+         iv++;
+     }
+     uio->uio_iovcnt   = iv - pq->iov;
+     sdebug(5, "opcode=%x iovcnt=%d uio_resid=%d itt=%x",
+           pp->ipdu.bhs.opcode, uio->uio_iovcnt, uio->uio_resid,
+           ntohl(pp->ipdu.bhs.itt));
+     sdebug(5, "sp=%p sp->soc=%p uio=%p sp->td=%p",
+           sp, sp->soc, uio, sp->td);
+     do {
+         len = uio->uio_resid;
+         error = sosend(sp->soc, NULL, uio, 0, 0, 0, sp->td);
+         if(uio->uio_resid == 0 || error || len == uio->uio_resid) {
+              if(uio->uio_resid) {
+                   sdebug(2, "uio->uio_resid=%d uio->uio_iovcnt=%d error=%d len=%d",
+                          uio->uio_resid, uio->uio_iovcnt, error, len);
+                   if(error == 0)
+                        error = EAGAIN; // 35
+              }
+              break;
+         }
+         /*
+          | XXX: untested code
+          */
+         sdebug(1, "uio->uio_resid=%d uio->uio_iovcnt=%d",
+               uio->uio_resid, uio->uio_iovcnt);
+         iv = uio->uio_iov;
+         len -= uio->uio_resid;
+         while(uio->uio_iovcnt > 0) {
+              if(iv->iov_len > len) {
+                   caddr_t     bp = (caddr_t)iv->iov_base;
+
+                   iv->iov_len -= len;
+                   iv->iov_base = (void *)&bp[len];
+                   break;
+              }
+              len -= iv->iov_len;
+              uio->uio_iovcnt--;
+              uio->uio_iov++;
+              iv++;
+         }
+     } while(uio->uio_resid);
+
+     if(error == 0) {
+         sp->stats.nsent++;
+         getmicrouptime(&sp->stats.t_sent);
+
+     }
+
+     return error;
+}
+#endif /* USE_MBUF */
+
+/*
+ | wait till a PDU header is received
+ | from the socket.
+ */
+/*
+   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
+ */
+static __inline int
+so_getbhs(isc_session_t *sp)
+{
+     bhs_t *bhs                = &sp->bhs;
+     struct uio                *uio = &sp->uio;
+     struct iovec      *iov = &sp->iov;
+     int               error, flags;
+
+     debug_called(8);
+
+     iov->iov_base     = bhs;
+     iov->iov_len      = sizeof(bhs_t);
+
+     uio->uio_iov      = iov;
+     uio->uio_iovcnt   = 1;
+     uio->uio_rw       = UIO_READ;
+     uio->uio_segflg   = UIO_SYSSPACE;
+     uio->uio_td       = curthread; // why ...
+     uio->uio_resid    = sizeof(bhs_t);
+
+     flags = MSG_WAITALL;
+     error = so_pru_soreceive(sp->soc, NULL, uio, NULL, NULL, &flags);
+
+     if(error)
+         debug(2, "error=%d so_error=%d uio->uio_resid=%d iov.iov_len=%zd",
+               error,
+               sp->soc->so_error, uio->uio_resid, iov->iov_len);
+     if(!error && (uio->uio_resid > 0)) {
+         error = EPIPE; // was EAGAIN
+         debug(2, "error=%d so_error=%d uio->uio_resid=%d iov.iov_len=%zd so_state=%x",
+               error,
+               sp->soc->so_error, uio->uio_resid, iov->iov_len, sp->soc->so_state);
+     }
+
+     return error;
+}
+
+/*
+ | so_recv gets called when there is at least
+ | an iSCSI header in the queue
+ */
+static int
+so_recv(isc_session_t *sp, pduq_t *pq)
+{
+     struct socket     *so = sp->soc;
+     sn_t              *sn = &sp->sn;
+     struct uio                *uio = &pq->uio;
+     struct sockbuf    sbp;
+     pdu_t             *pp;
+     int               error;
+     size_t            n, len;
+     bhs_t             *bhs;
+     u_int             max, exp;
+
+     debug_called(8);
+     /*
+      | now calculate how much data should be in the buffer
+      | NOTE: digest is not verified/calculated - yet
+      */
+     pp = &pq->pdu;
+     bhs = &pp->ipdu.bhs;
+
+     sbinit(&sbp, 0);
+     len = 0;
+     if(bhs->AHSLength) {
+         pp->ahs_len = bhs->AHSLength * 4;
+         len += pp->ahs_len;
+     }
+     if(sp->hdrDigest)
+         len += 4;
+     if(bhs->DSLength) {
+         n = bhs->DSLength;
+#if BYTE_ORDER == LITTLE_ENDIAN
+         pp->ds_len = ((n & 0x00ff0000) >> 16)
+              | (n & 0x0000ff00)
+              | ((n & 0x000000ff) << 16);
+#else
+         pp->ds_len = n;
+#endif
+         len += pp->ds_len;
+         while(len & 03)
+              len++;
+         if(sp->dataDigest)
+              len += 4;
+     }
+
+     if((sp->opt.maxRecvDataSegmentLength > 0) && (len > sp->opt.maxRecvDataSegmentLength)) {
+#if 0
+         xdebug("impossible PDU length(%d) opt.maxRecvDataSegmentLength=%d",
+                len, sp->opt.maxRecvDataSegmentLength);
+         // deep trouble here, probably all we can do is
+         // force a disconnect, XXX: check RFC ...
+         log(LOG_ERR,
+             "so_recv: impossible PDU length(%ld) from iSCSI %s/%s\n",
+             len, sp->opt.targetAddress, sp->opt.targetName);
+#endif
+         /*
+          | XXX: this will really screwup the stream.
+          | should clear up the buffer till a valid header
+          | is found, or just close connection ...
+          | should read the RFC.
+          */
+         error = E2BIG;
+         goto out;
+     }
+     if(len) {
+         int   flags;
+         struct mbuf **mpp;
+
+         mpp = &pq->mp;
+
+         sbp.sb_climit = len;
+         uio->uio_td = curthread; // why ...
+         if(sp->douio) {
+              // it's more efficient to use mbufs -- why?
+              if(bhs->opcode == ISCSI_READ_DATA) {
+                   pduq_t      *opq;
+
+                   opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
+                   if(opq != NULL) {
+                        union ccb *ccb                 = opq->ccb;
+                        struct ccb_scsiio *csio        = &ccb->csio;
+                        pdu_t *opp                     = &opq->pdu;
+                        scsi_req_t *cmd                = &opp->ipdu.scsi_req;
+                        data_in_t *rcmd                = &pq->pdu.ipdu.data_in;
+                        bhs_t *bhp                     = &opp->ipdu.bhs;
+                        int    r;
+
+                        if(bhp->opcode == ISCSI_SCSI_CMD
+                           && cmd->R
+                           && (ntohl(cmd->edtlen) >= pq->pdu.ds_len)) {
+                             struct iovec *iov = pq->iov;
+                             iov->iov_base = csio->data_ptr + ntohl(rcmd->bo);
+                             iov->iov_len = pq->pdu.ds_len;
+
+                             uio->uio_rw = UIO_READ;
+                             uio->uio_segflg = UIO_SYSSPACE;
+                             uio->uio_iov = iov;
+                             uio->uio_iovcnt = 1;
+                             if(len > pq->pdu.ds_len) {
+                                  pq->iov[1].iov_base = &r;
+                                  pq->iov[1].iov_len = len - pq->pdu.ds_len;
+                                  uio->uio_iovcnt++;
+                             }
+                             mpp = NULL;
+
+                             sdebug(4, "uio_resid=0x%zx itt=0x%x bp=%p bo=%x len=%x/%x",
+                                    uio->uio_resid,
+                                    ntohl(pq->pdu.ipdu.bhs.itt),
+                                    csio->data_ptr, ntohl(rcmd->bo), ntohl(cmd->edtlen), pq->pdu.ds_len);
+                        }
+                   }
+              }
+         }
+         /*
+          * Here we call so_pru_receive with a sockbuf so we can obtain
+          * the mbuf chain that can be assigned later to the pq->mp,
+          * which is the mbuf wanted.
+          * For the moment, resid will be saved in the uio.
+          */
+         flags = MSG_WAITALL;
+         error = so_pru_soreceive(so, NULL, NULL, &sbp, NULL, &flags);
+         pq->mp = sbp.sb_mb;
+         uio->uio_resid = sbp.sb_climit - sbp.sb_cc;
+         //if(error == EAGAIN)
+         // XXX: this needs work! it hangs iscontrol
+         if(error || uio->uio_resid)
+              goto out;
+     }
+     pq->len += len;
+     sdebug(6, "len=%d] opcode=0x%x ahs_len=0x%x ds_len=0x%x",
+           pq->len, bhs->opcode, pp->ahs_len, pp->ds_len);
+
+     max = ntohl(bhs->MaxCmdSN);
+     exp = ntohl(bhs->ExpStSN);
+
+     if(max < exp - 1 &&
+       max > exp - _MAXINCR) {
+         sdebug(2,  "bad cmd window size");
+         error = EIO; // XXX: for now;
+         goto out; // error
+     }
+
+     if(SNA_GT(max, sn->maxCmd))
+         sn->maxCmd = max;
+
+     if(SNA_GT(exp, sn->expCmd))
+         sn->expCmd = exp;
+
+     sp->cws = sn->maxCmd - sn->expCmd + 1;
+
+     return 0;
+
+ out:
+     // XXX: need some work here
+     xdebug("have a problem, error=%d", error);
+     pdu_free(sp->isc, pq);
+     if(!error && uio->uio_resid > 0)
+         error = EPIPE;
+     return error;
+}
+
+/*
+ | wait for something to arrive.
+ | and if the pdu is without errors, process it.
+ */
+static int
+so_input(isc_session_t *sp)
+{
+     pduq_t            *pq;
+     int               error;
+
+     debug_called(8);
+     /*
+      | first read in the iSCSI header
+      */
+     error = so_getbhs(sp);
+     if(error == 0) {
+         /*
+          | now read the rest.
+          */
+         pq = pdu_alloc(sp->isc, M_NOWAIT);
+         if(pq == NULL) { // XXX: might cause a deadlock ...
+              debug(3, "out of pdus, wait");
+              pq = pdu_alloc(sp->isc, M_NOWAIT);  // OK to WAIT
+         }
+         pq->pdu.ipdu.bhs = sp->bhs;
+         pq->len = sizeof(bhs_t);      // so far only the header was read
+         error = so_recv(sp, pq);
+         if(error != 0) {
+              error += 0x800; // XXX: just to see the error.
+              // terminal error
+              // XXX: close connection and exit
+         }
+         else {
+              sp->stats.nrecv++;
+              getmicrouptime(&sp->stats.t_recv);
+              ism_recv(sp, pq);
+         }
+     }
+     return error;
+}
+
+/*
+ | one per active (connected) session.
+ | this thread is responsible for reading
+ | in packets from the target.
+ */
+static void
+isc_soc(void *vp)
+{
+     isc_session_t     *sp = (isc_session_t *)vp;
+     struct socket     *so = sp->soc;
+     int               error;
+
+     debug_called(8);
+
+     if(sp->cam_path)
+         ic_release(sp);
+
+     error = 0;
+     while((sp->flags & (ISC_CON_RUN | ISC_LINK_UP)) == (ISC_CON_RUN | ISC_LINK_UP)) {
+         // XXX: hunting ...
+         if(sp->soc == NULL || !(so->so_state & SS_ISCONNECTED)) {
+              debug(2, "sp->soc=%p", sp->soc);
+              break;
+         }
+         error = so_input(sp);
+         if(error == 0) {
+              iscsi_lock_ex(&sp->io_mtx);
+              if(sp->flags & ISC_OWAITING) {
+              wakeup(&sp->flags);
+              }
+              iscsi_unlock_ex(&sp->io_mtx);
+         } else if(error == EPIPE) {
+              break;
+         }
+         else if(error == EAGAIN) {
+              if(so->so_state & SS_ISCONNECTED)
+                   // there seems to be a problem in 6.0 ...
+                   tsleep(sp, 0, "iscsoc", 2*hz);
+         }
+     }
+     sdebug(2, "terminated, flags=%x so_state=%x error=%d proc=%p",
+           sp->flags, so->so_state, error, sp->proc);
+     if((sp->proc != NULL) && sp->signal) {
+         PROC_LOCK(sp->proc);
+         ksignal(sp->proc, sp->signal);
+         PROC_UNLOCK(sp->proc);
+         sp->flags |= ISC_SIGNALED;
+         sdebug(2, "pid=%d signaled(%d)", sp->proc->p_pid, sp->signal);
+     }
+     else {
+         // we have to do something ourselves
+         // like closing this session ...
+     }
+     /*
+      | we've been terminated
+      */
+     // do we need this mutex ...?
+     //iscsi_lock_ex(&sp->io_mtx);
+     sp->flags &= ~(ISC_CON_RUNNING | ISC_LINK_UP);
+     wakeup(&sp->soc);
+     //iscsi_unlock_ex(&sp->io_mtx);
+
+     sdebug(2, "dropped ISC_CON_RUNNING");
+
+     kthread_exit();
+}
+
+void
+isc_stop_receiver(isc_session_t *sp)
+{
+     debug_called(8);
+     sdebug(3, "sp=%p sp->soc=%p", sp, sp? sp->soc: 0);
+     iscsi_lock_ex(&sp->io_mtx);
+     sp->flags &= ~ISC_LINK_UP;
+     if (sp->flags & ISC_CON_RUNNING) {
+            issleep(&sp->soc, &sp->io_mtx, 0, "iscstpc", 5*hz);
+     }
+     iscsi_unlock_ex(&sp->io_mtx);
+
+     if (sp->soc)
+            soshutdown(sp->soc, SHUT_RD);
+
+     iscsi_lock_ex(&sp->io_mtx);
+     sdebug(3, "soshutdown");
+     sp->flags &= ~ISC_CON_RUN;
+     while(sp->flags & ISC_CON_RUNNING) {
+         sdebug(3, "waiting flags=%x", sp->flags);
+         issleep(&sp->soc, &sp->io_mtx, 0, "iscstpc", hz);
+     }
+     iscsi_unlock_ex(&sp->io_mtx);
+
+     if (sp->fp != NULL) {
+         fdrop(sp->fp);
+        sp->fp = NULL;
+    }
+     /* sofree(sp->soc); fp deals with socket termination */
+     sp->soc = NULL;
+
+     sdebug(3, "done");
+}
+
+void
+isc_start_receiver(isc_session_t *sp)
+{
+     debug_called(8);
+
+     sp->flags |= ISC_CON_RUN | ISC_LINK_UP;
+     sp->flags |= ISC_CON_RUNNING;
+
+     kthread_create(isc_soc, sp, &sp->soc_thr, "iscsi%d", sp->sid);
+}
diff --git a/sys/dev/disk/iscsi/initiator/isc_subr.c b/sys/dev/disk/iscsi/initiator/isc_subr.c
new file mode 100644 (file)
index 0000000..bfda38d
--- /dev/null
@@ -0,0 +1,171 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/iscsi/initiator/isc_subr.c,v 1.3 2009/02/14 11:34:57 rrs Exp $
+ */
+/*
+ | iSCSI
+ | $Id: isc_subr.c,v 1.20 2006/12/01 09:10:17 danny Exp danny $
+ */
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/socketvar.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/syslog.h>
+#include <sys/mbuf.h>
+#include <sys/libkern.h>
+#include <sys/eventhandler.h>
+#include <sys/mutex.h>
+#include <sys/mutex2.h>
+
+#include <bus/cam/cam.h>
+
+#include "dev/disk/iscsi/initiator/iscsi.h"
+#include "dev/disk/iscsi/initiator/iscsivar.h"
+
+static char *
+i_strdupin(char *s, size_t maxlen)
+{
+     size_t    len;
+     char      *p, *q;
+
+     p = kmalloc(maxlen, M_ISCSI, M_WAITOK);
+     if(copyinstr(s, p, maxlen, &len)) {
+         kfree(p, M_ISCSI);
+         return NULL;
+     }
+     q = kmalloc(len, M_ISCSI, M_WAITOK);
+     bcopy(p, q, len);
+     kfree(p, M_ISCSI);
+
+     return q;
+}
+
+/*
+ | XXX: not finished coding
+ */
+int
+i_setopt(isc_session_t *sp, isc_opt_t *opt)
+{
+     const int digsize = 6;
+     int       len;
+     char      hdigest[digsize], ddigest[digsize];
+
+     debug_called(8);
+     if(opt->maxRecvDataSegmentLength > 0) {
+         sp->opt.maxRecvDataSegmentLength = opt->maxRecvDataSegmentLength;
+         sdebug(2, "maxRecvDataSegmentLength=%d", sp->opt.maxRecvDataSegmentLength);
+     }
+     if(opt->maxXmitDataSegmentLength > 0) {
+         // danny's RFC
+         sp->opt.maxXmitDataSegmentLength = opt->maxXmitDataSegmentLength;
+         sdebug(2, "maXmitDataSegmentLength=%d", sp->opt.maxXmitDataSegmentLength);
+     }
+     if(opt->maxBurstLength != 0) {
+         sp->opt.maxBurstLength = opt->maxBurstLength;
+         sdebug(2, "maxBurstLength=%d", sp->opt.maxBurstLength);
+     }
+
+     if(opt->targetAddress != NULL) {
+         if(sp->opt.targetAddress != NULL)
+              kfree(sp->opt.targetAddress, M_ISCSI);
+         sp->opt.targetAddress = i_strdupin(opt->targetAddress, 128);
+         sdebug(4, "opt.targetAddress='%s'", sp->opt.targetAddress);
+     }
+     if(opt->targetName != NULL) {
+         if(sp->opt.targetName != NULL)
+              kfree(sp->opt.targetName, M_ISCSI);
+         sp->opt.targetName = i_strdupin(opt->targetName, 128);
+         sdebug(4, "opt.targetName='%s'", sp->opt.targetName);
+     }
+     if(opt->initiatorName != NULL) {
+         if(sp->opt.initiatorName != NULL)
+              kfree(sp->opt.initiatorName, M_ISCSI);
+         sp->opt.initiatorName = i_strdupin(opt->initiatorName, 128);
+         sdebug(4, "opt.initiatorName='%s'", sp->opt.initiatorName);
+     }
+
+     if(opt->maxluns > 0) {
+         if(opt->maxluns > ISCSI_MAX_LUNS)
+              sp->opt.maxluns = ISCSI_MAX_LUNS; // silently chop it down ...
+         sp->opt.maxluns = opt->maxluns;
+         sdebug(4, "opt.maxluns=%d", sp->opt.maxluns);
+     }
+
+     /* Try to copy the userland pointer containing the header digest */
+     if(opt->headerDigest != NULL) {
+         if ((copyinstr(opt->headerDigest, &hdigest, digsize, &len)) != EFAULT) {
+              sdebug(2, "opt.headerDigest='%s'", hdigest);
+              if(strcmp(hdigest, "CRC32C") == 0) {
+                   sp->hdrDigest = (digest_t *)crc32_ext;
+                   sdebug(2, "headerDigest set");
+              }
+         }
+     }
+     /* Try to copy the userland pointer containing the data digest */
+     if(opt->dataDigest != NULL) {
+         if ((copyinstr(opt->dataDigest, &ddigest, digsize, &len)) != EFAULT) {
+              if (strcmp(ddigest, "CRC32C") == 0) {
+                   sp->dataDigest = (digest_t *)crc32_ext;
+                   sdebug(2, "dataDigest set");
+              }
+         }
+     }
+
+     return 0;
+}
+
+void
+i_freeopt(isc_opt_t *opt)
+{
+     if(opt->targetAddress != NULL) {
+         kfree(opt->targetAddress, M_ISCSI);
+         opt->targetAddress = NULL;
+     }
+     if(opt->targetName != NULL) {
+         kfree(opt->targetName, M_ISCSI);
+         opt->targetName = NULL;
+     }
+     if(opt->initiatorName != NULL) {
+         kfree(opt->initiatorName, M_ISCSI);
+         opt->initiatorName = NULL;
+     }
+}
diff --git a/sys/dev/disk/iscsi/initiator/iscsi.c b/sys/dev/disk/iscsi/initiator/iscsi.c
new file mode 100644 (file)
index 0000000..5467b79
--- /dev/null
@@ -0,0 +1,920 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/iscsi/initiator/iscsi.c,v 1.4 2008/11/25 07:17:11 scottl Exp $
+ */
+/*
+ | iSCSI
+ | $Id: iscsi.c,v 1.35 2007/04/22 08:58:29 danny Exp danny $
+ */
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/socketvar.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/mbuf.h>
+#include <sys/syslog.h>
+#include <sys/eventhandler.h>
+#include <sys/mutex.h>
+#include <sys/mutex2.h>
+#include <vfs/devfs/devfs.h>
+
+#include <bus/cam/cam.h>
+#include <dev/disk/iscsi/initiator/iscsi.h>
+#include <dev/disk/iscsi/initiator/iscsivar.h>
+
+static char *iscsi_driver_version = "2.1.0";
+
+static struct isc_softc isc;
+
+MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver");
+
+struct objcache_malloc_args iscsi_malloc_args = {
+     sizeof(pduq_t), M_ISCSI
+};
+
+#ifdef ISCSI_INITIATOR_DEBUG
+int iscsi_debug = ISCSI_INITIATOR_DEBUG;
+SYSCTL_INT(_debug, OID_AUTO, iscsi_initiator, CTLFLAG_RW, &iscsi_debug, 0, "iSCSI driver debug flag");
+
+struct lock iscsi_dbg_lock;
+#endif
+
+
+static char isid[6+1] = {
+     0x80,
+     'D',
+     'I',
+     'B',
+     '0',
+     '0',
+     0
+};
+
+static int     i_create_session(struct cdev *dev, int *ndev);
+
+static int     i_ping(struct cdev *dev);
+static int     i_send(struct cdev *dev, caddr_t arg, struct thread *td);
+static int     i_recv(struct cdev *dev, caddr_t arg, struct thread *td);
+static int     i_setsoc(isc_session_t *sp, int fd, struct thread *td);
+
+static void    free_pdus(struct isc_softc *sc);
+
+static d_open_t iscsi_open;
+static d_close_t iscsi_close;
+static d_ioctl_t iscsi_ioctl;
+#ifdef ISCSI_INITIATOR_DEBUG
+static d_read_t iscsi_read;
+#endif
+
+static struct dev_ops iscsi_ops = {
+     .head     = { "iscsi", ISCSI_CDEV_MAJOR, D_DISK},
+     .d_open   = iscsi_open,
+     .d_close  = iscsi_close,
+     .d_ioctl  = iscsi_ioctl,
+#ifdef ISCSI_INITIATOR_DEBUG
+     .d_read   = iscsi_read,
+#endif
+};
+
+static int
+iscsi_open(struct dev_open_args *ap)
+{
+     cdev_t dev = ap->a_head.a_dev;
+
+     debug_called(8);
+
+     debug(7, "dev=%d", dev->si_uminor);
+
+     if(minor(dev) > MAX_SESSIONS) {
+         // should not happen
+          return ENODEV;
+     }
+
+     /* Make sure the device is passed */
+     if (dev->si_drv1 == NULL)
+         dev->si_drv1 = (struct isc *)isc.dev->si_drv1;
+
+     if(minor(dev) == MAX_SESSIONS) {
+#if 1
+         struct isc_softc *sc = (struct isc_softc *)dev->si_drv1;
+
+         // this should be in iscsi_start
+         if(sc->cam_sim == NULL)
+              ic_init(sc);
+#endif
+     }
+     return 0;
+}
+
+static int
+iscsi_close(struct dev_close_args *ap)
+{
+     struct isc                *sc;
+     cdev_t            dev = ap->a_head.a_dev;
+     int               flag = ap->a_fflag;
+     isc_session_t     *sp;
+
+     debug_called(8);
+
+     debug(3, "flag=%x", flag);
+
+     sc = (struct isc *)dev->si_drv1;
+     if(minor(dev) == MAX_SESSIONS) {
+         return 0;
+     }
+     sp = (isc_session_t *)dev->si_drv2;
+     if(sp != NULL) {
+         sdebug(2, "session=%d flags=%x", minor(dev), sp->flags );
+         /*
+          | if still in full phase, this probably means
+          | that something went realy bad.
+          | it could be a result from 'shutdown', in which case
+          | we will ignore it (so buffers can be flushed).
+          | the problem is that there is no way of differentiating
+          | between a shutdown procedure and 'iscontrol' dying.
+          */
+         if(sp->flags & ISC_FFPHASE)
+              // delay in case this is a shutdown.
+              tsleep(sp, 0, "isc-cls", 60*hz);
+         ism_stop(sp);
+     }
+     debug(2, "done");
+     return 0;
+}
+
+static int
+iscsi_ioctl(struct dev_ioctl_args *ap)
+{
+     struct isc                *sc;
+     cdev_t            dev = ap->a_head.a_dev;
+     caddr_t           arg = ap->a_data;
+     isc_session_t     *sp;
+     isc_opt_t         *opt;
+     int               error;
+
+     sc = (struct isc *)dev->si_drv1;
+     debug_called(8);
+
+     error = 0;
+     if(minor(dev) == MAX_SESSIONS) {
+         /*
+          | non Session commands
+          */
+         if(sc == NULL)
+              return ENXIO;
+
+         switch(ap->a_cmd) {
+         case ISCSISETSES:
+              error = i_create_session(dev, (int *)arg);
+              if(error == 0)
+
+              break;
+
+         default:
+              error = ENXIO; // XXX:
+         }
+         return error;
+     }
+     sp = (isc_session_t *)dev->si_drv2;
+     /*
+      | session commands
+      */
+     if(sp == NULL)
+         return ENXIO;
+
+     sdebug(6, "dev=%d cmd=%d", minor(dev), (int)(ap->a_cmd & 0xff));
+
+     switch(ap->a_cmd) {
+     case ISCSISETSOC:
+         error = i_setsoc(sp, *(u_int *)arg, curthread);
+         break;
+
+     case ISCSISETOPT:
+         opt = (isc_opt_t *)arg;
+         error = i_setopt(sp, opt);
+         break;
+
+     case ISCSISEND:
+         error = i_send(dev, arg, curthread);
+         break;
+
+     case ISCSIRECV:
+         error = i_recv(dev, arg, curthread);
+         break;
+
+     case ISCSIPING:
+         error = i_ping(dev);
+         break;
+
+     case ISCSISTART:
+         error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 1);
+         if(error == 0) {
+              sp->proc = curthread->td_proc;
+              SYSCTL_ADD_UINT(&sp->clist,
+                              SYSCTL_CHILDREN(sp->oid),
+                              OID_AUTO,
+                              "pid",
+                              CTLFLAG_RD,
+                              &sp->proc->p_pid, sizeof(pid_t), "control process id");
+         }
+         break;
+
+     case ISCSIRESTART:
+         error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 2);
+         break;
+
+     case ISCSISTOP:
+         error = ism_fullfeature(dev, 0);
+         break;
+
+     case ISCSISIGNAL: {
+         int sig = *(int *)arg;
+
+         if(sig < 0 || sig > _SIG_MAXSIG)
+              error = EINVAL;
+         else
+               sp->signal = sig;
+         break;
+     }
+
+     case ISCSIGETCAM: {
+         iscsi_cam_t *cp = (iscsi_cam_t *)arg;
+
+         error = ic_getCamVals(sp, cp);
+         break;
+     }
+
+     default:
+         error = ENOIOCTL;
+     }
+
+     return error;
+}
+
+static int
+iscsi_read(struct dev_read_args *ra)
+{
+#ifdef  ISCSI_INITIATOR_DEBUG
+     struct isc_softc  *sc;
+     cdev_t            dev = ra->a_head.a_dev;
+     struct uio                *uio = ra->a_uio;
+     isc_session_t     *sp;
+     pduq_t            *pq;
+     char              buf[1024];
+
+     sc = (struct isc_softc *)dev->si_drv1;
+     sp = (isc_session_t *)dev->si_drv2;
+
+     if(minor(dev) == MAX_SESSIONS) {
+         ksprintf(buf, "/----- Session ------/\n");
+         uiomove(buf, strlen(buf), uio);
+         int   i = 0;
+
+         TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
+              if(uio->uio_resid == 0)
+                   return 0;
+              ksprintf(buf, "%03d] '%s' '%s'\n", i++, sp->opt.targetAddress, sp->opt.targetName);
+              uiomove(buf, strlen(buf), uio);
+         }
+         ksprintf(buf, "%d/%d /---- free -----/\n", sc->npdu_alloc, sc->npdu_max);
+         i = 0;
+         uiomove(buf, strlen(buf), uio);
+         TAILQ_FOREACH(pq, &sc->freepdu, pq_link) {
+              if(uio->uio_resid == 0)
+                   return 0;
+              ksprintf(buf, "%03d] %06x\n", i++, ntohl(pq->pdu.ipdu.bhs.itt));
+              uiomove(buf, strlen(buf), uio);
+         }
+     }
+     else {
+         int   i = 0;
+         struct socket *so = sp->soc;
+#define pukeit(i, pq) do {\
+              ksprintf(buf, "%03d] %06x %02x %x %ld\n",\
+                      i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
+                      pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
+                      (long)pq->ts.tv_sec);\
+              } while(0)
+
+         ksprintf(buf, "%d/%d /---- hld -----/\n", sp->stats.nhld, sp->stats.max_hld);
+         uiomove(buf, strlen(buf), uio);
+         TAILQ_FOREACH(pq, &sp->hld, pq_link) {
+              if(uio->uio_resid == 0)
+                   return 0;
+              pukeit(i, pq); i++;
+              uiomove(buf, strlen(buf), uio);
+         }
+         ksprintf(buf, "%d/%d /---- rsp -----/\n", sp->stats.nrsp, sp->stats.max_rsp);
+         uiomove(buf, strlen(buf), uio);
+         i = 0;
+         TAILQ_FOREACH(pq, &sp->rsp, pq_link) {
+              if(uio->uio_resid == 0)
+                   return 0;
+              pukeit(i, pq); i++;
+              uiomove(buf, strlen(buf), uio);
+         }
+         ksprintf(buf, "%d/%d /---- csnd -----/\n", sp->stats.ncsnd, sp->stats.max_csnd);
+         i = 0;
+         uiomove(buf, strlen(buf), uio);
+         TAILQ_FOREACH(pq, &sp->csnd, pq_link) {
+              if(uio->uio_resid == 0)
+                   return 0;
+              pukeit(i, pq); i++;
+              uiomove(buf, strlen(buf), uio);
+         }
+         ksprintf(buf, "%d/%d /---- wsnd -----/\n", sp->stats.nwsnd, sp->stats.max_wsnd);
+         i = 0;
+         uiomove(buf, strlen(buf), uio);
+         TAILQ_FOREACH(pq, &sp->wsnd, pq_link) {
+              if(uio->uio_resid == 0)
+                   return 0;
+              pukeit(i, pq); i++;
+              uiomove(buf, strlen(buf), uio);
+         }
+         ksprintf(buf, "%d/%d /---- isnd -----/\n", sp->stats.nisnd, sp->stats.max_isnd);
+         i = 0;
+         uiomove(buf, strlen(buf), uio);
+         TAILQ_FOREACH(pq, &sp->isnd, pq_link) {
+              if(uio->uio_resid == 0)
+                   return 0;
+              pukeit(i, pq); i++;
+              uiomove(buf, strlen(buf), uio);
+         }
+
+         ksprintf(buf, "/---- Stats ---/\n");
+         uiomove(buf, strlen(buf), uio);
+
+         ksprintf(buf, "recv=%d sent=%d\n", sp->stats.nrecv, sp->stats.nsent);
+         uiomove(buf, strlen(buf), uio);
+
+         ksprintf(buf, "flags=%x pdus: alloc=%d max=%d\n",
+                 sp->flags, sc->npdu_alloc, sc->npdu_max);
+         uiomove(buf, strlen(buf), uio);
+
+         ksprintf(buf, "cws=%d last cmd=%x exp=%x max=%x stat=%x itt=%x\n",
+                 sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
+         uiomove(buf, strlen(buf), uio);
+
+         if (so)
+             ksprintf(buf, "/---- socket -----/\nso_state=%x\n", so->so_state);
+         uiomove(buf, strlen(buf), uio);
+
+     }
+#endif
+     return 0;
+}
+
+static int
+i_ping(struct cdev *dev)
+{
+     return 0;
+}
+/*
+ | low level I/O
+ */
+static int
+i_setsoc(isc_session_t *sp, int fd, struct thread *td)
+{
+     int error = 0;
+     struct file *fp;
+
+     if (sp->soc != NULL)
+         isc_stop_receiver(sp);
+    if (sp->fp) {
+         fdrop(sp->fp);
+         sp->fp = NULL;
+    }
+
+     debug_called(8);
+
+     if ((error = holdsock(td->td_proc->p_fd, fd, &fp)) == 0) {
+         sp->td = td;
+         sp->soc = fp->f_data;
+         sp->fp = fp;
+         isc_start_receiver(sp);
+     }
+
+     return error;
+}
+
+static int
+i_send(struct cdev *dev, caddr_t arg, struct thread *td)
+{
+     isc_session_t     *sp = (isc_session_t *)dev->si_drv2;
+     struct isc_softc  *sc = (struct isc_softc *)dev->si_drv1;
+     caddr_t           bp;
+     pduq_t            *pq;
+     pdu_t             *pp;
+     int               n, error;
+
+     debug_called(8);
+
+     if(sp->soc == NULL)
+         return ENOTCONN;
+
+     if((pq = pdu_alloc(sc, M_NOWAIT)) == NULL)
+         return EAGAIN;
+     pp = &pq->pdu;
+     pq->pdu = *(pdu_t *)arg;
+     pq->refcnt = 0;
+     if((error = i_prepPDU(sp, pq)) != 0)
+         goto out;
+
+     sdebug(3, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
+
+     pq->buf = bp = kmalloc(pq->len - sizeof(union ipdu_u), M_ISCSI, M_NOWAIT);
+     if(pq->buf == NULL) {
+         error = EAGAIN;
+         goto out;
+     }
+
+     if(pp->ahs_len) {
+         n = pp->ahs_len;
+         error = copyin(pp->ahs, bp, n);
+         if(error != 0) {
+              sdebug(3, "copyin ahs: error=%d", error);
+              goto out;
+         }
+         pp->ahs = (ahs_t *)bp;
+         bp += n;
+     }
+     if(pp->ds_len) {
+         n = pp->ds_len;
+         error = copyin(pp->ds, bp, n);
+         if(error != 0) {
+              sdebug(3, "copyin ds: error=%d", error);
+              goto out;
+         }
+         pp->ds = bp;
+         bp += n;
+         while(n & 03) {
+              n++;
+              *bp++ = 0;
+         }
+     }
+
+     error = isc_qout(sp, pq);
+#if 1
+     if(error == 0)
+         wakeup(&sp->flags); // XXX: to 'push' proc_out ...
+#endif
+out:
+     if(error)
+         pdu_free(sc, pq);
+
+     return error;
+}
+
+/*
+ | NOTE: must calculate digest if requiered.
+ */
+static int
+i_recv(struct cdev *dev, caddr_t arg, struct thread *td)
+{
+     isc_session_t     *sp = (isc_session_t *)dev->si_drv2;
+     pduq_t            *pq;
+     pdu_t             *pp, *up;
+     caddr_t           bp;
+     int               error, mustfree, cnt;
+     size_t            need, have, n;
+
+     debug_called(8);
+
+     if(sp == NULL)
+         return EIO;
+
+     if(sp->soc == NULL)
+         return ENOTCONN;
+     sdebug(3, "");
+     cnt = 6;     // XXX: maybe the user can request a time out?
+     iscsi_lock_ex(&sp->rsp_mtx);
+     while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) {
+         issleep(&sp->rsp, &sp->rsp_mtx, 0, "isc_rsp", hz*10);
+         if(cnt-- == 0) break; // XXX: for now, needs work
+
+     }
+     if(pq != NULL) {
+         sp->stats.nrsp--;
+         TAILQ_REMOVE(&sp->rsp, pq, pq_link);
+     }
+     iscsi_unlock_ex(&sp->rsp_mtx);
+
+     sdebug(4, "cnt=%d", cnt);
+
+     if(pq == NULL) {
+         error = ENOTCONN;
+         sdebug(3, "error=%d sp->flags=%x ", error, sp->flags);
+         return error;
+     }
+     up = (pdu_t *)arg;
+     pp = &pq->pdu;
+     up->ipdu = pp->ipdu;
+     n = 0;
+     up->ds_len = 0;
+     up->ahs_len = 0;
+     error = 0;
+
+     if(pq->mp) {
+         u_int len;
+
+         // Grr...
+         len = 0;
+         if(pp->ahs_len) {
+              len += pp->ahs_len;
+              if(sp->hdrDigest)
+                   len += 4;
+         }
+         if(pp->ds_len) {
+              len += pp->ds_len;
+              if(sp->hdrDigest)
+                   len += 4;
+         }
+
+         mustfree = 0;
+         if(len > pq->mp->m_len) {
+              mustfree++;
+              bp = kmalloc(len, M_ISCSI, M_INTWAIT);
+              sdebug(4, "need mbufcopy: %d", len);
+              i_mbufcopy(pq->mp, bp, len);
+         }
+         else
+              bp = mtod(pq->mp, caddr_t);
+
+         if(pp->ahs_len) {
+              need = pp->ahs_len;
+              if(sp->hdrDigest)
+                   need += 4;
+              n = MIN(up->ahs_size, need);
+              error = copyout(bp, (caddr_t)up->ahs, n);
+              up->ahs_len = n;
+              bp += need;
+         }
+         if(!error && pp->ds_len) {
+              need = pp->ds_len;
+              if(sp->hdrDigest)
+                   need += 4;
+              if((have = up->ds_size) == 0) {
+                   have = up->ahs_size - n;
+                   up->ds = (caddr_t)up->ahs + n;
+              }
+              n = MIN(have, need);
+              error = copyout(bp, (caddr_t)up->ds, n);
+              up->ds_len = n;
+         }
+
+         if(mustfree)
+              kfree(bp, M_ISCSI);
+     }
+
+     sdebug(6, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
+
+     pdu_free(sp->isc, pq);
+
+     return error;
+}
+
+static int
+i_create_session(struct cdev *dev, int *ndev)
+{
+     struct isc_softc          *sc = (struct isc_softc *)dev->si_drv1;
+     isc_session_t     *sp;
+     int               error, n;
+
+     debug_called(8);
+     sp = (isc_session_t *)kmalloc(sizeof *sp, M_ISCSI, M_WAITOK | M_ZERO);
+     if(sp == NULL)
+         return ENOMEM;
+     lockmgr(&sc->lock, LK_EXCLUSIVE);
+     /*
+      | search for the lowest unused sid
+      */
+     for(n = 0; n < MAX_SESSIONS; n++)
+         if(sc->sessions[n] == NULL)
+              break;
+     if(n == MAX_SESSIONS) {
+         lockmgr(&sc->lock, LK_RELEASE);
+         kfree(sp, M_ISCSI);
+         return EPERM;
+     }
+     TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link);
+     sc->nsess++;
+     lockmgr(&sc->lock, LK_RELEASE);
+
+     sc->sessions[n] = sp;
+     debug(8, "n is %d", n);
+     sp->dev = make_dev(&iscsi_ops, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n);
+     devfs_config();
+
+     reference_dev(sp->dev);
+
+     *ndev = sp->sid = n;
+     sp->isc = sc;
+     sp->dev->si_drv1 = sc;
+     sp->dev->si_drv2 = sp;
+
+     sp->opt.maxRecvDataSegmentLength = 8192;
+     sp->opt.maxXmitDataSegmentLength = 8192;
+
+     sp->opt.maxBurstLength = 65536;   // 64k
+
+     sdebug(2, "sessionID=%d sp=%p", n, sp);
+     error = ism_start(sp);
+
+     return error;
+}
+
+#ifdef notused
+static void
+iscsi_counters(isc_session_t *sp)
+{
+     int       h, r, s;
+     pduq_t    *pq;
+
+#define _puke(i, pq) do {\
+              debug(2, "%03d] %06x %02x %x %ld %jd %x\n",\
+                      i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
+                      pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
+                      (long)pq->ts.sec, pq->ts.frac, pq->flags);\
+              } while(0)
+
+     h = r = s = 0;
+     TAILQ_FOREACH(pq, &sp->hld, pq_link) {
+         _puke(h, pq);
+         h++;
+     }
+     TAILQ_FOREACH(pq, &sp->rsp, pq_link) r++;
+     TAILQ_FOREACH(pq, &sp->csnd, pq_link) s++;
+     TAILQ_FOREACH(pq, &sp->wsnd, pq_link) s++;
+     TAILQ_FOREACH(pq, &sp->isnd, pq_link) s++;
+     debug(2, "hld=%d rsp=%d snd=%d", h, r, s);
+}
+#endif
+
+static void
+iscsi_shutdown(void *v)
+{
+     struct isc_softc  *sc = (struct isc_softc *)v;
+     isc_session_t     *sp;
+     int       n;
+
+     debug_called(8);
+     if(sc == NULL) {
+         xdebug("sc is NULL!");
+         return;
+     }
+     if(sc->eh == NULL)
+         debug(2, "sc->eh is NULL");
+     else {
+         EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->eh);
+         debug(2, "done n=%d", sc->nsess);
+     }
+     n = 0;
+     TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
+         debug(2, "%2d] sp->flags=0x%08x", n, sp->flags);
+         n++;
+     }
+     debug(2, "done");
+}
+
+static int
+init_pdus(struct isc_softc *sc)
+{
+     debug_called(8);
+
+     sc->pdu_zone = objcache_create("pdu", 0, 0,
+                               NULL, NULL, NULL,
+                               objcache_malloc_alloc,
+                               objcache_malloc_free,
+                               &iscsi_malloc_args);
+
+     if(sc->pdu_zone == NULL) {
+         kprintf("iscsi_initiator: objcache_create failed");
+         return -1;
+     }
+     TAILQ_INIT(&sc->freepdu);
+
+     return 0;
+}
+
+static void
+free_pdus(struct isc_softc *sc)
+{
+     pduq_t    *pq;
+
+     debug_called(8);
+
+     if(sc->pdu_zone != NULL) {
+         TAILQ_FOREACH(pq, &sc->freepdu, pq_link) {
+              TAILQ_REMOVE(&sc->freepdu, pq, pq_link);
+              objcache_put(sc->pdu_zone, pq);
+         }
+         objcache_destroy(sc->pdu_zone);
+         sc->pdu_zone = NULL;
+     }
+}
+
+static void
+iscsi_start(void)
+{
+     struct isc_softc *sc = &isc;
+
+     debug_called(8);
+
+     memset(sc, 0, sizeof(struct isc_softc));
+
+     sc->dev = make_dev(&iscsi_ops, MAX_SESSIONS, UID_ROOT, GID_WHEEL, 0600, "iscsi");
+     devfs_config();
+
+     sc->dev->si_drv1 = sc;
+
+     reference_dev(sc->dev);
+
+     TAILQ_INIT(&sc->isc_sess);
+     if(init_pdus(sc) != 0)
+         xdebug("pdu zone init failed!"); // XXX: should cause terminal failure ...
+
+     lockinit(&sc->lock, "iscsi", 0, LK_CANRECURSE);
+     lockinit(&sc->pdu_lock, "iscsi pdu pool", 0, LK_CANRECURSE);
+
+#if 0
+     // XXX: this will cause a panic if the
+     //      module is loaded too early
+     if(ic_init(sc) != 0)
+         return;
+#else
+     sc->cam_sim = NULL;
+#endif
+
+#ifdef DO_EVENTHANDLER
+     if((sc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown,
+                                       sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL)
+         xdebug("shutdown event registration failed\n");
+#endif
+     /*
+      | sysctl stuff
+      */
+     sysctl_ctx_init(&sc->clist);
+     sc->oid = SYSCTL_ADD_NODE(&sc->clist,
+                              SYSCTL_STATIC_CHILDREN(_net),
+                              OID_AUTO,
+                              "iscsi",
+                              CTLFLAG_RD,
+                              0,
+                              "iSCSI Subsystem");
+
+     SYSCTL_ADD_STRING(&sc->clist,
+                      SYSCTL_CHILDREN(sc->oid),
+                      OID_AUTO,
+                      "driver_version",
+                      CTLFLAG_RD,
+                      iscsi_driver_version,
+                      0,
+                      "iscsi driver version");
+
+     SYSCTL_ADD_STRING(&sc->clist,
+                      SYSCTL_CHILDREN(sc->oid),
+                      OID_AUTO,
+                      "isid",
+                      CTLFLAG_RW,
+                      isid,
+                      6+1,
+                      "initiator part of the Session Identifier");
+
+     SYSCTL_ADD_INT(&sc->clist,
+                   SYSCTL_CHILDREN(sc->oid),
+                   OID_AUTO,
+                   "sessions",
+                   CTLFLAG_RD,
+                   &sc->nsess,
+                   sizeof(sc->nsess),
+                   "number of active session");
+
+     kprintf("iscsi: version %s\n", iscsi_driver_version);
+}
+
+/*
+ | Notes:
+ |     unload SHOULD fail if there is activity
+ |     activity: there is/are active session/s
+ */
+static void
+iscsi_stop(void)
+{
+     struct isc_softc  *sc = &isc;
+     isc_session_t     *sp, *sp_tmp;
+
+     debug_called(8);
+
+     /*
+      | go through all the sessions
+      | Note: close should have done this ...
+      */
+     TAILQ_FOREACH_MUTABLE(sp, &sc->isc_sess, sp_link, sp_tmp) {
+         //XXX: check for activity ...
+         ism_stop(sp);
+     }
+     if(sc->cam_sim != NULL)
+         ic_destroy(sc);
+
+     lockuninit(&sc->lock);
+     lockuninit(&sc->pdu_lock);
+     free_pdus(sc);
+
+     if(sc->dev) {
+        release_dev(sc->dev);
+         destroy_dev(sc->dev);
+         //dev_ops_remove(&sc->dev, -1, 0);
+     }
+
+     if(sysctl_ctx_free(&sc->clist))
+         xdebug("sysctl_ctx_free failed");
+
+     iscsi_shutdown(sc); // XXX: check EVENTHANDLER_ ...
+}
+
+static int
+iscsi_modevent(module_t mod, int what, void *arg)
+{
+     debug_called(8);
+
+     switch(what) {
+     case MOD_LOAD:
+         iscsi_start();
+         break;
+
+     case MOD_SHUTDOWN:
+         break;
+
+     case MOD_UNLOAD:
+         iscsi_stop();
+         break;
+
+     default:
+         break;
+     }
+     return 0;
+}
+
+moduledata_t iscsi_mod = {
+         "iscsi",
+         (modeventhand_t) iscsi_modevent,
+         0
+};
+
+#ifdef ISCSI_ROOT
+static void
+iscsi_rootconf(void)
+{
+#if 0
+       nfs_setup_diskless();
+       if (nfs_diskless_valid)
+               rootdevnames[0] = "nfs:";
+#endif
+       kprintf("** iscsi_rootconf **\n");
+}
+
+SYSINIT(cpu_rootconf1, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, iscsi_rootconf, NULL)
+#endif
+
+DECLARE_MODULE(iscsi, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+MODULE_DEPEND(iscsi, cam, 1, 1, 1);
diff --git a/sys/dev/disk/iscsi/initiator/iscsi.h b/sys/dev/disk/iscsi/initiator/iscsi.h
new file mode 100644 (file)
index 0000000..ee9e4fc
--- /dev/null
@@ -0,0 +1,509 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/iscsi/initiator/iscsi.h,v 1.2 2008/11/25 07:17:11 scottl Exp $
+ */
+/*
+ | $Id: iscsi.h,v 1.17 2006/12/01 09:10:17 danny Exp danny $
+ */
+#define        TRUE    1
+#define FALSE  0
+#ifndef _KERNEL
+typedef int boolean_t;
+#endif
+
+#include <sys/objcache.h>
+#include <bus/cam/cam.h>
+#include <sys/device.h>
+#include <sys/bootmaj.h>
+
+#define ISCSIDEV       "iscsi"
+
+#define ISCSI_CDEV_MAJOR       115
+
+#define ISCSI_MAX_TARGETS      4 //64
+
+#define ISCSI_MAX_LUNS         4
+
+/*
+ | iSCSI commands
+ */
+
+/*
+ | Initiator Opcodes:
+ */
+#define ISCSI_NOP_OUT          0x00
+#define ISCSI_SCSI_CMD         0x01
+#define ISCSI_TASK_CMD         0x02
+#define ISCSI_LOGIN_CMD                0x03
+#define ISCSI_TEXT_CMD         0x04
+#define ISCSI_WRITE_DATA       0x05
+#define ISCSI_LOGOUT_CMD       0x06
+#define ISCSI_SNACK            0x10
+/*
+ | Target Opcodes:
+ */
+#define ISCSI_NOP_IN           0x20
+#define ISCSI_SCSI_RSP         0x21
+#define ISCSI_TASK_RSP         0x22
+#define ISCSI_LOGIN_RSP                0x23
+#define ISCSI_TEXT_RSP         0x24
+#define ISCSI_READ_DATA                0x25
+#define ISCSI_LOGOUT_RSP       0x26
+#define ISCSI_R2T              0x31
+#define ISCSI_ASYNC            0x32
+#define ISCSI_REJECT           0x3f
+/*
+ | PDU stuff
+ */
+/*
+ | BHS Basic Header Segment
+ */
+typedef struct bhs {
+     // the order is network byte order!
+     u_char    opcode:6;
+     u_char    I:1;
+     u_char    _:1;
+     u_char    __:7;
+     u_char    F:1;                    // Final bit
+     u_char    ___[2];
+
+     u_int     AHSLength:8;            // in 4byte words
+     u_int     DSLength:24;            // in bytes
+
+     u_int     LUN[2];                 // or Opcode-specific fields
+     u_int     itt;
+     u_int     OpcodeSpecificFields[7];
+#define        CmdSN           OpcodeSpecificFields[1]
+#define        ExpStSN         OpcodeSpecificFields[2]
+#define MaxCmdSN       OpcodeSpecificFields[3]
+} bhs_t;
+
+typedef struct ahs {
+     u_int     len:16;
+     u_int     type:8;
+     u_int     spec:8;
+     char      data[0];
+} ahs_t;
+
+typedef struct {
+     // Sequence Numbers
+     // (computers were invented to count, right?)
+     int       cmd;
+     int       expcmd;
+     int       maxcmd;
+} req_sn_t;
+
+typedef struct {
+     // Sequence Numbers
+     // (computers were invented to count, right?)
+     int       stat;
+     int       expcmd;
+     int       maxcmd;
+} rsp_sn_t;
+
+typedef struct scsi_req {
+     u_char    opcode:6; // 0x01
+     u_char    I:1;
+     u_char    _:1;
+
+     u_char    attr:3;
+     u_char    _0:2;
+     u_char    W:1;
+     u_char    R:1;
+     u_char    F:1;
+#define                iSCSI_TASK_UNTAGGED     0
+#define                iSCSI_TASK_SIMPLE       1
+#define                iSCSI_TASK_ORDER        2
+#define                iSCSI_TASK_HOFQ         3
+#define                iSCSI_TASK_ACA          4
+     char      _1[2];
+     int       len;
+     int       lun[2];
+     int       itt;
+     int       edtlen;         // expectect data transfere length
+     int       cmdSN;
+     int       extStatSN;
+     int       cdb[4];
+} scsi_req_t;
+
+typedef struct scsi_rsp {
+     char      opcode; // 0x21
+     u_char    flag;
+     u_char    response;
+     u_char    status;
+
+     int       len;
+     int       _[2];
+     int       itt;
+     int       stag;
+     rsp_sn_t  sn;
+     int       expdatasn;
+     int       bdrcnt; // bidirectional residual count
+     int       rcnt;   // residual count
+} scsi_rsp_t;
+
+typedef struct nop_out {
+     // the order is network byte order!
+     u_char    opcode:6;
+     u_char    I:1;
+     u_char    _:1;
+     u_char    __:7;
+     u_char    F:1;                    // Final bit
+     u_char    ___[2];
+
+     u_int     len;
+     u_int     lun[2];
+     u_int     itt;
+     u_int     ttt;
+     req_sn_t  sn;
+     u_int     mbz[3];
+} nop_out_t;
+
+typedef struct nop_in {
+     // the order is network byte order!
+     u_char    opcode:6;
+     u_char    I:1;
+     u_char    _:1;
+     u_char    __:7;
+     u_char    F:1;                    // Final bit
+     u_char    ___[2];
+
+     u_int     len;
+     u_int     lun[2];
+     u_int     itt;
+     u_int     ttt;
+     rsp_sn_t  sn;
+     u_int     ____[2];
+
+} nop_in_t;
+
+typedef struct r2t {
+     u_char    opcode:6;
+     u_char    I:1;
+     u_char    _:1;
+     u_char    __:7;
+     u_char    F:1;                    // Final bit
+     u_char    ___[2];
+
+     u_int     len;
+     u_int     lun[2];
+     u_int     itt;
+     u_int     ttt;
+     rsp_sn_t  sn;
+     u_int     r2tSN;
+     u_int     bo;
+     u_int     ddtl;
+} r2t_t;
+
+typedef struct data_out {
+     u_char    opcode:6;
+     u_char    I:1;
+     u_char    _:1;
+     u_char    __:7;
+     u_char    F:1;                    // Final bit
+     u_char    ___[2];
+
+     u_int     len;
+     u_int     lun[2];
+     u_int     itt;
+     u_int     ttt;
+     rsp_sn_t  sn;
+     u_int     dsn;    // data seq. number
+     u_int     bo;
+     u_int     ____;
+} data_out_t;
+
+typedef struct data_in {
+     u_char    opcode:6;
+     u_char    I:1;
+     u_char    _:1;
+
+     u_char    S:1;
+     u_char    U:1;
+     u_char    O:1;
+     u_char    __:3;
+     u_char    A:1;
+     u_char    F:1;                    // Final bit
+     u_char    ___[1];
+     u_char    status;
+
+     u_int     len;
+     u_int     lun[2];
+     u_int     itt;
+     u_int     ttt;
+     rsp_sn_t  sn;
+     u_int     dataSN;
+     u_int     bo;
+     u_int     ____;
+} data_in_t;
+
+typedef struct reject {
+     u_char    opcode:6;
+     u_char    _:2;
+     u_char    F:1;
+     u_char    __:7;
+     u_char    reason;
+     u_char    ___;
+
+     u_int     len;
+     u_int     ____[2];
+     u_int     tt[2];  // must be -1
+     rsp_sn_t  sn;
+     u_int     dataSN; // or R2TSN or reserved
+     u_int     _____[2];
+} reject_t;
+
+typedef struct async {
+     u_char    opcode:6;
+     u_char    _:2;
+     u_char    F:1;
+     u_char    __:7;
+     u_char    ___[2];
+
+     u_int     len;
+     u_int     lun[2];
+     u_int     itt;    // must be -1
+     u_int     ____;
+     rsp_sn_t  sn;
+
+     u_char    asyncEvent;
+     u_char    asyncVCode;
+     u_char    param1[2];
+     u_char    param2[2];
+     u_char    param3[2];
+
+     u_int     _____;
+
+} async_t;
+
+typedef struct login_req {
+     char      cmd;    // 0x03
+
+     u_char    NSG:2;
+     u_char    CSG:2;
+     u_char    _:2;
+     u_char    C:1;
+     u_char    T:1;
+
+     char      v_max;
+     char      v_min;
+
+     int       len;    // remapped via standard bhs
+     char      isid[6];
+     short     tsih;
+     int       itt;    // Initiator Task Tag;
+
+     int       CID:16;
+     int       rsv:16;
+
+     int       cmdSN;
+     int       expStatSN;
+     int       unused[4];
+} login_req_t;
+
+typedef struct login_rsp {
+     char      cmd;    // 0x23
+     u_char    NSG:2;
+     u_char    CSG:2;
+     u_char    _1:2;
+     u_char    C:1;
+     u_char    T:1;
+
+     char      v_max;
+     char      v_act;
+
+     int       len;    // remapped via standard bhs
+     char      isid[6];
+     short     tsih;
+     int       itt;    // Initiator Task Tag;
+     int       _2;
+     rsp_sn_t  sn;
+     int       status:16;
+     int       _3:16;
+     int       _4[2];
+} login_rsp_t;
+
+typedef struct text_req {
+     char      cmd;    // 0x04
+
+     u_char    _1:6;
+     u_char    C:1;    // Continuation
+     u_char    F:1;    // Final
+     char      _2[2];
+
+     int       len;
+     int       itt;            // Initiator Task Tag
+     int       LUN[2];
+     int       ttt;            // Target Transfer Tag
+     int       cmdSN;
+     int       expStatSN;
+     int       unused[4];
+} text_req_t;
+
+typedef struct logout_req {
+     char      cmd;    // 0x06
+     char      reason; // 0 - close session
+                       // 1 - close connection
+                       // 2 - remove the connection for recovery
+     char      _2[2];
+
+     int       len;
+     int       _r[2];
+     int       itt;    // Initiator Task Tag;
+
+     u_int     CID:16;
+     u_int     rsv:16;
+
+     int       cmdSN;
+     int       expStatSN;
+     int       unused[4];
+} logout_req_t;
+
+typedef struct logout_rsp {
+     char      cmd;    // 0x26
+     char      cbits;
+     char      _1[2];
+     int       len;
+     int       _2[2];
+     int       itt;
+     int       _3;
+     rsp_sn_t  sn;
+     short     time2wait;
+     short     time2retain;
+     int       _4;
+} logout_rsp_t;
+
+union ipdu_u {
+     bhs_t     bhs;
+     scsi_req_t        scsi_req;
+     scsi_rsp_t        scsi_rsp;
+     nop_out_t nop_out;
+     nop_in_t  nop_in;
+     r2t_t     r2t;
+     data_out_t        data_out;
+     data_in_t data_in;
+     reject_t  reject;
+     async_t   async;
+};
+
+/*
+ | Sequence Numbers
+ */
+typedef struct {
+     u_int     itt;
+     u_int      cmd;
+     u_int      expCmd;
+     u_int      maxCmd;
+     u_int      stat;
+     u_int      expStat;
+     u_int      data;
+} sn_t;
+
+/*
+ | in-core version of a Protocol Data Unit
+ */
+typedef struct {
+     union ipdu_u      ipdu;
+
+     ahs_t             *ahs;
+     u_int             ahs_len;
+     u_int             ahs_size;       // the allocated size
+     u_int             hdr_dig;        // header digest
+
+     u_char            *ds;
+     u_int             ds_len;
+     u_int             ds_size;        // the allocated size
+     u_int             ds_dig;         // data digest
+} pdu_t;
+
+typedef struct opvals {
+     int       port;
+     int       tags;
+     int       maxluns;
+     int       sockbufsize;
+
+     int       maxConnections;
+     int       maxRecvDataSegmentLength;
+     int       maxXmitDataSegmentLength; // pseudo ...
+     int       maxBurstLength;
+     int       firstBurstLength;
+     int       defaultTime2Wait;
+     int       defaultTime2Retain;
+     int       maxOutstandingR2T;
+     int       errorRecoveryLevel;
+     int       targetPortalGroupTag;
+
+     boolean_t initialR2T;
+     boolean_t immediateData;
+     boolean_t dataPDUInOrder;
+     boolean_t dataSequenceInOrder;
+     char      *headerDigest;
+     char      *dataDigest;
+     char      *sessionType;
+     char      *sendTargets;
+     char      *targetAddress;
+     char      *targetAlias;
+     char      *targetName;
+     char      *initiatorName;
+     char      *initiatorAlias;
+     char      *authMethod;
+     char      *chapSecret;
+     char      *chapIName;
+     char      *chapDigest;
+     char      *tgtChapName;
+     char      *tgtChapSecret;
+     int       tgtChallengeLen;
+     u_char    tgtChapID;
+     char      *tgtChapDigest;
+     char      *iqn;
+} isc_opt_t;
+
+/*
+ | ioctl
+ */
+#define ISCSISETSES    _IOR('i', 1, int)
+#define ISCSISETSOC    _IOW('i', 2, int)
+#define ISCSISETOPT    _IOW('i', 5, isc_opt_t)
+#define ISCSIGETOPT    _IOR('i', 6, isc_opt_t)
+
+#define ISCSISEND      _IOW('i', 10, pdu_t)
+#define ISCSIRECV      _IOWR('i', 11, pdu_t)
+
+#define ISCSIPING      _IO('i', 20)
+#define ISCSISIGNAL    _IOW('i', 21, int *)
+
+#define ISCSISTART     _IO('i', 30)
+#define ISCSIRESTART   _IO('i', 31)
+#define ISCSISTOP      _IO('i', 32)
+
+typedef struct iscsi_cam {
+     path_id_t         path_id;
+     target_id_t       target_id;
+     int               target_nluns;
+     lun_id_t          target_lun[ISCSI_MAX_LUNS];
+} iscsi_cam_t;
+
+#define ISCSIGETCAM    _IOR('i', 33, iscsi_cam_t)
diff --git a/sys/dev/disk/iscsi/initiator/iscsi_subr.c b/sys/dev/disk/iscsi/initiator/iscsi_subr.c
new file mode 100644 (file)
index 0000000..fa915f7
--- /dev/null
@@ -0,0 +1,613 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/iscsi/initiator/iscsi_subr.c,v 1.2 2008/11/25 07:17:11 scottl Exp $
+ */
+/*
+ | $Id: iscsi_subr.c,v 1.17 2006/11/26 14:50:43 danny Exp danny $
+ */
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/uio.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/mutex.h>
+
+#include <bus/cam/cam.h>
+#include <bus/cam/cam_ccb.h>
+#include <bus/cam/cam_sim.h>
+#include <bus/cam/cam_xpt_sim.h>
+#include <bus/cam/cam_periph.h>
+#include <bus/cam/scsi/scsi_message.h>
+#include <sys/eventhandler.h>
+
+#include <sys/mutex2.h>
+
+#include <dev/disk/iscsi/initiator/iscsi.h>
+#include <dev/disk/iscsi/initiator/iscsivar.h>
+
+/*
+ | Interface to the SCSI layer
+ */
+void
+iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
+{
+     union ccb                 *ccb = opq->ccb;
+     struct ccb_scsiio *csio = &ccb->csio;
+     pdu_t             *opp = &opq->pdu;
+     bhs_t             *bhp = &opp->ipdu.bhs;
+     r2t_t             *r2t = &pq->pdu.ipdu.r2t;
+     pduq_t    *wpq;
+     int       error;
+
+     debug_called(8);
+     sdebug(4, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt),
+          ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W);
+
+     switch(bhp->opcode) {
+     case ISCSI_SCSI_CMD:
+         if(opp->ipdu.scsi_req.W) {
+              data_out_t       *cmd;
+              u_int            ddtl = ntohl(r2t->ddtl);
+              u_int            edtl = ntohl(opp->ipdu.scsi_req.edtlen);
+              u_int            bleft, bs, dsn, bo;
+              caddr_t          bp = csio->data_ptr;
+
+              bo = ntohl(r2t->bo);
+              bleft = ddtl;
+
+              if(sp->opt.maxXmitDataSegmentLength > 0) // danny's RFC
+                   bs = MIN(sp->opt.maxXmitDataSegmentLength, ddtl);
+              else
+                   bs = ddtl;
+              dsn = 0;
+              sdebug(4, "edtl=%x ddtl=%x bo=%x dsn=%x bs=%x maxX=%x",
+                     edtl, ddtl, bo, dsn, bs, sp->opt.maxXmitDataSegmentLength);
+              while(bleft > 0) {
+                   wpq = pdu_alloc(sp->isc, M_NOWAIT); // testing ...
+                   if(wpq == NULL) {
+                        sdebug(3, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt),
+                               ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W);
+                        sdebug(1, "npdu_max=%d npdu_alloc=%d", sp->isc->npdu_max, sp->isc->npdu_alloc);
+
+                        while((wpq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) {
+                             sdebug(2, "waiting...");
+#if __FreeBSD_version >= 700000
+                             pause("isc_r2t", 5*hz);
+#else
+                             tsleep(sp->isc, 0, "isc_r2t", 5*hz);
+#endif
+                        }
+                   }
+                   cmd = &wpq->pdu.ipdu.data_out;
+                   cmd->opcode = ISCSI_WRITE_DATA;
+                   cmd->lun[0] = r2t->lun[0];
+                   cmd->lun[1] = r2t->lun[1];
+                   cmd->ttt    = r2t->ttt;
+                   cmd->itt    = r2t->itt;
+
+                   cmd->dsn    = htonl(dsn);
+                   cmd->bo     = htonl(bo);
+
+                   cmd->F      = (bs < bleft)? 0: 1; // is this the last one?
+                   bs = MIN(bs, bleft);
+
+                   wpq->pdu.ds_len     = bs;
+                   wpq->pdu.ds         = bp;
+
+                   error = isc_qout(sp, wpq);
+                   sdebug(6, "bs=%x bo=%x bp=%p dsn=%x error=%d", bs, bo, bp, dsn, error);
+                   if(error)
+                        break;
+                   bo += bs;
+                   bp += bs;
+                   bleft -= bs;
+                   dsn++;
+              }
+         }
+         break;
+
+     default:
+         // XXX: should not happen ...
+         xdebug("huh? opcode=0x%x", bhp->opcode);
+     }
+}
+
+static int
+getSenseData(u_int status, union ccb *ccb, pduq_t *pq)
+{
+     pdu_t             *pp = &pq->pdu;
+     struct            ccb_scsiio *scsi = (struct ccb_scsiio *)ccb;
+     struct            scsi_sense_data *sense = &scsi->sense_data;
+     struct mbuf       *m = pq->mp;
+     scsi_rsp_t                *cmd = &pp->ipdu.scsi_rsp;
+     caddr_t           bp;
+     int               sense_len, mustfree = 0;
+
+     bp = mtod(pq->mp, caddr_t);
+     if((sense_len = scsi_2btoul(bp)) == 0)
+         return 0;
+     debug(4, "sense_len=%d", sense_len);
+     /*
+      | according to the specs, the sense data cannot
+      | be larger than 252 ...
+      */
+     if(sense_len > m->m_len) {
+         bp = kmalloc(sense_len, M_ISCSI, M_WAITOK);
+         debug(3, "calling i_mbufcopy(len=%d)", sense_len);
+         i_mbufcopy(pq->mp, bp, sense_len);
+         mustfree++;
+     }
+     scsi->scsi_status = status;
+
+     bcopy(bp+2, sense, min(sense_len, scsi->sense_len));
+     scsi->sense_resid = 0;
+     if(cmd->flag & (BIT(1)|BIT(2)))
+         scsi->sense_resid = ntohl(pp->ipdu.scsi_rsp.rcnt);
+     debug(3, "sense_len=%d rcnt=%d sense_resid=%d dsl=%d error_code=%x flags=%x",
+          sense_len,
+          ntohl(pp->ipdu.scsi_rsp.rcnt), scsi->sense_resid,
+          pp->ds_len, sense->error_code, sense->flags);
+
+     if(mustfree)
+         kfree(bp, M_ISCSI);
+
+     return 1;
+}
+
+/*
+ | Some information is from SAM draft.
+ */
+static void
+_scsi_done(struct isc_softc *isp, u_int response, u_int status, union ccb *ccb, pduq_t *pq)
+{
+     struct ccb_hdr    *ccb_h = &ccb->ccb_h;
+
+     debug_called(8);
+
+     if(status || response) {
+         debug(3, "response=%x status=%x ccb=%p pq=%p", response, status, ccb, pq);
+         if(pq != NULL)
+              debug(3, "mp=%p buf=%p len=%d", pq->mp, pq->buf, pq->len);
+     }
+     ccb_h->status = 0;
+     switch(response) {
+     case 0: // Command Completed at Target
+         switch(status) {
+         case 0:       // Good, all is ok
+              ccb_h->status = CAM_REQ_CMP;
+              break;
+
+         case 0x02:    // Check Condition
+              if((pq != NULL) && (pq->mp != NULL) && getSenseData(status, ccb, pq))
+                   ccb_h->status |= CAM_AUTOSNS_VALID;
+
+         case 0x14:    // Intermediate-Condition Met
+         case 0x10:    // Intermediate
+         case 0x04:    // Condition Met
+              ccb_h->status |= CAM_SCSI_STATUS_ERROR;
+              break;
+
+         case 0x08:
+              ccb_h->status = CAM_BUSY;
+              break;
+
+         case 0x18: // Reservation Conflict
+         case 0x28: // Task Set Full
+              ccb_h->status = CAM_REQUEUE_REQ;
+              break;
+         default:
+              //case 0x22: // Command Terminated
+              //case 0x30: // ACA Active
+              //case 0x40: // Task Aborted
+              ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED;
+         }
+         break;
+
+     default:
+         if((response >= 0x80) && (response <= 0xFF)) {
+              // Vendor specific ...
+         }
+     case 1: // target failure
+         ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED;
+         break;
+     }
+     debug(5, "ccb_h->status=%x", ccb_h->status);
+
+     XPT_DONE(isp, ccb);
+}
+
+/*
+ | returns the lowest cmdseq that was not acked
+ */
+int
+iscsi_requeue(isc_session_t *sp)
+{
+     pduq_t    *pq;
+     u_int     i, n, last;
+
+     debug_called(8);
+     last = -1;
+     i = 0;
+     sp->flags |= ISC_HOLD;
+     while((pq = i_dqueue_hld(sp)) != NULL) {
+         i++;
+         _scsi_done(sp->isc, 0, 0x28, pq->ccb, NULL);
+         n = ntohl(pq->pdu.ipdu.bhs.CmdSN);
+         if(last > n)
+              last = n;
+         sdebug(2, "last=%x n=%x", last, n);
+         pdu_free(sp->isc, pq);
+     }
+     sp->flags &= ~ISC_HOLD;
+     return i? last: sp->sn.cmd;
+}
+
+int
+i_pdu_flush(isc_session_t *sp)
+{
+     int       n = 0;
+     pduq_t    *pq;
+
+     debug_called(8);
+     while((pq = i_dqueue_rsp(sp)) != NULL) {
+         pdu_free(sp->isc, pq);
+         n++;
+     }
+     while((pq = i_dqueue_rsv(sp)) != NULL) {
+         pdu_free(sp->isc, pq);
+         n++;
+     }
+     while((pq = i_dqueue_snd(sp, -1)) != NULL) {
+         pdu_free(sp->isc, pq);
+         n++;
+     }
+     while((pq = i_dqueue_hld(sp)) != NULL) {
+         pdu_free(sp->isc, pq);
+         n++;
+     }
+     while((pq = i_dqueue_wsnd(sp)) != NULL) {
+         pdu_free(sp->isc, pq);
+         n++;
+     }
+     if(n != 0)
+         xdebug("%d pdus recovered, should have been ZERO!", n);
+     return n;
+}
+/*
+ | called from ism_destroy.
+ */
+void
+iscsi_cleanup(isc_session_t *sp)
+{
+     pduq_t *pq, *pqtmp;
+
+     debug_called(8);
+
+     TAILQ_FOREACH_MUTABLE(pq, &sp->hld, pq_link, pqtmp) {
+         sdebug(3, "hld pq=%p", pq);
+         if(pq->ccb)
+              _scsi_done(sp->isc, 1, 0x40, pq->ccb, NULL);
+         TAILQ_REMOVE(&sp->hld, pq, pq_link);
+         pdu_free(sp->isc, pq);
+     }
+     while((pq = i_dqueue_snd(sp, BIT(0)|BIT(1)|BIT(2))) != NULL) {
+         sdebug(3, "pq=%p", pq);
+         if(pq->ccb)
+              _scsi_done(sp->isc, 1, 0x40, pq->ccb, NULL);
+         pdu_free(sp->isc, pq);
+     }
+
+     wakeup(&sp->rsp);
+}
+
+void
+iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
+{
+     pdu_t             *pp = &pq->pdu;
+     scsi_rsp_t                *cmd = &pp->ipdu.scsi_rsp;
+
+     debug_called(8);
+
+     _scsi_done(sp->isc, cmd->response, cmd->status, opq->ccb, pq);
+
+     pdu_free(sp->isc, opq);
+}
+
+// see RFC 3720, 10.9.1 page 146
+/*
+ | NOTE:
+ | the call to isc_stop_receiver is a kludge,
+ | instead, it should be handled by the userland controller,
+ | but that means that there should be a better way, other than
+ | sending a signal. Somehow, this packet should be supplied to
+ | the userland via read.
+ */
+void
+iscsi_async(isc_session_t *sp, pduq_t *pq)
+{
+     pdu_t             *pp = &pq->pdu;
+     async_t           *cmd = &pp->ipdu.async;
+
+     debug_called(8);
+
+     sdebug(3, "asyncevent=0x%x asyncVCode=0x%0x", cmd->asyncEvent, cmd->asyncVCode);
+     switch(cmd->asyncEvent) {
+     case 0: // check status ...
+         break;
+
+     case 1: // target request logout
+         isc_stop_receiver(sp);        // XXX: temporary solution
+         break;
+
+     case 2: // target indicates it wants to drop connection
+         isc_stop_receiver(sp);        // XXX: temporary solution
+         break;
+
+     case 3: // target indicates it will drop all connections.
+         isc_stop_receiver(sp);        // XXX: temporary solution
+         break;
+
+     case 4: // target request parameter negotiation
+         break;
+
+     default:
+         break;
+     }
+}
+
+void
+iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
+{
+     union ccb                 *ccb = opq->ccb;
+     //reject_t                *reject = &pq->pdu.ipdu.reject;
+
+     debug_called(8);
+     //XXX: check RFC 10.17.1 (page 176)
+     ccb->ccb_h.status = CAM_REQ_ABORTED;
+     XPT_DONE(sp->isc, ccb);
+
+     pdu_free(sp->isc, opq);
+}
+
+/*
+ | deal with lun
+ */
+static int
+dwl(isc_session_t *sp, int lun, u_char *lp)
+{
+     int       i;
+
+     debug_called(8);
+
+     /*
+      | mapping LUN to iSCSI LUN
+      | check the SAM-2 specs
+      | hint: maxLUNS is a small number, cam's LUN is 32bits
+      | iSCSI is 64bits, scsi is ?
+      */
+     // XXX: check if this will pass the endian test
+     if(lun < 256) {
+         lp[0] = 0;
+         lp[1] = lun;
+     } else
+     if(lun < 16384) {
+         lp[0] = (1 << 5) | ((lun >> 8) & 0x3f);
+         lp[1] = lun & 0xff;
+     }
+     else {
+         xdebug("lun %d: is unsupported!", lun);
+         return -1;
+     }
+
+     for(i = 0; i < sp->target_nluns; i++)
+         if(sp->target_lun[i] == lun)
+              return 0;
+     if(sp->target_nluns < ISCSI_MAX_LUNS)
+         sp->target_lun[sp->target_nluns++] = lun;
+
+     sdebug(3, "nluns=%d lun=%d", sp->target_nluns, lun);
+
+     return 0;
+}
+
+/*
+ | encapsulate the scsi command and
+ */
+int
+scsi_encap(struct cam_sim *sim, union ccb *ccb)
+{
+     struct isc_softc  *isp = (struct isc_softc *)cam_sim_softc(sim);
+     isc_session_t     *sp;
+     struct ccb_scsiio *csio = &ccb->csio;
+     struct ccb_hdr    *ccb_h = &ccb->ccb_h;
+     pduq_t            *pq;
+     scsi_req_t                *cmd;
+
+     debug_called(8);
+
+     debug(4, "ccb->sp=%p", ccb_h->spriv_ptr0);
+     sp = ccb_h->spriv_ptr0;
+
+     if((pq = pdu_alloc(isp, M_NOWAIT)) == NULL) {
+         debug(2, "ccb->sp=%p", ccb_h->spriv_ptr0);
+         sdebug(1, "pdu_alloc failed sc->npdu_max=%d npdu_alloc=%d",
+                sp->isc->npdu_max, sp->isc->npdu_alloc);
+         while((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) {
+              sdebug(3, "waiting...");
+#if __FreeBSD_version >= 700000
+              pause("isc_encap", 5*hz);
+#else
+              tsleep(sp->isc, 0, "isc_encap", 5*hz);
+#endif
+         }
+#if 0
+         sdebug(3, "freezing");
+         ccb->ccb_h.status = CAM_REQUEUE_REQ;
+         ic_freeze(sp);
+         return 0;
+#endif
+     }
+
+#if 0
+     if((sp->flags & ISC_FFPHASE) == 0) {
+         ccb->ccb_h.status = CAM_DEV_NOT_THERE; // CAM_NO_NEXUS;
+         sdebug(3, "no active session with target %d", ccb_h->target_id);
+         goto bad;
+     }
+#endif
+     cmd = &pq->pdu.ipdu.scsi_req;
+     cmd->opcode = ISCSI_SCSI_CMD;
+     cmd->F = 1;
+     /*
+      | map tag option, default is UNTAGGED
+      */
+     switch(csio->tag_action) {
+     case MSG_SIMPLE_Q_TAG:    cmd->attr = iSCSI_TASK_SIMPLE;  break;
+     case MSG_HEAD_OF_Q_TAG:   cmd->attr = iSCSI_TASK_ORDER;   break;
+     case MSG_ORDERED_Q_TAG:   cmd->attr = iSCSI_TASK_HOFQ;    break;
+     case MSG_ACA_TASK:                cmd->attr = iSCSI_TASK_ACA;     break;
+     }
+
+     dwl(sp, ccb_h->target_lun, (u_char *)&cmd->lun);
+
+     if((ccb_h->flags & CAM_CDB_POINTER) != 0) {
+         if((ccb_h->flags & CAM_CDB_PHYS) == 0) {
+              if(csio->cdb_len > 16) {
+                   sdebug(3, "oversize cdb %d > 16", csio->cdb_len);
+                   goto invalid;
+              }
+         }
+         else {
+              sdebug(3, "not phys");
+              goto invalid;
+         }
+     }
+
+     if(csio->cdb_len > sizeof(cmd->cdb))
+         xdebug("guevalt! %d > %ld", csio->cdb_len, (long)sizeof(cmd->cdb));
+
+     memcpy(cmd->cdb,
+           ccb_h->flags & CAM_CDB_POINTER? csio->cdb_io.cdb_ptr: csio->cdb_io.cdb_bytes,
+           csio->cdb_len);
+
+     cmd->W = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT;
+     cmd->R = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN;
+     cmd->edtlen = htonl(csio->dxfer_len);
+
+     pq->ccb = ccb;
+     /*
+      | place it in the out queue
+      */
+     if(isc_qout(sp, pq) == 0)
+         return 1;
+ invalid:
+     ccb->ccb_h.status = CAM_REQ_INVALID;
+     pdu_free(isp, pq);
+     return 0;
+}
+
+int
+scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
+{
+     union ccb                 *ccb = opq->ccb;
+     struct ccb_scsiio *csio = &ccb->csio;
+     pdu_t             *opp = &opq->pdu;
+     bhs_t             *bhp = &opp->ipdu.bhs;
+
+     debug_called(8);
+     sdebug(6, "pq=%p opq=%p bhp->opcode=0x%x len=%d",
+           pq, opq, bhp->opcode, pq->pdu.ds_len);
+     if(ccb == NULL) {
+         sdebug(1, "itt=0x%x pq=%p opq=%p bhp->opcode=0x%x len=%d",
+                ntohl(pq->pdu.ipdu.bhs.itt),
+                pq, opq, bhp->opcode, pq->pdu.ds_len);
+         xdebug("%d] ccb == NULL!", sp->sid);
+         return 0;
+     }
+     if(pq->pdu.ds_len != 0) {
+         switch(bhp->opcode) {
+         case ISCSI_SCSI_CMD: {
+              scsi_req_t *cmd = &opp->ipdu.scsi_req;
+              sdebug(5, "itt=0x%x opcode=%x R=%d",
+                     ntohl(pq->pdu.ipdu.bhs.itt),
+                     pq->pdu.ipdu.bhs.opcode, cmd->R);
+
+              switch(pq->pdu.ipdu.bhs.opcode) {
+              case ISCSI_READ_DATA: // SCSI Data in
+              {
+                   caddr_t     bp = NULL; // = mtod(pq->mp, caddr_t);
+                   data_in_t   *rcmd = &pq->pdu.ipdu.data_in;
+
+                   if(cmd->R) {
+                        sdebug(5, "copy to=%p from=%p l1=%d l2=%d mp@%p",
+                               csio->data_ptr, bp? mtod(pq->mp, caddr_t): 0,
+                               ntohl(cmd->edtlen), pq->pdu.ds_len, pq->mp);
+                        if(ntohl(cmd->edtlen) >= pq->pdu.ds_len) {
+                             int               offset, len = pq->pdu.ds_len;
+
+                             if(pq->mp != NULL) {
+                             caddr_t           dp;
+
+                             offset = ntohl(rcmd->bo);
+                             dp = csio->data_ptr + offset;
+                             i_mbufcopy(pq->mp, dp, len);
+                        }
+                        }
+                        else {
+                             xdebug("edtlen=%d < ds_len=%d",
+                                    ntohl(cmd->edtlen), pq->pdu.ds_len);
+                        }
+                   }
+                   if(rcmd->S) {
+                        /*
+                         | contains also the SCSI Status
+                         */
+                        _scsi_done(sp->isc, 0, rcmd->status, opq->ccb, NULL);
+                        return 0;
+                   } else
+                        return 1;
+              }
+              break;
+              }
+         }
+         default:
+              sdebug(3, "opcode=%02x", bhp->opcode);
+              break;
+         }
+     }
+     /*
+      | XXX: error ...
+      */
+     return 1;
+}
diff --git a/sys/dev/disk/iscsi/initiator/iscsivar.h b/sys/dev/disk/iscsi/initiator/iscsivar.h
new file mode 100644 (file)
index 0000000..32b154b
--- /dev/null
@@ -0,0 +1,581 @@
+/*-
+ * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/iscsi/initiator/iscsivar.h,v 1.2 2008/11/25 07:17:11 scottl Exp $
+ */
+/*
+ | $Id: iscsivar.h,v 1.30 2007/04/22 10:12:11 danny Exp danny $
+ */
+#ifndef ISCSI_INITIATOR_DEBUG
+#define ISCSI_INITIATOR_DEBUG 1
+#endif
+
+#ifdef ISCSI_INITIATOR_DEBUG
+extern int iscsi_debug;
+#define debug(level, fmt, args...)     do {if(level <= iscsi_debug)\
+       kprintf("%s: " fmt "\n", __func__ , ##args);} while(0)
+#define sdebug(level, fmt, args...)    do {if(level <= iscsi_debug)\
+       kprintf("%d] %s: " fmt "\n", sp->sid, __func__ , ##args);} while(0)
+#define debug_called(level)            do {if(level <= iscsi_debug)\
+       kprintf("%s: called\n",  __func__);} while(0)
+#else
+#define debug(level, fmt, args...)
+#define debug_called(level)
+#define sdebug(level, fmt, args...)
+#endif /* ISCSI_INITIATOR_DEBUG */
+
+#define xdebug(fmt, args...)   kprintf(">>> %s: " fmt "\n", __func__ , ##args)
+
+#define PROC_LOCK(p)
+#define PROC_UNLOCK(p)
+
+#define MAX_SESSIONS           256
+
+typedef uint32_t digest_t(const void *, int len, uint32_t ocrc);
+
+typedef struct objcache        *objcache_t;
+
+MALLOC_DECLARE(M_ISCSI);
+MALLOC_DECLARE(M_PDU);
+
+#ifndef BIT
+#define BIT(n) (1 <<(n))
+#endif
+
+#define ISC_SM_RUN     BIT(0)
+#define ISC_SM_RUNNING BIT(1)
+
+#define ISC_LINK_UP    BIT(2)
+#define ISC_CON_RUN    BIT(3)
+#define ISC_CON_RUNNING        BIT(4)
+#define ISC_KILL       BIT(5)
+#define ISC_OQNOTEMPTY BIT(6)
+#define ISC_OWAITING   BIT(7)
+#define ISC_FFPHASE    BIT(8)
+#define ISC_FFPWAIT    BIT(9)
+
+#define ISC_MEMWAIT    BIT(10)
+#define ISC_SIGNALED   BIT(11)
+#define ISC_FROZEN     BIT(12)
+#define ISC_STALLED    BIT(13)
+
+#define ISC_HOLD       BIT(14)
+#define ISC_HOLDED     BIT(15)
+
+#define ISC_SHUTDOWN   BIT(31)
+
+/*
+ | some stats
+ */
+struct i_stats {
+     int       npdu;   // number of pdus malloc'ed.
+     int       nrecv;  // unprocessed received pdus
+     int       nsent;  // sent pdus
+
+     int       nrsp, max_rsp;
+     int       nrsv, max_rsv;
+     int       ncsnd, max_csnd;
+     int       nisnd, max_isnd;
+     int       nwsnd, max_wsnd;
+     int       nhld, max_hld;
+
+     struct timeval t_sent;
+     struct timeval t_recv;
+};
+
+/*
+ | one per 'session'
+ */
+
+typedef TAILQ_HEAD(, pduq) queue_t;
+
+typedef struct isc_session {
+     TAILQ_ENTRY(isc_session)  sp_link;
+     int               flags;
+     struct cdev       *dev;
+     struct socket     *soc;
+     struct file       *fp;
+     struct thread     *td;
+
+     struct proc       *proc; // the userland process
+     int               signal;
+
+     struct thread     *soc_thr;
+
+     struct thread     *stp;   // the sm thread
+
+     struct isc_softc  *isc;
+
+     digest_t          *hdrDigest;     // the digest alg. if any
+     digest_t          *dataDigest;    // the digest alg. if any
+
+     int               sid;            // Session ID
+     int               targetid;
+//     int             cid;            // Connection ID
+//     int             tsih;           // target session identifier handle
+     sn_t              sn;             // sequence number stuff;
+     int               cws;            // current window size
+
+     int               target_nluns; // this and target_lun are
+                                     // hopefully temporal till I
+                                     // figure out a better way.
+     lun_id_t          target_lun[ISCSI_MAX_LUNS];
+
+     struct mtx                rsp_mtx;
+     struct mtx                rsv_mtx;
+     struct mtx                snd_mtx;
+     struct mtx                hld_mtx;
+     struct mtx                io_mtx;
+     queue_t           rsp;
+     queue_t           rsv;
+     queue_t           csnd;
+     queue_t           isnd;
+     queue_t           wsnd;
+     queue_t           hld;
+
+     /*
+      | negotiable values
+      */
+     isc_opt_t         opt;
+
+     struct i_stats    stats;
+     struct cam_path   *cam_path;
+     bhs_t             bhs;
+     struct uio                uio;
+     struct iovec      iov;
+     /*
+      | sysctl stuff
+      */
+     struct sysctl_ctx_list    clist;
+     struct sysctl_oid *oid;
+     int       douio;  //XXX: turn on/off uio on read
+} isc_session_t;
+
+typedef struct pduq {
+     TAILQ_ENTRY(pduq) pq_link;
+
+     caddr_t           buf;
+     u_int             len;    // the total length of the pdu
+     pdu_t             pdu;
+     union ccb         *ccb;
+
+     struct uio                uio;
+     struct iovec      iov[5]; // XXX: careful ...
+     struct mbuf       *mp;
+     struct timeval    ts;
+     int               refcnt;
+     queue_t           *pduq;
+} pduq_t;
+
+struct isc_softc {
+     //int             state;
+     struct cdev       *dev;
+     eventhandler_tag  eh;
+     char              isid[6];        // Initiator Session ID (48 bits)
+     struct lock       lock;
+
+     int                       nsess;
+     TAILQ_HEAD(,isc_session)  isc_sess;
+     isc_session_t             *sessions[MAX_SESSIONS];
+
+     struct lock               pdu_lock;
+#ifdef  ISCSI_INITIATOR_DEBUG
+     int                        npdu_alloc, npdu_max; // for instrumentation
+#endif
+#define MAX_PDUS       (MAX_SESSIONS*256) // XXX: at the moment this is arbitrary
+     objcache_t                        pdu_zone; // pool of free pdu's
+     TAILQ_HEAD(,pduq)         freepdu;
+     /*
+      | cam stuff
+      */
+     struct cam_sim            *cam_sim;
+     struct cam_path           *cam_path;
+     struct lock               cam_lock;
+     /*
+      | sysctl stuff
+      */
+     struct sysctl_ctx_list    clist;
+     struct sysctl_oid         *oid;
+};
+
+#ifdef  ISCSI_INITIATOR_DEBUG
+extern struct lock iscsi_dbg_lock;
+#endif
+
+void   isc_start_receiver(isc_session_t *sp);
+void   isc_stop_receiver(isc_session_t *sp);
+
+int    isc_sendPDU(isc_session_t *sp, pduq_t *pq);
+int    isc_qout(isc_session_t *sp, pduq_t *pq);
+int    i_prepPDU(isc_session_t *sp, pduq_t *pq);
+
+int    ism_fullfeature(struct cdev *dev, int flag);
+
+int    i_pdu_flush(isc_session_t *sc);
+int    i_setopt(isc_session_t *sp, isc_opt_t *opt);
+void   i_freeopt(isc_opt_t *opt);
+
+int    ic_init(struct isc_softc *sc);
+void   ic_destroy(struct isc_softc *sc);
+int    ic_fullfeature(struct cdev *dev);
+void   ic_lost_target(isc_session_t *sp, int target);
+int    ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp);
+
+void   ism_recv(isc_session_t *sp, pduq_t *pq);
+int    ism_start(isc_session_t *sp);
+void   ism_stop(isc_session_t *sp);
+
+int    scsi_encap(struct cam_sim *sim, union ccb *ccb);
+int    scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
+void   iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
+void   iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
+void   iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
+void   iscsi_async(isc_session_t *sp,  pduq_t *pq);
+void   iscsi_cleanup(isc_session_t *sp);
+int    iscsi_requeue(isc_session_t *sp);
+
+void   ic_freeze(isc_session_t *sp);
+void   ic_release(isc_session_t *sp);
+
+// Serial Number Arithmetic
+#define _MAXINCR       0x7FFFFFFF      // 2 ^ 31 - 1
+#define SNA_GT(i1, i2) ((i1 != i2) && (\
+       (i1 < i2 && i2 - i1 > _MAXINCR) ||\
+       (i1 > i2 && i1 - i2 < _MAXINCR))?1: 0)
+
+/*
+ * inlines
+ *
+ * DragonFly note: CAM locks itself, peripherals do not lock CAM.
+ */
+#ifdef _CAM_CAM_XPT_SIM_H
+
+#define CAM_LOCK(arg)  /*lockmgr(&arg->cam_lock, LK_EXCLUSIVE)*/
+#define CAM_UNLOCK(arg)        /*lockmgr(&arg->cam_lock, LK_RELEASE)*/
+
+static __inline void
+XPT_DONE(struct isc_softc *isp, union ccb *ccb)
+{
+     CAM_LOCK(isp);
+     xpt_done(ccb);
+     CAM_UNLOCK(isp);
+}
+
+#endif /* _CAM_CAM_XPT_SIM_H */
+
+#define iscsi_lock_ex(mtx)     mtx_lock_ex_quick(mtx, __func__)
+#define iscsi_unlock_ex(mtx)   mtx_unlock(mtx)
+#define issleep(id, mtx, flags, wmesg, to)     \
+                               mtxsleep(id, mtx, flags, wmesg, to)
+
+static __inline pduq_t *
+pdu_alloc(struct isc_softc *isc, int wait)
+{
+     pduq_t    *pq;
+
+     lockmgr(&isc->pdu_lock, LK_EXCLUSIVE);
+     if((pq = TAILQ_FIRST(&isc->freepdu)) == NULL) {
+         lockmgr(&isc->pdu_lock, LK_RELEASE);
+         pq = objcache_get(isc->pdu_zone, wait /* M_WAITOK or M_NOWAIT*/);
+     }
+     else {
+         TAILQ_REMOVE(&isc->freepdu, pq, pq_link);
+         lockmgr(&isc->pdu_lock, LK_RELEASE);
+     }
+
+     if(pq == NULL) {
+         debug(7, "out of mem");
+         return NULL;
+     }
+#ifdef ISCSI_INITIATOR_DEBUG
+     lockmgr(&isc->pdu_lock, LK_EXCLUSIVE);
+     isc->npdu_alloc++;
+     if(isc->npdu_alloc > isc->npdu_max)
+         isc->npdu_max = isc->npdu_alloc;
+     lockmgr(&isc->pdu_lock, LK_RELEASE);
+#endif
+     memset(pq, 0, sizeof(pduq_t));
+
+     return pq;
+}
+
+static __inline void
+pdu_free(struct isc_softc *isc, pduq_t *pq)
+{
+     if(pq->mp)
+         m_freem(pq->mp);
+#ifdef NO_USE_MBUF
+     if(pq->buf != NULL)
+         kfree(pq->buf, M_ISCSI);
+#endif
+     lockmgr(&isc->pdu_lock, LK_EXCLUSIVE);
+     TAILQ_INSERT_TAIL(&isc->freepdu, pq, pq_link);
+#ifdef ISCSI_INITIATOR_DEBUG
+     isc->npdu_alloc--;
+#endif
+     lockmgr(&isc->pdu_lock, LK_RELEASE);
+}
+
+static __inline void
+i_nqueue_rsp(isc_session_t *sp, pduq_t *pq)
+{
+     iscsi_lock_ex(&sp->rsp_mtx);
+     if(++sp->stats.nrsp > sp->stats.max_rsp)
+         sp->stats.max_rsp = sp->stats.nrsp;
+     TAILQ_INSERT_TAIL(&sp->rsp, pq, pq_link);
+     iscsi_unlock_ex(&sp->rsp_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_rsp(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     iscsi_lock_ex(&sp->rsp_mtx);
+     if((pq = TAILQ_FIRST(&sp->rsp)) != NULL) {
+         sp->stats.nrsp--;
+         TAILQ_REMOVE(&sp->rsp, pq, pq_link);
+     }
+     iscsi_unlock_ex(&sp->rsp_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_nqueue_rsv(isc_session_t *sp, pduq_t *pq)
+{
+     iscsi_lock_ex(&sp->rsv_mtx);
+     if(++sp->stats.nrsv > sp->stats.max_rsv)
+         sp->stats.max_rsv = sp->stats.nrsv;
+     TAILQ_INSERT_TAIL(&sp->rsv, pq, pq_link);
+     iscsi_unlock_ex(&sp->rsv_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_rsv(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     iscsi_lock_ex(&sp->rsv_mtx);
+     if((pq = TAILQ_FIRST(&sp->rsv)) != NULL) {
+         sp->stats.nrsv--;
+         TAILQ_REMOVE(&sp->rsv, pq, pq_link);
+     }
+     iscsi_unlock_ex(&sp->rsv_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_nqueue_csnd(isc_session_t *sp, pduq_t *pq)
+{
+     iscsi_lock_ex(&sp->snd_mtx);
+     if(++sp->stats.ncsnd > sp->stats.max_csnd)
+         sp->stats.max_csnd = sp->stats.ncsnd;
+     TAILQ_INSERT_TAIL(&sp->csnd, pq, pq_link);
+     iscsi_unlock_ex(&sp->snd_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_csnd(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     iscsi_lock_ex(&sp->snd_mtx);
+     if((pq = TAILQ_FIRST(&sp->csnd)) != NULL) {
+         sp->stats.ncsnd--;
+         TAILQ_REMOVE(&sp->csnd, pq, pq_link);
+     }
+     iscsi_unlock_ex(&sp->snd_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_nqueue_isnd(isc_session_t *sp, pduq_t *pq)
+{
+     iscsi_lock_ex(&sp->snd_mtx);
+     if(++sp->stats.nisnd > sp->stats.max_isnd)
+         sp->stats.max_isnd = sp->stats.nisnd;
+     TAILQ_INSERT_TAIL(&sp->isnd, pq, pq_link);
+     iscsi_unlock_ex(&sp->snd_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_isnd(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     iscsi_lock_ex(&sp->snd_mtx);
+     if((pq = TAILQ_FIRST(&sp->isnd)) != NULL) {
+         sp->stats.nisnd--;
+         TAILQ_REMOVE(&sp->isnd, pq, pq_link);
+     }
+     iscsi_unlock_ex(&sp->snd_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_nqueue_wsnd(isc_session_t *sp, pduq_t *pq)
+{
+     iscsi_lock_ex(&sp->snd_mtx);
+     if(++sp->stats.nwsnd > sp->stats.max_wsnd)
+         sp->stats.max_wsnd = sp->stats.nwsnd;
+     TAILQ_INSERT_TAIL(&sp->wsnd, pq, pq_link);
+     iscsi_unlock_ex(&sp->snd_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_wsnd(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     iscsi_lock_ex(&sp->snd_mtx);
+     if((pq = TAILQ_FIRST(&sp->wsnd)) != NULL) {
+         sp->stats.nwsnd--;
+         TAILQ_REMOVE(&sp->wsnd, pq, pq_link);
+     }
+     iscsi_unlock_ex(&sp->snd_mtx);
+
+     return pq;
+}
+
+static __inline pduq_t *
+i_dqueue_snd(isc_session_t *sp, int which)
+{
+     pduq_t *pq;
+
+     pq = NULL;
+     iscsi_lock_ex(&sp->snd_mtx);
+     if((which & BIT(0)) && (pq = TAILQ_FIRST(&sp->isnd)) != NULL) {
+         sp->stats.nisnd--;
+         TAILQ_REMOVE(&sp->isnd, pq, pq_link);
+         pq->pduq = &sp->isnd; // remember where you came from
+     } else
+     if((which & BIT(1)) && (pq = TAILQ_FIRST(&sp->wsnd)) != NULL) {
+         sp->stats.nwsnd--;
+         TAILQ_REMOVE(&sp->wsnd, pq, pq_link);
+         pq->pduq = &sp->wsnd; // remember where you came from
+     } else
+     if((which & BIT(2)) && (pq = TAILQ_FIRST(&sp->csnd)) != NULL) {
+         sp->stats.ncsnd--;
+         TAILQ_REMOVE(&sp->csnd, pq, pq_link);
+         pq->pduq = &sp->csnd; // remember where you came from
+     }
+     iscsi_unlock_ex(&sp->snd_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_rqueue_pdu(isc_session_t *sp, pduq_t *pq)
+{
+     iscsi_lock_ex(&sp->snd_mtx);
+     KASSERT(pq->pduq != NULL, ("pq->pduq is NULL"));
+     TAILQ_INSERT_TAIL(pq->pduq, pq, pq_link);
+     iscsi_unlock_ex(&sp->snd_mtx);
+}
+
+/*
+ | Waiting for ACK (or something :-)
+ */
+static __inline void
+i_nqueue_hld(isc_session_t *sp, pduq_t *pq)
+{
+     getmicrouptime(&pq->ts);
+     iscsi_lock_ex(&sp->hld_mtx);
+     if(++sp->stats.nhld > sp->stats.max_hld)
+         sp->stats.max_hld = sp->stats.nhld;
+     TAILQ_INSERT_TAIL(&sp->hld, pq, pq_link);
+     iscsi_unlock_ex(&sp->hld_mtx);
+     return;
+}
+
+static __inline void
+i_remove_hld(isc_session_t *sp, pduq_t *pq)
+{
+     iscsi_lock_ex(&sp->hld_mtx);
+     sp->stats.nhld--;
+     TAILQ_REMOVE(&sp->hld, pq, pq_link);
+     iscsi_unlock_ex(&sp->hld_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_hld(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     iscsi_lock_ex(&sp->hld_mtx);
+     if((pq = TAILQ_FIRST(&sp->hld)) != NULL) {
+         sp->stats.nhld--;
+         TAILQ_REMOVE(&sp->hld, pq, pq_link);
+     }
+     iscsi_unlock_ex(&sp->hld_mtx);
+
+     return pq;
+}
+
+static __inline pduq_t *
+i_search_hld(isc_session_t *sp, int itt, int keep)
+{
+     pduq_t    *pq, *tmp;
+
+     pq = NULL;
+
+     iscsi_lock_ex(&sp->hld_mtx);
+     TAILQ_FOREACH_MUTABLE(pq, &sp->hld, pq_link, tmp) {
+         if(pq->pdu.ipdu.bhs.itt == itt) {
+              if(!keep) {
+                   sp->stats.nhld--;
+                   TAILQ_REMOVE(&sp->hld, pq, pq_link);
+              }
+              break;
+         }
+     }
+     iscsi_unlock_ex(&sp->hld_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_mbufcopy(struct mbuf *mp, caddr_t dp, int len)
+{
+     struct mbuf *m;
+     caddr_t bp;
+
+     for(m = mp; m != NULL; m = m->m_next) {
+         bp = mtod(m, caddr_t);
+         /*
+          | the pdu is word (4 octed) aligned
+          | so len <= packet
+          */
+         memcpy(dp, bp, MIN(len, m->m_len));
+         dp += m->m_len;
+         len -= m->m_len;
+         if(len <= 0)
+              break;
+     }
+}