These patches reflect a preliminary version of iser-related changes and are presented as a new implementation concept. More changes in iscsi infrastructure design are required. This RFC contains a re-write of the iser code (file iscsi_rdma.c is replaced by iser_ib.c to simplify diff). New header iser.h is created. A separate transport lld (named "iser") is defined. Some code, mostly iscsi text related, is replicated and put into a new file iser_text.c, mainly because the functions there are not general enough, and rely on specifics of iscsi/tcp structs. Large portions of code are moved from iscsid.c and shuffled around. This code seems to fix an occasional data corruption that happens with the current version. There are many unhandled error cases and rare conditions, left until there is a solid common iscsi framework to rely upon. Most of them are marked with ToDo comments. The code is fairly RDMA-transport agnostic (ib/iwarp), but it was never verified over iwarp (this fact is reflected in the file name iser_ib.c). The code implies RDMA-only mode of work. This means the first burst incl. immediate data should be disabled, so that the entire data transfer is performed using RDMA. It introduces some preparations for handling other (general) scenarios, but as tgt has no framework for multi-buffer commands, these extra code segments are either commented or conditioned upon events that should never take place. All such places have a ToDo comment over them. This specific patch compiles when applied to the current git head, but was not verified to work with it. It'd been branched off from a rather old commit. For the same reason, some newer features (like redirect etc.) produce some diffs, although they are quite irrelevant. Files in patch 1/3: usr/iscsi/iser.h usr/iscsi/iser_text.c Files in patch 2/3: usr/iscsi/iser_ib.c Files in patch 3/3: usr/Makefile usr/iscsi/iscsi_tcp.c usr/iscsi/iscsid.c usr/iscsi/iscsid.h usr/iscsi/isns.c usr/iscsi/session.c usr/iscsi/target.c usr/list.h Signed-off-by: Alexander Nezhinsky <alexandern at voltaire.com> --- usr/iscsi/iser.h | 238 +++++++++++++++ usr/iscsi/iser_text.c | 805 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1043 insertions(+), 0 deletions(-) create mode 100644 usr/iscsi/iser.h create mode 100644 usr/iscsi/iser_text.c diff --git a/usr/iscsi/iser.h b/usr/iscsi/iser.h new file mode 100644 index 0000000..459afb5 --- /dev/null +++ b/usr/iscsi/iser.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2010 Voltaire, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef ISER_H +#define ISER_H + +#include "iscsid.h" + +/* + * The IB-extended version from the kernel. Stags and VAs are in + * big-endian format. + */ +struct iser_hdr { + uint8_t flags; + uint8_t rsvd[3]; + uint32_t write_stag; /* write rkey */ + uint64_t write_va; + uint32_t read_stag; /* read rkey */ + uint64_t read_va; +} __attribute__((packed)); + +#define ISER_WSV (0x08) +#define ISER_RSV (0x04) +#define ISCSI_CTRL (0x10) +#define ISER_HELLO (0x20) +#define ISER_HELLORPLY (0x30) + +struct iser_conn; + +/* + * Work requests are either posted Receives for control messages, + * or Send and RDMA ops (also considered "send" by IB) + * They have different work request descriptors. + * During a flush, we need to know the type of op and the + * task to which it is related. + */ +struct iser_work_req { + struct ibv_sge sge; + int recv; /* type of op: recv or send */ + struct iser_task *task; + union { + struct ibv_recv_wr recv_wr; + struct ibv_send_wr send_wr; + }; + struct list_head wr_list; +}; + +/* + * Pre-registered memory. Buffers are allocated by iscsi from us, handed + * to device to fill, then iser can send them directly without registration. + * Also for write path. + */ +struct membuf { + void *buf; + unsigned size; + unsigned offset; /* offset within task data */ + struct list_head task_list; + int rdma; + struct list_head pool_list; +}; + +struct iser_pdu { + struct iser_hdr *iser_hdr; + struct iscsi_hdr *bhs; + unsigned int ahssize; + void *ahs; + struct membuf data; // describes pdu data section only, + // the original buffer is reflected in ibv_sge +}; + +/* + * Each SCSI command may have its own RDMA parameters. These appear on + * the connection then later are assigned to the particular task to be + * used when the target responds. + */ +struct iser_task { + struct iser_conn *conn; + + struct iser_work_req rxd; + struct iser_pdu req; + + /* read and write from the initiator's point of view */ + uint32_t rem_read_stag, rem_write_stag; + uint64_t rem_read_va, rem_write_va; + + int opcode; + uint64_t tag; + int is_read; + int is_write; + + unsigned long flags; + + int in_len; + int out_len; + + int unsol_sz; + int unsol_remains; + int rdma_rd_sz; + int rdma_rd_remains; + // int rdma_rd_offset; // ToDo: multiple RDMA-Write buffers + int rdma_wr_sz; + int rdma_wr_remains; + + int unsolicited; + + struct iser_work_req txd; + struct iser_pdu rsp; + + struct list_head in_buf_list; + int in_buf_num; + + struct list_head out_buf_list; + int out_buf_num; + + struct iser_work_req rdmad; + + struct list_head exec_list; + struct list_head rdma_list; + struct list_head tx_list; + struct list_head recv_list; + + /* linked to session->cmd_list */ + struct list_head session_list; + + struct list_head dout_task_list; + + int result; + struct scsi_cmd scmd; + + unsigned long extdata[0]; +}; + +struct iser_device; + +/* + * Parallels iscsi_connection. Adds more fields for iser. + */ +struct iser_conn { + struct iscsi_connection h; + + struct ibv_qp *qp_hndl; + struct rdma_cm_id *cm_id; + struct iser_device *dev; + struct sockaddr_storage peer_addr; /* initiator address */ + struct sockaddr_storage self_addr; /* target address */ + unsigned int ssize, rsize, max_outst_pdu; + + enum { + LOGIN_PHASE_START, /* keep 1 send spot and 1 recv posted */ + LOGIN_PHASE_LAST_SEND, /* need 1 more send before ff */ + LOGIN_PHASE_FF, /* full feature */ + } login_phase; + + int recvl_posted; + + struct event_data sched_buf_alloc; + struct list_head buf_alloc_list; + + struct event_data sched_rdma_rd; + struct list_head rdma_rd_list; + + struct event_data sched_iosubmit; + struct list_head iosubmit_list; + + struct event_data sched_tx; + struct list_head resp_tx_list; + + struct list_head sent_list; + + struct event_data sched_post_recv; + struct list_head post_recv_list; + + struct event_data sched_conn_free; + + /* FF resources */ + int ff_res_alloc; + void *task_pool; /* space for the send, recv, rdma list elements */ + void *pdu_data_pool; /* registered space for non-rdma send and recv */ + struct ibv_mr *pdu_data_mr; /* mr for registered pdu_data_buf */ + struct iser_task *nop_in_task; + + /* login phase resources, freed at full-feature */ + int login_res_alloc; + void *login_task_pool; + void *login_data_pool; + struct ibv_mr *login_data_mr; + struct iser_task *login_tx_task; + + /* list of all iser conns */ + struct list_head conn_list; +}; + +/* + * Shared variables for a particular device. The conn[] array will + * have to be broken out when multiple device support is added, maybe with + * a pointer into this "device" struct. + */ +struct iser_device { + struct list_head list; + struct ibv_context *ibv_hndl; + struct ibv_pd *pd; + struct ibv_cq *cq; + struct ibv_comp_channel *cq_channel; + struct ibv_device_attr device_attr; + + /* membuf registered buffer, list area, handle */ + void *membuf_regbuf; + void *membuf_listbuf; + struct ibv_mr *membuf_mr; + int waiting_for_mem; + + struct event_data poll_sched; + + /* free and allocated membuf entries */ + struct list_head membuf_free, membuf_alloc; +}; + +void iser_login_exec(struct iser_task *task); +int iser_login_complete(struct iscsi_connection *conn); +int iser_text_exec(struct iser_task *task); +void iser_conn_close(struct iser_conn *conn); + +#endif /* ISER_H */ diff --git a/usr/iscsi/iser_text.c b/usr/iscsi/iser_text.c new file mode 100644 index 0000000..d9e2f22 --- /dev/null +++ b/usr/iscsi/iser_text.c @@ -0,0 +1,805 @@ +/* + * iSCSI extensions for RDMA (iSER) + * LOGIN and TEXT related code + * + * Copyright (C) 2007 Dennis Dalessandro (dennis at osc.edu) + * Copyright (C) 2007 Ananth Devulapalli (ananth at osc.edu) + * Copyright (C) 2007 Pete Wyckoff (pw at osc.edu) + * Copyright (c) 2010 Voltaire, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <assert.h> +#include <netdb.h> +#include <sys/epoll.h> +#include <infiniband/verbs.h> +#include <rdma/rdma_cma.h> + +#include "util.h" +#include "iscsid.h" +#include "iser.h" + +#if defined(HAVE_VALGRIND) && !defined(NDEBUG) + #include <valgrind/memcheck.h> +#else + #define VALGRIND_MAKE_MEM_DEFINED(addr, len) +#endif + + +static struct iscsi_key login_keys[] = { + {"InitiatorName",}, + {"InitiatorAlias",}, + {"SessionType",}, + {"TargetName",}, + {NULL, 0, 0, 0, NULL}, +}; + +static char *iser_text_next_key(char **data, int *datasize, char **value) +{ + char *key, *p, *q; + int size = *datasize; + + key = p = *data; + for (; size > 0 && *p != '='; p++, size--) + ; + if (!size) + return NULL; + *p++ = 0; + size--; + + for (q = p; size > 0 && *p != 0; p++, size--) + ; + if (!size) + return NULL; + p++; + size--; + + *data = p; + *value = q; + *datasize = size; + + return key; +} + +char *iser_text_key_find(char *data, int datasize, char *searchKey) +{ + int keylen = strlen(searchKey); + char *key, *value; + + while (1) { + for (key = data; datasize > 0 && *data != '='; data++, datasize--) + ; + if (!datasize) + return NULL; + data++; + datasize--; + + for (value = data; datasize > 0 && *data != 0; data++, datasize--) + ; + if (!datasize) + return NULL; + data++; + datasize--; + + if (keylen == value - key - 1 + && !strncmp(key, searchKey, keylen)) + return value; + } +} + +void iser_text_key_add(struct iser_task *task, char *key, char *value) +{ + struct iser_conn *conn = task->conn; + int keylen = strlen(key); + int valuelen = strlen(value); + int len = keylen + valuelen + 2; + char *buffer = task->rsp.data.buf; + + if (task->rsp.data.size + len > conn->ssize) { + log_warning("Dropping key (%s=%s)", key, value); + return; + } + + buffer += task->rsp.data.size; + task->rsp.data.size += len; + + strcpy(buffer, key); + buffer += keylen; + *buffer++ = '='; + strcpy(buffer, value); +} + +static void iser_text_key_add_reject(struct iser_task *task, char *key) +{ + iser_text_key_add(task, key, "Reject"); +} + +static void iser_login_security_scan(struct iser_task *task) +{ + struct iscsi_connection *iscsi_conn = &task->conn->h; + struct iscsi_login_rsp *rsp = (struct iscsi_login_rsp *)task->rsp.bhs; + char *key, *value, *data, *nextValue; + int datasize; + + data = task->req.data.buf; + datasize = task->req.data.size; + + while ((key = iser_text_next_key(&data, &datasize, &value))) { + if (!(param_index_by_name(key, login_keys) < 0)) + ; + else if (!strcmp(key, "AuthMethod")) { + do { + nextValue = strchr(value, ','); + if (nextValue) + *nextValue++ = 0; + + if (!strcmp(value, "None")) { + if (account_available(iscsi_conn->tid, AUTH_DIR_INCOMING)) + continue; + iscsi_conn->auth_method = AUTH_NONE; + iser_text_key_add(task, key, "None"); + break; + } else if (!strcmp(value, "CHAP")) { + if (!account_available(iscsi_conn->tid, AUTH_DIR_INCOMING)) + continue; + iscsi_conn->auth_method = AUTH_CHAP; + iser_text_key_add(task, key, "CHAP"); + break; + } + } while ((value = nextValue)); + + if (iscsi_conn->auth_method == AUTH_UNKNOWN) + iser_text_key_add_reject(task, key); + } else + iser_text_key_add(task, key, "NotUnderstood"); + } + if (iscsi_conn->auth_method == AUTH_UNKNOWN) { + rsp->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp->status_detail = ISCSI_LOGIN_STATUS_AUTH_FAILED; + iscsi_conn->state = STATE_EXIT; + } +} + +static void iser_login_security_done(struct iser_task *task) +{ + struct iscsi_connection *iscsi_conn = &task->conn->h; + struct iscsi_login_rsp *rsp = (struct iscsi_login_rsp *)task->rsp.bhs; + struct iscsi_login *req = (struct iscsi_login *)task->req.bhs; + struct iscsi_session *session; + + if (!iscsi_conn->tid) + return; + + session = session_find_name(iscsi_conn->tid, iscsi_conn->initiator, req->isid); + if (session) { + if (!req->tsih) { + struct iscsi_connection *ent, *next; + struct iser_conn *c; + + /* do session reinstatement */ + + list_for_each_entry_safe(ent, next, &session->conn_list, + clist) { + c = container_of(ent, struct iser_conn, h); + iser_conn_close(c); + } + + session = NULL; + } else if (req->tsih != session->tsih) { + /* fail the login */ + rsp->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp->status_detail = ISCSI_LOGIN_STATUS_TGT_NOT_FOUND; + iscsi_conn->state = STATE_EXIT; + return; + } else if (conn_find(session, iscsi_conn->cid)) { + /* do connection reinstatement */ + } + + /* add a new connection to the session */ + if (session) + conn_add_to_session(iscsi_conn, session); + } else { + if (req->tsih) { + /* fail the login */ + rsp->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp->status_detail = ISCSI_LOGIN_STATUS_NO_SESSION; + iscsi_conn->state = STATE_EXIT; + return; + } + /* + * We do nothing here and instantiate a new session + * later at login_finish(). + */ + } +} + +static void iser_login_oper_scan(struct iser_task *task) +{ + struct iscsi_connection *iscsi_conn = &task->conn->h; + struct iscsi_login_rsp *rsp = (struct iscsi_login_rsp *)task->rsp.bhs; + char *key, *value, *data; + int datasize, idx, is_rdma = 0; + + data = task->req.data.buf; + datasize = task->req.data.size; + + while ((key = iser_text_next_key(&data, &datasize, &value))) { + if (!(param_index_by_name(key, login_keys) < 0)) + ; + else if (!strcmp(key, "AuthMethod")) + ; + else if (!((idx = param_index_by_name(key, session_keys)) < 0)) { + int err; + unsigned int val; + char buf[32]; + + if (idx == ISCSI_PARAM_MAX_RECV_DLENGTH) + idx = ISCSI_PARAM_MAX_XMIT_DLENGTH; + + if (idx == ISCSI_PARAM_RDMA_EXTENSIONS) + is_rdma = 1; + + if (param_str_to_val(session_keys, idx, value, &val) < 0) { + if (iscsi_conn->session_param[idx].state + == KEY_STATE_START) { + iser_text_key_add_reject(task, key); + continue; + } else { + rsp->status_class = + ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp->status_detail = + ISCSI_LOGIN_STATUS_INIT_ERR; + iscsi_conn->state = STATE_EXIT; + goto out; + } + } + + err = param_check_val(session_keys, idx, &val); + err = param_set_val(session_keys, iscsi_conn->session_param, idx, &val); + + switch (iscsi_conn->session_param[idx].state) { + case KEY_STATE_START: + if (idx == ISCSI_PARAM_MAX_XMIT_DLENGTH) + break; + memset(buf, 0, sizeof(buf)); + param_val_to_str(session_keys, idx, val, buf); + iser_text_key_add(task, key, buf); + break; + case KEY_STATE_REQUEST: + if (val != iscsi_conn->session_param[idx].val) { + rsp->status_class = + ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp->status_detail = + ISCSI_LOGIN_STATUS_INIT_ERR; + iscsi_conn->state = STATE_EXIT; + log_warning("%s %u %u\n", key, + val, iscsi_conn->session_param[idx].val); + goto out; + } + break; + case KEY_STATE_DONE: + break; + } + iscsi_conn->session_param[idx].state = KEY_STATE_DONE; + } else + iser_text_key_add(task, key, "NotUnderstood"); + } + + if (is_rdma) { + /* do not try to do digests, not supported in iser */ + iscsi_conn->session_param[ISCSI_PARAM_HDRDGST_EN].val = DIGEST_NONE; + iscsi_conn->session_param[ISCSI_PARAM_DATADGST_EN].val = DIGEST_NONE; + } else { + /* do not offer RDMA, initiator must explicitly request */ + iscsi_conn->session_param[ISCSI_PARAM_RDMA_EXTENSIONS].val = 0; + } + +out: + return; +} + +static int iser_login_check_params(struct iser_task *task) +{ + struct iscsi_connection *iscsi_conn = &task->conn->h; + struct param *p = iscsi_conn->session_param; + char buf[32]; + int i, cnt; + + for (i = 0, cnt = 0; session_keys[i].name; i++) { + if (p[i].state == KEY_STATE_START && p[i].val != session_keys[i].def) { + if (iscsi_conn->state == STATE_LOGIN) { + if (i == ISCSI_PARAM_MAX_XMIT_DLENGTH) { + if (p[i].val > session_keys[i].def) + p[i].val = session_keys[i].def; + p[i].state = KEY_STATE_DONE; + continue; + } + if (p[ISCSI_PARAM_RDMA_EXTENSIONS].val == 1) { + if (i == ISCSI_PARAM_MAX_RECV_DLENGTH) + continue; + } else { + if (i >= ISCSI_PARAM_RDMA_EXTENSIONS) + continue; + } + memset(buf, 0, sizeof(buf)); + param_val_to_str(session_keys, i, p[i].val, + buf); + iser_text_key_add(task, session_keys[i].name, buf); + p[i].state = KEY_STATE_REQUEST; + } + cnt++; + } + } + + return cnt; +} + +static int iser_login_auth_exec(struct iscsi_connection *iscsi_conn) +{ + int res; + + switch (iscsi_conn->auth_method) { + case AUTH_CHAP: + res = cmnd_exec_auth_chap(iscsi_conn); + eprintf("CHAP currently unsupported\n"); + res = -3; + break; + case AUTH_NONE: + res = 0; + break; + default: + eprintf("Unknown auth. method %d\n", iscsi_conn->auth_method); + res = -3; + } + + return res; +} + +static void iser_login_start(struct iser_task *task) +{ + struct iscsi_connection *iscsi_conn = &task->conn->h; + struct iscsi_login *req_bhs = (struct iscsi_login *)task->req.bhs; + struct iscsi_login_rsp *rsp_bhs = (struct iscsi_login_rsp *)task->rsp.bhs; + char *req_data = task->req.data.buf; + int req_datasize = task->req.data.size; + char *name, *alias, *session_type, *target_name; + struct iscsi_target *target; + + iscsi_conn->cid = be16_to_cpu(req_bhs->cid); + memcpy(iscsi_conn->isid, req_bhs->isid, sizeof(req_bhs->isid)); + iscsi_conn->tsih = req_bhs->tsih; + + if (!sid64(iscsi_conn->isid, iscsi_conn->tsih)) { + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_MISSING_FIELDS; + iscsi_conn->state = STATE_EXIT; + return; + } + + name = iser_text_key_find(req_data, req_datasize, "InitiatorName"); + if (!name) { + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_MISSING_FIELDS; + iscsi_conn->state = STATE_EXIT; + return; + } + iscsi_conn->initiator = strdup(name); + alias = iser_text_key_find(req_data, req_datasize, "InitiatorAlias"); + session_type = iser_text_key_find(req_data, req_datasize, "SessionType"); + target_name = iser_text_key_find(req_data, req_datasize, "TargetName"); + + iscsi_conn->auth_method = -1; + iscsi_conn->session_type = SESSION_NORMAL; + + if (session_type) { + if (!strcmp(session_type, "Discovery")) + iscsi_conn->session_type = SESSION_DISCOVERY; + else if (strcmp(session_type, "Normal")) { + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_NO_SESSION_TYPE; + iscsi_conn->state = STATE_EXIT; + return; + } + } + + if (iscsi_conn->session_type == SESSION_NORMAL) { + if (!target_name) { + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_MISSING_FIELDS; + iscsi_conn->state = STATE_EXIT; + return; + } + + target = target_find_by_name(target_name); + if (!target) { + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_TGT_NOT_FOUND; + iscsi_conn->state = STATE_EXIT; + return; + } + if (!target->rdma) { + eprintf("Target %s is TCP, but conn cid:%d from %s is RDMA\n", + target_name, iscsi_conn->cid, iscsi_conn->initiator); + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_TGT_NOT_FOUND; + iscsi_conn->state = STATE_EXIT; + return; + } + iscsi_conn->tid = target->tid; + + if (tgt_get_target_state(target->tid) != SCSI_TARGET_READY) { + rsp_bhs->status_class = ISCSI_STATUS_CLS_TARGET_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_TARGET_ERROR; + iscsi_conn->state = STATE_EXIT; + return; + } + + if (ip_acl(iscsi_conn->tid, iscsi_conn)) { + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_TGT_NOT_FOUND; + iscsi_conn->state = STATE_EXIT; + return; + } + + if (isns_scn_access(iscsi_conn->tid, name)) { + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_TGT_NOT_FOUND; + iscsi_conn->state = STATE_EXIT; + return; + } + +/* if (iscsi_conn->target->max_sessions && */ +/* (++iscsi_conn->target->session_cnt > iscsi_conn->target->max_sessions)) { */ +/* iscsi_conn->target->session_cnt--; */ +/* rsp_bhs->status_class = ISCSI_STATUS_INITIATOR_ERR; */ +/* rsp_bhs->status_detail = ISCSI_STATUS_TOO_MANY_CONN; */ +/* iscsi_conn->state = STATE_EXIT; */ +/* return; */ +/* } */ + + memcpy(iscsi_conn->session_param, target->session_param, + sizeof(iscsi_conn->session_param)); + iscsi_conn->exp_cmd_sn = be32_to_cpu(req_bhs->cmdsn); + dprintf("exp_cmd_sn: %d,%d\n", iscsi_conn->exp_cmd_sn, req_bhs->cmdsn); + iscsi_conn->max_cmd_sn = iscsi_conn->exp_cmd_sn; + } + iser_text_key_add(task, "TargetPortalGroupTag", "1"); +} + +static void iser_login_finish(struct iser_task *task) +{ + struct iscsi_connection *iscsi_conn = &task->conn->h; + struct iscsi_login_rsp *rsp_bhs = (struct iscsi_login_rsp *)task->rsp.bhs; + int err; + uint8_t class, detail; + + switch (iscsi_conn->session_type) { + case SESSION_NORMAL: + /* + * update based on negotiations (but ep_login_complete + * could override) + */ + //iscsi_conn->data_inout_max_length = + //iscsi_conn->session_param[ISCSI_PARAM_MAX_XMIT_DLENGTH].val; + + /* + * Allocate transport resources for this connection. + */ + err = iser_login_complete(iscsi_conn); + if (err) { + class = ISCSI_STATUS_CLS_TARGET_ERR; + detail = ISCSI_LOGIN_STATUS_NO_RESOURCES; + goto fail; + } + if (!iscsi_conn->session) { + err = session_create(iscsi_conn); + if (err) { + class = ISCSI_STATUS_CLS_TARGET_ERR; + detail = ISCSI_LOGIN_STATUS_TARGET_ERROR; + goto fail; + } + } else { + /* + if (iscsi_conn->rdma ^ iscsi_conn->session->rdma) { + eprintf("new iscsi_conn rdma %d, but session %d\n", + iscsi_conn->rdma, iscsi_conn->session->rdma); + + class = ISCSI_STATUS_CLS_INITIATOR_ERR; + detail =ISCSI_LOGIN_STATUS_INVALID_REQUEST; + goto fail; + } + */ + } + memcpy(iscsi_conn->isid, iscsi_conn->session->isid, sizeof(iscsi_conn->isid)); + iscsi_conn->tsih = iscsi_conn->session->tsih; + break; + case SESSION_DISCOVERY: + /* set a dummy tsih value */ + iscsi_conn->tsih = 1; + break; + } + + return; +fail: + rsp_bhs->flags = 0; + rsp_bhs->status_class = class; + rsp_bhs->status_detail = detail; + iscsi_conn->state = STATE_EXIT; + return; +} + +void iser_login_exec(struct iser_task *task) +{ + struct iscsi_connection *iscsi_conn = &task->conn->h; + struct iscsi_login *req_bhs = (struct iscsi_login *)task->req.bhs; + struct iscsi_login_rsp *rsp_bhs = (struct iscsi_login_rsp *)task->rsp.bhs; + int stay = 0, nsg_disagree = 0; + + memset(rsp_bhs, 0, BHS_SIZE); + if ((req_bhs->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_LOGIN || + !(req_bhs->opcode & ISCSI_OP_IMMEDIATE)) { + /* reject */ + } + + rsp_bhs->opcode = ISCSI_OP_LOGIN_RSP; + rsp_bhs->max_version = ISCSI_DRAFT20_VERSION; + rsp_bhs->active_version = ISCSI_DRAFT20_VERSION; + rsp_bhs->itt = req_bhs->itt; + + if (/* req_bhs->max_version < ISCSI_VERSION || */ + req_bhs->min_version > ISCSI_DRAFT20_VERSION) { + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_NO_VERSION; + iscsi_conn->state = STATE_EXIT; + return; + } + + iscsi_conn->exp_cmd_sn = iscsi_conn->max_cmd_sn = ntohl(req_bhs->cmdsn); + + switch (ISCSI_LOGIN_CURRENT_STAGE(req_bhs->flags)) { + case ISCSI_SECURITY_NEGOTIATION_STAGE: + dprintf("Login request (security negotiation): %d\n", + iscsi_conn->state); + rsp_bhs->flags = ISCSI_SECURITY_NEGOTIATION_STAGE << 2; + + switch (iscsi_conn->state) { + case STATE_READY: + iscsi_conn->state = STATE_SECURITY; + iser_login_start(task); + + if (rsp_bhs->status_class) + return; + /* fall through */ + case STATE_SECURITY: + iser_login_security_scan(task); + if (rsp_bhs->status_class) + return; + if (iscsi_conn->auth_method != AUTH_NONE) { + iscsi_conn->state = STATE_SECURITY_AUTH; + iscsi_conn->auth_state = AUTH_STATE_START; + } + break; + case STATE_SECURITY_AUTH: + switch (iser_login_auth_exec(iscsi_conn)) { + case 0: + break; + default: + case -1: + goto init_err; + case -2: + goto auth_err; + } + break; + default: + goto init_err; + } + + break; + case ISCSI_OP_PARMS_NEGOTIATION_STAGE: + dprintf("Login request (operational negotiation): %d\n", + iscsi_conn->state); + rsp_bhs->flags = ISCSI_OP_PARMS_NEGOTIATION_STAGE << 2; + + switch (iscsi_conn->state) { + case STATE_READY: + iscsi_conn->state = STATE_LOGIN; + iser_login_start(task); + + if (account_available(iscsi_conn->tid, AUTH_DIR_INCOMING)) + goto auth_err; + if (rsp_bhs->status_class) + return; + iser_login_oper_scan(task); + if (rsp_bhs->status_class) + return; + stay = iser_login_check_params(task); + break; + case STATE_LOGIN: + iser_login_oper_scan(task); + if (rsp_bhs->status_class) + return; + stay = iser_login_check_params(task); + break; + default: + goto init_err; + } + break; + default: + goto init_err; + } + + if (rsp_bhs->status_class) + return; + if (iscsi_conn->state != STATE_SECURITY_AUTH && + req_bhs->flags & ISCSI_FLAG_LOGIN_TRANSIT) { + int nsg = ISCSI_LOGIN_NEXT_STAGE(req_bhs->flags); + + switch (nsg) { + case ISCSI_OP_PARMS_NEGOTIATION_STAGE: + switch (iscsi_conn->state) { + case STATE_SECURITY: + case STATE_SECURITY_DONE: + iscsi_conn->state = STATE_SECURITY_LOGIN; + iser_login_security_done(task); + break; + default: + goto init_err; + } + break; + case ISCSI_FULL_FEATURE_PHASE: + switch (iscsi_conn->state) { + case STATE_SECURITY: + case STATE_SECURITY_DONE: + if ((nsg_disagree = iser_login_check_params(task))) { + iscsi_conn->state = STATE_LOGIN; + nsg = ISCSI_OP_PARMS_NEGOTIATION_STAGE; + break; + } + iscsi_conn->state = STATE_SECURITY_FULL; + iser_login_security_done(task); + break; + case STATE_LOGIN: + if (stay) + nsg = ISCSI_OP_PARMS_NEGOTIATION_STAGE; + else + iscsi_conn->state = STATE_LOGIN_FULL; + break; + default: + goto init_err; + } + if (!stay && !nsg_disagree) { + iser_login_finish(task); + + if (rsp_bhs->status_class) + return; + } + break; + default: + goto init_err; + } + rsp_bhs->flags |= nsg | (stay ? 0 : ISCSI_FLAG_LOGIN_TRANSIT); + } + + if (iscsi_conn->exp_cmd_sn == ntohl(req_bhs->cmdsn)) + iscsi_conn->exp_cmd_sn ++; + + memcpy(rsp_bhs->isid, iscsi_conn->isid, sizeof(rsp_bhs->isid)); + rsp_bhs->tsih = iscsi_conn->tsih; + rsp_bhs->statsn = cpu_to_be32(iscsi_conn->stat_sn++); + rsp_bhs->exp_cmdsn = cpu_to_be32(iscsi_conn->exp_cmd_sn); + rsp_bhs->max_cmdsn = cpu_to_be32(iscsi_conn->max_cmd_sn); + return; + +init_err: + rsp_bhs->flags = 0; + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_INIT_ERR; + iscsi_conn->state = STATE_EXIT; + return; + +auth_err: + rsp_bhs->flags = 0; + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_AUTH_FAILED; + iscsi_conn->state = STATE_EXIT; + return; +} + +static void iser_text_scan(struct iser_task *task) +{ +/* + struct iscsi_connection *iscsi_conn = &task->conn->h; + char *key, *value, *data; + int datasize; + + data = task->req.data.buf; + datasize = task->req.data.size; + + while ((key = iser_text_next_key(&data, &datasize, &value))) { + if (!strcmp(key, "SendTargets")) { + struct sockaddr_storage ss; + socklen_t slen, blen; + char *p, buf[NI_MAXHOST + 128]; + + if (value[0] == 0) + continue; + + p = buf; + blen = sizeof(buf); + + slen = sizeof(ss); + iscsi_conn->op.conn_getsockname(iscsi_conn, (struct sockaddr *) &ss, + &slen); + if (ss.ss_family == AF_INET6) { + *p++ = '['; + blen--; + } + + slen = sizeof(ss); + getnameinfo((struct sockaddr *) &ss, slen, p, blen, + NULL, 0, NI_NUMERICHOST); + + p = buf + strlen(buf); + + if (ss.ss_family == AF_INET6) + *p++ = ']'; + + sprintf(p, ":%d,1", ISCSI_LISTEN_PORT); + target_list_build(iscsi_conn, buf, + strcmp(value, "All") ? value : NULL); + } else + iser_text_key_add(task, key, "NotUnderstood"); + } +*/ +} + +int iser_text_exec(struct iser_task *task) +{ + struct iscsi_connection *iscsi_conn = &task->conn->h; + struct iscsi_text *req = (struct iscsi_text *)task->rsp.bhs; + struct iscsi_text_rsp *rsp = (struct iscsi_text_rsp *)task->req.bhs; + + memset(rsp, 0, BHS_SIZE); + + if (be32_to_cpu(req->ttt) != 0xffffffff) { + /* reject */; + } + rsp->opcode = ISCSI_OP_TEXT_RSP; + rsp->itt = req->itt; + /* rsp->ttt = rsp->ttt; */ + rsp->ttt = 0xffffffff; + iscsi_conn->exp_cmd_sn = be32_to_cpu(req->cmdsn); + if (!(req->opcode & ISCSI_OP_IMMEDIATE)) + iscsi_conn->exp_cmd_sn++; + + dprintf("Text request: %d\n", iscsi_conn->state); + iser_text_scan(task); + + if (req->flags & ISCSI_FLAG_CMD_FINAL) + rsp->flags = ISCSI_FLAG_CMD_FINAL; + + rsp->statsn = cpu_to_be32(iscsi_conn->stat_sn++); + rsp->exp_cmdsn = cpu_to_be32(iscsi_conn->exp_cmd_sn); + rsp->max_cmdsn = cpu_to_be32(iscsi_conn->max_cmd_sn); + + return 0; +} + -- 1.6.5 -- To unsubscribe from this list: send the line "unsubscribe stgt" in the body of a message to majordomo at vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html |