75a17f3ce7
NetBSD Foundation by Wasabi Systems, from othersrc/external/bsd/iscsi/{iscsictl,iscsid} to src/sbin/{iscsictl,iscsid}
1146 lines
26 KiB
C
1146 lines
26 KiB
C
/* $NetBSD: iscsic_test.c,v 1.1 2011/10/23 21:11:23 agc Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2006,2011 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Wasabi Systems, Inc.
|
|
*
|
|
* 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
|
|
*/
|
|
|
|
#ifdef ISCSI_TEST_MODE
|
|
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "iscsic_globals.h"
|
|
#include "iscsi_test.h"
|
|
|
|
#define WRITE_BUFFER_SIZE (64 * 1024)
|
|
|
|
|
|
#define hexdig(c) ((c & 0x40) ? (c & 0x07) + 9 : c & 0x0f)
|
|
|
|
|
|
typedef struct {
|
|
char *name;
|
|
int offset;
|
|
int size;
|
|
} off_sel_t;
|
|
|
|
typedef struct {
|
|
char *name;
|
|
int opt;
|
|
} str_sel_t;
|
|
|
|
typedef struct {
|
|
char *name;
|
|
int opt;
|
|
int type;
|
|
} mlopt_sel_t;
|
|
|
|
|
|
off_sel_t offtab[] = {
|
|
{"op", 0, 1}, /* Opcode */
|
|
{"dsl", 5, 3}, /* Data Segment Length */
|
|
{"lun", 8, 8}, /* SCSI,LUN */
|
|
{"itt", 16, 4}, /* Initiator Task Tag */
|
|
{"ttt", 20, 4}, /* Target Transfer Tag */
|
|
{"cmdsn", 24, 4}, /* Command Sequence Number (tx) */
|
|
{"statsn", 24, 4}, /* Status Sequence Number (rx) */
|
|
{"expstatsn", 28, 4}, /* Expected StatSN (tx) */
|
|
{"expcmdsn", 28, 4}, /* Expected CmdSN (rx) */
|
|
{"maxcmdsn", 32, 4}, /* Maximum CmdSN (rx) */
|
|
{"datasn", 36, 4}, /* Data Sequence Number */
|
|
{"expdatasn", 36, 4}, /* Expected DataSN */
|
|
{"boff", 40, 4}, /* Buffer Offset */
|
|
{"rescnt", 44, 4}, /* Residual Count */
|
|
{"hdigest", ISCSITEST_OFFSET_HEADERDIGEST, 4}, /* Header Digest */
|
|
{"data", ISCSITEST_OFFSET_DATA, 0}, /* Data Segment */
|
|
{"ddigest", ISCSITEST_OFFSET_DATADIGEST, 4}, /* Data Digest */
|
|
{"drvcmdsn", ISCSITEST_OFFSET_DRV_CMDSN, 4}, /* Driver CmdSN */
|
|
{NULL, 0, 0}
|
|
};
|
|
|
|
|
|
str_sel_t pdutab[] = {
|
|
{"any", ANY_PDU},
|
|
{"command", COMMAND_PDU},
|
|
{"response", RESPONSE_PDU},
|
|
{"taskreq", TASK_REQ_PDU},
|
|
{"taskrsp", TASK_RSP_PDU},
|
|
{"dataout", DATA_OUT_PDU},
|
|
{"datain", DATA_IN_PDU},
|
|
{"r2t", R2T_PDU},
|
|
{"asynch", ASYNCH_PDU},
|
|
{"textreq", TEXT_REQ_PDU},
|
|
{"textrsp", TEXT_RSP_PDU},
|
|
{"loginreq", LOGIN_REQ_PDU},
|
|
{"loginrsp", LOGIN_RSP_PDU},
|
|
{"logoutreq", LOGOUT_REQ_PDU},
|
|
{"logoutrsp", LOGOUT_RSP_PDU},
|
|
{"snack", SNACK_PDU},
|
|
{"reject", REJECT_PDU},
|
|
{"nopout", NOP_OUT_PDU},
|
|
{"nopin", NOP_IN_PDU},
|
|
{"invalid", INVALID_PDU},
|
|
{NULL, 0}
|
|
};
|
|
|
|
|
|
str_sel_t authtab[] = {
|
|
{"secneg", NEG_SECURITY}, /* First security negotiation PDU */
|
|
{"chapalg", NEG_CHAP_ALG}, /* CHAP authentication: algorithm specification. */
|
|
{"chapnr", NEG_CHAP_NAME_RESPONSE}, /* CHAP: Name and Response. */
|
|
{"opneg", NEG_OP_NEG}, /* First operational negotiation PDU. */
|
|
{NULL, 0}
|
|
};
|
|
|
|
|
|
str_sel_t offkindtab[] = {
|
|
{"aa", ABSOLUTE_ANY}, /* Absolute to start of connection, any kind */
|
|
{"ra", RELATIVE_ANY}, /* Relative to last processed PDU, any kind */
|
|
{"ar", ABSOLUTE_RX}, /* Absolute to start of connection, any kind, rx */
|
|
{"rr", RELATIVE_RX}, /* Relative to last processed PDU, any kind, rx */
|
|
{"at", ABSOLUTE_TX}, /* Absolute to start of connection, any kind, tx */
|
|
{"rt", RELATIVE_TX}, /* Relative to last processed PDU, any kind, tx */
|
|
{"ak", ABSOLUTE_PDUKIND}, /* Absolute to start of connection, same PDU kind */
|
|
{"ra", RELATIVE_PDUKIND}, /* Relative to last processed PDU, same PDU kind */
|
|
{NULL, 0}
|
|
};
|
|
|
|
|
|
/*
|
|
* get_hexbyte:
|
|
* Get a hex byte from argument.
|
|
*
|
|
* Parameter:
|
|
* arg argument
|
|
*
|
|
* Returns: value of byte, or -1 if not a hex byte.
|
|
*/
|
|
|
|
STATIC int
|
|
get_hexbyte(char *arg)
|
|
{
|
|
if (isxdigit(arg[0]) && !arg[1]) {
|
|
return hexdig(arg[0]);
|
|
}
|
|
if (isxdigit(arg[1]) && !arg[2]) {
|
|
return hexdig(arg[0]) << 4 | hexdig(arg[1]);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* get_connid:
|
|
* Get connection ID. Queries Daemon for numeric ID if the user specifies
|
|
* a symbolic connection name.
|
|
*
|
|
* Parameter:
|
|
* argc, argv (shifted)
|
|
* session
|
|
*
|
|
* Returns: connection ID or 0 if OK - else it doesn't return at all.
|
|
*/
|
|
|
|
STATIC uint32_t
|
|
get_connid(int argc, char **argv, uint32_t session)
|
|
{
|
|
iscsid_sym_id_t sid;
|
|
iscsid_get_connection_info_req_t req;
|
|
iscsid_response_t *rsp;
|
|
|
|
if (!cl_get_id('C', &sid, argc, argv)) {
|
|
return 0;
|
|
}
|
|
if (!sid.id) {
|
|
req.session_id.name[0] = '\0';
|
|
req.session_id.id = session;
|
|
strlcpy(req.connection_id.name, sid.name,
|
|
sizeof(req.connection_id.name));
|
|
req.connection_id.id = 0;
|
|
|
|
send_request(ISCSID_GET_CONNECTION_INFO, sizeof(req), &req);
|
|
rsp = get_response(FALSE);
|
|
if (rsp->status) {
|
|
status_error(rsp->status);
|
|
} else {
|
|
sid.id = ((iscsid_get_connection_info_rsp_t *) &rsp->parameter)
|
|
->connection_id.id;
|
|
}
|
|
free_response(rsp);
|
|
}
|
|
return sid.id;
|
|
}
|
|
|
|
/*
|
|
* get_offset:
|
|
* Get a PDU modification record
|
|
*
|
|
* Parameter:
|
|
* argc, argv program parameters (shifted)
|
|
* modp pointer to PDU modification record
|
|
*
|
|
* Returns: 1 if parameter present, 0 if not.
|
|
* Aborts app on bad parameter.
|
|
*/
|
|
|
|
STATIC int
|
|
get_pdumod(int argc, char **argv, iscsi_pdu_mod_t * modp)
|
|
{
|
|
int i, j, found;
|
|
char *sp;
|
|
int off = 0, size = 4, flags = 0;
|
|
|
|
for (i = 0, found = FALSE; i < argc; i++) {
|
|
if (argv[i] && argv[i][0] == '-' && argv[i][1] == 'o') {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
return 0;
|
|
}
|
|
sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
|
|
if (!sp || !*sp) {
|
|
arg_error(argv[i], "Offset value missing");
|
|
}
|
|
if (isdigit(*sp)) {
|
|
if (!sscanf(sp, "%i", &off)) {
|
|
arg_error(argv[i], "Numeric value expected");
|
|
}
|
|
} else {
|
|
for (j = 0; offtab[j].name != NULL; j++) {
|
|
if (strcmp(sp, offtab[j].name) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (offtab[j].name == NULL) {
|
|
arg_error(argv[i], "Invalid offset '%s'", sp);
|
|
}
|
|
off = offtab[j].offset;
|
|
size = offtab[j].size;
|
|
}
|
|
|
|
if (!argv[i][2]) {
|
|
argv[i] = argv[i + 1] = NULL;
|
|
i += 2;
|
|
} else
|
|
argv[i++] = NULL;
|
|
|
|
if (i >= argc)
|
|
arg_missing("Modification Value");
|
|
|
|
if (argv[i] && argv[i][0] == '-' && argv[i][1] == 's') {
|
|
sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
|
|
|
|
if (!sp || !*sp)
|
|
arg_error(argv[i], "Size value missing");
|
|
if (sscanf(sp, "%i", &size) == 0)
|
|
arg_error(argv[i], "Numeric value expected");
|
|
if (size <= 0 || size > 8)
|
|
arg_error(argv[i], "Invalid size %d", size);
|
|
|
|
if (!argv[i][2]) {
|
|
argv[i] = argv[i + 1] = NULL;
|
|
i += 2;
|
|
} else
|
|
argv[i++] = NULL;
|
|
|
|
if (i >= argc)
|
|
arg_missing("Modification Value");
|
|
}
|
|
|
|
if (argv[i] && argv[i][0] == '-' && argv[i][1] == 'a') {
|
|
flags |= ISCSITEST_MOD_FLAG_ADD_VAL;
|
|
argv[i++] = NULL;
|
|
if (i >= argc)
|
|
arg_missing("Modification Value");
|
|
}
|
|
|
|
sp = argv[i];
|
|
if (!sp || (!isxdigit(*sp) && *sp != '-' && *sp != '+'))
|
|
arg_missing("Modification Value");
|
|
|
|
if (i + 1 < argc && argv[i + 1] && isxdigit(argv[i + 1][0])) {
|
|
char *dp = modp->value;
|
|
int val, n = size;
|
|
|
|
do {
|
|
if (0 > (val = get_hexbyte(sp)))
|
|
arg_error(argv[i], "Hex byte expected");
|
|
if (n) {
|
|
*dp++ = (uint8_t) val;
|
|
n--;
|
|
}
|
|
argv[i++] = NULL;
|
|
sp = (i < argc) ? argv[i] : NULL;
|
|
} while (sp && isxdigit(*sp));
|
|
} else {
|
|
int64_t val;
|
|
|
|
if (!sscanf(sp, "%qi", &val))
|
|
arg_error(argv[i], "Numeric value expected");
|
|
*((int64_t *) modp->value) = val;
|
|
argv[i] = NULL;
|
|
modp->flags |= ISCSITEST_MOD_FLAG_REORDER;
|
|
}
|
|
|
|
modp->offset = (uint32_t) off;
|
|
modp->size = (uint16_t) size;
|
|
modp->flags = (uint16_t) flags;
|
|
|
|
DEB(9, ("get_pdumod offset %d, size %d, flags %x, val %qd\n", off, size,
|
|
flags, *((uint64_t *) modp->value)));
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* get_updopt:
|
|
* Get PDU update options for send_pdu
|
|
*
|
|
* Parameter:
|
|
* argc, argv program parameters (shifted)
|
|
*
|
|
* Returns: Value of option, 0 if not present.
|
|
* Aborts app on bad parameter.
|
|
*/
|
|
|
|
STATIC int
|
|
get_updopt(int argc, char **argv, int send)
|
|
{
|
|
int i;
|
|
char *sp;
|
|
int val = ISCSITEST_SFLAG_UPDATE_FIELDS;
|
|
|
|
for (i = 0;
|
|
i < argc && (!argv[i] || argv[i][0] != '-' || argv[i][1] != 'u');
|
|
i++) {
|
|
}
|
|
if (i >= argc) {
|
|
return 0;
|
|
}
|
|
sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
|
|
if (!sp || !*sp || *sp == '-') {
|
|
argv[i] = NULL;
|
|
return val;
|
|
}
|
|
|
|
while (*sp) {
|
|
switch (*sp) {
|
|
case 'h':
|
|
val |= ISCSITEST_SFLAG_NO_HEADER_DIGEST;
|
|
break;
|
|
|
|
case 'i':
|
|
val |= ISCSITEST_SFLAG_NO_DATA_DIGEST;
|
|
break;
|
|
|
|
case 'p':
|
|
if (send) {
|
|
val |= ISCSITEST_SFLAG_NO_PADDING;
|
|
break;
|
|
}
|
|
/* FALLTHROUGH */
|
|
default:
|
|
arg_error(argv[i], "Invalid update option '%c'", *sp);
|
|
}
|
|
sp++;
|
|
}
|
|
if (!argv[i][2]) {
|
|
argv[i + 1] = NULL;
|
|
}
|
|
argv[i] = NULL;
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
* get_stropt:
|
|
* Get an option specified by a string
|
|
*
|
|
* Parameter:
|
|
* argc, argv program parameters (shifted)
|
|
* flagp pointer to flags result
|
|
*
|
|
* Returns: Value of string, -1 if not present.
|
|
* Aborts app on bad parameter.
|
|
*/
|
|
|
|
STATIC int
|
|
get_stropt(int argc, char **argv, char selc, str_sel_t * table)
|
|
{
|
|
int i, j;
|
|
char *sp;
|
|
int val = -1;
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
if (!argv[i] || argv[i][0] != '-') {
|
|
continue;
|
|
}
|
|
|
|
if (argv[i][1] == selc) {
|
|
sp = (argv[i][2]) ? &argv[i][2]
|
|
: ((i + 1 < argc) ? argv[i + 1] : NULL);
|
|
|
|
if (!sp || !*sp) {
|
|
arg_error(argv[i], "Option missing");
|
|
}
|
|
for (j = 0; table[j].name != NULL; j++) {
|
|
if (strcmp(sp, table[j].name) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (table[j].name == NULL) {
|
|
arg_error(argv[i], "Invalid option '%s'", sp);
|
|
}
|
|
val = table[j].opt;
|
|
|
|
if (!argv[i][2]) {
|
|
argv[i + 1] = NULL;
|
|
}
|
|
argv[i] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
DEB(10, ("get_stropt -%c returns %d\n", selc, val));
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
* get_mlint:
|
|
* Get a multiletter integer option
|
|
*
|
|
* Parameter:
|
|
* argc, argv program parameters (shifted)
|
|
*
|
|
* Returns: Value of option, -1 if not present.
|
|
* Aborts app on bad parameter.
|
|
*/
|
|
|
|
STATIC int
|
|
get_mlint(int argc, char **argv, char *opt)
|
|
{
|
|
int i;
|
|
char *sp;
|
|
int val = -1;
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
if (!argv[i] || argv[i][0] != '-') {
|
|
continue;
|
|
}
|
|
if (strcmp(&argv[i][1], opt) == 0) {
|
|
sp = (i + 1 < argc) ? argv[i + 1] : NULL;
|
|
|
|
if (!sp || !*sp) {
|
|
arg_error(argv[i], "Missing numeric value");
|
|
}
|
|
if (sscanf(sp, "%i", &val) == 0) {
|
|
arg_error(argv[i], "Invalid number");
|
|
}
|
|
argv[i] = argv[i + 1] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
DEB(10, ("get_mlint returns %d\n", val));
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
* get_mlbool:
|
|
* Get a multiletter boolean option
|
|
*
|
|
* Parameter:
|
|
* argc, argv program parameters (shifted)
|
|
*
|
|
* Returns: Value of option (0 or 1), -1 if not present.
|
|
* Aborts app on bad parameter.
|
|
*/
|
|
|
|
STATIC int
|
|
get_mlbool(int argc, char **argv, char *opt)
|
|
{
|
|
int i;
|
|
char *sp;
|
|
int val = -1;
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
if (!argv[i] || argv[i][0] != '-') {
|
|
continue;
|
|
}
|
|
if (strcmp(&argv[i][1], opt) == 0) {
|
|
sp = (i + 1 < argc) ? argv[i + 1] : NULL;
|
|
|
|
if (!sp || !*sp) {
|
|
arg_error(argv[i], "Missing truth value");
|
|
}
|
|
if (sp[1]) {
|
|
arg_error(argv[i], "Invalid truth value '%s'", sp);
|
|
}
|
|
switch (*sp) {
|
|
case 't':
|
|
case 'T':
|
|
case '1':
|
|
val = 1;
|
|
break;
|
|
|
|
case 'f':
|
|
case 'F':
|
|
case '0':
|
|
val = 0;
|
|
break;
|
|
|
|
default:
|
|
arg_error(argv[i], "Bad truth value '%c'", *sp);
|
|
}
|
|
|
|
argv[i] = argv[i + 1] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
DEB(10, ("get_mlbool returns %d\n", val));
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
* get_blob:
|
|
* Get a large binary value.
|
|
*
|
|
* Parameter:
|
|
* argc, argv program parameters (shifted)
|
|
* dp destination
|
|
* maxlen maximum size
|
|
* name name of parameter for error message if missing
|
|
*
|
|
* Returns: Size of value in bytes.
|
|
* Aborts app on bad parameter.
|
|
*/
|
|
|
|
STATIC int
|
|
get_blob(char ident, int argc, char **argv, uint8_t * dp, int maxlen, char *name)
|
|
{
|
|
int i, l;
|
|
char *arg;
|
|
int size = 0;
|
|
|
|
for (i = 0;
|
|
i < argc && (!argv[i] || argv[i][0] != '-' || argv[i][1] != ident);
|
|
i++) {
|
|
}
|
|
if (i >= argc) {
|
|
arg_missing(name);
|
|
}
|
|
arg = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
|
|
if (!arg || !*arg) {
|
|
arg_missing(name);
|
|
}
|
|
if (!argv[i][2]) {
|
|
argv[i++] = NULL;
|
|
}
|
|
while (arg && *arg != '-') {
|
|
if (0 <= (l = get_hexbyte(arg))) {
|
|
if (!maxlen) {
|
|
arg_error(argv[i], "Buffer size exceeded");
|
|
}
|
|
*dp++ = (uint8_t) l;
|
|
size++;
|
|
maxlen--;
|
|
} else {
|
|
if (arg[0] == '.') {
|
|
arg++;
|
|
}
|
|
if (!arg[0]) {
|
|
arg_error(argv[i], "Leading dot removed, nothing left");
|
|
}
|
|
l = strlen(arg);
|
|
if (l >= maxlen) {
|
|
arg_error(argv[i], "String exceeds buffer size");
|
|
}
|
|
strlcpy(dp, arg, maxlen);
|
|
dp += l;
|
|
size += l;
|
|
maxlen -= l;
|
|
}
|
|
argv[i++] = NULL;
|
|
if (i >= argc) {
|
|
break;
|
|
}
|
|
arg = argv[i];
|
|
}
|
|
if (!size) {
|
|
arg_missing(name);
|
|
}
|
|
DEB(10, ("get_blob returns %d\n", size));
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
/*
|
|
* get_file:
|
|
* Get a large binary value from file, or, if no file parameter is present,
|
|
* allocate buffer for blob.
|
|
*
|
|
* Parameter:
|
|
* argc, argv program parameters (shifted)
|
|
* pbufp pointer to buffer pointer
|
|
* plen pointer to size of buffer. The initial size contained
|
|
* in this parameter is used as an offset into the target
|
|
* buffer and is added to the allocation size.
|
|
* psize pointer to int to receive size option.
|
|
* rd TRUE if read, FALSE if write
|
|
*
|
|
* Returns: handle of file if file parameter present, -1 if not.
|
|
* Aborts app on bad parameter.
|
|
*/
|
|
|
|
STATIC int
|
|
get_file(int argc, char **argv, caddr_t *pbufp, uint32_t *plen, int *psize,
|
|
int rd)
|
|
{
|
|
char fname[ISCSI_STRING_LENGTH];
|
|
int hnd = -1, size;
|
|
off_t fsize = 0;
|
|
uint8_t *bp = buf;
|
|
|
|
size = *psize = cl_get_int('s', argc, argv);
|
|
|
|
if (cl_get_string('f', fname, argc, argv)) {
|
|
hnd = open(fname, (rd) ? O_RDONLY : (O_WRONLY | O_CREAT | O_TRUNC),
|
|
0x666);
|
|
if (hnd < 0) {
|
|
io_error("Opening file '%s'", fname);
|
|
}
|
|
if (rd) {
|
|
fsize = lseek(hnd, 0, SEEK_END);
|
|
lseek(hnd, 0, SEEK_SET);
|
|
if (!fsize) {
|
|
gen_error("File '%s' is empty", fname);
|
|
}
|
|
}
|
|
}
|
|
if (!size) {
|
|
if (hnd >= 0) {
|
|
size = (rd) ? fsize : WRITE_BUFFER_SIZE;
|
|
} else {
|
|
size = BUF_SIZE - *plen;
|
|
}
|
|
}
|
|
|
|
if (size < fsize) {
|
|
fsize = size;
|
|
}
|
|
size += *plen;
|
|
|
|
if (size > BUF_SIZE && (bp = calloc(1, size)) == NULL) {
|
|
gen_error("Can't allocate %d bytes for buffer", size);
|
|
}
|
|
(void) memset(bp, 0x0, size);
|
|
|
|
*pbufp = (caddr_t) bp;
|
|
bp += *plen;
|
|
|
|
*plen = size;
|
|
|
|
if (rd && hnd >= 0) {
|
|
if (read(hnd, bp, fsize) != fsize) {
|
|
io_error("Reading file '%s'", fname);
|
|
}
|
|
close(hnd);
|
|
}
|
|
|
|
return hnd;
|
|
}
|
|
|
|
|
|
/*
|
|
* t_define:
|
|
* Handle the test define command.
|
|
*
|
|
* Parameter: argc, argv (shifted)
|
|
*
|
|
* Returns: 0 if OK.
|
|
*/
|
|
|
|
STATIC int
|
|
t_define(int argc, char **argv)
|
|
{
|
|
iscsi_test_define_parameters_t def = { 0 };
|
|
int rc;
|
|
|
|
if ((def.test_id = cl_get_int('T', argc, argv)) == 0) {
|
|
arg_missing("define: Test ID");
|
|
}
|
|
if ((def.session_id = get_sessid(argc, argv, TRUE)) != 0) {
|
|
def.connection_id = get_connid(argc, argv, def.session_id);
|
|
}
|
|
if ((rc = get_mlint(argc, argv, "mr2t")) != -1) {
|
|
def.r2t_val = (uint32_t) rc;
|
|
def.options |= ISCSITEST_NEGOTIATE_R2T;
|
|
}
|
|
if ((rc = get_mlint(argc, argv, "mbl")) != -1) {
|
|
def.maxburst_val = (uint32_t) rc;
|
|
def.options |= ISCSITEST_NEGOTIATE_MAXBURST;
|
|
}
|
|
if ((rc = get_mlint(argc, argv, "fbl")) != -1) {
|
|
def.firstburst_val = (uint32_t) rc;
|
|
def.options |= ISCSITEST_NEGOTIATE_FIRSTBURST;
|
|
}
|
|
if (get_mlbool(argc, argv, "ir2t") > 0) {
|
|
def.options |= ISCSITEST_OVERRIDE_INITIALR2T;
|
|
}
|
|
if (get_mlbool(argc, argv, "imm") == 0) {
|
|
def.options |= ISCSITEST_OVERRIDE_IMMDATA;
|
|
}
|
|
if ((rc = get_mlbool(argc, argv, "tcmd")) != -1) {
|
|
def.options |= (rc) ? ISCSITEST_OPT_ENABLE_CCB_TIMEOUT
|
|
: ISCSITEST_OPT_DISABLE_CCB_TIMEOUT;
|
|
}
|
|
if ((rc = get_mlbool(argc, argv, "tconn")) != -1) {
|
|
def.options |= (rc) ? ISCSITEST_OPT_ENABLE_CONN_TIMEOUT
|
|
: ISCSITEST_OPT_DISABLE_CONN_TIMEOUT;
|
|
}
|
|
if ((rc = get_mlint(argc, argv, "rlrx")) != -1) {
|
|
def.options |= ISCSITEST_OPT_USE_RANDOM_RX;
|
|
def.lose_random_rx = (uint8_t) rc;
|
|
}
|
|
if ((rc = get_mlint(argc, argv, "rltx")) != -1) {
|
|
def.options |= ISCSITEST_OPT_USE_RANDOM_TX;
|
|
def.lose_random_tx = (uint8_t) rc;
|
|
}
|
|
check_extra_args(argc, argv);
|
|
|
|
DEB(1, ("DefTest: tid=%d, opt=%x, lrtx=%d, lrrx=%d, sid=%d, cid=%d\n",
|
|
def.test_id, def.options, def.lose_random_tx, def.lose_random_rx,
|
|
def.session_id, def.connection_id));
|
|
DEB(1, (" r2t=%d, mbl=%d, fbl=%d, erl=%d\n",
|
|
def.r2t_val, def.maxburst_val, def.firstburst_val, def.erl_val));
|
|
|
|
rc = ioctl(driver, ISCSI_TEST_DEFINE, &def);
|
|
|
|
if (def.status) {
|
|
status_error(def.status);
|
|
}
|
|
if (rc) {
|
|
io_error("define: I/O command");
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* t_add_pdumod:
|
|
* Handle the test add_pdumod command.
|
|
*
|
|
* Parameter: argc, argv (shifted)
|
|
*
|
|
* Returns: 0 if OK.
|
|
*/
|
|
|
|
STATIC int
|
|
t_add_pdumod(int argc, char **argv)
|
|
{
|
|
iscsi_test_add_modification_parameters_t def = { 0 };
|
|
int rc, rd, hnd = -1;
|
|
|
|
if ((def.test_id = cl_get_int('T', argc, argv)) == 0) {
|
|
arg_missing("add_pdumod: Test ID");
|
|
}
|
|
if ((def.pdu_offset = cl_get_uint('p', argc, argv)) == (uint32_t) -1) {
|
|
arg_missing("add_pdumod: PDU Number");
|
|
}
|
|
if ((rc = get_stropt(argc, argv, 'b', offkindtab)) != -1) {
|
|
def.which_offset = rc;
|
|
}
|
|
if ((rc = get_stropt(argc, argv, 'k', pdutab)) != -1) {
|
|
def.which_pdu = rc;
|
|
}
|
|
def.options = get_updopt(argc, argv, FALSE);
|
|
|
|
if (cl_get_opt('w', argc, argv)) {
|
|
def.options |= ISCSITEST_OPT_WAIT_FOR_COMPLETION;
|
|
}
|
|
if (cl_get_opt('n', argc, argv)) {
|
|
def.options |= ISCSITEST_OPT_NO_RESPONSE_PDU;
|
|
}
|
|
if ((rc = get_mlbool(argc, argv, "tcmd")) != -1) {
|
|
def.options |= (rc) ? ISCSITEST_OPT_ENABLE_CCB_TIMEOUT
|
|
: ISCSITEST_OPT_DISABLE_CCB_TIMEOUT;
|
|
}
|
|
if ((rc = get_mlbool(argc, argv, "tconn")) != -1) {
|
|
def.options |= (rc) ? ISCSITEST_OPT_ENABLE_CONN_TIMEOUT
|
|
: ISCSITEST_OPT_DISABLE_CONN_TIMEOUT;
|
|
}
|
|
if ((rc = get_mlint(argc, argv, "rlrx")) != -1) {
|
|
def.options |= ISCSITEST_OPT_USE_RANDOM_RX;
|
|
def.lose_random_rx = (uint8_t) rc;
|
|
}
|
|
if ((rc = get_mlint(argc, argv, "rltx")) != -1) {
|
|
def.options |= ISCSITEST_OPT_USE_RANDOM_TX;
|
|
def.lose_random_tx = (uint8_t) rc;
|
|
}
|
|
|
|
|
|
if ((rd = cl_get_opt('r', argc, argv)) != 0)
|
|
hnd = get_file(argc, argv, &def.pdu_ptr, &def.pdu_size, &rc, FALSE);
|
|
|
|
if (cl_get_opt('D', argc, argv))
|
|
def.options |= ISCSITEST_OPT_DISCARD_PDU;
|
|
|
|
if (cl_get_opt('K', argc, argv))
|
|
def.options |= ISCSITEST_KILL_CONNECTION;
|
|
|
|
if (!(def.options & (ISCSITEST_OPT_DISCARD_PDU |
|
|
ISCSITEST_KILL_CONNECTION))) {
|
|
iscsi_pdu_mod_t *modp = def.mod_ptr = (iscsi_pdu_mod_t *) buf;
|
|
|
|
while (get_pdumod(argc, argv, modp)) {
|
|
if (modp->offset == ISCSITEST_OFFSET_HEADERDIGEST)
|
|
def.options |= ISCSITEST_SFLAG_NO_HEADER_DIGEST;
|
|
else if (modp->offset == ISCSITEST_OFFSET_DATADIGEST)
|
|
def.options |= ISCSITEST_SFLAG_NO_DATA_DIGEST;
|
|
|
|
modp++;
|
|
def.num_pdu_mods++;
|
|
}
|
|
if (!rd && !def.num_pdu_mods)
|
|
arg_missing("add_pdumod: PDU Modification");
|
|
}
|
|
|
|
check_extra_args(argc, argv);
|
|
|
|
DEB(1, ("AddMod: tid=%d, opt=%x, offs=%d, which=%d, pdukind=%d, "
|
|
"num=%d, rd_size=%d\n",
|
|
def.test_id, def.options, def.pdu_offset, def.which_offset,
|
|
def.which_pdu, def.num_pdu_mods, def.pdu_size));
|
|
|
|
#ifdef ISCSI_DEBUG
|
|
if (def.num_pdu_mods)
|
|
DEB(1, (" 0: offset=%d, size=%d, flags=%d, val=%qx\n",
|
|
def.mod_ptr[0].offset, def.mod_ptr[0].size,
|
|
def.mod_ptr[0].flags, *((uint64_t *) def.mod_ptr[0].value)));
|
|
#endif
|
|
|
|
rc = ioctl(driver, ISCSI_TEST_ADD_MODIFICATION, &def);
|
|
|
|
if (def.status)
|
|
status_error(def.status);
|
|
if (rc)
|
|
io_error("add_pdumod: I/O command");
|
|
|
|
if (!rd)
|
|
return 0;
|
|
|
|
if (!def.pdu_actual_size)
|
|
gen_error("add_pdumod: No data read");
|
|
|
|
if (hnd >= 0) {
|
|
rc = def.pdu_actual_size;
|
|
rc -= write(hnd, def.pdu_ptr, def.pdu_actual_size);
|
|
close(hnd);
|
|
if (rc) {
|
|
io_error("add_pdumod: Writing file");
|
|
}
|
|
} else {
|
|
dump_data("PDU Data", def.pdu_ptr, def.pdu_actual_size);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* t_add_negmod:
|
|
* Handle the test add_negmod command.
|
|
*
|
|
* Parameter: argc, argv (shifted)
|
|
*
|
|
* Returns: 0 if OK.
|
|
*/
|
|
|
|
STATIC int
|
|
t_add_negmod(int argc, char **argv)
|
|
{
|
|
iscsi_test_add_negotiation_parameters_t def = { 0 };
|
|
iscsi_test_negotiation_descriptor_t *neg;
|
|
int hnd, rc, size;
|
|
|
|
if ((def.test_id = cl_get_int('T', argc, argv)) == 0) {
|
|
arg_missing("add_negmond: Test ID");
|
|
}
|
|
def.neg_descriptor_size = sizeof(iscsi_test_negotiation_descriptor_t);
|
|
hnd = get_file(argc, argv, &def.neg_descriptor_ptr,
|
|
&def.neg_descriptor_size, &size, TRUE);
|
|
neg = (iscsi_test_negotiation_descriptor_t *) def.neg_descriptor_ptr;
|
|
neg->size = def.neg_descriptor_size
|
|
- sizeof(iscsi_test_negotiation_descriptor_t);
|
|
|
|
if (hnd < 0) {
|
|
neg->size = get_blob('d', argc, argv, neg->value, neg->size,
|
|
"Negotiation Value");
|
|
if (size) {
|
|
neg->size = size;
|
|
}
|
|
def.neg_descriptor_size = neg->size
|
|
+ sizeof(iscsi_test_negotiation_descriptor_t);
|
|
}
|
|
|
|
/* XXX - agc */
|
|
if (0 > (neg->state = get_stropt(argc, argv, 'p', authtab)))
|
|
arg_missing("add_negmod: Negotiation Phase");
|
|
|
|
if (cl_get_opt('r', argc, argv))
|
|
neg->flags |= ISCSITEST_NEGOPT_REPLACE;
|
|
|
|
check_extra_args(argc, argv);
|
|
|
|
DEB(1, ("AddNeg: tid=%d, phase=%d, flags=%x, hnd=%d, size=%d, "
|
|
"ptr=%x, data=%s\n",
|
|
def.test_id, neg->state, neg->flags, hnd, def.neg_descriptor_size,
|
|
(int) def.neg_descriptor_ptr, neg->value));
|
|
|
|
rc = ioctl(driver, ISCSI_TEST_ADD_NEGOTIATION, &def);
|
|
|
|
if (def.status) {
|
|
status_error(def.status);
|
|
}
|
|
if (rc) {
|
|
io_error("add_negmod: I/O command");
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* t_send_pdu:
|
|
* Handle the test send_pdu command.
|
|
*
|
|
* Parameter: argc, argv (shifted)
|
|
*
|
|
* Returns: 0 if OK.
|
|
*/
|
|
|
|
STATIC int
|
|
t_send_pdu(int argc, char **argv)
|
|
{
|
|
iscsi_test_send_pdu_parameters_t def = { 0 };
|
|
int rc, size;
|
|
|
|
if ((def.test_id = cl_get_int('T', argc, argv)) == 0) {
|
|
arg_missing("send_pdu: Test ID");
|
|
}
|
|
def.options = get_updopt(argc, argv, TRUE);
|
|
|
|
if (cl_get_opt('K', argc, argv)) {
|
|
def.options |= ISCSITEST_KILL_CONNECTION;
|
|
}
|
|
/* weird */
|
|
if (0 > get_file(argc, argv, &def.pdu_ptr, &def.pdu_size, &size, TRUE)) {
|
|
def.pdu_size = get_blob('d', argc, argv, buf, def.pdu_size, "PDU Data");
|
|
if (size) {
|
|
def.pdu_size = size;
|
|
}
|
|
}
|
|
check_extra_args(argc, argv);
|
|
|
|
rc = ioctl(driver, ISCSI_TEST_SEND_PDU, &def);
|
|
|
|
if (def.status) {
|
|
status_error(def.status);
|
|
}
|
|
if (rc) {
|
|
io_error("send_pdu: I/O command");
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* t_iocommand:
|
|
* Handle the test iocommand command.
|
|
*
|
|
* Parameter: argc, argv (shifted)
|
|
*
|
|
* Returns: 0 if OK - else it doesn't return at all.
|
|
*/
|
|
|
|
STATIC int
|
|
t_iocommand(int argc, char **argv)
|
|
{
|
|
iscsi_iocommand_parameters_t io = { 0 };
|
|
int nodata, rd, rc, size, hnd = -1;
|
|
|
|
if ((io.session_id = get_sessid(argc, argv, FALSE)) == 0) {
|
|
return 1;
|
|
}
|
|
io.connection_id = get_connid(argc, argv, io.session_id);
|
|
|
|
io.lun = cl_get_longlong('l', argc, argv);
|
|
|
|
if (cl_get_opt('i', argc, argv)) {
|
|
io.options.immediate = 1;
|
|
}
|
|
rd = cl_get_opt('r', argc, argv);
|
|
|
|
nodata = cl_get_opt('n', argc, argv);
|
|
|
|
if (rd && nodata) {
|
|
gen_error("iocommand: Conflicting options -r -n\n");
|
|
}
|
|
io.req.cmdlen = get_blob('c', argc, argv, io.req.cmd, sizeof(io.req.cmd),
|
|
"CCB");
|
|
|
|
if ((rc = cl_get_int('S', argc, argv)) != 0) {
|
|
if (rc < 0 || rc > 16) {
|
|
gen_error("Iocommand: Invalid CCB size %d", rc);
|
|
}
|
|
io.req.cmdlen = rc;
|
|
}
|
|
|
|
if (!nodata) {
|
|
/* weird */
|
|
if (0 > (hnd = get_file(argc, argv, &io.req.databuf,
|
|
(uint32_t *) &io.req.datalen, &size, !rd)) && !rd) {
|
|
io.req.datalen = get_blob('d', argc, argv, io.req.databuf,
|
|
io.req.datalen, "Command Data");
|
|
if (size) {
|
|
io.req.datalen = size;
|
|
}
|
|
}
|
|
}
|
|
|
|
check_extra_args(argc, argv);
|
|
|
|
DEB(1, ("IoCmd: sid=%d, rd=%d, cmdlen=%d, cmd=%02x %02x %02x.., "
|
|
"len=%d, ptr=%x\n",
|
|
io.session_id, rd, io.req.cmdlen, io.req.cmd[0], io.req.cmd[1],
|
|
io.req.cmd[2], (int) io.req.datalen, (int) io.req.databuf));
|
|
|
|
if ((rc = do_ioctl(&io, rd)) != 0) {
|
|
return rc;
|
|
}
|
|
if (rd) {
|
|
if (!io.req.datalen_used) {
|
|
gen_error("iocommand: No Data!");
|
|
}
|
|
if (hnd >= 0) {
|
|
rc = io.req.datalen_used;
|
|
rc -= write(hnd, io.req.databuf, io.req.datalen_used);
|
|
close(hnd);
|
|
if (rc) {
|
|
io_error("iocommand: Writing file");
|
|
}
|
|
} else {
|
|
dump_data("Command Data", io.req.databuf, io.req.datalen_used);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* t_cancel:
|
|
* Handle the test cancel command.
|
|
*
|
|
* Parameter: argc, argv (shifted)
|
|
*
|
|
* Returns: 0 if OK.
|
|
*/
|
|
|
|
STATIC int
|
|
t_cancel(int argc, char **argv)
|
|
{
|
|
iscsi_test_cancel_parameters_t def = { 0 };
|
|
int rc;
|
|
|
|
if ((def.test_id = cl_get_int('T', argc, argv)) == 0) {
|
|
arg_missing("cancel: Test ID");
|
|
}
|
|
check_extra_args(argc, argv);
|
|
|
|
DEB(1, ("Cancel Test: tid=%d\n", def.test_id));
|
|
|
|
rc = ioctl(driver, ISCSI_TEST_CANCEL, &def);
|
|
|
|
if (def.status) {
|
|
status_error(def.status);
|
|
}
|
|
if (rc) {
|
|
io_error("cancel: I/O command");
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------ */
|
|
|
|
static command_t tests[] = {
|
|
{"define", t_define},
|
|
{"add_pdumod", t_add_pdumod},
|
|
{"add_negmod", t_add_negmod},
|
|
{"send_pdu", t_send_pdu},
|
|
{"iocommand", t_iocommand},
|
|
{"cancel", t_cancel},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
/*
|
|
* test:
|
|
* Handle the test command.
|
|
*
|
|
* Parameter: argc, argv (shifted)
|
|
*
|
|
* Returns: 0 if OK.
|
|
*/
|
|
|
|
int
|
|
test(int argc, char **argv)
|
|
{
|
|
command_t *c;
|
|
|
|
if (argv[0] == NULL || argv[0][1] == '-') {
|
|
arg_missing("Test command");
|
|
}
|
|
for (c = tests; c->cmd != NULL; c++) {
|
|
if (strcmp(c->cmd, argv[0]) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (c->cmd == NULL) {
|
|
gen_error("Unkown test command %s", argv[0]);
|
|
}
|
|
return c->proc(argc - 1, &argv[1]);
|
|
}
|
|
|
|
|
|
#endif
|