diff options
29 files changed, 7523 insertions, 0 deletions
diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist index dd039d5..c9501d5 100644 --- a/etc/mtree/BSD.usr.dist +++ b/etc/mtree/BSD.usr.dist @@ -218,6 +218,8 @@ .. ipfw .. + iscsi + .. isdn contrib .. diff --git a/sbin/Makefile b/sbin/Makefile index f510e4d..7c78a99 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -41,6 +41,7 @@ SUBDIR= adjkerntz \ init \ ${_ipf} \ ipfw \ + iscontrol \ kldconfig \ kldload \ kldstat \ diff --git a/sbin/iscontrol/Makefile b/sbin/iscontrol/Makefile new file mode 100644 index 0000000..2b09fa9 --- /dev/null +++ b/sbin/iscontrol/Makefile @@ -0,0 +1,13 @@ +# $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/iscsi/initiator +#CFLAGS += -g -DDEBUG + +MAN= iscsi.conf.5 iscontrol.8 + +.include <bsd.prog.mk> diff --git a/sbin/iscontrol/auth_subr.c b/sbin/iscontrol/auth_subr.c new file mode 100644 index 0000000..8381687 --- /dev/null +++ b/sbin/iscontrol/auth_subr.c @@ -0,0 +1,208 @@ +/*- + * Copyright (c) 2005-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. + * + */ + +/* + | $Id: auth_subr.c,v 2.2 2007/06/01 08:09:37 danny Exp $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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" +#include "pdu.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, int 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 index 0000000..409ed2c --- /dev/null +++ b/sbin/iscontrol/config.c @@ -0,0 +1,376 @@ + /*- + * Copyright (c) 2005-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. + * + */ +/* + | $Id: config.c,v 2.1 2006/11/12 08:06:51 danny Exp danny $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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} +}; + +#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 index 0000000..6a1c529 --- /dev/null +++ b/sbin/iscontrol/fsm.c @@ -0,0 +1,721 @@ +/*- + * Copyright (c) 2005-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. + * + */ + +/* + | $Id: fsm.c,v 2.8 2007/05/19 16:34:21 danny Exp danny $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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" +#include "pdu.h" + +typedef enum { + T1 = 1, + T2, /*T3,*/ T4, T5, /*T6,*/ T7, T8, T9, + T10, T11, T12, T13, T14, T15, T16, T18 +} trans_t; + +static trans_t +tcpConnect(isess_t *sess) +{ + isc_opt_t *op = sess->op; + int val, sv_errno; + struct addrinfo *res, hints; + struct sockaddr_in sn; + struct in_addr ipn; + time_t sec; + + debug_called(3); + if(sess->flags & (SESS_RECONNECT|SESS_REDIRECT)) { + syslog(LOG_INFO, "%s", (sess->flags & SESS_RECONNECT) + ? "Reconnect": "Redirected"); + + debug(3, "%s", (sess->flags & SESS_RECONNECT) ? "Reconnect": "Redirected"); + shutdown(sess->soc, SHUT_RDWR); + //close(sess->soc); + sleep(5); // XXX: actually should be ? + sess->soc = -1; + + sess->flags &= ~SESS_CONNECTED; + if(sess->flags & SESS_REDIRECT) { + if(sess->redirect_cnt++ > MAXREDIRECTS) { + syslog(LOG_WARNING, "too many redirects > %d", MAXREDIRECTS); + return 0; + } + sess->flags |= SESS_RECONNECT; + } + if((sess->flags & SESS_RECONNECT) == 0) + return 0; + + // 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; + } + } + sess->reconnect_cnt++; + // sess->flags &= ~(SESS_RECONNECT|SESS_REDIRECT); + } + + if((sess->soc = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "tcpConnect: socket: %m"); + return 0; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + + debug(3, "targetAddress=%s port=%d", op->targetAddress, op->port); + if(inet_aton(op->targetAddress, &ipn)) + hints.ai_flags |= AI_NUMERICHOST; + if((val = getaddrinfo(op->targetAddress, NULL, &hints, &res)) != 0) { + fprintf(stderr, "getaddrinfo(%s): %s\n", op->targetAddress, gai_strerror(val)); + return 0; + } + memcpy(&sn, res->ai_addr, sizeof(struct sockaddr_in)); + sn.sin_port = htons(op->port); + freeaddrinfo(res); + + // from Patrick.Guelat@imp.ch: + // iscontrol can be called without waiting for the socket entry to time out + val = 1; + if(setsockopt(sess->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)); + } + + sess->flags &= ~SESS_CONNECTED; + + if(connect(sess->soc, (struct sockaddr *)&sn, sizeof(struct sockaddr_in)) != -1) { +#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; + + } + sv_errno = errno; + fprintf(stderr, "errno=%d\n", sv_errno); + perror("connect"); + switch(sv_errno) { + case ECONNREFUSED: + case ENETUNREACH: + case ETIMEDOUT: + 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; + } + 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 + case 3: // target terminal error -- could retry ... + 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, S2, /*S3,*/ S4, S5, S6, S7, S8 +} state_t; + +#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->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; + } + } while(1); +} diff --git a/sbin/iscontrol/iscontrol.8 b/sbin/iscontrol/iscontrol.8 new file mode 100644 index 0000000..f9a3dd1 --- /dev/null +++ b/sbin/iscontrol/iscontrol.8 @@ -0,0 +1,116 @@ +.\" 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 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 +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 succesful 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 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 iscsi.conf(5) +.It Fl t Ar target +is the target's IP address or name +.It Ar variable Ns = Ns Ar value +see 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 permition, but a +.Em full session +certainly does. +.sp +.Dl iscontrol -c /etc/iscsi.conf -n myiscsi +.Pp +will read options from file /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 iscsi_initiator 4 , +.Xr iscsi.conf 5 , +.Xr camcontrol 8 , +.Xr da 4 , +.Xr sa 4 +.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 Funtions. +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 index 0000000..d5d5929 --- /dev/null +++ b/sbin/iscontrol/iscontrol.c @@ -0,0 +1,227 @@ +/*- + * 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. + * + */ +/* + | $Id: iscontrol.c,v 2.2 2006/12/01 09:11:56 danny Exp danny $ + */ +/* + | the user level initiator (client) + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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" +//#include "pdu.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} +}; + +token_t DigestMethods[] = { + {"None", 0}, + {"CRC32", 1}, + {"CRC32C", 1}, + {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, *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; + } + if((p = strchr(op->targetAddress, ':')) != NULL) { + *p++ = 0; + op->port = atoi(p); + p = strchr(p, ','); + } + if(p || ((p = strchr(op->targetAddress, ',')) != 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 index 0000000..c93c35a --- /dev/null +++ b/sbin/iscontrol/iscontrol.h @@ -0,0 +1,159 @@ +/*- + * 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: 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 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 + 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, int 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 index 0000000..a8d558a --- /dev/null +++ b/sbin/iscontrol/iscsi.conf.5 @@ -0,0 +1,204 @@ +.\" 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 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 +current 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 kilobites. The default is 128. +.El +.sp +If +.Em AutheMethod +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 ascci 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 don't 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 index 0000000..44ea889 --- /dev/null +++ b/sbin/iscontrol/login.c @@ -0,0 +1,440 @@ +/*- + * 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. + * + */ +/* + | $Id: login.c,v 1.4 2007/04/27 07:40:40 danny Exp danny $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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" +#include "pdu.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; + + // 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++; + 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); + } + } 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) { + 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 index 0000000..6586082 --- /dev/null +++ b/sbin/iscontrol/misc.c @@ -0,0 +1,225 @@ +/*- + * 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. + * + */ + +/* + | $Id: misc.c,v 2.1 2006/11/12 08:06:51 danny Exp $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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> + +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, *cp; + + if(strncasecmp(encoding, "0x", 2) == 0) { + char ofmt[5]; + + len = blen * 2; + dst = malloc(len + 3); + strcpy(dst, encoding); + ds = dst + 2; + cp = (char *)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 = (char *)md; + b6 = 0; // to keep copiler 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/pdu.c b/sbin/iscontrol/pdu.c new file mode 100644 index 0000000..18dfdfc --- /dev/null +++ b/sbin/iscontrol/pdu.c @@ -0,0 +1,175 @@ +/*- + * 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. + * + */ +/* + | $Id: pdu.c,v 2.2 2006/12/01 09:11:56 danny Exp danny $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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" +#include "pdu.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 index 0000000..0154f9e --- /dev/null +++ b/sbin/iscontrol/pdu.h @@ -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/Makefile b/share/man/man4/Makefile index 689ccab..9c92e22 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -127,6 +127,7 @@ MAN= aac.4 \ ips.4 \ ipsec.4 \ ipw.4 \ + iscsi_initiator.4 \ isp.4 \ ispfw.4 \ iwi.4 \ diff --git a/share/man/man4/iscsi_initiator.4 b/share/man/man4/iscsi_initiator.4 new file mode 100644 index 0000000..5f3bf16 --- /dev/null +++ b/share/man/man4/iscsi_initiator.4 @@ -0,0 +1,104 @@ +.\" 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 ISCSI (iSCSI) network +protocol standard, the user land companion is +.Xr iscontrol 8, and permits access to remote +.Em virtual +SCSI devices via the +.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 +.It Pa /dev/iscsi +used to creat new sessions. +.It Pa /dev/iscsi%d +for each new session. +.El +.\" .Sh ERRORS +.Sh SEE ALSO +.Xr iscontrol 8 , +.Xr cam 4 , +.Xr camcontrol 8 +.Sh STANDARDS +ISCSI RFC 3720 +.\" .Sh HISTORY +.\" .Sh AUTHORS +.Sh BUGS +The lun discovery method is old-fashioned. diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 4837628..2daa91a 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -1441,6 +1441,7 @@ device ahc device ahd device amd device esp +device iscsi_initiator device isp hint.isp.0.disable="1" hint.isp.0.role="3" @@ -1506,6 +1507,10 @@ options AHD_TMODE_ENABLE # controllers that have it configured only if this option is set. options ADW_ALLOW_MEMIO +# Options used in dev/iscsi (Software iSCSI stack) +# +options ISCSI_INITIATOR_DEBUG=9 + # Options used in dev/isp/ (Qlogic SCSI/FC driver). # # ISP_TARGET_MODE - enable target mode operation diff --git a/sys/conf/files b/sys/conf/files index 7368134..d503801 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -721,6 +721,12 @@ dev/ips/ips_disk.c optional ips dev/ips/ips_ioctl.c optional ips dev/ips/ips_pci.c optional ips pci dev/ipw/if_ipw.c optional ipw +dev/iscsi/initiator/iscsi.c optional iscsi_initiator scbus +dev/iscsi/initiator/iscsi_subr.c optional iscsi_initiator scbus +dev/iscsi/initiator/isc_cam.c optional iscsi_initiator scbus +dev/iscsi/initiator/isc_soc.c optional iscsi_initiator scbus +dev/iscsi/initiator/isc_sm.c optional iscsi_initiator scbus +dev/iscsi/initiator/isc_subr.c optional iscsi_initiator scbus dev/isp/isp.c optional isp dev/isp/isp_freebsd.c optional isp dev/isp/isp_library.c optional isp diff --git a/sys/conf/options b/sys/conf/options index cd64e63..633cdbb 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -323,6 +323,9 @@ ISP_TARGET_MODE opt_isp.h ISP_FW_CRASH_DUMP opt_isp.h ISP_DEFAULT_ROLES opt_isp.h +# Options used only in dev/iscsi +ISCSI_INITIATOR_DEBUG opt_iscsi_initiator.h + # Options used in the 'ata' ATA/ATAPI driver ATA_STATIC_ID opt_ata.h ATA_NOPCI opt_ata.h diff --git a/sys/dev/iscsi/initiator/isc_cam.c b/sys/dev/iscsi/initiator/isc_cam.c new file mode 100644 index 0000000..ed0b74a --- /dev/null +++ b/sys/dev/iscsi/initiator/isc_cam.c @@ -0,0 +1,424 @@ +/*- + * Copyright (c) 2005-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. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_iscsi_initiator.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/callout.h> +#if __FreeBSD_version >= 700000 +#include <sys/lock.h> +#include <sys/mutex.h> +#endif +#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 <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_periph.h> + +#include <dev/iscsi/initiator/iscsi.h> +#include <dev/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) { + mtx_lock(&isp->cam_mtx); + xpt_async(AC_LOST_DEVICE, sp->cam_path, NULL); + xpt_free_path(sp->cam_path); + mtx_unlock(&isp->cam_mtx); + 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); + + free(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 = malloc(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, PRIBIO, "ffp", 5*hz); // the timeout time 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, "FreeBSD", 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 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; +#if __FreeBSD_version < 700000 + XPT_DONE(isp, ccb); +#else + xpt_done(ccb); +#endif + 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; +#if __FreeBSD_version < 700000 + XPT_DONE(isp, ccb); +#else + xpt_done(ccb); +#endif + 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 __FreeBSD_version < 700000 + if(scsi_encap(sim, ccb) != 0) + return; +#else + mtx_unlock(&isp->cam_mtx); + if(scsi_encap(sim, ccb) != 0) { + mtx_lock(&isp->cam_mtx); + return; + } + mtx_lock(&isp->cam_mtx); +#endif + 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; + } +#if __FreeBSD_version < 700000 + XPT_DONE(isp, ccb); +#else + xpt_done(ccb); +#endif + 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, TRUE /*free_devq*/); + + 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; + +#if __FreeBSD_version >= 700000 + mtx_init(&isp->cam_mtx, "isc-cam", NULL, MTX_DEF); +#else + isp->cam_mtx = Giant; +#endif + sim = cam_sim_alloc(ic_action, ic_poll, + "iscsi", isp, 0/*unit*/, +#if __FreeBSD_version >= 700000 + &isp->cam_mtx, +#endif + 1/*max_dev_transactions*/, + 100/*max_tagged_dev_transactions*/, + devq); + if(sim == NULL) { + cam_simq_free(devq); +#if __FreeBSD_version >= 700000 + mtx_destroy(&isp->cam_mtx); +#endif + return ENXIO; + } + CAM_LOCK(isp); + if(xpt_bus_register(sim, NULL, 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, /*free_devq*/TRUE); + CAM_UNLOCK(isp); +#if __FreeBSD_version >= 700000 + mtx_destroy(&isp->cam_mtx); +#endif + return ENXIO; +} diff --git a/sys/dev/iscsi/initiator/isc_sm.c b/sys/dev/iscsi/initiator/isc_sm.c new file mode 100644 index 0000000..c767477 --- /dev/null +++ b/sys/dev/iscsi/initiator/isc_sm.c @@ -0,0 +1,786 @@ +/*- + * Copyright (c) 2005-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. + * + */ +/* + | iSCSI - Session Manager + | $Id: isc_sm.c,v 1.30 2007/04/22 09:53:09 danny Exp danny $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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 <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_periph.h> + +#include <dev/iscsi/initiator/iscsi.h> +#include <dev/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, 0)) == 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); +#ifdef ISC_OWAITING + if(sp->flags & ISC_OWAITING) { + mtx_lock(&sp->io_mtx); // XXX + wakeup(&sp->flags); + mtx_unlock(&sp->io_mtx); // XXX + } +#else + wakeup(&sp->flags); +#endif + return error; +} +/* + | called when a fullPhase is restarted + */ +static int +ism_restart(isc_session_t *sp) +{ + int lastcmd; + + sdebug(2, "restart ..."); + sp->flags |= ISC_SM_HOLD; + 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 + sp->flags &= ~ISC_SM_HOLD; + return 0; +} + +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 + error = 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; + } +} + +static int +proc_out(isc_session_t *sp) +{ + sn_t *sn = &sp->sn; + pduq_t *pq; + int error, ndone = 0; + int which; + + debug_called(8); + + while(1) { + pdu_t *pp; + bhs_t *bhs; + + /* + | check if there is outstanding work in: + | 1- the Inmediate 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) + which |= BIT(2); + + if((pq = i_dqueue_snd(sp, which)) == NULL) + break; + + 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(5, "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++; + else { + xdebug("error=%d ndone=%d opcode=0x%x ccb=%p itt=%x", + error, ndone, bhs->opcode, pq->ccb, ntohl(bhs->itt)); + if(error == EPIPE) { + // XXX: better do some error recovery ... + break; + } +#if 0 + if(pq->ccb) { + i_remove_hld(sp, pq); + pq->ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; // some better error? + XPT_DONE(pq->ccb); + } + else { + // XXX: now what? + // how do we pass back an error? + } +#endif + } + if(pq->ccb == NULL || error) + pdu_free(sp->isc, pq); + } + return ndone; +} + +/* + | survives link breakdowns. + */ +static void +ism_proc(void *vp) +{ + isc_session_t *sp = (isc_session_t *)vp; + int odone; + + debug_called(8); + sdebug(3, "started"); + + sp->flags |= ISC_SM_RUNNING; + do { + if(sp->flags & ISC_SM_HOLD) + odone = 0; + else + odone = proc_out(sp); + sdebug(7, "odone=%d", odone); + if(odone == 0) { + mtx_lock(&sp->io_mtx); +#ifdef ISC_OWAITING + sp->flags |= ISC_OWAITING; +#endif + if((msleep(&sp->flags, &sp->io_mtx, PRIBIO, "isc_proc", hz*30) == EWOULDBLOCK) + && (sp->flags & ISC_CON_RUNNING)) + _nop_out(sp); +#ifdef ISC_OWAITING + sp->flags &= ~ISC_OWAITING; +#endif + mtx_unlock(&sp->io_mtx); + } + } while(sp->flags & ISC_SM_RUN); + + sp->flags &= ~ISC_SM_RUNNING; + +#if __FreeBSD_version >= 700000 + destroy_dev(sp->dev); +#endif + + sdebug(3, "terminated"); + + wakeup(sp); + kthread_exit(0); +} + +#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; + sprintf(bp, "targetname='%s'", sp->opt.targetName); + bp += strlen(bp); + sprintf(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); + snprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent); + bp += strlen(bp); + n -= strlen(bp); + snprintf(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); + snprintf(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; + snprintf(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; + snprintf(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"); +} + +void +ism_stop(isc_session_t *sp) +{ + struct isc_softc *sc = sp->isc; + int n; + + debug_called(8); + sdebug(2, "terminating"); + /* + | first stop the receiver + */ + isc_stop_receiver(sp); + /* + | now stop the xmitter + */ + n = 5; + sp->flags &= ~ISC_SM_RUN; + while(n-- && (sp->flags & ISC_SM_RUNNING)) { + sdebug(2, "n=%d", n); + wakeup(&sp->flags); + tsleep(sp, PRIBIO, "-", 5*hz); + } + sdebug(2, "final n=%d", n); + sp->flags &= ~ISC_FFPHASE; + + iscsi_cleanup(sp); + + (void)i_pdu_flush(sp); + + ic_lost_target(sp, sp->sid); + + mtx_lock(&sc->mtx); + TAILQ_REMOVE(&sc->isc_sess, sp, sp_link); + sc->nsess--; + mtx_unlock(&sc->mtx); + +#if __FreeBSD_version < 700000 + destroy_dev(sp->dev); +#endif + + mtx_destroy(&sp->rsp_mtx); + mtx_destroy(&sp->rsv_mtx); + mtx_destroy(&sp->hld_mtx); + mtx_destroy(&sp->snd_mtx); + mtx_destroy(&sp->io_mtx); + + i_freeopt(&sp->opt); + sc->sessions[sp->sid] = NULL; + + if(sysctl_ctx_free(&sp->clist)) + xdebug("sysctl_ctx_free failed"); + + free(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); +#if 1 + mtx_init(&sp->rsv_mtx, "iscsi-rsv", NULL, MTX_DEF); + mtx_init(&sp->rsp_mtx, "iscsi-rsp", NULL, MTX_DEF); + mtx_init(&sp->snd_mtx, "iscsi-snd", NULL, MTX_DEF); + mtx_init(&sp->hld_mtx, "iscsi-hld", NULL, MTX_DEF); +#else + mtx_init(&sp->rsv_mtx, "iscsi-rsv", NULL, MTX_SPIN); + mtx_init(&sp->rsp_mtx, "iscsi-rsp", NULL, MTX_SPIN); + mtx_init(&sp->snd_mtx, "iscsi-snd", NULL, MTX_SPIN); + mtx_init(&sp->hld_mtx, "iscsi-hld", NULL, MTX_SPIN); +#endif + mtx_init(&sp->io_mtx, "iscsi-io", NULL, MTX_DEF); + + isc_add_sysctls(sp); + + sp->flags |= ISC_SM_RUN; + + return kthread_create(ism_proc, sp, &sp->stp, 0, 0, "ism_%d", sp->sid); +} diff --git a/sys/dev/iscsi/initiator/isc_soc.c b/sys/dev/iscsi/initiator/isc_soc.c new file mode 100644 index 0000000..49d530d --- /dev/null +++ b/sys/dev/iscsi/initiator/isc_soc.c @@ -0,0 +1,576 @@ +/*- + * Copyright (c) 2005-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. + * + */ +/* + | iSCSI + | $Id: isc_soc.c,v 1.26 2007/05/19 06:09:01 danny Exp danny $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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 <dev/iscsi/initiator/iscsi.h> +#include <dev/iscsi/initiator/iscsivar.h> + +#ifndef USE_MBUF +#define USE_MBUF +#endif + +#ifdef USE_MBUF +/* + | a dummy function for freeing external storage for mbuf + */ +static void +nil_fn(void *a, void *b) +{ +} +static int nil_refcnt = 0; +#endif /* USE_MBUF */ + +int +isc_sendPDU(isc_session_t *sp, pduq_t *pq) +{ + pdu_t *pp = &pq->pdu; + int len, error; +#ifdef USE_MBUF + struct mbuf *mh, **mp; +#else + struct uio *uio = &pq->uio; + struct iovec *iv; +#endif /* USE_MBUF */ + + debug_called(8); + +#ifndef USE_MBUF + 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++; +#else /* USE_MBUF */ + /* mbuf for the iSCSI header */ + MGETHDR(mh, M_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; +#endif /* USE_MBUF */ + + if(sp->hdrDigest) + pq->pdu.hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0); + if(pp->ahs_len) { +#ifndef USE_MBUF + iv->iov_base = pp->ahs; + iv->iov_len = pp->ahs_len; + iv++; +#else /* USE_MBUF */ + /* 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; +#endif /* USE_MBUF */ + 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); +#ifndef USE_MBUF + iv->iov_base = &pp->hdr_dig; + iv->iov_len = sizeof(int); + iv++; +#else /* USE_MBUF */ + /* 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); +#endif /* USE_MBUF */ + } +#ifdef USE_MBUF + mp = &mh->m_next; +#endif /* USE_MBUF */ + if(pq->pdu.ds) { +#ifndef USE_MBUF + 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++; +#else /* USE_MBUF */ + 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, M_TRYWAIT, MT_DATA); + md->m_ext.ref_cnt = &nil_refcnt; + l = min(MCLBYTES, len); + MEXTADD(md, pp->ds + off, l, nil_fn, + NULL, 0, EXT_EXTREF); + md->m_len = l; + md->m_next = NULL; + mh->m_pkthdr.len += l; + *mp = md; + mp = &md->m_next; + + len -= l; + off += l; + } +#endif /* USE_MBUF */ + } + if(sp->dataDigest) { +#ifdef USE_MBUF + struct mbuf *me; + +#endif /* USE_MBUF */ + pp->ds_dig = sp->dataDigest(pp->ds, pp->ds_len, 0); +#ifndef USE_MBUF + iv->iov_base = &pp->ds_dig; + iv->iov_len = sizeof(int); + iv++; +#else /* USE_MBUF */ + MGET(me, M_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; +#endif /* USE_MBUF */ + } + +#ifndef USE_MBUF + 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++; + getbintime(&sp->stats.t_sent); +#else /* USE_MBUF */ + if ((error = sosend(sp->soc, NULL, NULL, mh, 0, 0, sp->td)) != 0) { + m_freem(mh); + return (error); +#endif /* USE_MBUF */ + } +#ifndef USE_MBUF + return error; +#else /* USE_MBUF */ + sp->stats.nsent++; + getbintime(&sp->stats.t_sent); + return 0; +#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 = soreceive(sp->soc, NULL, uio, 0, 0, &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)) { + 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); + error = EAGAIN; // EPIPE; + } + + 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; + 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; + + 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 realy 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; + + uio->uio_resid = len; + uio->uio_td = curthread; // why ... + flags = MSG_WAITALL; + + error = soreceive(so, NULL, uio, &pq->mp, NULL, &flags); + //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, 1); // 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++; + getbintime(&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); + + sp->flags |= ISC_CON_RUNNING; + + if(sp->cam_path) + ic_release(sp); + + error = 0; + while(sp->flags & ISC_CON_RUN) { + // 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) { +#ifdef ISC_OWAITING + mtx_lock(&sp->io_mtx); + if(sp->flags & ISC_OWAITING) { + sp->flags &= ~ISC_OWAITING; + } + wakeup(&sp->flags); + mtx_unlock(&sp->io_mtx); +#else + wakeup(&sp->flags); +#endif + + } 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, PRIBIO, "isc_soc", 2*hz); + } + } + sdebug(2, "terminated, flags=%x so_count=%d so_state=%x error=%d", + sp->flags, so->so_count, so->so_state, error); + if((sp->proc != NULL) && sp->signal) { + PROC_LOCK(sp->proc); + psignal(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 ...? + mtx_lock(&sp->io_mtx); + sp->flags &= ~ISC_CON_RUNNING; + wakeup(&sp->soc); + + mtx_unlock(&sp->io_mtx); + + kthread_exit(0); +} + +void +isc_stop_receiver(isc_session_t *sp) +{ + int n = 5; + debug_called(8); + + sdebug(4, "sp=%p sp->soc=%p", sp, sp? sp->soc: 0); + soshutdown(sp->soc, SHUT_RD); + + mtx_lock(&sp->io_mtx); + sp->flags &= ~ISC_CON_RUN; + while(n-- && (sp->flags & ISC_CON_RUNNING)) { + sdebug(3, "waiting n=%d... flags=%x", n, sp->flags); + msleep(&sp->soc, &sp->io_mtx, PRIBIO, "isc_stpc", 5*hz); + } + mtx_unlock(&sp->io_mtx); + + if(sp->fp != NULL) + fdrop(sp->fp, sp->td); + fputsock(sp->soc); + sp->soc = NULL; + sp->fp = NULL; +} + +void +isc_start_receiver(isc_session_t *sp) +{ + debug_called(8); + + sp->flags |= ISC_CON_RUN; + kthread_create(isc_soc, sp, &sp->soc_proc, 0, 0, "iscsi%d", sp->sid); +} diff --git a/sys/dev/iscsi/initiator/isc_subr.c b/sys/dev/iscsi/initiator/isc_subr.c new file mode 100644 index 0000000..333ee72 --- /dev/null +++ b/sys/dev/iscsi/initiator/isc_subr.c @@ -0,0 +1,258 @@ +/*- + * Copyright (c) 2005-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. + * + */ +/* + | iSCSI + | $Id: isc_subr.c,v 1.20 2006/12/01 09:10:17 danny Exp danny $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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 <dev/iscsi/initiator/iscsi.h> +#include <dev/iscsi/initiator/iscsivar.h> + +MALLOC_DEFINE(M_PDU, "iSCSI pdu", "iSCSI driver"); + +static char * +i_strdupin(char *s, size_t maxlen) +{ + size_t len; + char *p, *q; + + p = malloc(maxlen, M_ISCSI, M_WAITOK); + if(copyinstr(s, p, maxlen, &len)) { + free(p, M_ISCSI); + return NULL; + } + q = malloc(len, M_ISCSI, M_WAITOK); + bcopy(p, q, len); + free(p, M_ISCSI); + + return q; +} + +/*****************************************************************/ +/* */ +/* CRC LOOKUP TABLE */ +/* ================ */ +/* The following CRC lookup table was generated automagically */ +/* by the Rocksoft^tm Model CRC Algorithm Table Generation */ +/* Program V1.0 using the following model parameters: */ +/* */ +/* Width : 4 bytes. */ +/* Poly : 0x1EDC6F41L */ +/* Reverse : TRUE. */ +/* */ +/* For more information on the Rocksoft^tm Model CRC Algorithm, */ +/* see the document titled "A Painless Guide to CRC Error */ +/* Detection Algorithms" by Ross Williams */ +/* (ross@guest.adelaide.edu.au.). This document is likely to be */ +/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ +/* */ +/*****************************************************************/ + +static uint32_t crc32Table[256] = { + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L +}; + +static uint32_t +i_crc32c(const void *buf, size_t size, uint32_t crc) +{ + const uint8_t *p = buf; + + crc = crc ^ 0xffffffff; + while (size--) + crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8); + crc = crc ^ 0xffffffff; + return crc; +} + +/* + | XXX: not finished coding + */ +int +i_setopt(isc_session_t *sp, isc_opt_t *opt) +{ + 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) + free(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) + free(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) + free(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); + } + + if(opt->headerDigest != NULL) { + sdebug(2, "opt.headerDigest='%s'", opt->headerDigest); + if(strcmp(opt->headerDigest, "CRC32C") == 0) { + sp->hdrDigest = (digest_t *)i_crc32c; + sdebug(2, "headerDigest set"); + } + } + if(opt->dataDigest != NULL) { + if(strcmp(opt->dataDigest, "CRC32C") == 0) { + sp->dataDigest = (digest_t *)i_crc32c; + sdebug(2, "dataDigest set"); + } + } + + return 0; +} + +void +i_freeopt(isc_opt_t *opt) +{ + if(opt->targetAddress != NULL) { + free(opt->targetAddress, M_ISCSI); + opt->targetAddress = NULL; + } + if(opt->targetName != NULL) { + free(opt->targetName, M_ISCSI); + opt->targetName = NULL; + } + if(opt->initiatorName != NULL) { + free(opt->initiatorName, M_ISCSI); + opt->initiatorName = NULL; + } +} diff --git a/sys/dev/iscsi/initiator/iscsi.c b/sys/dev/iscsi/initiator/iscsi.c new file mode 100644 index 0000000..4bd4ed2 --- /dev/null +++ b/sys/dev/iscsi/initiator/iscsi.c @@ -0,0 +1,810 @@ +/*- + * Copyright (c) 2005-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. + * + */ +/* + | iSCSI + | $Id: iscsi.c,v 1.35 2007/04/22 08:58:29 danny Exp danny $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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 <vm/uma.h> + +#include <dev/iscsi/initiator/iscsi.h> +#include <dev/iscsi/initiator/iscsivar.h> + +static char *iscsi_driver_version = "2.0.99"; + +static struct isc_softc isc; + +MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver"); + +#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 mtx iscsi_dbg_mtx; +#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 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 cdevsw iscsi_cdevsw = { + .d_version = D_VERSION, + .d_open = iscsi_open, + .d_close = iscsi_close, + .d_ioctl = iscsi_ioctl, +#ifdef ISCSI_INITIATOR_DEBUG + .d_read = iscsi_read, +#endif + .d_name = "iSCSI", +}; + +static int +iscsi_open(struct cdev *dev, int flags, int otype, struct thread *td) +{ + debug_called(8); + + debug(7, "dev=%d", minor(dev)); + + if(minor(dev) > MAX_SESSIONS) { + // should not happen + return ENODEV; + } + 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 cdev *dev, int flag, int otyp, struct thread *td) +{ + struct isc *sc; + 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, PRIBIO, "isc-cls", 60*hz); + ism_stop(sp); + } + debug(2, "done"); + return 0; +} + +static int +iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td) +{ + struct isc *sc; + 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(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)(cmd & 0xff)); + + switch(cmd) { + case ISCSISETSOC: + error = i_setsoc(sp, *(u_int *)arg, td); + break; + + case ISCSISETOPT: + opt = (isc_opt_t *)arg; + error = i_setopt(sp, opt); + break; + + case ISCSISEND: + error = i_send(dev, arg, td); + break; + + case ISCSIRECV: + error = i_recv(dev, arg, td); + break; + + case ISCSIPING: + error = i_ping(dev); + break; + + case ISCSISTART: + error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 1); + if(error == 0) { + sp->proc = td->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 cdev *dev, struct uio *uio, int ioflag) +{ +#ifdef ISCSI_INITIATOR_DEBUG + struct isc_softc *sc; + 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) { + sprintf(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; + sprintf(buf, "%03d] '%s' '%s'\n", i++, sp->opt.targetAddress, sp->opt.targetName); + uiomove(buf, strlen(buf), uio); + } + } + else { + int i = 0; + struct socket *so = sp->soc; +#define pukeit(i, pq) do {\ + sprintf(buf, "%03d] %06x %02x %x %ld %jd\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);\ + } while(0) + + sprintf(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); + } + sprintf(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); + } + sprintf(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); + } + sprintf(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); + } + sprintf(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); + } + + sprintf(buf, "/---- Stats ---/\n"); + uiomove(buf, strlen(buf), uio); + + sprintf(buf, "recv=%d sent=%d\n", sp->stats.nrecv, sp->stats.nsent); + uiomove(buf, strlen(buf), uio); + + sprintf(buf, "flags=%x pdus: alloc=%d max=%d\n", + sp->flags, sc->npdu_alloc, sc->npdu_max); + uiomove(buf, strlen(buf), uio); + + sprintf(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); + + sprintf(buf, "/---- socket -----/\nso_count=%d so_state=%x\n", so->so_count, 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; + + if(sp->soc != NULL) + isc_stop_receiver(sp); + + error = fget(td, fd, &sp->fp); + if(error) + return error; + + if((error = fgetsock(td, fd, &sp->soc, 0)) == 0) { + sp->td = td; + isc_start_receiver(sp); + } + else { + fdrop(sp->fp, td); + sp->fp = NULL; + } + + 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, 0)) == NULL) + return EAGAIN; + pp = &pq->pdu; + + pq->pdu = *(pdu_t *)arg; + if((error = i_prepPDU(sp, pq)) != 0) + return error; + + sdebug(4, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len); + + pq->buf = bp = malloc(pq->len - sizeof(union ipdu_u), M_ISCSI, M_WAITOK); + + if(pp->ahs_len) { + n = pp->ahs_len; + copyin(pp->ahs, bp, n); + pp->ahs = (ahs_t *)bp; + bp += n; + } + if(pp->ds_len) { + n = pp->ds_len; + copyin(pp->ds, bp, n); // can fail ... + pp->ds = bp; + bp += n; + while(n & 03) { + n++; + *bp++ = 0; + } + } + + error = isc_qout(sp, 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; + cnt = 6; // XXX: maybe the user can request a time out? + mtx_lock(&sp->rsp_mtx); + while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) { + msleep(&sp->rsp, &sp->rsp_mtx, PRIBIO, "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); + } + mtx_unlock(&sp->rsp_mtx); + + sdebug(3, "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 = malloc(len, M_ISCSI, M_WAITOK); + 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) + free(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 *)malloc(sizeof *sp, M_ISCSI, M_WAITOK | M_ZERO); + if(sp == NULL) + return ENOMEM; + mtx_lock(&sc->mtx); + /* + | search for the lowest unused sid + */ + for(n = 0; n < MAX_SESSIONS; n++) + if(sc->sessions[n] == NULL) + break; + if(n == MAX_SESSIONS) { + mtx_unlock(&sc->mtx); + free(sp, M_ISCSI); + return EPERM; + } + TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link); + sc->nsess++; + mtx_unlock(&sc->mtx); + + sc->sessions[n] = sp; + sp->dev = make_dev(&iscsi_cdevsw, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n); + *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", n); + error = ism_start(sp); + + sdebug(2, "error=%d", error); + + 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++; + } +} + +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_cdevsw, MAX_SESSIONS, UID_ROOT, GID_WHEEL, 0600, "iscsi"); + sc->dev->si_drv1 = sc; + + TAILQ_INIT(&sc->isc_sess); + + sc->pdu_zone = uma_zcreate("pdu", sizeof(pduq_t), + NULL, NULL, NULL, NULL, + 0, 0); + if(sc->pdu_zone == NULL) { + printf("iscsi_initiator: uma_zcreate failed"); + // XXX: and now what? + } + uma_zone_set_max(sc->pdu_zone, MAX_PDUS*2+1); + mtx_init(&sc->mtx, "iscsi", NULL, MTX_DEF); + mtx_init(&sc->pdu_mtx, "iscsi pdu pool", NULL, MTX_DEF); + +#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 + if((sc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown, + sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL) + xdebug("shutdown event registration failed\n"); + /* + | 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"); +} + +/* + | 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_SAFE(sp, &sc->isc_sess, sp_link, sp_tmp) { + //XXX: check for activity ... + ism_stop(sp); + } + if(sc->cam_sim != NULL) + ic_destroy(sc); + + mtx_destroy(&sc->mtx); + mtx_destroy(&sc->pdu_mtx); + uma_zdestroy(sc->pdu_zone); + + if(sc->dev) + destroy_dev(sc->dev); + + if(sysctl_ctx_free(&sc->clist)) + xdebug("sysctl_ctx_free failed"); +} + +static int +iscsi_modevent(module_t mod, int what, void *arg) +{ + debug_called(8); + + switch(what) { + case MOD_LOAD: + iscsi_start(); + break; + + case MOD_QUIESCE: +#if 1 + if(isc.nsess) { + xdebug("iscsi module busy(nsess=%d), cannot unload", isc.nsess); + log(LOG_ERR, "iscsi module busy, cannot unload"); + } + return isc.nsess; +#endif + case MOD_SHUTDOWN: + break; + + case MOD_UNLOAD: + iscsi_stop(); + break; + + default: + break; + } + return 0; +} +moduledata_t iscsi_mod = { + "iscsi", + (modeventhand_t) iscsi_modevent, + 0 +}; + +DECLARE_MODULE(iscsi, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); diff --git a/sys/dev/iscsi/initiator/iscsi.h b/sys/dev/iscsi/initiator/iscsi.h new file mode 100644 index 0000000..cbd9f22 --- /dev/null +++ b/sys/dev/iscsi/initiator/iscsi.h @@ -0,0 +1,407 @@ +/*- + * Copyright (c) 2005-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$ + */ +/* + | $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 <cam/cam.h> + +#define ISCSIDEV "iscsi" + +#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; + +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/iscsi/initiator/iscsi_subr.c b/sys/dev/iscsi/initiator/iscsi_subr.c new file mode 100644 index 0000000..6abe1f6 --- /dev/null +++ b/sys/dev/iscsi/initiator/iscsi_subr.c @@ -0,0 +1,567 @@ +/*- + * Copyright (c) 2005-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. + * + */ +/* + | $Id: iscsi_subr.c,v 1.17 2006/11/26 14:50:43 danny Exp danny $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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/mutex.h> +#include <sys/uio.h> +#include <sys/sysctl.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_periph.h> +#include <cam/scsi/scsi_message.h> +#include <sys/eventhandler.h> + +#include <dev/iscsi/initiator/iscsi.h> +#include <dev/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, 1); + if(wpq == NULL) { + // should not happen if above is 1 + sdebug(1, "now what?"); + return; + } + 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 = malloc(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) + free(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_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; + 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); + } + 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++; + } + 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_SAFE(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 +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 + break; + case 2: // target indicates it wants to drop connection + break; + + case 3: // target indicates it will drop all connections. + isc_stop_receiver(sp); + 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, 1)) == NULL) { // cannot happen + sdebug(3, "freezing"); + ccb->ccb_h.status = CAM_REQUEUE_REQ; + ic_freeze(sp); + return 0; + } +#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 = 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", + csio->data_ptr, bp, + ntohl(cmd->edtlen), pq->pdu.ds_len); + if(ntohl(cmd->edtlen) >= pq->pdu.ds_len) { + int offset, len = pq->pdu.ds_len; + 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/iscsi/initiator/iscsivar.h b/sys/dev/iscsi/initiator/iscsivar.h new file mode 100644 index 0000000..fa54b19 --- /dev/null +++ b/sys/dev/iscsi/initiator/iscsivar.h @@ -0,0 +1,556 @@ +/*- + * Copyright (c) 2005-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$ + */ +/* + | $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)\ + printf("%s: " fmt "\n", __func__ , ##args);} while(0) +#define sdebug(level, fmt, args...) do {if(level <= iscsi_debug)\ + printf("%d] %s: " fmt "\n", sp->sid, __func__ , ##args);} while(0) +#define debug_called(level) do {if(level <= iscsi_debug)\ + printf("%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...) printf("%s: " fmt "\n", __func__ , ##args) + +#define MAX_SESSIONS ISCSI_MAX_TARGETS + +typedef uint32_t digest_t(const void *, int len, uint32_t ocrc); + +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_SM_HOLD BIT(2) + +#define ISC_CON_RUN BIT(3) +#define ISC_CON_RUNNING BIT(4) +#define ISC_KILL BIT(5) +#define ISC_IWAITING 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_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 bintime t_sent; + struct bintime t_recv; +}; + +/* + | one per 'session' + */ +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 proc *soc_proc; + + struct proc *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; + + TAILQ_HEAD(,pduq) rsp; + TAILQ_HEAD(,pduq) rsv; + TAILQ_HEAD(,pduq) csnd; + TAILQ_HEAD(,pduq) isnd; + TAILQ_HEAD(,pduq) wsnd; + TAILQ_HEAD(,pduq) 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; +} 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 bintime ts; +} pduq_t; + +struct isc_softc { + //int state; + struct cdev *dev; + eventhandler_tag eh; + char isid[6]; // Initiator Session ID (48 bits) + struct mtx mtx; + + int nsess; + TAILQ_HEAD(,isc_session) isc_sess; + isc_session_t *sessions[MAX_SESSIONS]; + + struct mtx pdu_mtx; +#ifdef ISCSI_INITIATOR_DEBUG + int npdu_alloc, npdu_max; // for instrumentation +#endif +#define MAX_PDUS 256 // XXX: at the moment this is arbitrary + uma_zone_t pdu_zone; // pool of free pdu's + /* + | cam stuff + */ + struct cam_sim *cam_sim; + struct cam_path *cam_path; + struct mtx cam_mtx; + /* + | sysctl stuff + */ + struct sysctl_ctx_list clist; + struct sysctl_oid *oid; +}; + +#ifdef ISCSI_INITIATOR_DEBUG +extern struct mtx iscsi_dbg_mtx; +#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 + */ +#ifdef _CAM_CAM_XPT_SIM_H + +#if __FreeBSD_version < 600000 +#define CAM_LOCK(arg) +#define CAM_ULOCK(arg) + +static __inline void +XPT_DONE(struct isc_softc *isp, union ccb *ccb) +{ + mtx_lock(&Giant); + xpt_done(ccb); + mtx_unlock(&Giant); +} +#elif __FreeBSD_version >= 700000 +#define CAM_LOCK(arg) mtx_lock(&arg->cam_mtx) +#define CAM_UNLOCK(arg) mtx_unlock(&arg->cam_mtx) + +static __inline void +XPT_DONE(struct isc_softc *isp, union ccb *ccb) +{ + CAM_LOCK(isp); + xpt_done(ccb); + CAM_UNLOCK(isp); +} +#else +//__FreeBSD_version >= 600000 +#define CAM_LOCK(arg) +#define CAM_UNLOCK(arg) +#define XPT_DONE(ignore, arg) xpt_done(arg) +#endif + +#endif /* _CAM_CAM_XPT_SIM_H */ + +static __inline pduq_t * +pdu_alloc(struct isc_softc *isc, int wait) +{ + pduq_t *pq; + + pq = (pduq_t *)uma_zalloc(isc->pdu_zone, wait? M_WAITOK: M_NOWAIT); + if(pq == NULL) { + // will not happend if M_WAITOK ... + mtx_unlock(&isc->pdu_mtx); + debug(1, "out of mem"); + return NULL; + } +#ifdef ISCSI_INITIATOR_DEBUG + mtx_lock(&isc->pdu_mtx); + isc->npdu_alloc++; + if(isc->npdu_alloc > isc->npdu_max) + isc->npdu_max = isc->npdu_alloc; + mtx_unlock(&isc->pdu_mtx); +#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); + if(pq->buf != NULL) + free(pq->buf, M_ISCSI); + mtx_lock(&isc->pdu_mtx); + uma_zfree(isc->pdu_zone, pq); +#ifdef ISCSI_INITIATOR_DEBUG + isc->npdu_alloc--; +#endif + mtx_unlock(&isc->pdu_mtx); +} + +static __inline void +i_nqueue_rsp(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&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); + mtx_unlock(&sp->rsp_mtx); +} + +static __inline pduq_t * +i_dqueue_rsp(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->rsp_mtx); + if((pq = TAILQ_FIRST(&sp->rsp)) != NULL) { + sp->stats.nrsp--; + TAILQ_REMOVE(&sp->rsp, pq, pq_link); + } + mtx_unlock(&sp->rsp_mtx); + + return pq; +} + +static __inline void +i_nqueue_rsv(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&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); + mtx_unlock(&sp->rsv_mtx); +} + +static __inline pduq_t * +i_dqueue_rsv(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->rsv_mtx); + if((pq = TAILQ_FIRST(&sp->rsv)) != NULL) { + sp->stats.nrsv--; + TAILQ_REMOVE(&sp->rsv, pq, pq_link); + } + mtx_unlock(&sp->rsv_mtx); + + return pq; +} + +static __inline void +i_nqueue_csnd(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&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); + mtx_unlock(&sp->snd_mtx); +} + +static __inline pduq_t * +i_dqueue_csnd(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->snd_mtx); + if((pq = TAILQ_FIRST(&sp->csnd)) != NULL) { + sp->stats.ncsnd--; + TAILQ_REMOVE(&sp->csnd, pq, pq_link); + } + mtx_unlock(&sp->snd_mtx); + + return pq; +} + +static __inline void +i_nqueue_isnd(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&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); + mtx_unlock(&sp->snd_mtx); +} + +static __inline pduq_t * +i_dqueue_isnd(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->snd_mtx); + if((pq = TAILQ_FIRST(&sp->isnd)) != NULL) { + sp->stats.nisnd--; + TAILQ_REMOVE(&sp->isnd, pq, pq_link); + } + mtx_unlock(&sp->snd_mtx); + + return pq; +} + +static __inline void +i_nqueue_wsnd(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&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); + mtx_unlock(&sp->snd_mtx); +} + +static __inline pduq_t * +i_dqueue_wsnd(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->snd_mtx); + if((pq = TAILQ_FIRST(&sp->wsnd)) != NULL) { + sp->stats.nwsnd--; + TAILQ_REMOVE(&sp->wsnd, pq, pq_link); + } + mtx_unlock(&sp->snd_mtx); + + return pq; +} + +static __inline pduq_t * +i_dqueue_snd(isc_session_t *sp, int which) +{ + pduq_t *pq; + + pq = NULL; + mtx_lock(&sp->snd_mtx); + if((which & BIT(0)) && (pq = TAILQ_FIRST(&sp->isnd)) != NULL) { + sp->stats.nisnd--; + TAILQ_REMOVE(&sp->isnd, pq, pq_link); + } else + if((which & BIT(1)) && (pq = TAILQ_FIRST(&sp->wsnd)) != NULL) { + sp->stats.nwsnd--; + TAILQ_REMOVE(&sp->wsnd, pq, pq_link); + } else + if((which & BIT(2)) && (pq = TAILQ_FIRST(&sp->csnd)) != NULL) { + sp->stats.ncsnd--; + TAILQ_REMOVE(&sp->csnd, pq, pq_link); + } + mtx_unlock(&sp->snd_mtx); + + return pq; +} + +/* + | Waiting for ACK (or something :-) + */ +static __inline void +i_nqueue_hld(isc_session_t *sp, pduq_t *pq) +{ + getbintime(&pq->ts); + mtx_lock(&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); + mtx_unlock(&sp->hld_mtx); + return; +} + +static __inline void +i_remove_hld(isc_session_t *sp, pduq_t *pq) +{ + mtx_lock(&sp->hld_mtx); + sp->stats.nhld--; + TAILQ_REMOVE(&sp->hld, pq, pq_link); + mtx_unlock(&sp->hld_mtx); +} + +static __inline pduq_t * +i_dqueue_hld(isc_session_t *sp) +{ + pduq_t *pq; + + mtx_lock(&sp->hld_mtx); + if((pq = TAILQ_FIRST(&sp->hld)) != NULL) { + sp->stats.nhld--; + TAILQ_REMOVE(&sp->hld, pq, pq_link); + } + mtx_unlock(&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; + + mtx_lock(&sp->hld_mtx); + TAILQ_FOREACH_SAFE(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; + } + } + mtx_unlock(&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; + } +} diff --git a/sys/modules/iscsi/Makefile b/sys/modules/iscsi/Makefile new file mode 100644 index 0000000..625e3be --- /dev/null +++ b/sys/modules/iscsi/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR= initiator + +.include <bsd.subdir.mk> diff --git a/sys/modules/iscsi/initiator/Makefile b/sys/modules/iscsi/initiator/Makefile new file mode 100644 index 0000000..3295c7b --- /dev/null +++ b/sys/modules/iscsi/initiator/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/iscsi/initiator +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+= -DISCSI_INITIATOR_DEBUG=9 + +.include <bsd.kmod.mk> |