mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-12-28 05:55:27 +01:00
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:
parent
aa222db26f
commit
c5933b2086
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=171568
@ -218,6 +218,8 @@
|
||||
..
|
||||
ipfw
|
||||
..
|
||||
iscsi
|
||||
..
|
||||
isdn
|
||||
contrib
|
||||
..
|
||||
|
@ -41,6 +41,7 @@ SUBDIR= adjkerntz \
|
||||
init \
|
||||
${_ipf} \
|
||||
ipfw \
|
||||
iscontrol \
|
||||
kldconfig \
|
||||
kldload \
|
||||
kldstat \
|
||||
|
13
sbin/iscontrol/Makefile
Normal file
13
sbin/iscontrol/Makefile
Normal 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
208
sbin/iscontrol/auth_subr.c
Normal 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
376
sbin/iscontrol/config.c
Normal 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
721
sbin/iscontrol/fsm.c
Normal 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
116
sbin/iscontrol/iscontrol.8
Normal 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
227
sbin/iscontrol/iscontrol.c
Normal 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
159
sbin/iscontrol/iscontrol.h
Normal 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
204
sbin/iscontrol/iscsi.conf.5
Normal 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
440
sbin/iscontrol/login.c
Normal 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
225
sbin/iscontrol/misc.c
Normal 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
175
sbin/iscontrol/pdu.c
Normal 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
134
sbin/iscontrol/pdu.h
Normal 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;
|
@ -127,6 +127,7 @@ MAN= aac.4 \
|
||||
ips.4 \
|
||||
ipsec.4 \
|
||||
ipw.4 \
|
||||
iscsi_initiator.4 \
|
||||
isp.4 \
|
||||
ispfw.4 \
|
||||
iwi.4 \
|
||||
|
104
share/man/man4/iscsi_initiator.4
Normal file
104
share/man/man4/iscsi_initiator.4
Normal 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.
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
424
sys/dev/iscsi/initiator/isc_cam.c
Normal file
424
sys/dev/iscsi/initiator/isc_cam.c
Normal 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;
|
||||
}
|
786
sys/dev/iscsi/initiator/isc_sm.c
Normal file
786
sys/dev/iscsi/initiator/isc_sm.c
Normal 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);
|
||||
}
|
576
sys/dev/iscsi/initiator/isc_soc.c
Normal file
576
sys/dev/iscsi/initiator/isc_soc.c
Normal 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);
|
||||
}
|
258
sys/dev/iscsi/initiator/isc_subr.c
Normal file
258
sys/dev/iscsi/initiator/isc_subr.c
Normal 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;
|
||||
}
|
||||
}
|
810
sys/dev/iscsi/initiator/iscsi.c
Normal file
810
sys/dev/iscsi/initiator/iscsi.c
Normal 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);
|
407
sys/dev/iscsi/initiator/iscsi.h
Normal file
407
sys/dev/iscsi/initiator/iscsi.h
Normal 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)
|
567
sys/dev/iscsi/initiator/iscsi_subr.c
Normal file
567
sys/dev/iscsi/initiator/iscsi_subr.c
Normal 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;
|
||||
}
|
556
sys/dev/iscsi/initiator/iscsivar.h
Normal file
556
sys/dev/iscsi/initiator/iscsivar.h
Normal 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;
|
||||
}
|
||||
}
|
5
sys/modules/iscsi/Makefile
Normal file
5
sys/modules/iscsi/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SUBDIR= initiator
|
||||
|
||||
.include <bsd.subdir.mk>
|
14
sys/modules/iscsi/initiator/Makefile
Normal file
14
sys/modules/iscsi/initiator/Makefile
Normal 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>
|
Loading…
Reference in New Issue
Block a user