iscsi/iser_text.c: login and text processing for iser (more or less duplicated from iscsi/tgtd.c with some necessary alterations) Signed-off-by: Alexander Nezhinsky <alexandern@xxxxxxxxxxxx> --- usr/iscsi/iser_text.c | 840 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 840 insertions(+), 0 deletions(-) create mode 100644 usr/iscsi/iser_text.c diff --git a/usr/iscsi/iser_text.c b/usr/iscsi/iser_text.c new file mode 100644 index 0000000..bcd7d4f --- /dev/null +++ b/usr/iscsi/iser_text.c @@ -0,0 +1,840 @@ +/* + * iSCSI extensions for RDMA (iSER) + * LOGIN and TEXT related code + * + * Copyright (C) 2007 Dennis Dalessandro (dennis@xxxxxxx) + * Copyright (C) 2007 Ananth Devulapalli (ananth@xxxxxxx) + * Copyright (C) 2007 Pete Wyckoff (pw@xxxxxxx) + * Copyright (C) 2010 Voltaire, Inc. All rights reserved. + * Copyright (C) 2010 Alexander Nezhinsky (alexandern@xxxxxxxxxxxx) + * + * 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; +} + +static 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; + } +} + +static void iser_text_key_add(struct iscsi_connection *iscsi_conn, + struct iser_pdu *pdu, + char *key, char *value) +{ + struct iser_conn *iser_conn = ISER_CONN(iscsi_conn); + int keylen = strlen(key); + int valuelen = strlen(value); + int len = keylen + valuelen + 2; + char *buffer; + + if (pdu->membuf.size + len > iser_conn->ssize) { + log_warning("Dropping key: %s=%s, pdu_sz:%d, key_sz:%d, ssize:%d\n", + key, value, pdu->membuf.size, len, iser_conn->ssize); + return; + } + + dprintf("%s=%s, offset:%d\n", key, value, pdu->membuf.size); + + buffer = pdu->membuf.addr + pdu->membuf.size; + pdu->membuf.size += len; + + strcpy(buffer, key); + buffer += keylen; + *buffer++ = '='; + strcpy(buffer, value); +} + +static void iser_text_key_add_reject(struct iscsi_connection *iscsi_conn, + struct iser_pdu *pdu, + char *key) +{ + iser_text_key_add(iscsi_conn, pdu, key, "Reject"); +} + +static void iser_login_security_scan(struct iscsi_connection *iscsi_conn, + struct iser_pdu *rx_pdu, + struct iser_pdu *tx_pdu) +{ + struct iscsi_login_rsp *rsp_bhs = (struct iscsi_login_rsp *)tx_pdu->bhs; + char *key, *value, *data, *nextValue; + int datasize; + + data = rx_pdu->membuf.addr; + datasize = rx_pdu->membuf.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(iscsi_conn, tx_pdu, 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(iscsi_conn, tx_pdu, key, "CHAP"); + break; + } + } while ((value = nextValue)); + + if (iscsi_conn->auth_method == AUTH_UNKNOWN) + iser_text_key_add_reject(iscsi_conn, tx_pdu, key); + } else + iser_text_key_add(iscsi_conn, tx_pdu, key, "NotUnderstood"); + } + if (iscsi_conn->auth_method == AUTH_UNKNOWN) { + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->status_detail = ISCSI_LOGIN_STATUS_AUTH_FAILED; + iscsi_conn->state = STATE_EXIT; + } +} + +static void iser_login_security_done(struct iscsi_connection *iscsi_conn, + struct iser_pdu *rx_pdu, + struct iser_pdu *tx_pdu) +{ + struct iscsi_login *req_bhs = (struct iscsi_login *)rx_pdu->bhs; + struct iscsi_login_rsp *rsp_bhs = (struct iscsi_login_rsp *)tx_pdu->bhs; + struct iscsi_session *session; + + if (!iscsi_conn->tid) + return; + + session = session_find_name(iscsi_conn->tid, iscsi_conn->initiator, req_bhs->isid); + if (session) { + if (!req_bhs->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_bhs->tsih != session->tsih) { + /* fail the login */ + 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; + } 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_bhs->tsih) { + /* fail the login */ + rsp_bhs->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->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 iscsi_connection *iscsi_conn, + struct iser_pdu *rx_pdu, + struct iser_pdu *tx_pdu) +{ + struct iscsi_login_rsp *rsp_bhs = (struct iscsi_login_rsp *)tx_pdu->bhs; + char *key, *value, *data; + int datasize, idx, is_rdma = 0; + + data = rx_pdu->membuf.addr; + datasize = rx_pdu->membuf.size; + + while ((key = iser_text_next_key(&data, &datasize, &value))) { + dprintf("%s=%s\n", key, 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(iscsi_conn, tx_pdu, key); + continue; + } else { + rsp_bhs->status_class = + ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->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(iscsi_conn, tx_pdu, key, buf); + break; + case KEY_STATE_REQUEST: + if (val != iscsi_conn->session_param[idx].val) { + rsp_bhs->status_class = + ISCSI_STATUS_CLS_INITIATOR_ERR; + rsp_bhs->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(iscsi_conn, tx_pdu, 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 iscsi_connection *iscsi_conn, + struct iser_pdu *pdu) +{ + 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(iscsi_conn, pdu, 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, + struct iser_pdu *rx_pdu, + struct iser_pdu *tx_pdu) +{ + struct iscsi_connection fake_iscsi_conn; + int res; + + switch (iscsi_conn->auth_method) { + case AUTH_CHAP: + /* ToDo: need to implement buffer-based chap functions */ + memset(&fake_iscsi_conn, 0, sizeof(fake_iscsi_conn)); + memcpy(&fake_iscsi_conn, iscsi_conn, sizeof(*iscsi_conn)); + + fake_iscsi_conn.req_buffer = rx_pdu->membuf.addr; + fake_iscsi_conn.req.data = rx_pdu->membuf.addr; + fake_iscsi_conn.req.datasize = rx_pdu->membuf.size; + + fake_iscsi_conn.rsp.datasize = 0; // here the new size will be returned + fake_iscsi_conn.rsp.data = 0; + fake_iscsi_conn.rsp_buffer = tx_pdu->membuf.addr; + fake_iscsi_conn.rsp_buffer_size = 8192; // max buffer capacity + + res = cmnd_exec_auth_chap(&fake_iscsi_conn); + if (!res) { + memcpy(iscsi_conn, &fake_iscsi_conn, sizeof(*iscsi_conn)); + tx_pdu->membuf.size = fake_iscsi_conn.rsp.datasize; + } + 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 iscsi_connection *iscsi_conn, + struct iser_pdu *rx_pdu, + struct iser_pdu *tx_pdu) +{ + struct iscsi_login *req_bhs = (struct iscsi_login *)rx_pdu->bhs; + struct iscsi_login_rsp *rsp_bhs = (struct iscsi_login_rsp *)tx_pdu->bhs; + char *req_data = rx_pdu->membuf.addr; + int req_datasize = rx_pdu->membuf.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); + iscsi_conn->max_cmd_sn = iscsi_conn->exp_cmd_sn; + dprintf("set exp_cmdsn:0x%0x\n", iscsi_conn->exp_cmd_sn); + } + iser_text_key_add(iscsi_conn, tx_pdu, "TargetPortalGroupTag", "1"); +} + +static void iser_login_finish(struct iscsi_connection *iscsi_conn, + struct iser_pdu *tx_pdu) +{ + struct iscsi_login_rsp *rsp_bhs = (struct iscsi_login_rsp *)tx_pdu->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 iscsi_connection *iscsi_conn, + struct iser_pdu *rx_pdu, + struct iser_pdu *tx_pdu) +{ + struct iscsi_login *req_bhs = (struct iscsi_login *)rx_pdu->bhs; + struct iscsi_login_rsp *rsp_bhs = (struct iscsi_login_rsp *)tx_pdu->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("conn:%p Login request (security negotiation): %d\n", + iscsi_conn, 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(iscsi_conn, rx_pdu, tx_pdu); + + if (rsp_bhs->status_class) + return; + /* fall through */ + case STATE_SECURITY: + iser_login_security_scan(iscsi_conn, rx_pdu, tx_pdu); + 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, rx_pdu, tx_pdu)) { + 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("conn:%p Login request (operational negotiation): %d\n", + iscsi_conn, 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(iscsi_conn, rx_pdu, tx_pdu); + + if (account_available(iscsi_conn->tid, AUTH_DIR_INCOMING)) + goto auth_err; + if (rsp_bhs->status_class) + return; + iser_login_oper_scan(iscsi_conn, rx_pdu, tx_pdu); + if (rsp_bhs->status_class) + return; + stay = iser_login_check_params(iscsi_conn, tx_pdu); + break; + case STATE_LOGIN: + iser_login_oper_scan(iscsi_conn, rx_pdu, tx_pdu); + if (rsp_bhs->status_class) + return; + stay = iser_login_check_params(iscsi_conn, tx_pdu); + 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(iscsi_conn, rx_pdu, tx_pdu); + 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(iscsi_conn, tx_pdu))) { + iscsi_conn->state = STATE_LOGIN; + nsg = ISCSI_OP_PARMS_NEGOTIATION_STAGE; + break; + } + iscsi_conn->state = STATE_SECURITY_FULL; + iser_login_security_done(iscsi_conn, rx_pdu, tx_pdu); + 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(iscsi_conn, tx_pdu); + + 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: + eprintf("conn:%p, Initiator error\n", iscsi_conn); + 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: + eprintf("conn:%p, Authentication error\n", iscsi_conn); + 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 iscsi_connection *iscsi_conn, + struct iser_pdu *rx_pdu, + struct iser_pdu *tx_pdu) +{ + char *key, *value, *data; + int datasize; + + data = rx_pdu->membuf.addr; + datasize = rx_pdu->membuf.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->tp->ep_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(iscsi_conn, tx_pdu, key, "NotUnderstood"); + } +} + +int iser_text_exec(struct iscsi_connection *iscsi_conn, + struct iser_pdu *rx_pdu, + struct iser_pdu *tx_pdu) +{ + struct iscsi_text *req = (struct iscsi_text *)rx_pdu->bhs; + struct iscsi_text_rsp *rsp = (struct iscsi_text_rsp *)tx_pdu->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(iscsi_conn, rx_pdu, tx_pdu); + + 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.5 -- To unsubscribe from this list: send the line "unsubscribe stgt" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html