Introduce Danny Braniss' iSCSI initiator, version 2.0.99. Please read the

included man pages on how to use it.  This code is still somewhat experimental
but has been successfully tested on a number of targets.  Many thanks to
Danny for contributing this.

Approved by: re
This commit is contained in:
Scott Long 2007-07-24 15:35:02 +00:00
parent aa222db26f
commit c5933b2086
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=171568
29 changed files with 7523 additions and 0 deletions

View File

@ -218,6 +218,8 @@
..
ipfw
..
iscsi
..
isdn
contrib
..

View File

@ -41,6 +41,7 @@ SUBDIR= adjkerntz \
init \
${_ipf} \
ipfw \
iscontrol \
kldconfig \
kldload \
kldstat \

13
sbin/iscontrol/Makefile Normal file
View File

@ -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>

208
sbin/iscontrol/auth_subr.c Normal file
View File

@ -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

376
sbin/iscontrol/config.c Normal file
View File

@ -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);
}

721
sbin/iscontrol/fsm.c Normal file
View File

@ -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);
}

116
sbin/iscontrol/iscontrol.8 Normal file
View File

@ -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.

227
sbin/iscontrol/iscontrol.c Normal file
View File

@ -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);
}

159
sbin/iscontrol/iscontrol.h Normal file
View File

@ -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);

204
sbin/iscontrol/iscsi.conf.5 Normal file
View File

@ -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.

440
sbin/iscontrol/login.c Normal file
View File

@ -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;
}

225
sbin/iscontrol/misc.c Normal file
View File

@ -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;
}

175
sbin/iscontrol/pdu.c Normal file
View File

@ -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;
}
}

134
sbin/iscontrol/pdu.h Normal file
View File

@ -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;

View File

@ -127,6 +127,7 @@ MAN= aac.4 \
ips.4 \
ipsec.4 \
ipw.4 \
iscsi_initiator.4 \
isp.4 \
ispfw.4 \
iwi.4 \

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -0,0 +1,5 @@
# $FreeBSD$
SUBDIR= initiator
.include <bsd.subdir.mk>

View File

@ -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>