[stgt] [PATCH 3/4] iser, new implementation

Alexander Nezhinsky alexandern at Voltaire.COM
Wed Oct 13 15:31:54 CEST 2010


Signed-off-by: Alexander Nezhinsky <alexandern at voltaire.com>
---
 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 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.
+ * Copyright (C) 2010 Alexander Nezhinsky (alexandern at voltaire.com)
+ *
+ * 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.7.1
--
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



More information about the stgt mailing list