/*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
* By downloading, copying, installing or using the software you agree
* to this license. If you do not agree to this license, do not
* download, install, copy or use the software.
*
* Intel License Agreement
*
* Copyright (c) 2000, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* -Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* -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.
*
* -The name of Intel Corporation may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL
* 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 "config.h"
#include <sys/types.h>
#include <sys/param.h>
#include <assert.h>
#include <stdlib.h>
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#include "iscsiprotocol.h"
#include "conffile.h"
#include "storage.h"
#include "target.h"
#include "device.h"
#include "iscsi-md5.h"
#include "parameters.h"
#include "iscsi.h"
enum {
TARGET_SHUT_DOWN = 0,
TARGET_INITIALIZING = 1,
TARGET_INITIALIZED = 2,
TARGET_SHUTTING_DOWN = 3
};
/***********
* Private *
***********/
static target_session_t *g_session;
static iscsi_queue_t g_session_q;
static iscsi_mutex_t g_session_q_mutex;
/*********************
* Private Functions *
*********************/
static char *
get_iqn(target_session_t *sess, uint32_t t, char *buf, size_t size)
{
targv_t *targv;
targv = sess->target->lunv;
if (targv->v[t].iqn != NULL) {
(void) strlcpy(buf, targv->v[t].iqn, size);
return buf;
}
(void) snprintf(buf, size, "%s:%s",
iscsi_target_getvar(sess->target, "iqn"),
targv->v[t].target);
return buf;
}
static int
reject_t(target_session_t * sess, uint8_t *header, uint8_t reason)
{
iscsi_reject_t reject;
uint8_t rsp_header[ISCSI_HEADER_LEN];
iscsi_err(__FILE__, __LINE__, "reject %x\n", reason);
reject.reason = reason;
reject.length = ISCSI_HEADER_LEN;
reject.StatSN = ++(sess->StatSN);
reject.ExpCmdSN = sess->ExpCmdSN;
reject.MaxCmdSN = sess->MaxCmdSN;
reject.DataSN = 0; /* SNACK not yet implemented */
if (iscsi_reject_encap(rsp_header, &reject) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_reject_encap() failed\n");
return -1;
}
if (iscsi_sock_send_header_and_data(sess->sock, rsp_header,
ISCSI_HEADER_LEN, header, ISCSI_HEADER_LEN, 0) !=
2 * ISCSI_HEADER_LEN) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_send_header_and_data() failed\n");
return -1;
}
return 0;
}
static int
scsi_command_t(target_session_t *sess, uint8_t *header)
{
iscsi_scsi_cmd_args_t scsi_cmd;
iscsi_read_data_t data;
iscsi_scsi_rsp_t scsi_rsp;
target_cmd_t cmd;
uint32_t DataSN = 0;
uint8_t rsp_header[ISCSI_HEADER_LEN];
struct iovec *sg_new = NULL;
int result;
(void) memset(&scsi_cmd, 0x0, sizeof(scsi_cmd));
scsi_cmd.ahs = NULL;
scsi_cmd.send_buffer = NULL;
if (iscsi_scsi_cmd_decap(header, &scsi_cmd) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_scsi_cmd_decap() failed\n");
result = -1;
goto out;
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"session %d: SCSI Command (CmdSN %u, op %#x)\n",
sess->id, scsi_cmd.CmdSN, scsi_cmd.cdb[0]);
/* For Non-immediate commands, the CmdSN should be between ExpCmdSN */
/* and MaxCmdSN, inclusive of both. Otherwise, ignore the command */
if (!scsi_cmd.immediate &&
(scsi_cmd.CmdSN < sess->ExpCmdSN ||
scsi_cmd.CmdSN > sess->MaxCmdSN)) {
iscsi_err(__FILE__, __LINE__,
"CmdSN(%d) of SCSI Command not valid, "
"ExpCmdSN(%d) MaxCmdSN(%d). Ignoring the command\n",
scsi_cmd.CmdSN, sess->ExpCmdSN, sess->MaxCmdSN);
result = 0;
goto out;
}
/* Arg check. */
scsi_cmd.attr = 0; /* Temp fix FIXME */
/*
* RETURN_NOT_EQUAL("ATTR (FIX ME)", scsi_cmd.attr, 0, NO_CLEANUP,
* -1);
*/
/* Check Numbering */
if (scsi_cmd.CmdSN != sess->ExpCmdSN) {
iscsi_warn(__FILE__, __LINE__,
"Expected CmdSN %d, got %d. "
"(ignoring and resetting expectations)\n",
sess->ExpCmdSN, scsi_cmd.CmdSN);
sess->ExpCmdSN = scsi_cmd.CmdSN;
}
/* Check Transfer Lengths */
if (sess->sess_params.first_burst_length
&& (scsi_cmd.length > sess->sess_params.first_burst_length)) {
iscsi_err(__FILE__, __LINE__,
"scsi_cmd.length (%u) > FirstBurstLength (%u)\n",
scsi_cmd.length, sess->sess_params.first_burst_length);
scsi_cmd.status = 0x02;
scsi_cmd.length = 0;
goto response;
}
if (sess->sess_params.max_dataseg_len &&
scsi_cmd.length > sess->sess_params.max_dataseg_len) {
iscsi_err(__FILE__, __LINE__,
"scsi_cmd.length (%u) > MaxRecvDataSegmentLength "
"(%u)\n",
scsi_cmd.length, sess->sess_params.max_dataseg_len);
result = -1;
goto out;
}
#if 0
/* commented out in original Intel reference code */
if (scsi_cmd.final && scsi_cmd.output) {
RETURN_NOT_EQUAL("Length", scsi_cmd.length,
scsi_cmd.trans_len, NO_CLEANUP, -1);
}
#endif
/* Read AHS. Need to optimize/clean this. */
/* We should not be calling malloc(). */
/* We need to check for properly formated AHS segments. */
if (scsi_cmd.ahs_len) {
uint32_t ahs_len;
uint8_t *ahs_ptr;
uint8_t ahs_type;
iscsi_trace(TRACE_ISCSI_DEBUG,
"reading %u bytes AHS\n", scsi_cmd.ahs_len);
scsi_cmd.ahs = iscsi_malloc_atomic((unsigned)scsi_cmd.ahs_len);
if (scsi_cmd.ahs == NULL) {
iscsi_err(__FILE__, __LINE__,
"iscsi_malloc_atomic() failed\n");
result = -1;
goto out;
}
if (iscsi_sock_msg(sess->sock, 0, (unsigned)scsi_cmd.ahs_len,
scsi_cmd.ahs, 0) != scsi_cmd.ahs_len) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_msg() failed\n");
result = -1;
goto out;
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"read %u bytes AHS\n", scsi_cmd.ahs_len);
for (ahs_ptr = scsi_cmd.ahs;
ahs_ptr < (scsi_cmd.ahs + scsi_cmd.ahs_len - 1) ;
ahs_ptr += ahs_len) {
ahs_len = ISCSI_NTOHS(*((uint16_t *) (void *)ahs_ptr));
if (ahs_len == 0) {
iscsi_err(__FILE__, __LINE__,
"Zero ahs_len\n");
result = -1;
goto out;
}
switch (ahs_type = *(ahs_ptr + 2)) {
case ISCSI_AHS_EXTENDED_CDB:
iscsi_trace(TRACE_ISCSI_DEBUG,
"Got ExtendedCDB AHS - %u bytes extra "
"CDB)\n", ahs_len - 1);
scsi_cmd.ext_cdb = ahs_ptr + 4;
break;
case ISCSI_AHS_BIDI_READ:
scsi_cmd.bidi_trans_len =
ISCSI_NTOHL(*((uint32_t *)(void *)
(ahs_ptr + 4)));
*((uint32_t *)(void *)(ahs_ptr + 4)) =
scsi_cmd.bidi_trans_len;
iscsi_trace(TRACE_ISCSI_DEBUG,
"Got Bidirectional Read AHS "
"(expected read length %u)\n",
scsi_cmd.bidi_trans_len);
break;
default:
iscsi_err(__FILE__, __LINE__,
"unknown AHS type %x\n", ahs_type);
result = -1;
goto out;
}
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"done parsing %u bytes AHS\n", scsi_cmd.ahs_len);
} else {
iscsi_trace(TRACE_ISCSI_DEBUG, "no AHS to read\n");
scsi_cmd.ahs = NULL;
}
sess->ExpCmdSN++;
sess->MaxCmdSN++;
/* Execute cdb. device_command() will set scsi_cmd.input if
* there is input data and set the length of the input to
* either scsi_cmd.trans_len or scsi_cmd.bidi_trans_len,
* depending on whether scsi_cmd.output was set. */
scsi_cmd.send_data = sess->buff;
scsi_cmd.input = 0;
cmd.scsi_cmd = &scsi_cmd;
cmd.callback = NULL;
if (device_command(sess, &cmd) != 0) {
iscsi_err(__FILE__, __LINE__,
"device_command() failed\n");
result = -1;
goto out;
}
/* Send any input data */
scsi_cmd.bytes_sent = 0;
if (!scsi_cmd.status && scsi_cmd.input) {
struct iovec sg_singleton;
struct iovec *sg, *sg_orig;
int sg_len_orig, sg_len;
uint32_t offset, trans_len;
int fragment_flag = 0;
int offset_inc;
if (scsi_cmd.output) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"sending %u bytes bi-directional input data\n",
scsi_cmd.bidi_trans_len);
trans_len = scsi_cmd.bidi_trans_len;
} else {
trans_len = scsi_cmd.trans_len;
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"sending %u bytes input data as separate PDUs\n",
trans_len);
if (scsi_cmd.send_sg_len) {
sg_orig = (struct iovec *)(void *)scsi_cmd.send_data;
sg_len_orig = scsi_cmd.send_sg_len;
} else {
sg_len_orig = 1;
sg_singleton.iov_base = scsi_cmd.send_data;
sg_singleton.iov_len = trans_len;
sg_orig = &sg_singleton;
}
sg = sg_orig;
sg_len = sg_len_orig;
offset_inc = (sess->sess_params.max_dataseg_len) ?
sess->sess_params.max_dataseg_len : trans_len;
for (offset = 0; offset < trans_len; offset += offset_inc) {
(void) memset(&data, 0x0, sizeof(data));
data.length = (sess->sess_params.max_dataseg_len) ?
MIN(trans_len - offset,
sess->sess_params.max_dataseg_len) :
trans_len - offset;
if (data.length != trans_len) {
if (!fragment_flag) {
sg_new = iscsi_malloc_atomic(sizeof(struct iovec) * sg_len_orig);
if (sg_new == NULL) {
iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
result = -1;
goto out;
}
fragment_flag++;
}
sg = sg_new;
sg_len = sg_len_orig;
(void) memcpy(sg, sg_orig, sizeof(struct iovec) * sg_len_orig);
if (modify_iov(&sg, &sg_len, offset, data.length) != 0) {
iscsi_err(__FILE__, __LINE__, "modify_iov() failed\n");
result = -1;
goto out;
}
}
iscsi_trace(TRACE_ISCSI_DEBUG, "sending read data PDU (offset %u, len %u)\n", offset, data.length);
if (offset + data.length == trans_len) {
data.final = 1;
if (sess->UsePhaseCollapsedRead) {
data.status = 1;
data.status = scsi_cmd.status;
data.StatSN = ++(sess->StatSN);
iscsi_trace(TRACE_ISCSI_DEBUG, "status %#x collapsed into last data PDU\n", data.status);
} else {
iscsi_trace(TRACE_ISCSI_DEBUG, "NOT collapsing status with last data PDU\n");
}
} else if (offset + data.length > trans_len) {
iscsi_err(__FILE__, __LINE__, "offset+data.length > trans_len??\n");
result = -1;
goto out;
}
data.task_tag = scsi_cmd.tag;
data.ExpCmdSN = sess->ExpCmdSN;
data.MaxCmdSN = sess->MaxCmdSN;
data.DataSN = DataSN++;
data.offset = offset;
if (iscsi_read_data_encap(rsp_header, &data) != 0) {
iscsi_err(__FILE__, __LINE__, "iscsi_read_data_encap() failed\n");
result = -1;
goto out;
}
if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN, sg, data.length, sg_len)
!= ISCSI_HEADER_LEN + data.length) {
iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n");
result = -1;
goto out;
}
scsi_cmd.bytes_sent += data.length;
iscsi_trace(TRACE_ISCSI_DEBUG, "sent read data PDU ok (offset %u, len %u)\n", data.offset, data.length);
}
iscsi_trace(TRACE_ISCSI_DEBUG, "successfully sent %u bytes read data\n", trans_len);
}
/*
* Send a response PDU if
*
* 1) we're not using phase collapsed input (and status was good)
* 2) we are using phase collapsed input, but there was no input data (e.g., TEST UNIT READY)
* 3) command had non-zero status and possible sense data
*/
response:
if (!sess->UsePhaseCollapsedRead || !scsi_cmd.length || scsi_cmd.status) {
iscsi_trace(TRACE_ISCSI_DEBUG, "sending SCSI response PDU\n");
(void) memset(&scsi_rsp, 0x0, sizeof(scsi_rsp));
scsi_rsp.length = scsi_cmd.status ? scsi_cmd.length : 0;
scsi_rsp.tag = scsi_cmd.tag;
/* If r2t send, then the StatSN is already incremented */
if (sess->StatSN < scsi_cmd.ExpStatSN) {
++sess->StatSN;
}
scsi_rsp.StatSN = sess->StatSN;
scsi_rsp.ExpCmdSN = sess->ExpCmdSN;
scsi_rsp.MaxCmdSN = sess->MaxCmdSN;
scsi_rsp.ExpDataSN = (!scsi_cmd.status && scsi_cmd.input) ? DataSN : 0;
scsi_rsp.response = 0x00; /* iSCSI response */
scsi_rsp.status = scsi_cmd.status; /* SCSI status */
if (iscsi_scsi_rsp_encap(rsp_header, &scsi_rsp) != 0) {
iscsi_err(__FILE__, __LINE__, "iscsi_scsi_rsp_encap() failed\n");
result = -1;
goto out;
}
if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN,
scsi_cmd.send_data, scsi_rsp.length, scsi_cmd.send_sg_len)
!= ISCSI_HEADER_LEN + scsi_rsp.length) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_send_header_and_data() failed\n");
result = -1;
goto out;
}
/* Make sure all data was transferred */
if (scsi_cmd.output) {
if (scsi_cmd.bytes_recv != scsi_cmd.trans_len) {
iscsi_err(__FILE__, __LINE__,
"scsi_cmd.bytes_recv");
result = -1;
goto out;
}
if (scsi_cmd.input) {
if (scsi_cmd.bytes_sent !=
scsi_cmd.bidi_trans_len) {
iscsi_err(__FILE__, __LINE__,
"scsi_cmd.bytes_sent");
result = -1;
goto out;
}
}
} else {
if (scsi_cmd.input) {
if (scsi_cmd.bytes_sent != scsi_cmd.trans_len) {
iscsi_err(__FILE__, __LINE__,
"scsi_cmd.bytes_sent");
result = -1;
goto out;
}
}
}
}
/* Device callback after command has completed */
if (cmd.callback) {
iscsi_trace(TRACE_ISCSI_DEBUG, "issuing device callback\n");
if ((*cmd.callback)(cmd.callback_arg) != 0) {
iscsi_err(__FILE__, __LINE__,
"device callback failed\n");
result = -1;
goto out;
}
}
result = 0;
out:
if (scsi_cmd.ahs != NULL) { \
iscsi_free_atomic(scsi_cmd.ahs); \
} \
if (sg_new != NULL) {
iscsi_free_atomic(sg_new);
}
free(scsi_cmd.send_buffer);
return result;
}
static int
task_command_t(target_session_t * sess, uint8_t *header)
{
iscsi_task_cmd_t cmd;
iscsi_task_rsp_t rsp;
uint8_t rsp_header[ISCSI_HEADER_LEN];
/* Get & check args */
if (iscsi_task_cmd_decap(header, &cmd) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_task_cmd_decap() failed\n");
return -1;
}
if (cmd.CmdSN != sess->ExpCmdSN) {
iscsi_warn(__FILE__, __LINE__,
"Expected CmdSN %d, got %d. "
"(ignoring and resetting expectations)\n",
cmd.CmdSN, sess->ExpCmdSN);
sess->ExpCmdSN = cmd.CmdSN;
}
sess->MaxCmdSN++;
(void) memset(&rsp, 0x0, sizeof(rsp));
rsp.response = ISCSI_TASK_RSP_FUNCTION_COMPLETE;
switch (cmd.function) {
case ISCSI_TASK_CMD_ABORT_TASK:
printf("ISCSI_TASK_CMD_ABORT_TASK\n");
break;
case ISCSI_TASK_CMD_ABORT_TASK_SET:
printf("ISCSI_TASK_CMD_ABORT_TASK_SET\n");
break;
case ISCSI_TASK_CMD_CLEAR_ACA:
printf("ISCSI_TASK_CMD_CLEAR_ACA\n");
break;
case ISCSI_TASK_CMD_CLEAR_TASK_SET:
printf("ISCSI_TASK_CMD_CLEAR_TASK_SET\n");
break;
case ISCSI_TASK_CMD_LOGICAL_UNIT_RESET:
printf("ISCSI_TASK_CMD_LOGICAL_UNIT_RESET\n");
break;
case ISCSI_TASK_CMD_TARGET_WARM_RESET:
printf("ISCSI_TASK_CMD_TARGET_WARM_RESET\n");
break;
case ISCSI_TASK_CMD_TARGET_COLD_RESET:
printf("ISCSI_TASK_CMD_TARGET_COLD_RESET\n");
break;
case ISCSI_TASK_CMD_TARGET_REASSIGN:
printf("ISCSI_TASK_CMD_TARGET_REASSIGN\n");
break;
default:
iscsi_err(__FILE__, __LINE__, "Unknown task function %d\n", cmd.function);
rsp.response = ISCSI_TASK_RSP_REJECTED;
}
rsp.tag = cmd.tag;
rsp.StatSN = ++(sess->StatSN);
rsp.ExpCmdSN = sess->ExpCmdSN;
rsp.MaxCmdSN = sess->MaxCmdSN;
if (iscsi_task_rsp_encap(rsp_header, &rsp) != 0) {
iscsi_err(__FILE__, __LINE__, "iscsi_task_cmd_decap() failed\n");
return -1;
}
if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) != ISCSI_HEADER_LEN) {
iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
return -1;
}
return 0;
}
static int
nop_out_t(target_session_t * sess, uint8_t *header)
{
iscsi_nop_out_args_t nop_out;
char *ping_data = NULL;
if (iscsi_nop_out_decap(header, &nop_out) != 0) {
iscsi_err(__FILE__, __LINE__, "iscsi_nop_out_decap() failed\n");
return -1;
}
if (nop_out.CmdSN != sess->ExpCmdSN) {
iscsi_warn(__FILE__, __LINE__, "Expected CmdSN %d, got %d. (ignoring and resetting expectations)\n",
nop_out.CmdSN, sess->ExpCmdSN);
sess->ExpCmdSN = nop_out.CmdSN;
}
/* TODO Clarify whether we need to update the CmdSN */
/* sess->ExpCmdSN++; */
/* sess->MaxCmdSN++; */
if (nop_out.length) {
iscsi_trace(TRACE_ISCSI_DEBUG, "reading %u bytes ping data\n", nop_out.length);
if ((ping_data = iscsi_malloc(nop_out.length)) == NULL) {
iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n");
return -1;
}
if ((uint32_t)iscsi_sock_msg(sess->sock, 0, nop_out.length, ping_data, 0) != nop_out.length) {
iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
if (ping_data) {
iscsi_free(ping_data);
}
return -1;
}
iscsi_trace(TRACE_ISCSI_DEBUG, "successfully read %u bytes ping data:\n", nop_out.length);
iscsi_print_buffer(ping_data, nop_out.length);
}
if (nop_out.tag != 0xffffffff) {
iscsi_nop_in_args_t nop_in;
uint8_t rsp_header[ISCSI_HEADER_LEN];
iscsi_trace(TRACE_ISCSI_DEBUG, "sending %u bytes ping response\n", nop_out.length);
(void) memset(&nop_in, 0x0, sizeof(nop_in));
nop_in.length = nop_out.length;
nop_in.lun = nop_out.lun;
nop_in.tag = nop_out.tag;
nop_in.transfer_tag = 0xffffffff;
nop_in.StatSN = ++(sess->StatSN);
nop_in.ExpCmdSN = sess->ExpCmdSN;
nop_in.MaxCmdSN = sess->MaxCmdSN;
if (iscsi_nop_in_encap(rsp_header, &nop_in) != 0) {
iscsi_err(__FILE__, __LINE__, "iscsi_nop_in_encap() failed\n");
if (ping_data) {
iscsi_free(ping_data);
}
return -1;
}
if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN,
ping_data, nop_in.length, 0) != ISCSI_HEADER_LEN + nop_in.length) {
iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n");
if (ping_data) {
iscsi_free(ping_data);
}
return -1;
}
iscsi_trace(TRACE_ISCSI_DEBUG, "successfully sent %u bytes ping response\n", nop_out.length);
}
if (ping_data) {
iscsi_free(ping_data);
}
return 0;
}
/*
* text_command_t
*/
static int
text_command_t(target_session_t * sess, uint8_t *header)
{
iscsi_text_cmd_args_t text_cmd;
iscsi_text_rsp_args_t text_rsp;
unsigned len_in;
uint32_t i;
uint8_t rsp_header[ISCSI_HEADER_LEN];
targv_t *targv;
char *text_in = NULL;
char *text_out = NULL;
char buf[BUFSIZ];
int len_out = 0;
#define TC_CLEANUP do { \
if (text_in != NULL) { \
iscsi_free_atomic(text_in); \
} \
if (text_out != NULL) { \
iscsi_free_atomic(text_out); \
} \
} while (/* CONSTCOND */ 0)
#define TC_ERROR { \
TC_CLEANUP; \
return -1; \
}
/* Get text args */
if (iscsi_text_cmd_decap(header, &text_cmd) != 0) {
iscsi_err(__FILE__, __LINE__, "iscsi_text_cmd_decap() failed\n");
return -1;
}
/* Check args & update numbering */
#if 0
RETURN_NOT_EQUAL("Continue", text_cmd.cont, 0, NO_CLEANUP, -1);
RETURN_NOT_EQUAL("CmdSN", text_cmd.CmdSN, sess->ExpCmdSN, NO_CLEANUP, -1);
#else
if (text_cmd.cont != 0) {
iscsi_err(__FILE__, __LINE__, "Continue");
NO_CLEANUP;
return -1;
}
if (text_cmd.CmdSN != sess->ExpCmdSN) {
iscsi_err(__FILE__, __LINE__, "CmdSN");
NO_CLEANUP;
return -1;
}
#endif
sess->ExpCmdSN++;
sess->MaxCmdSN++;
if ((text_out = iscsi_malloc_atomic(2048)) == NULL) {
iscsi_err(__FILE__, __LINE__,
"iscsi_malloc_atomic() failed\n");
return -1;
}
/* Read text parameters */
if ((len_in = text_cmd.length) != 0) {
iscsi_parameter_t *ptr;
if ((text_in = iscsi_malloc_atomic(len_in + 1)) == NULL) {
iscsi_err(__FILE__, __LINE__,
"iscsi_malloc_atomic() failed\n");
TC_CLEANUP;
return -1;
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"reading %u bytes text parameters\n", len_in);
if ((unsigned)iscsi_sock_msg(sess->sock, 0, len_in, text_in,
0) != len_in) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_msg() failed\n");
TC_CLEANUP;
return -1;
}
text_in[len_in] = 0x0;
PARAM_TEXT_PARSE(sess->params, &sess->sess_params.cred,
text_in, (int) len_in, text_out,
(int *)(void *)&len_out, 2048, 0, TC_ERROR);
/*
* Handle exceptional cases not covered by parameters.c
* (e.g., SendTargets)
*/
if ((ptr = param_get(sess->params, "SendTargets")) == NULL) {
iscsi_err(__FILE__, __LINE__,
"param_get() failed\n");
TC_CLEANUP;
return -1;
}
if (ptr->rx_offer) {
if (strcmp(ptr->offer_rx, "All") == 0 &&
!param_equiv(sess->params, "SessionType",
"Discovery")) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"Rejecting SendTargets=All in a "
"non Discovery session\n");
PARAM_TEXT_ADD(sess->params, "SendTargets",
"Reject", text_out, &len_out, 2048,
0, TC_ERROR);
} else {
targv = sess->target->lunv;
for (i = 0 ; i < targv->c ; i++) {
if (sess->address_family == 6 ||
(sess->address_family == 4 &&
allow_netmask(targv->v[i].mask,
sess->initiator))) {
(void) get_iqn(sess, i, buf,
sizeof(buf));
PARAM_TEXT_ADD(sess->params,
"TargetName", buf,
text_out, &len_out,
2048, 0, TC_ERROR);
PARAM_TEXT_ADD(sess->params,
"TargetAddress",
iscsi_target_getvar(sess->target, "target address"),
text_out, &len_out,
2048, 0, TC_ERROR);
} else {
#ifdef HAVE_SYSLOG_H
syslog(LOG_INFO,
"WARNING: attempt to "
"discover targets from "
"%s (not allowed by %s)"
" has been rejected",
sess->initiator,
targv->v[0].mask);
#endif
}
}
}
ptr->rx_offer = 0;
}
/* Parse outgoing offer */
if (len_out) {
PARAM_TEXT_PARSE(sess->params,
&sess->sess_params.cred, text_out, len_out,
NULL, NULL, 2048, 1, TC_ERROR);
}
}
if (sess->IsFullFeature) {
set_session_parameters(sess->params, &sess->sess_params);
}
/* Send response */
text_rsp.final = text_cmd.final;
text_rsp.cont = 0;
text_rsp.length = len_out;
text_rsp.lun = text_cmd.lun;
text_rsp.tag = text_cmd.tag;
text_rsp.transfer_tag = (text_rsp.final) ? 0xffffffff : 0x1234;
text_rsp.StatSN = ++(sess->StatSN);
text_rsp.ExpCmdSN = sess->ExpCmdSN;
text_rsp.MaxCmdSN = sess->MaxCmdSN;
if (iscsi_text_rsp_encap(rsp_header, &text_rsp) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_text_rsp_encap() failed\n");
TC_CLEANUP;
return -1;
}
if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) !=
ISCSI_HEADER_LEN) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_msg() failed\n");
TC_CLEANUP;
return -1;
}
if (len_out && iscsi_sock_msg(sess->sock, 1, (unsigned) len_out,
text_out, 0) != len_out) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_msg() failed\n");
TC_CLEANUP;
return -1;
}
TC_CLEANUP;
return 0;
}
/* given a target's iqn, find the relevant target that we're exporting */
int
find_target_iqn(target_session_t *sess)
{
uint32_t i;
targv_t *targv;
char buf[BUFSIZ];
targv = sess->target->lunv;
for (i = 0 ; i < targv->c ; i++) {
if (param_equiv(sess->params, "TargetName",
get_iqn(sess, i, buf, sizeof(buf)))) {
return sess->d = i;
}
}
return -1;
}
/* given a tsih, find the relevant target that we're exporting */
int
find_target_tsih(iscsi_target_t *target, int tsih)
{
uint32_t i;
targv_t *targv;
targv = target->lunv;
for (i = 0 ; i < targv->c ; i++) {
if (targv->v[i].tsih == tsih) {
return i;
}
}
return -1;
}
/*
* login_command_t() handles login requests and replies.
*/
static int
login_command_t(target_session_t * sess, uint8_t *header)
{
iscsi_login_cmd_args_t cmd;
iscsi_login_rsp_args_t rsp;
uint8_t rsp_header[ISCSI_HEADER_LEN];
targv_t *targv;
char *text_in = NULL;
char *text_out = NULL;
char logbuf[BUFSIZ];
int len_in = 0;
int len_out = 0;
int status = 0;
int i;
/* Initialize response */
#define LC_CLEANUP do { \
if (text_in != NULL) { \
iscsi_free_atomic(text_in); \
} \
if (text_out != NULL) { \
iscsi_free_atomic(text_out); \
} \
} while (/* CONSTCOND */ 0)
#define LC_ERROR { \
TC_CLEANUP; \
return -1; \
}
(void) memset(&rsp, 0x0, sizeof(rsp));
rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR;
/* Get login args & check preconditions */
if (iscsi_login_cmd_decap(header, &cmd) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_login_cmd_decap() failed\n");
goto response;
}
if (sess->IsLoggedIn) {
iscsi_err(__FILE__, __LINE__,
"duplicate login attempt on sess %d\n", sess->id);
goto response;
}
if ((cmd.cont != 0) && (cmd.transit != 0)) {
iscsi_err(__FILE__, __LINE__,
"Bad cmd.continue. Expected 0.\n");
goto response;
} else if ((cmd.version_max < ISCSI_VERSION) ||
(cmd.version_min > ISCSI_VERSION)) {
iscsi_err(__FILE__, __LINE__,
"Target iscsi version (%u) not supported by initiator "
"[Max Ver (%u) and Min Ver (%u)]\n",
ISCSI_VERSION, cmd.version_max, cmd.version_min);
rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR;
rsp.status_detail = ISCSI_LOGIN_DETAIL_VERSION_NOT_SUPPORTED;
rsp.version_max = ISCSI_VERSION;
rsp.version_active = ISCSI_VERSION;
goto response;
} else if (cmd.tsih != 0) {
iscsi_err(__FILE__, __LINE__,
"Bad cmd.tsih (%u). Expected 0.\n", cmd.tsih);
goto response;
}
/* Parse text parameters and build response */
if ((text_out = iscsi_malloc_atomic(2048)) == NULL) {
iscsi_err(__FILE__, __LINE__,
"iscsi_malloc_atomic() failed\n");
return -1;
}
if ((len_in = cmd.length) != 0) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"reading %d bytes text data\n", len_in);
text_in = iscsi_malloc_atomic((unsigned)(len_in + 1));
if (text_in == NULL) {
iscsi_err(__FILE__, __LINE__,
"iscsi_malloc() failed\n");
LC_CLEANUP;
return -1;
}
if (iscsi_sock_msg(sess->sock, 0, (unsigned) len_in, text_in,
0) != len_in) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_msg() failed\n");
LC_CLEANUP;
return -1;
}
text_in[len_in] = 0x0;
iscsi_trace(TRACE_ISCSI_DEBUG,
"successfully read %d bytes text data\n", len_in);
/*
* Parse incoming parameters (text_out will contain the
* response we need
*/
/* to send back to the initiator */
status = param_text_parse(sess->params,
&sess->sess_params.cred, text_in, len_in,
text_out, &len_out, 2048, 0);
if (status != 0) {
switch (status) {
case ISCSI_PARAM_STATUS_FAILED:
rsp.status_detail = ISCSI_LOGIN_DETAIL_SUCCESS;
break;
case ISCSI_PARAM_STATUS_AUTH_FAILED:
rsp.status_detail =
ISCSI_LOGIN_DETAIL_INIT_AUTH_FAILURE;
break;
default:
/*
* We will need to set the detail
* field based on more detailed error
* cases. Will need to fix this if
* compliciance test break
* (status_detail field).
*/
break;
}
goto response;
}
/* Parse the outgoing offer */
if (!sess->LoginStarted) {
PARAM_TEXT_ADD(sess->params, "TargetPortalGroupTag",
"1", text_out, &len_out, 2048, 0, LC_ERROR);
}
if (len_out) {
PARAM_TEXT_PARSE(sess->params,
&sess->sess_params.cred, text_out, len_out,
NULL, NULL, 2048, 1, LC_ERROR;
);
}
}
if (!sess->LoginStarted) {
sess->LoginStarted = 1;
}
/*
* For now, we accept what ever the initiators' current and next
* states are. And le are always
*/
/* ready to transitition to that state. */
rsp.csg = cmd.csg;
rsp.nsg = cmd.nsg;
rsp.transit = cmd.transit;
if (cmd.csg == ISCSI_LOGIN_STAGE_SECURITY) {
if (param_equiv(sess->params, "AuthResult", "No")) {
rsp.transit = 0;
} else if (param_equiv(sess->params, "AuthResult", "Fail")) {
rsp.status_class = rsp.status_detail =
ISCSI_LOGIN_DETAIL_INIT_AUTH_FAILURE;
goto response;
}
}
if (cmd.transit && cmd.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"transitioning to ISCSI_LOGIN_STAGE_FULL_FEATURE\n");
/* Check post conditions */
if (param_equiv(sess->params, "InitiatorName", "")) {
iscsi_err(__FILE__, __LINE__,
"InitiatorName not specified\n");
goto response;
}
if (param_equiv(sess->params, "SessionType", "Normal")) {
if (param_equiv(sess->params, "TargetName", "")) {
iscsi_err(__FILE__, __LINE__,
"TargetName not specified\n");
goto response;
}
if ((i = find_target_iqn(sess)) < 0) {
iscsi_err(__FILE__, __LINE__,
"Bad TargetName \"%s\"\n",
param_val(sess->params, "TargetName"));
goto response;
}
if (cmd.tsih != 0 &&
find_target_tsih(sess->target, cmd.tsih) != i) {
targv = sess->target->lunv;
iscsi_err(__FILE__, __LINE__,
"target tsih expected %d, cmd.tsih %d, "
"i %d\n", targv->v[i].tsih, cmd.tsih,
i);
}
sess->d = i;
} else if ((i = find_target_tsih(sess->target, cmd.tsih)) < 0) {
iscsi_err(__FILE__, __LINE__,
"Abnormal SessionType cmd.tsih %d not found\n",
cmd.tsih);
i = sess->d;
}
if (param_equiv(sess->params, "SessionType", "")) {
iscsi_err(__FILE__, __LINE__,
"SessionType not specified\n");
goto response;
}
sess->ExpCmdSN = sess->MaxCmdSN = cmd.CmdSN;
sess->cid = cmd.cid;
sess->isid = cmd.isid;
targv = sess->target->lunv;
targv->v[i].tsih = sess->tsih = ++sess->target->last_tsih;
sess->IsFullFeature = 1;
sess->IsLoggedIn = 1;
if (!param_equiv(sess->params, "SessionType", "Discovery")) {
(void) strlcpy(param_val(sess->params,
"MaxConnections"), "1", 2);
}
set_session_parameters(sess->params, &sess->sess_params);
} else {
if ((i = find_target_tsih(sess->target, cmd.tsih)) < 0) {
iscsi_err(__FILE__, __LINE__,
"cmd.tsih %d not found\n", cmd.tsih);
}
}
/* No errors */
rsp.status_class = rsp.status_detail = ISCSI_LOGIN_DETAIL_SUCCESS;
rsp.length = len_out;
/* Send login response */
response:
sess->ExpCmdSN = sess->MaxCmdSN = cmd.CmdSN;
rsp.isid = cmd.isid;
rsp.StatSN = cmd.ExpStatSN; /* debug */
rsp.tag = cmd.tag;
rsp.cont = cmd.cont;
rsp.ExpCmdSN = sess->ExpCmdSN;
rsp.MaxCmdSN = sess->MaxCmdSN;
if (!rsp.status_class) {
if (rsp.transit &&
(rsp.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE)) {
rsp.version_max = ISCSI_VERSION;
rsp.version_active = ISCSI_VERSION;
rsp.StatSN = ++(sess->StatSN);
rsp.tsih = sess->tsih;
}
}
if (iscsi_login_rsp_encap(rsp_header, &rsp) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_login_rsp_encap() failed\n");
LC_CLEANUP;
return -1;
}
iscsi_trace(TRACE_ISCSI_DEBUG, "sending login response\n");
if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header,
ISCSI_HEADER_LEN, text_out, rsp.length, 0) !=
ISCSI_HEADER_LEN + rsp.length) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_send_header_and_data() failed\n");
LC_CLEANUP;
return -1;
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"sent login response ok\n");
if (rsp.status_class != 0) {
LC_CLEANUP;
return -1;
}
if (cmd.transit && cmd.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE) {
/* log information to stdout */
(void) snprintf(logbuf, sizeof(logbuf),
"> iSCSI %s login successful from %s on %s disk %d, "
"ISID %" PRIu64 ", TSIH %u",
param_val(sess->params, "SessionType"),
param_val(sess->params, "InitiatorName"),
sess->initiator,
sess->d,
sess->isid,
sess->tsih);
printf("%s\n", logbuf);
#ifdef HAVE_SYSLOG_H
/* log information to syslog */
syslog(LOG_INFO, "%s", logbuf);
#endif
/* Buffer for data xfers to/from the scsi device */
if (!param_equiv(sess->params, "MaxRecvDataSegmentLength",
"0")) {
sess->buff = iscsi_malloc((unsigned)(
param_atoi(sess->params,
"MaxRecvDataSegmentLength")));
if (sess->buff == NULL) {
iscsi_err(__FILE__, __LINE__,
"iscsi_malloc() failed\n");
LC_CLEANUP;
return -1;
}
} else {
iscsi_err(__FILE__, __LINE__,
"0 MaxRecvDataSegmentLength not supported\n");
LC_CLEANUP;
return -1;
}
}
LC_CLEANUP;
return 0;
}
static int
logout_command_t(target_session_t * sess, uint8_t *header)
{
iscsi_logout_cmd_args_t cmd;
iscsi_logout_rsp_args_t rsp;
targv_t *targv;
uint8_t rsp_header[ISCSI_HEADER_LEN];
char logbuf[BUFSIZ];
int i;
(void) memset(&rsp, 0x0, sizeof(rsp));
if (iscsi_logout_cmd_decap(header, &cmd) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_logout_cmd_decap() failed\n");
return -1;
}
sess->StatSN = cmd.ExpStatSN;
if ((cmd.reason == ISCSI_LOGOUT_CLOSE_RECOVERY) &&
(param_equiv(sess->params, "ErrorRecoveryLevel", "0"))) {
rsp.response = ISCSI_LOGOUT_STATUS_NO_RECOVERY;
}
#if 0
RETURN_NOT_EQUAL("CmdSN", cmd.CmdSN, sess->ExpCmdSN, NO_CLEANUP, -1);
RETURN_NOT_EQUAL("ExpStatSN", cmd.ExpStatSN, sess->StatSN, NO_CLEANUP, -1);
#else
if (cmd.CmdSN != sess->ExpCmdSN) {
iscsi_err(__FILE__, __LINE__, "CmdSN");
NO_CLEANUP;
return -1;
}
if (cmd.ExpStatSN != sess->StatSN) {
iscsi_err(__FILE__, __LINE__, "ExpStatSN");
NO_CLEANUP;
return -1;
}
#endif
rsp.tag = cmd.tag;
rsp.StatSN = sess->StatSN;
rsp.ExpCmdSN = ++sess->ExpCmdSN;
rsp.MaxCmdSN = sess->MaxCmdSN;
if (iscsi_logout_rsp_encap(rsp_header, &rsp) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_logout_rsp_encap() failed\n");
return -1;
}
if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) !=
ISCSI_HEADER_LEN) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_msg() failed\n");
return -1;
}
iscsi_trace(TRACE_ISCSI_DEBUG, "sent logout response OK\n");
/* log information to stdout */
(void) snprintf(logbuf, sizeof(logbuf),
"< iSCSI %s logout successful from %s on %s "
"disk %d, ISID %" PRIu64 ", TSIH %u",
param_val(sess->params, "SessionType"),
param_val(sess->params, "InitiatorName"),
sess->initiator,
sess->d,
sess->isid,
sess->tsih);
printf("%s\n", logbuf);
#ifdef HAVE_SYSLOG
/* log information to syslog */
syslog(LOG_INFO, "%s", logbuf);
#endif
sess->IsLoggedIn = 0;
if (sess->sess_params.cred.user) {
free(sess->sess_params.cred.user);
sess->sess_params.cred.user = NULL;
}
if ((i = find_target_tsih(sess->target, sess->tsih)) < 0) {
iscsi_err(__FILE__, __LINE__,
"logout sess->tsih %d not found\n", sess->tsih);
} else {
targv = sess->target->lunv;
targv->v[i].tsih = 0;
}
sess->tsih = 0;
return 0;
}
static int
verify_cmd_t(target_session_t * sess, uint8_t *header)
{
int op = ISCSI_OPCODE(header);
if ((!sess->LoginStarted) && (op != ISCSI_LOGIN_CMD)) {
/* Terminate the connection */
iscsi_err(__FILE__, __LINE__,
"session %d: iSCSI op %#x attempted "
"before LOGIN PHASE\n",
sess->id, op);
return -1;
}
if (!sess->IsFullFeature &&
((op != ISCSI_LOGIN_CMD) && (op != ISCSI_LOGOUT_CMD))) {
iscsi_login_rsp_args_t rsp;
uint8_t rsp_header[ISCSI_HEADER_LEN];
iscsi_err(__FILE__, __LINE__,
"session %d: iSCSI op %#x before FULL FEATURE\n",
sess->id, op);
/* Create Login Reject response */
(void) memset(&rsp, 0x0, sizeof(rsp));
rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR;
rsp.status_detail = ISCSI_LOGIN_DETAIL_NOT_LOGGED_IN;
rsp.version_max = ISCSI_VERSION;
rsp.version_active = ISCSI_VERSION;
if (iscsi_login_rsp_encap(rsp_header, &rsp) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_login_rsp_encap() failed\n");
return -1;
}
iscsi_trace(TRACE_ISCSI_DEBUG, "sending login response\n");
if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock,
rsp_header, ISCSI_HEADER_LEN, NULL, 0, 0) !=
ISCSI_HEADER_LEN + rsp.length) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_send_header_and_data() failed\n");
return -1;
}
iscsi_trace(TRACE_ISCSI_DEBUG, "sent login response ok\n");
return -1;
}
return 0;
}
/*
* this function looks at the opcode in the received header for the session,
* and does a switch on the opcode to call the required function.
*/
static int
execute_t(target_session_t *sess, uint8_t *header)
{
int op = ISCSI_OPCODE(header);
if (verify_cmd_t(sess, header) != 0) {
return -1;
}
switch (op) {
case ISCSI_TASK_CMD:
iscsi_trace(TRACE_ISCSI_CMD,
"session %d: Task Command\n", sess->id);
if (task_command_t(sess, header) != 0) {
iscsi_err(__FILE__, __LINE__,
"task_command_t() failed\n");
return -1;
}
break;
case ISCSI_NOP_OUT:
iscsi_trace(TRACE_ISCSI_CMD, "session %d: NOP-Out\n", sess->id);
if (nop_out_t(sess, header) != 0) {
iscsi_err(__FILE__, __LINE__,
"nop_out_t() failed\n");
return -1;
}
break;
case ISCSI_LOGIN_CMD:
iscsi_trace(TRACE_ISCSI_CMD,
"session %d: Login Command\n", sess->id);
if (login_command_t(sess, header) != 0) {
iscsi_err(__FILE__, __LINE__,
"login_command_t() failed\n");
return -1;
}
break;
case ISCSI_TEXT_CMD:
iscsi_trace(TRACE_ISCSI_CMD,
"session %d: Text Command\n", sess->id);
if (text_command_t(sess, header) != 0) {
iscsi_err(__FILE__, __LINE__,
"text_command_t() failed\n");
return -1;
}
break;
case ISCSI_LOGOUT_CMD:
iscsi_trace(TRACE_ISCSI_CMD,
"session %d: Logout Command\n", sess->id);
if (logout_command_t(sess, header) != 0) {
iscsi_err(__FILE__, __LINE__,
"logout_command_t() failed\n");
return -1;
}
break;
case ISCSI_SCSI_CMD:
iscsi_trace(TRACE_ISCSI_CMD,
"session %d: SCSI Command\n", sess->id);
if (scsi_command_t(sess, header) != 0) {
iscsi_err(__FILE__, __LINE__,
"scsi_command_t() failed\n");
return -1;
}
break;
default:
iscsi_err(__FILE__, __LINE__, "Unknown Opcode %#x\n",
ISCSI_OPCODE(header));
if (reject_t(sess, header, 0x04) != 0) {
iscsi_err(__FILE__, __LINE__,
"reject_t() failed\n");
return -1;
}
break;
}
return 0;
}
/*
* Currently one thread per session, used for both Rx and Tx.
*/
static int
worker_proc_t(void *arg)
{
target_session_t *sess = (target_session_t *) arg;
uint8_t header[ISCSI_HEADER_LEN];
iscsi_parameter_t **l = &sess->params;
ISCSI_THREAD_START("worker_thread");
sess->worker.pid = getpid();
sess->worker.state |= ISCSI_WORKER_STATE_STARTED;
iscsi_trace(TRACE_ISCSI_DEBUG, "session %d: started\n", sess->id);
/*
* ISCSI_PARAM_TYPE_LIST format: <type> <key> <dflt> <valid list values>
* ISCSI_PARAM_TYPE_BINARY format: <type> <key> <dflt> <valid binary values>
* ISCSI_PARAM_TYPE_NUMERICAL format: <type> <key> <dflt> <max>
* ISCSI_PARAM_TYPE_DECLARATIVE format: <type> <key> <dflt> ""
*/
sess->params = NULL;
l = &sess->params;
/* CHAP Parameters */
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "AuthMethod", "CHAP", "CHAP,None", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "CHAP_A", "None", "5", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_N", "", "", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_R", "", "", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_I", "", "", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_C", "", "", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetPortalGroupTag", "1", "1", return -1);
/* CHAP Parameters */
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "HeaderDigest", "None", "None", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "DataDigest", "None", "None", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "MaxConnections", "1", "1", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "SendTargets", "", "", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetName", "", "", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "InitiatorName", "", "", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetAlias", "", "", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "InitiatorAlias", "", "", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetAddress", "", "", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "InitialR2T", "Yes", "Yes,No", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "OFMarker", "No", "Yes,No", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "IFMarker", "No", "Yes,No", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "OFMarkInt", "1", "65536", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "IFMarkInt", "1", "65536", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "ImmediateData", "Yes", "Yes,No", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "MaxRecvDataSegmentLength", "8192", "16777215", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "MaxBurstLength", "262144", "16777215", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "FirstBurstLength", "65536", "16777215", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "DefaultTime2Wait", "2", "2", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "DefaultTime2Retain", "20", "20", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "MaxOutstandingR2T", "1", "1", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "DataPDUInOrder", "Yes", "Yes,No", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "DataSequenceInOrder", "Yes", "Yes,No", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "ErrorRecoveryLevel", "0", "0", return -1);
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "SessionType", "Normal", "Normal,Discovery", return -1);
/*
* Auth Result is not in specs, we use this key to pass
* authentication result
*/
PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "AuthResult", "No", "Yes,No,Fail", return -1);
/* Set remaining session parameters */
sess->UsePhaseCollapsedRead = ISCSI_USE_PHASE_COLLAPSED_READ_DFLT;
/* Loop for commands */
while (sess->target->state != TARGET_SHUT_DOWN) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"session %d: reading header\n", sess->id);
if (iscsi_sock_msg(sess->sock, 0, ISCSI_HEADER_LEN, header, 0)
!= ISCSI_HEADER_LEN) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"session %d: iscsi_sock_msg() failed\n",
sess->id);
break;
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"session %d: iscsi op %#x\n", sess->id,
ISCSI_OPCODE(header));
if (execute_t(sess, header) != 0) {
iscsi_err(__FILE__, __LINE__,
"execute_t() failed\n");
break;
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"session %d: iscsi op %#x complete\n", sess->id,
ISCSI_OPCODE(header));
if (ISCSI_OPCODE(header) == ISCSI_LOGOUT_CMD) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"session %d: logout received, ending session\n",
sess->id);
break;
}
}
/* Clean up */
iscsi_free(sess->buff);
if (param_list_destroy(sess->params) != 0) {
iscsi_err(__FILE__, __LINE__,
"param_list_destroy() failed\n");
return -1;
}
/* Terminate connection */
if (iscsi_sock_close(sess->sock) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_close() failed\n");
}
/* Make session available */
ISCSI_LOCK(&g_session_q_mutex, return -1);
(void) memset(sess, 0x0, sizeof(*sess));
sess->d = -1;
if (iscsi_queue_insert(&g_session_q, sess) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_queue_insert() failed\n");
return -1;
}
ISCSI_UNLOCK(&g_session_q_mutex, return -1);
iscsi_trace(TRACE_ISCSI_DEBUG, "session %d: ended\n", sess->id);
return 0;
}
static int
read_data_pdu(target_session_t * sess,
iscsi_write_data_t * data,
iscsi_scsi_cmd_args_t * args)
{
uint8_t header[ISCSI_HEADER_LEN];
int ret_val = -1;
if (iscsi_sock_msg(sess->sock, 0, ISCSI_HEADER_LEN, header, 0) !=
ISCSI_HEADER_LEN) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_msg() failed\n");
return -1;
}
if ((ret_val = iscsi_write_data_decap(header, data)) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_write_data_decap() failed\n");
return ret_val;
}
/* Check args */
if (sess->sess_params.max_dataseg_len) {
if (data->length > sess->sess_params.max_dataseg_len) {
args->status = 0x02;
return -1;
}
}
if ((args->bytes_recv + data->length) > args->trans_len) {
args->status = 0x02;
return -1;
}
if (data->tag != args->tag) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"Data ITT (%d) does not match with command ITT (%d)\n",
data->tag, args->tag);
if (data->final) {
args->status = 0x02;
return -1;
} else {
/* Send a reject PDU */
iscsi_trace(TRACE_ISCSI_DEBUG, "Sending Reject PDU\n");
if (reject_t(sess, header, 0x09) != 0) {
/* Invalid PDU Field */
iscsi_trace(TRACE_ISCSI_DEBUG,
"Sending Reject PDU failed\n");
return 1;
}
}
}
return 0;
}
int
target_transfer_data(target_session_t * sess, iscsi_scsi_cmd_args_t * args,
struct iovec * sg, int sg_len)
{
iscsi_write_data_t data;
struct iovec *iov, *iov_ptr = NULL;
int iov_len;
#define TTD_CLEANUP do { \
if (iov_ptr != NULL) { \
iscsi_free_atomic(iov_ptr); \
} \
} while (/* CONSTCOND */ 0)
args->bytes_recv = 0;
if ((!sess->sess_params.immediate_data) && args->length) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"Cannot accept any Immediate data\n");
args->status = 0x02;
return -1;
}
/* Make a copy of the iovec */
iov_ptr = iscsi_malloc_atomic(sizeof(struct iovec) * sg_len);
if (iov_ptr == NULL) {
iscsi_err(__FILE__, __LINE__,
"iscsi_malloc_atomic() failed\n");
return -1;
}
iov = iov_ptr;
(void) memcpy(iov, sg, sizeof(struct iovec) * sg_len);
iov_len = sg_len;
/*
* Read any immediate data.
*/
if (sess->sess_params.immediate_data && args->length) {
if (sess->sess_params.max_dataseg_len &&
args->length > sess->sess_params.max_dataseg_len) {
iscsi_err(__FILE__, __LINE__,
"args->length (%u) too long\n",
args->length);
TTD_CLEANUP;
return -1;
}
/* Modify iov to include just immediate data */
if (modify_iov(&iov, &iov_len, 0, args->length) != 0) {
iscsi_err(__FILE__, __LINE__,
"modify_iov() failed\n");
TTD_CLEANUP;
return -1;
}
iscsi_trace(TRACE_SCSI_DATA,
"reading %u bytes immediate write data\n",
args->length);
if ((uint32_t)iscsi_sock_msg(sess->sock, 0, args->length, iov,
iov_len) != args->length) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_msg() failed\n");
TTD_CLEANUP;
return -1;
}
iscsi_trace(TRACE_SCSI_DATA,
"successfully read %u bytes immediate write data\n",
args->length);
args->bytes_recv += args->length;
}
/*
* Read iSCSI data PDUs
*/
if (args->bytes_recv < args->trans_len) {
int r2t_flag = 0;
int read_status = 0;
iscsi_r2t_t r2t;
int desired_xfer_len;
desired_xfer_len = MIN(sess->sess_params.first_burst_length,
args->trans_len) - args->bytes_recv;
(void) memset(&r2t, 0x0, sizeof(r2t));
do {
/*
* Send R2T if we're either operating in solicted
* mode or we're operating in unsolicted
*/
/* mode and have reached the first burst */
if (!r2t_flag &&
(sess->sess_params.initial_r2t ||
(sess->sess_params.first_burst_length &&
(args->bytes_recv >=
sess->sess_params.first_burst_length)))) {
uint8_t header[ISCSI_HEADER_LEN];
desired_xfer_len = MIN(args->trans_len -
args->bytes_recv,
sess->sess_params.max_burst_length);
iscsi_trace(TRACE_ISCSI_DEBUG,
"sending R2T for %u bytes data\n",
desired_xfer_len);
r2t.tag = args->tag;
r2t.transfer_tag = 0x1234;
r2t.ExpCmdSN = sess->ExpCmdSN;
r2t.MaxCmdSN = sess->MaxCmdSN;
r2t.StatSN = ++(sess->StatSN);
r2t.length = desired_xfer_len;
r2t.offset = args->bytes_recv;
if (iscsi_r2t_encap(header, &r2t) != 0) {
iscsi_err(__FILE__, __LINE__,
"r2t_encap() failed\n");
TTD_CLEANUP;
return -1;
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"sending R2T tag %u transfer tag "
"%u len %u offset %u\n",
r2t.tag, r2t.transfer_tag, r2t.length,
r2t.offset);
if (iscsi_sock_msg(sess->sock, 1,
ISCSI_HEADER_LEN, header, 0) !=
ISCSI_HEADER_LEN) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_msg() failed\n");
TTD_CLEANUP;
return -1;
}
r2t_flag = 1;
r2t.R2TSN += 1;
}
/* Read iSCSI data PDU */
iscsi_trace(TRACE_ISCSI_DEBUG, "reading data pdu\n");
read_status = read_data_pdu(sess, &data, args);
if (read_status != 0) {
if (read_status == 1) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"Unknown PDU received and "
"ignored. Expecting "
"Data PDU\n");
continue;
} else {
iscsi_err(__FILE__, __LINE__,
"read_data_pdu() failed\n");
args->status = 0x02;
TTD_CLEANUP;
return -1;
}
}
if (data.ExpStatSN != sess->StatSN) {
iscsi_warn(__FILE__, __LINE__,
"Bad \"ExpStatSN\": Got %u "
"expected %u.\n",
data.ExpStatSN, sess->StatSN);
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"read data pdu OK (offset %u, length %u)\n",
data.offset, data.length);
/* Modify iov with offset and length. */
iov = iov_ptr;
(void) memcpy(iov, sg, sizeof(struct iovec) * sg_len);
iov_len = sg_len;
if (modify_iov(&iov, &iov_len, data.offset,
data.length) != 0) {
iscsi_err(__FILE__, __LINE__,
"modify_iov() failed\n");
TTD_CLEANUP;
return -1;
}
/* Scatter into destination buffers */
if ((uint32_t)iscsi_sock_msg(sess->sock, 0,
data.length, iov, iov_len) != data.length) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_msg() failed\n");
TTD_CLEANUP;
return -1;
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"successfully scattered %u bytes\n",
data.length);
args->bytes_recv += data.length;
desired_xfer_len -= data.length;
if ((!r2t_flag) &&
(args->bytes_recv >
sess->sess_params.first_burst_length)) {
iscsi_err(__FILE__, __LINE__,
"Received unsolicited data (%u) "
"more than first_burst_length (%u)\n",
args->bytes_recv,
sess->sess_params.first_burst_length);
args->status = 0x02;
TTD_CLEANUP;
return -1;
}
if ((desired_xfer_len != 0) && data.final) {
iscsi_err(__FILE__, __LINE__,
"Expecting more data (%d) "
"from initiator for this sequence\n",
desired_xfer_len);
args->status = 0x02;
TTD_CLEANUP;
return -1;
}
if ((desired_xfer_len == 0) && !data.final) {
iscsi_err(__FILE__, __LINE__,
"Final bit not set on the last "
"data PDU of this sequence\n");
args->status = 0x02;
TTD_CLEANUP;
return -1;
}
if ((desired_xfer_len == 0) &&
(args->bytes_recv < args->trans_len)) {
r2t_flag = 0;
}
} while (args->bytes_recv < args->trans_len);
#if 0
RETURN_NOT_EQUAL("Final bit", data.final, 1, TTD_CLEANUP, -1);
#else
if (data.final != 1) {
iscsi_err(__FILE__, __LINE__, "Final bit\n");
TTD_CLEANUP;
return -1;
}
#endif
} else {
#if 0
RETURN_NOT_EQUAL("Final bit", args->final, 1, TTD_CLEANUP, -1);
#else
if (args->final != 1) {
iscsi_err(__FILE__, __LINE__, "Final bit\n");
TTD_CLEANUP;
return -1;
}
#endif
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"successfully transferred %u bytes write data\n",
args->trans_len);
TTD_CLEANUP;
return 0;
}
/* check there's enough space in the arrays */
static void
size_arrays(iscsi_target_t *tgt, unsigned needed)
{
if (tgt->size == 0) {
/* only get here first time around */
tgt->size = needed;
tgt->name = calloc(sizeof(char *), needed);
tgt->value = calloc(sizeof(char *), needed);
} else if (tgt->c == tgt->size) {
/* only uses 'needed' when filled array */
tgt->size += needed;
tgt->name = realloc(tgt->name, sizeof(char *) * needed);
tgt->value = realloc(tgt->value, sizeof(char *) * needed);
}
}
/* find the name in the array */
static int
findvar(iscsi_target_t *tgt, const char *name)
{
unsigned i;
for (i = 0 ; i < tgt->c && strcmp(tgt->name[i], name) != 0; i++) {
}
return (i == tgt->c) ? -1 : (int)i;
}
/********************
* Public Functions *
********************/
int
iscsi_target_set_defaults(iscsi_target_t *tgt)
{
char buf[32];
/* set defaults */
(void) memset(tgt, 0x0, sizeof(*tgt));
iscsi_target_setvar(tgt, "iqn", DEFAULT_TARGET_NAME);
(void) snprintf(buf, sizeof(buf), "%d", ISCSI_PORT);
iscsi_target_setvar(tgt, "target port", buf);
iscsi_target_setvar(tgt, "address family", "unspec");
(void) snprintf(buf, sizeof(buf), "%d", DEFAULT_TARGET_MAX_SESSIONS);
iscsi_target_setvar(tgt, "max sessions", buf);
iscsi_target_setvar(tgt, "configfile", _PATH_ISCSI_TARGETS);
iscsi_target_setvar(tgt, "blocklen", "512");
return 1;
}
/* re-read the configuration file */
int
iscsi_target_reconfigure(iscsi_target_t *tgt)
{
targv_t *oldluns;
devv_t *olddevices;
extv_t *oldextents;
targv_t *luns;
devv_t *devices;
extv_t *extents;
char *config;
NEW(targv_t, luns, "iscsi_target_reconf 1", return -1);
NEW(devv_t, devices, "iscsi_target_reconf 2", return -1);
NEW(extv_t, extents, "iscsi_target_reconf 3", return -1);
config = iscsi_target_getvar(tgt, "configfile");
if (!read_conf_file(config, tgt->lunv, tgt->devv, tgt->extentv)) {
(void) fprintf(stderr, "Error: can't open `%s'\n", config);
return 0;
}
/* it worked - let's reassign things */
/* XXX - agc - lock */
oldluns = tgt->lunv;
olddevices = tgt->devv;
oldextents = tgt->extentv;
tgt->lunv = luns;
tgt->devv = devices;
tgt->extentv = extents;
/* XXX - agc - unlock */
/* free up storage */
(void) free(oldluns);
(void) free(olddevices);
(void) free(oldextents);
return 1;
}
int
iscsi_target_start(iscsi_target_t *tgt)
{
uint32_t j;
targv_t *lunv;
char *config;
char *dbg;
int maxsessions;
int i;
if ((dbg = iscsi_target_getvar(tgt, "debug")) != NULL) {
set_debug(dbg);
}
/* allocate space for disks, extents and targets */
NEW(targv_t, tgt->lunv, "iscsi_target_start 1", return -1);
NEW(devv_t, tgt->devv, "iscsi_target_start 2", return -1);
NEW(extv_t, tgt->extentv, "iscsi_target_start 3", return -1);
/* read the configuration file */
config = iscsi_target_getvar(tgt, "configfile");
if (!read_conf_file(config, tgt->lunv, tgt->devv, tgt->extentv)) {
(void) fprintf(stderr, "Error: can't open `%s'\n", config);
return 0;
}
lunv = tgt->lunv;
if (lunv->c == 0) {
(void) fprintf(stderr, "No targets to initialise\n");
return -1;
}
maxsessions = atoi(iscsi_target_getvar(tgt, "max sessions"));
NEWARRAY(target_session_t, g_session, maxsessions, "iscsi_target_start",
return -1);
device_set_var("blocklen", iscsi_target_getvar(tgt, "blocklen"));
if (tgt->state == TARGET_INITIALIZING ||
tgt->state == TARGET_INITIALIZED) {
iscsi_err(__FILE__, __LINE__,
"duplicate target initialization attempted\n");
return -1;
}
tgt->state = TARGET_INITIALIZING;
if (iscsi_queue_init(&g_session_q, maxsessions) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_queue_init() failed\n");
return -1;
}
tgt->main_pid = getpid();
for (i = 0; i < maxsessions; i++) {
g_session[i].id = i;
g_session[i].d = -1;
if (iscsi_queue_insert(&g_session_q, &g_session[i]) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_queue_insert() failed\n");
return -1;
}
}
for (j = 0 ; j < lunv->c ; j++) {
int d = device_init(tgt, lunv, &lunv->v[j]);
if (d < 0) {
iscsi_err(__FILE__, __LINE__,
"device_init() failed\n");
return -1;
}
}
ISCSI_MUTEX_INIT(&g_session_q_mutex, return -1);
tgt->listener_listening = 0;
tgt->listener_pid = -1;
tgt->state = TARGET_INITIALIZED;
printf("TARGET: iSCSI Qualified Name (IQN) is %s\n",
iscsi_target_getvar(tgt, "iqn"));
for (i = 0 ; i < tgt->sockc ; i++) {
printf("\tsocket %d listening on port %s\n", tgt->sockv[i],
iscsi_target_getvar(tgt, "target port"));
}
return 0;
}
int
iscsi_target_shutdown(iscsi_target_t *tgt)
{
target_session_t *sess;
int maxsessions;
int i;
if ((tgt->state == TARGET_SHUTTING_DOWN) ||
(tgt->state == TARGET_SHUT_DOWN)) {
iscsi_err(__FILE__, __LINE__,
"duplicate target shutdown attempted\n");
return -1;
}
tgt->state = TARGET_SHUTTING_DOWN;
iscsi_trace(TRACE_ISCSI_DEBUG, "shutting down target\n");
maxsessions = atoi(iscsi_target_getvar(tgt, "max sessions"));
for (i = 0; i < maxsessions; i++) {
sess = &g_session[i];
/* Need to replace with a call to session_destroy() */
if (sess->IsLoggedIn) {
printf("shutting down socket on sess %d\n", i);
iscsi_trace(TRACE_ISCSI_DEBUG,
"shutting down socket on sess %d\n", i);
if (iscsi_sock_shutdown(sess->sock, 2) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_shutdown() failed\n");
return -1;
}
printf("waiting for worker %d (pid %d, state %d)\n",
i, sess->worker.pid, sess->worker.state);
iscsi_trace(TRACE_ISCSI_DEBUG,
"waiting for worker %d (pid %d, state %d)\n",
i, sess->worker.pid, sess->worker.state);
while (sess->worker.state &
ISCSI_WORKER_STATE_STARTED) {
ISCSI_SPIN;
}
iscsi_trace(TRACE_ISCSI_DEBUG,
"worker %d has exited\n", i);
}
if (device_shutdown(sess) != 0) {
iscsi_err(__FILE__, __LINE__,
"device_shutdown() failed\n");
return -1;
}
}
iscsi_trace(TRACE_ISCSI_DEBUG, "shutting down accept socket\n");
if (iscsi_sock_shutdown(tgt->sockv[0], 2) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_shutdown() failed\n");
return -1;
}
if (tgt->listener_pid != getpid()) {
iscsi_trace(TRACE_ISCSI_DEBUG, "waiting for listener thread\n");
while (tgt->listener_listening) {
ISCSI_SPIN;
}
iscsi_trace(TRACE_ISCSI_DEBUG, "listener thread has exited\n");
}
iscsi_trace(TRACE_ISCSI_DEBUG, "closing accept socket\n");
if (iscsi_sock_close(tgt->sockv[0]) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_close() failed\n");
return -1;
}
ISCSI_MUTEX_DESTROY(&g_session_q_mutex, return -1);
iscsi_trace(TRACE_ISCSI_DEBUG, "target shutdown complete\n");
tgt->state = TARGET_SHUT_DOWN;
return 0;
}
int
iscsi_target_listen(iscsi_target_t *tgt)
{
struct sockaddr_in6 remoteAddrStorage6;
struct sockaddr_in6 localAddrStorage6;
struct sockaddr_in remoteAddrStorage;
struct sockaddr_in localAddrStorage;
target_session_t *sess;
socklen_t remoteAddrLen;
socklen_t localAddrLen;
char targetaddress[1024];
char remote[1024];
char local[1024];
char *config;
int newconn;
int i;
ISCSI_THREAD_START("listen_thread");
tgt->listener_pid = getpid();
tgt->listener_listening++;
iscsi_trace(TRACE_ISCSI_DEBUG, "listener thread started\n");
if (!iscsi_socks_establish(tgt->sockv, tgt->famv, &tgt->sockc,
iscsi_target_getvar(tgt, "address family"),
atoi(iscsi_target_getvar(tgt, "target port")))) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_establish() failed\n");
goto done;
}
iscsi_trace(TRACE_NET_DEBUG, "create, bind, listen OK\n");
/* Loop for connections: FIX ME with queue */
while (tgt->state != TARGET_SHUT_DOWN) {
ISCSI_LOCK(&g_session_q_mutex, return -1);
if ((sess = iscsi_queue_remove(&g_session_q)) == NULL) {
iscsi_err(__FILE__, __LINE__,
"no free sessions: iscsi_queue_remove() failed\n");
goto done;
}
ISCSI_UNLOCK(&g_session_q_mutex, return -1);
assert(sess->d == -1);
#if 0
(void) memset(sess, 0x0, sizeof(*sess));
#endif
sess->target = tgt;
/* Accept connection, spawn session thread, and */
/* clean up old threads */
config = iscsi_target_getvar(tgt, "configfile");
i = iscsi_waitfor_connection(tgt->sockv, tgt->sockc, config,
&newconn);
iscsi_trace(TRACE_NET_DEBUG,
"waiting for %s connection on port %s\n",
iscsi_address_family(tgt->famv[i]),
iscsi_target_getvar(tgt, "target port"));
if (!iscsi_sock_accept(newconn, &sess->sock)) {
iscsi_trace(TRACE_ISCSI_DEBUG,
"iscsi_sock_accept() failed\n");
goto done;
}
switch (tgt->famv[i]) {
case AF_INET:
sess->address_family = 4;
(void) memset(&localAddrStorage, 0x0,
localAddrLen = sizeof(localAddrStorage));
if (getsockname(sess->sock,
(struct sockaddr *)(void *)&localAddrStorage,
&localAddrLen) < 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_getsockname() failed\n");
goto done;
}
(void) memset(&remoteAddrStorage, 0x0,
remoteAddrLen = sizeof(remoteAddrStorage));
if (getpeername(sess->sock,
(struct sockaddr *)(void *) &remoteAddrStorage,
&remoteAddrLen) < 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_getpeername() failed\n");
goto done;
}
#ifdef HAVE_GETNAMEINFO
if (getnameinfo((struct sockaddr *)(void *)
&localAddrStorage,
sizeof(localAddrStorage), local,
sizeof(local), NULL, 0, NI_NUMERICHOST) < 0) {
iscsi_err(__FILE__, __LINE__,
"getnameinfo local failed\n");
}
if (getnameinfo((struct sockaddr *)(void *)
&remoteAddrStorage,
sizeof(remoteAddrStorage), remote,
sizeof(remote), NULL, 0, NI_NUMERICHOST) < 0) {
iscsi_err(__FILE__, __LINE__,
"getnameinfo remote failed\n");
}
(void) strlcpy(sess->initiator, remote,
sizeof(sess->initiator));
#else
(void) strlcpy(local,
inet_ntoa(localAddrStorage.sin_addr),
sizeof(local));
(void) strlcpy(sess->initiator,
inet_ntoa(remoteAddrStorage.sin_addr),
sizeof(sess->initiator));
#endif
(void) snprintf(targetaddress, sizeof(targetaddress),
"%s:%s,1", local,
iscsi_target_getvar(tgt, "target port"));
iscsi_target_setvar(tgt, "target address",
targetaddress);
iscsi_trace(TRACE_ISCSI_DEBUG,
"IPv4 connection accepted on port %s "
"(local IP %s, remote IP %s)\n",
iscsi_target_getvar(tgt, "target port"),
local, sess->initiator);
iscsi_trace(TRACE_ISCSI_DEBUG,
"TargetAddress = \"%s\"\n", targetaddress);
break;
case AF_INET6:
sess->address_family = 6;
(void) memset(&localAddrStorage6, 0x0,
localAddrLen = sizeof(localAddrStorage6));
if (getsockname(sess->sock, (struct sockaddr *)(void *)
&localAddrStorage6, &localAddrLen) < 0) {
iscsi_err(__FILE__, __LINE__,
"getsockname() failed\n");
goto done;
}
(void) memset(&remoteAddrStorage6, 0x0,
remoteAddrLen = sizeof(remoteAddrStorage6));
if (getpeername(sess->sock, (struct sockaddr *)(void *)
&remoteAddrStorage6, &remoteAddrLen) < 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_sock_getpeername() failed\n");
goto done;
}
if (getnameinfo((struct sockaddr *)(void *)
&localAddrStorage6, sizeof(localAddrStorage6),
local, sizeof(local), NULL, 0,
NI_NUMERICHOST) < 0) {
iscsi_err(__FILE__, __LINE__,
"getnameinfo local failed\n");
}
if (getnameinfo((struct sockaddr *)(void *)
&remoteAddrStorage6,
sizeof(remoteAddrStorage6), remote,
sizeof(remote), NULL, 0, NI_NUMERICHOST) < 0) {
iscsi_err(__FILE__, __LINE__,
"getnameinfo remote failed\n");
}
(void) strlcpy(sess->initiator, remote,
sizeof(sess->initiator));
(void) snprintf(targetaddress, sizeof(targetaddress),
"%s:%s,1", local,
iscsi_target_getvar(tgt, "target port"));
iscsi_target_setvar(tgt, "target address",
targetaddress);
iscsi_trace(TRACE_ISCSI_DEBUG,
"IPv6 connection accepted on port %s "
"(local IP %s, remote IP %s)\n",
iscsi_target_getvar(tgt, "target port"),
local, sess->initiator);
iscsi_trace(TRACE_ISCSI_DEBUG,
"TargetAddress = \"%s\"\n", targetaddress);
break;
}
if (iscsi_thread_create(&sess->worker.thread,
(void *) worker_proc_t, sess) != 0) {
iscsi_err(__FILE__, __LINE__,
"iscsi_thread_create() failed\n");
goto done;
}
}
done:
tgt->listener_listening--;
return 0;
}
/* write the pid to the pid file */
void
iscsi_target_write_pidfile(const char *f)
{
FILE *fp;
if (f == NULL) {
f = _PATH_ISCSI_PID_FILE;
}
if ((fp = fopen(f, "w")) == NULL) {
(void) fprintf(stderr, "Couldn't create pid file \"%s\": %s",
f, strerror(errno));
} else {
(void) fprintf(fp, "%ld\n", (long) getpid());
(void) fclose(fp);
}
}
/* set a variable */
int
iscsi_target_setvar(iscsi_target_t *tgt, const char *name, const char *value)
{
int i;
if ((i = findvar(tgt, name)) < 0) {
/* add the element to the array */
size_arrays(tgt, tgt->size + 15);
tgt->name[i = tgt->c++] = strdup(name);
} else {
/* replace the element in the array */
if (tgt->value[i]) {
(void) free(tgt->value[i]);
tgt->value[i] = NULL;
}
}
/* sanity checks for range of values would go here */
tgt->value[i] = strdup(value);
return 1;
}
/* get a variable's value (NULL if not set) */
char *
iscsi_target_getvar(iscsi_target_t *tgt, const char *name)
{
int i;
return ((i = findvar(tgt, name)) < 0) ? NULL : tgt->value[i];
}