diff options
| author | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 15:08:05 +0200 | 
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 15:08:18 +0200 | 
| commit | e060c38434b2caa78efe7cedaff4191040b65a15 (patch) | |
| tree | 407361230bf6733f63d8e788e4b5e6566ee04818 /drivers/target/iscsi/iscsi_target_parameters.c | |
| parent | 10e4ac572eeffe5317019bd7330b6058a400dfc2 (diff) | |
| parent | cc39c6a9bbdebfcf1a7dee64d83bf302bc38d941 (diff) | |
| download | olio-linux-3.10-e060c38434b2caa78efe7cedaff4191040b65a15.tar.xz olio-linux-3.10-e060c38434b2caa78efe7cedaff4191040b65a15.zip  | |
Merge branch 'master' into for-next
Fast-forward merge with Linus to be able to merge patches
based on more recent version of the tree.
Diffstat (limited to 'drivers/target/iscsi/iscsi_target_parameters.c')
| -rw-r--r-- | drivers/target/iscsi/iscsi_target_parameters.c | 1894 | 
1 files changed, 1894 insertions, 0 deletions
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c new file mode 100644 index 00000000000..497b2e718a7 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -0,0 +1,1894 @@ +/******************************************************************************* + * This file contains main functions related to iSCSI Parameter negotiation. + * + * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. + * + * Licensed to the Linux Foundation under the General Public License (GPL) version 2. + * + * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + ******************************************************************************/ + +#include <linux/slab.h> + +#include "iscsi_target_core.h" +#include "iscsi_target_util.h" +#include "iscsi_target_parameters.h" + +int iscsi_login_rx_data( +	struct iscsi_conn *conn, +	char *buf, +	int length) +{ +	int rx_got; +	struct kvec iov; + +	memset(&iov, 0, sizeof(struct kvec)); +	iov.iov_len	= length; +	iov.iov_base	= buf; + +	/* +	 * Initial Marker-less Interval. +	 * Add the values regardless of IFMarker/OFMarker, considering +	 * it may not be negoitated yet. +	 */ +	conn->of_marker += length; + +	rx_got = rx_data(conn, &iov, 1, length); +	if (rx_got != length) { +		pr_err("rx_data returned %d, expecting %d.\n", +				rx_got, length); +		return -1; +	} + +	return 0 ; +} + +int iscsi_login_tx_data( +	struct iscsi_conn *conn, +	char *pdu_buf, +	char *text_buf, +	int text_length) +{ +	int length, tx_sent; +	struct kvec iov[2]; + +	length = (ISCSI_HDR_LEN + text_length); + +	memset(&iov[0], 0, 2 * sizeof(struct kvec)); +	iov[0].iov_len		= ISCSI_HDR_LEN; +	iov[0].iov_base		= pdu_buf; +	iov[1].iov_len		= text_length; +	iov[1].iov_base		= text_buf; + +	/* +	 * Initial Marker-less Interval. +	 * Add the values regardless of IFMarker/OFMarker, considering +	 * it may not be negoitated yet. +	 */ +	conn->if_marker += length; + +	tx_sent = tx_data(conn, &iov[0], 2, length); +	if (tx_sent != length) { +		pr_err("tx_data returned %d, expecting %d.\n", +				tx_sent, length); +		return -1; +	} + +	return 0; +} + +void iscsi_dump_conn_ops(struct iscsi_conn_ops *conn_ops) +{ +	pr_debug("HeaderDigest: %s\n", (conn_ops->HeaderDigest) ? +				"CRC32C" : "None"); +	pr_debug("DataDigest: %s\n", (conn_ops->DataDigest) ? +				"CRC32C" : "None"); +	pr_debug("MaxRecvDataSegmentLength: %u\n", +				conn_ops->MaxRecvDataSegmentLength); +	pr_debug("OFMarker: %s\n", (conn_ops->OFMarker) ? "Yes" : "No"); +	pr_debug("IFMarker: %s\n", (conn_ops->IFMarker) ? "Yes" : "No"); +	if (conn_ops->OFMarker) +		pr_debug("OFMarkInt: %u\n", conn_ops->OFMarkInt); +	if (conn_ops->IFMarker) +		pr_debug("IFMarkInt: %u\n", conn_ops->IFMarkInt); +} + +void iscsi_dump_sess_ops(struct iscsi_sess_ops *sess_ops) +{ +	pr_debug("InitiatorName: %s\n", sess_ops->InitiatorName); +	pr_debug("InitiatorAlias: %s\n", sess_ops->InitiatorAlias); +	pr_debug("TargetName: %s\n", sess_ops->TargetName); +	pr_debug("TargetAlias: %s\n", sess_ops->TargetAlias); +	pr_debug("TargetPortalGroupTag: %hu\n", +			sess_ops->TargetPortalGroupTag); +	pr_debug("MaxConnections: %hu\n", sess_ops->MaxConnections); +	pr_debug("InitialR2T: %s\n", +			(sess_ops->InitialR2T) ? "Yes" : "No"); +	pr_debug("ImmediateData: %s\n", (sess_ops->ImmediateData) ? +			"Yes" : "No"); +	pr_debug("MaxBurstLength: %u\n", sess_ops->MaxBurstLength); +	pr_debug("FirstBurstLength: %u\n", sess_ops->FirstBurstLength); +	pr_debug("DefaultTime2Wait: %hu\n", sess_ops->DefaultTime2Wait); +	pr_debug("DefaultTime2Retain: %hu\n", +			sess_ops->DefaultTime2Retain); +	pr_debug("MaxOutstandingR2T: %hu\n", +			sess_ops->MaxOutstandingR2T); +	pr_debug("DataPDUInOrder: %s\n", +			(sess_ops->DataPDUInOrder) ? "Yes" : "No"); +	pr_debug("DataSequenceInOrder: %s\n", +			(sess_ops->DataSequenceInOrder) ? "Yes" : "No"); +	pr_debug("ErrorRecoveryLevel: %hu\n", +			sess_ops->ErrorRecoveryLevel); +	pr_debug("SessionType: %s\n", (sess_ops->SessionType) ? +			"Discovery" : "Normal"); +} + +void iscsi_print_params(struct iscsi_param_list *param_list) +{ +	struct iscsi_param *param; + +	list_for_each_entry(param, ¶m_list->param_list, p_list) +		pr_debug("%s: %s\n", param->name, param->value); +} + +static struct iscsi_param *iscsi_set_default_param(struct iscsi_param_list *param_list, +		char *name, char *value, u8 phase, u8 scope, u8 sender, +		u16 type_range, u8 use) +{ +	struct iscsi_param *param = NULL; + +	param = kzalloc(sizeof(struct iscsi_param), GFP_KERNEL); +	if (!param) { +		pr_err("Unable to allocate memory for parameter.\n"); +		goto out; +	} +	INIT_LIST_HEAD(¶m->p_list); + +	param->name = kzalloc(strlen(name) + 1, GFP_KERNEL); +	if (!param->name) { +		pr_err("Unable to allocate memory for parameter name.\n"); +		goto out; +	} + +	param->value = kzalloc(strlen(value) + 1, GFP_KERNEL); +	if (!param->value) { +		pr_err("Unable to allocate memory for parameter value.\n"); +		goto out; +	} + +	memcpy(param->name, name, strlen(name)); +	param->name[strlen(name)] = '\0'; +	memcpy(param->value, value, strlen(value)); +	param->value[strlen(value)] = '\0'; +	param->phase		= phase; +	param->scope		= scope; +	param->sender		= sender; +	param->use		= use; +	param->type_range	= type_range; + +	switch (param->type_range) { +	case TYPERANGE_BOOL_AND: +		param->type = TYPE_BOOL_AND; +		break; +	case TYPERANGE_BOOL_OR: +		param->type = TYPE_BOOL_OR; +		break; +	case TYPERANGE_0_TO_2: +	case TYPERANGE_0_TO_3600: +	case TYPERANGE_0_TO_32767: +	case TYPERANGE_0_TO_65535: +	case TYPERANGE_1_TO_65535: +	case TYPERANGE_2_TO_3600: +	case TYPERANGE_512_TO_16777215: +		param->type = TYPE_NUMBER; +		break; +	case TYPERANGE_AUTH: +	case TYPERANGE_DIGEST: +		param->type = TYPE_VALUE_LIST | TYPE_STRING; +		break; +	case TYPERANGE_MARKINT: +		param->type = TYPE_NUMBER_RANGE; +		param->type_range |= TYPERANGE_1_TO_65535; +		break; +	case TYPERANGE_ISCSINAME: +	case TYPERANGE_SESSIONTYPE: +	case TYPERANGE_TARGETADDRESS: +	case TYPERANGE_UTF8: +		param->type = TYPE_STRING; +		break; +	default: +		pr_err("Unknown type_range 0x%02x\n", +				param->type_range); +		goto out; +	} +	list_add_tail(¶m->p_list, ¶m_list->param_list); + +	return param; +out: +	if (param) { +		kfree(param->value); +		kfree(param->name); +		kfree(param); +	} + +	return NULL; +} + +/* #warning Add extension keys */ +int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr) +{ +	struct iscsi_param *param = NULL; +	struct iscsi_param_list *pl; + +	pl = kzalloc(sizeof(struct iscsi_param_list), GFP_KERNEL); +	if (!pl) { +		pr_err("Unable to allocate memory for" +				" struct iscsi_param_list.\n"); +		return -1 ; +	} +	INIT_LIST_HEAD(&pl->param_list); +	INIT_LIST_HEAD(&pl->extra_response_list); + +	/* +	 * The format for setting the initial parameter definitions are: +	 * +	 * Parameter name: +	 * Initial value: +	 * Allowable phase: +	 * Scope: +	 * Allowable senders: +	 * Typerange: +	 * Use: +	 */ +	param = iscsi_set_default_param(pl, AUTHMETHOD, INITIAL_AUTHMETHOD, +			PHASE_SECURITY, SCOPE_CONNECTION_ONLY, SENDER_BOTH, +			TYPERANGE_AUTH, USE_INITIAL_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, HEADERDIGEST, INITIAL_HEADERDIGEST, +			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, +			TYPERANGE_DIGEST, USE_INITIAL_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, DATADIGEST, INITIAL_DATADIGEST, +			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, +			TYPERANGE_DIGEST, USE_INITIAL_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, MAXCONNECTIONS, +			INITIAL_MAXCONNECTIONS, PHASE_OPERATIONAL, +			SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_1_TO_65535, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, SENDTARGETS, INITIAL_SENDTARGETS, +			PHASE_FFP0, SCOPE_SESSION_WIDE, SENDER_INITIATOR, +			TYPERANGE_UTF8, 0); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, TARGETNAME, INITIAL_TARGETNAME, +			PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_ISCSINAME, USE_ALL); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, INITIATORNAME, +			INITIAL_INITIATORNAME, PHASE_DECLARATIVE, +			SCOPE_SESSION_WIDE, SENDER_INITIATOR, +			TYPERANGE_ISCSINAME, USE_INITIAL_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, TARGETALIAS, INITIAL_TARGETALIAS, +			PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_TARGET, +			TYPERANGE_UTF8, USE_ALL); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, INITIATORALIAS, +			INITIAL_INITIATORALIAS, PHASE_DECLARATIVE, +			SCOPE_SESSION_WIDE, SENDER_INITIATOR, TYPERANGE_UTF8, +			USE_ALL); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, TARGETADDRESS, +			INITIAL_TARGETADDRESS, PHASE_DECLARATIVE, +			SCOPE_SESSION_WIDE, SENDER_TARGET, +			TYPERANGE_TARGETADDRESS, USE_ALL); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, TARGETPORTALGROUPTAG, +			INITIAL_TARGETPORTALGROUPTAG, +			PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_TARGET, +			TYPERANGE_0_TO_65535, USE_INITIAL_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, INITIALR2T, INITIAL_INITIALR2T, +			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_BOOL_OR, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, IMMEDIATEDATA, +			INITIAL_IMMEDIATEDATA, PHASE_OPERATIONAL, +			SCOPE_SESSION_WIDE, SENDER_BOTH, TYPERANGE_BOOL_AND, +			USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, MAXRECVDATASEGMENTLENGTH, +			INITIAL_MAXRECVDATASEGMENTLENGTH, +			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, +			TYPERANGE_512_TO_16777215, USE_ALL); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, MAXBURSTLENGTH, +			INITIAL_MAXBURSTLENGTH, PHASE_OPERATIONAL, +			SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_512_TO_16777215, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, FIRSTBURSTLENGTH, +			INITIAL_FIRSTBURSTLENGTH, +			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_512_TO_16777215, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, DEFAULTTIME2WAIT, +			INITIAL_DEFAULTTIME2WAIT, +			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_0_TO_3600, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, DEFAULTTIME2RETAIN, +			INITIAL_DEFAULTTIME2RETAIN, +			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_0_TO_3600, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, MAXOUTSTANDINGR2T, +			INITIAL_MAXOUTSTANDINGR2T, +			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_1_TO_65535, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, DATAPDUINORDER, +			INITIAL_DATAPDUINORDER, PHASE_OPERATIONAL, +			SCOPE_SESSION_WIDE, SENDER_BOTH, TYPERANGE_BOOL_OR, +			USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, DATASEQUENCEINORDER, +			INITIAL_DATASEQUENCEINORDER, +			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_BOOL_OR, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, ERRORRECOVERYLEVEL, +			INITIAL_ERRORRECOVERYLEVEL, +			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, +			TYPERANGE_0_TO_2, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, SESSIONTYPE, INITIAL_SESSIONTYPE, +			PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_INITIATOR, +			TYPERANGE_SESSIONTYPE, USE_LEADING_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, IFMARKER, INITIAL_IFMARKER, +			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, +			TYPERANGE_BOOL_AND, USE_INITIAL_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, OFMARKER, INITIAL_OFMARKER, +			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, +			TYPERANGE_BOOL_AND, USE_INITIAL_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, IFMARKINT, INITIAL_IFMARKINT, +			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, +			TYPERANGE_MARKINT, USE_INITIAL_ONLY); +	if (!param) +		goto out; + +	param = iscsi_set_default_param(pl, OFMARKINT, INITIAL_OFMARKINT, +			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, +			TYPERANGE_MARKINT, USE_INITIAL_ONLY); +	if (!param) +		goto out; + +	*param_list_ptr = pl; +	return 0; +out: +	iscsi_release_param_list(pl); +	return -1; +} + +int iscsi_set_keys_to_negotiate( +	int sessiontype, +	struct iscsi_param_list *param_list) +{ +	struct iscsi_param *param; + +	list_for_each_entry(param, ¶m_list->param_list, p_list) { +		param->state = 0; +		if (!strcmp(param->name, AUTHMETHOD)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, HEADERDIGEST)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, DATADIGEST)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, MAXCONNECTIONS)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, TARGETNAME)) { +			continue; +		} else if (!strcmp(param->name, INITIATORNAME)) { +			continue; +		} else if (!strcmp(param->name, TARGETALIAS)) { +			if (param->value) +				SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, INITIATORALIAS)) { +			continue; +		} else if (!strcmp(param->name, TARGETPORTALGROUPTAG)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, INITIALR2T)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, IMMEDIATEDATA)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, MAXBURSTLENGTH)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, FIRSTBURSTLENGTH)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, DEFAULTTIME2WAIT)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, DEFAULTTIME2RETAIN)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, MAXOUTSTANDINGR2T)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, DATAPDUINORDER)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, DATASEQUENCEINORDER)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, ERRORRECOVERYLEVEL)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, SESSIONTYPE)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, IFMARKER)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, OFMARKER)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, IFMARKINT)) { +			SET_PSTATE_NEGOTIATE(param); +		} else if (!strcmp(param->name, OFMARKINT)) { +			SET_PSTATE_NEGOTIATE(param); +		} +	} + +	return 0; +} + +int iscsi_set_keys_irrelevant_for_discovery( +	struct iscsi_param_list *param_list) +{ +	struct iscsi_param *param; + +	list_for_each_entry(param, ¶m_list->param_list, p_list) { +		if (!strcmp(param->name, MAXCONNECTIONS)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, INITIALR2T)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, IMMEDIATEDATA)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, MAXBURSTLENGTH)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, FIRSTBURSTLENGTH)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, MAXOUTSTANDINGR2T)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, DATAPDUINORDER)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, DATASEQUENCEINORDER)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, ERRORRECOVERYLEVEL)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, DEFAULTTIME2WAIT)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, DEFAULTTIME2RETAIN)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, IFMARKER)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, OFMARKER)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, IFMARKINT)) +			param->state &= ~PSTATE_NEGOTIATE; +		else if (!strcmp(param->name, OFMARKINT)) +			param->state &= ~PSTATE_NEGOTIATE; +	} + +	return 0; +} + +int iscsi_copy_param_list( +	struct iscsi_param_list **dst_param_list, +	struct iscsi_param_list *src_param_list, +	int leading) +{ +	struct iscsi_param *param = NULL; +	struct iscsi_param *new_param = NULL; +	struct iscsi_param_list *param_list = NULL; + +	param_list = kzalloc(sizeof(struct iscsi_param_list), GFP_KERNEL); +	if (!param_list) { +		pr_err("Unable to allocate memory for struct iscsi_param_list.\n"); +		goto err_out; +	} +	INIT_LIST_HEAD(¶m_list->param_list); +	INIT_LIST_HEAD(¶m_list->extra_response_list); + +	list_for_each_entry(param, &src_param_list->param_list, p_list) { +		if (!leading && (param->scope & SCOPE_SESSION_WIDE)) { +			if ((strcmp(param->name, "TargetName") != 0) && +			    (strcmp(param->name, "InitiatorName") != 0) && +			    (strcmp(param->name, "TargetPortalGroupTag") != 0)) +				continue; +		} + +		new_param = kzalloc(sizeof(struct iscsi_param), GFP_KERNEL); +		if (!new_param) { +			pr_err("Unable to allocate memory for struct iscsi_param.\n"); +			goto err_out; +		} + +		new_param->name = kstrdup(param->name, GFP_KERNEL); +		new_param->value = kstrdup(param->value, GFP_KERNEL); +		if (!new_param->value || !new_param->name) { +			kfree(new_param->value); +			kfree(new_param->name); +			kfree(new_param); +			pr_err("Unable to allocate memory for parameter name/value.\n"); +			goto err_out; +		} + +		new_param->set_param = param->set_param; +		new_param->phase = param->phase; +		new_param->scope = param->scope; +		new_param->sender = param->sender; +		new_param->type = param->type; +		new_param->use = param->use; +		new_param->type_range = param->type_range; + +		list_add_tail(&new_param->p_list, ¶m_list->param_list); +	} + +	if (!list_empty(¶m_list->param_list)) { +		*dst_param_list = param_list; +	} else { +		pr_err("No parameters allocated.\n"); +		goto err_out; +	} + +	return 0; + +err_out: +	iscsi_release_param_list(param_list); +	return -1; +} + +static void iscsi_release_extra_responses(struct iscsi_param_list *param_list) +{ +	struct iscsi_extra_response *er, *er_tmp; + +	list_for_each_entry_safe(er, er_tmp, ¶m_list->extra_response_list, +			er_list) { +		list_del(&er->er_list); +		kfree(er); +	} +} + +void iscsi_release_param_list(struct iscsi_param_list *param_list) +{ +	struct iscsi_param *param, *param_tmp; + +	list_for_each_entry_safe(param, param_tmp, ¶m_list->param_list, +			p_list) { +		list_del(¶m->p_list); + +		kfree(param->name); +		param->name = NULL; +		kfree(param->value); +		param->value = NULL; +		kfree(param); +		param = NULL; +	} + +	iscsi_release_extra_responses(param_list); + +	kfree(param_list); +} + +struct iscsi_param *iscsi_find_param_from_key( +	char *key, +	struct iscsi_param_list *param_list) +{ +	struct iscsi_param *param; + +	if (!key || !param_list) { +		pr_err("Key or parameter list pointer is NULL.\n"); +		return NULL; +	} + +	list_for_each_entry(param, ¶m_list->param_list, p_list) { +		if (!strcmp(key, param->name)) +			return param; +	} + +	pr_err("Unable to locate key \"%s\".\n", key); +	return NULL; +} + +int iscsi_extract_key_value(char *textbuf, char **key, char **value) +{ +	*value = strchr(textbuf, '='); +	if (!*value) { +		pr_err("Unable to locate \"=\" seperator for key," +				" ignoring request.\n"); +		return -1; +	} + +	*key = textbuf; +	**value = '\0'; +	*value = *value + 1; + +	return 0; +} + +int iscsi_update_param_value(struct iscsi_param *param, char *value) +{ +	kfree(param->value); + +	param->value = kzalloc(strlen(value) + 1, GFP_KERNEL); +	if (!param->value) { +		pr_err("Unable to allocate memory for value.\n"); +		return -1; +	} + +	memcpy(param->value, value, strlen(value)); +	param->value[strlen(value)] = '\0'; + +	pr_debug("iSCSI Parameter updated to %s=%s\n", +			param->name, param->value); +	return 0; +} + +static int iscsi_add_notunderstood_response( +	char *key, +	char *value, +	struct iscsi_param_list *param_list) +{ +	struct iscsi_extra_response *extra_response; + +	if (strlen(value) > VALUE_MAXLEN) { +		pr_err("Value for notunderstood key \"%s\" exceeds %d," +			" protocol error.\n", key, VALUE_MAXLEN); +		return -1; +	} + +	extra_response = kzalloc(sizeof(struct iscsi_extra_response), GFP_KERNEL); +	if (!extra_response) { +		pr_err("Unable to allocate memory for" +			" struct iscsi_extra_response.\n"); +		return -1; +	} +	INIT_LIST_HEAD(&extra_response->er_list); + +	strncpy(extra_response->key, key, strlen(key) + 1); +	strncpy(extra_response->value, NOTUNDERSTOOD, +			strlen(NOTUNDERSTOOD) + 1); + +	list_add_tail(&extra_response->er_list, +			¶m_list->extra_response_list); +	return 0; +} + +static int iscsi_check_for_auth_key(char *key) +{ +	/* +	 * RFC 1994 +	 */ +	if (!strcmp(key, "CHAP_A") || !strcmp(key, "CHAP_I") || +	    !strcmp(key, "CHAP_C") || !strcmp(key, "CHAP_N") || +	    !strcmp(key, "CHAP_R")) +		return 1; + +	/* +	 * RFC 2945 +	 */ +	if (!strcmp(key, "SRP_U") || !strcmp(key, "SRP_N") || +	    !strcmp(key, "SRP_g") || !strcmp(key, "SRP_s") || +	    !strcmp(key, "SRP_A") || !strcmp(key, "SRP_B") || +	    !strcmp(key, "SRP_M") || !strcmp(key, "SRP_HM")) +		return 1; + +	return 0; +} + +static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param) +{ +	if (IS_TYPE_BOOL_AND(param)) { +		if (!strcmp(param->value, NO)) +			SET_PSTATE_REPLY_OPTIONAL(param); +	} else if (IS_TYPE_BOOL_OR(param)) { +		if (!strcmp(param->value, YES)) +			SET_PSTATE_REPLY_OPTIONAL(param); +		 /* +		  * Required for gPXE iSCSI boot client +		  */ +		if (!strcmp(param->name, IMMEDIATEDATA)) +			SET_PSTATE_REPLY_OPTIONAL(param); +	} else if (IS_TYPE_NUMBER(param)) { +		if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) +			SET_PSTATE_REPLY_OPTIONAL(param); +		/* +		 * The GlobalSAN iSCSI Initiator for MacOSX does +		 * not respond to MaxBurstLength, FirstBurstLength, +		 * DefaultTime2Wait or DefaultTime2Retain parameter keys. +		 * So, we set them to 'reply optional' here, and assume the +		 * the defaults from iscsi_parameters.h if the initiator +		 * is not RFC compliant and the keys are not negotiated. +		 */ +		if (!strcmp(param->name, MAXBURSTLENGTH)) +			SET_PSTATE_REPLY_OPTIONAL(param); +		if (!strcmp(param->name, FIRSTBURSTLENGTH)) +			SET_PSTATE_REPLY_OPTIONAL(param); +		if (!strcmp(param->name, DEFAULTTIME2WAIT)) +			SET_PSTATE_REPLY_OPTIONAL(param); +		if (!strcmp(param->name, DEFAULTTIME2RETAIN)) +			SET_PSTATE_REPLY_OPTIONAL(param); +		/* +		 * Required for gPXE iSCSI boot client +		 */ +		if (!strcmp(param->name, MAXCONNECTIONS)) +			SET_PSTATE_REPLY_OPTIONAL(param); +	} else if (IS_PHASE_DECLARATIVE(param)) +		SET_PSTATE_REPLY_OPTIONAL(param); +} + +static int iscsi_check_boolean_value(struct iscsi_param *param, char *value) +{ +	if (strcmp(value, YES) && strcmp(value, NO)) { +		pr_err("Illegal value for \"%s\", must be either" +			" \"%s\" or \"%s\".\n", param->name, YES, NO); +		return -1; +	} + +	return 0; +} + +static int iscsi_check_numerical_value(struct iscsi_param *param, char *value_ptr) +{ +	char *tmpptr; +	int value = 0; + +	value = simple_strtoul(value_ptr, &tmpptr, 0); + +/* #warning FIXME: Fix this */ +#if 0 +	if (strspn(endptr, WHITE_SPACE) != strlen(endptr)) { +		pr_err("Illegal value \"%s\" for \"%s\".\n", +			value, param->name); +		return -1; +	} +#endif +	if (IS_TYPERANGE_0_TO_2(param)) { +		if ((value < 0) || (value > 2)) { +			pr_err("Illegal value for \"%s\", must be" +				" between 0 and 2.\n", param->name); +			return -1; +		} +		return 0; +	} +	if (IS_TYPERANGE_0_TO_3600(param)) { +		if ((value < 0) || (value > 3600)) { +			pr_err("Illegal value for \"%s\", must be" +				" between 0 and 3600.\n", param->name); +			return -1; +		} +		return 0; +	} +	if (IS_TYPERANGE_0_TO_32767(param)) { +		if ((value < 0) || (value > 32767)) { +			pr_err("Illegal value for \"%s\", must be" +				" between 0 and 32767.\n", param->name); +			return -1; +		} +		return 0; +	} +	if (IS_TYPERANGE_0_TO_65535(param)) { +		if ((value < 0) || (value > 65535)) { +			pr_err("Illegal value for \"%s\", must be" +				" between 0 and 65535.\n", param->name); +			return -1; +		} +		return 0; +	} +	if (IS_TYPERANGE_1_TO_65535(param)) { +		if ((value < 1) || (value > 65535)) { +			pr_err("Illegal value for \"%s\", must be" +				" between 1 and 65535.\n", param->name); +			return -1; +		} +		return 0; +	} +	if (IS_TYPERANGE_2_TO_3600(param)) { +		if ((value < 2) || (value > 3600)) { +			pr_err("Illegal value for \"%s\", must be" +				" between 2 and 3600.\n", param->name); +			return -1; +		} +		return 0; +	} +	if (IS_TYPERANGE_512_TO_16777215(param)) { +		if ((value < 512) || (value > 16777215)) { +			pr_err("Illegal value for \"%s\", must be" +				" between 512 and 16777215.\n", param->name); +			return -1; +		} +		return 0; +	} + +	return 0; +} + +static int iscsi_check_numerical_range_value(struct iscsi_param *param, char *value) +{ +	char *left_val_ptr = NULL, *right_val_ptr = NULL; +	char *tilde_ptr = NULL, *tmp_ptr = NULL; +	u32 left_val, right_val, local_left_val, local_right_val; + +	if (strcmp(param->name, IFMARKINT) && +	    strcmp(param->name, OFMARKINT)) { +		pr_err("Only parameters \"%s\" or \"%s\" may contain a" +		       " numerical range value.\n", IFMARKINT, OFMARKINT); +		return -1; +	} + +	if (IS_PSTATE_PROPOSER(param)) +		return 0; + +	tilde_ptr = strchr(value, '~'); +	if (!tilde_ptr) { +		pr_err("Unable to locate numerical range indicator" +			" \"~\" for \"%s\".\n", param->name); +		return -1; +	} +	*tilde_ptr = '\0'; + +	left_val_ptr = value; +	right_val_ptr = value + strlen(left_val_ptr) + 1; + +	if (iscsi_check_numerical_value(param, left_val_ptr) < 0) +		return -1; +	if (iscsi_check_numerical_value(param, right_val_ptr) < 0) +		return -1; + +	left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0); +	right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0); +	*tilde_ptr = '~'; + +	if (right_val < left_val) { +		pr_err("Numerical range for parameter \"%s\" contains" +			" a right value which is less than the left.\n", +				param->name); +		return -1; +	} + +	/* +	 * For now,  enforce reasonable defaults for [I,O]FMarkInt. +	 */ +	tilde_ptr = strchr(param->value, '~'); +	if (!tilde_ptr) { +		pr_err("Unable to locate numerical range indicator" +			" \"~\" for \"%s\".\n", param->name); +		return -1; +	} +	*tilde_ptr = '\0'; + +	left_val_ptr = param->value; +	right_val_ptr = param->value + strlen(left_val_ptr) + 1; + +	local_left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0); +	local_right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0); +	*tilde_ptr = '~'; + +	if (param->set_param) { +		if ((left_val < local_left_val) || +		    (right_val < local_left_val)) { +			pr_err("Passed value range \"%u~%u\" is below" +				" minimum left value \"%u\" for key \"%s\"," +				" rejecting.\n", left_val, right_val, +				local_left_val, param->name); +			return -1; +		} +	} else { +		if ((left_val < local_left_val) && +		    (right_val < local_left_val)) { +			pr_err("Received value range \"%u~%u\" is" +				" below minimum left value \"%u\" for key" +				" \"%s\", rejecting.\n", left_val, right_val, +				local_left_val, param->name); +			SET_PSTATE_REJECT(param); +			if (iscsi_update_param_value(param, REJECT) < 0) +				return -1; +		} +	} + +	return 0; +} + +static int iscsi_check_string_or_list_value(struct iscsi_param *param, char *value) +{ +	if (IS_PSTATE_PROPOSER(param)) +		return 0; + +	if (IS_TYPERANGE_AUTH_PARAM(param)) { +		if (strcmp(value, KRB5) && strcmp(value, SPKM1) && +		    strcmp(value, SPKM2) && strcmp(value, SRP) && +		    strcmp(value, CHAP) && strcmp(value, NONE)) { +			pr_err("Illegal value for \"%s\", must be" +				" \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"" +				" or \"%s\".\n", param->name, KRB5, +					SPKM1, SPKM2, SRP, CHAP, NONE); +			return -1; +		} +	} +	if (IS_TYPERANGE_DIGEST_PARAM(param)) { +		if (strcmp(value, CRC32C) && strcmp(value, NONE)) { +			pr_err("Illegal value for \"%s\", must be" +				" \"%s\" or \"%s\".\n", param->name, +					CRC32C, NONE); +			return -1; +		} +	} +	if (IS_TYPERANGE_SESSIONTYPE(param)) { +		if (strcmp(value, DISCOVERY) && strcmp(value, NORMAL)) { +			pr_err("Illegal value for \"%s\", must be" +				" \"%s\" or \"%s\".\n", param->name, +					DISCOVERY, NORMAL); +			return -1; +		} +	} + +	return 0; +} + +/* + *	This function is used to pick a value range number,  currently just + *	returns the lesser of both right values. + */ +static char *iscsi_get_value_from_number_range( +	struct iscsi_param *param, +	char *value) +{ +	char *end_ptr, *tilde_ptr1 = NULL, *tilde_ptr2 = NULL; +	u32 acceptor_right_value, proposer_right_value; + +	tilde_ptr1 = strchr(value, '~'); +	if (!tilde_ptr1) +		return NULL; +	*tilde_ptr1++ = '\0'; +	proposer_right_value = simple_strtoul(tilde_ptr1, &end_ptr, 0); + +	tilde_ptr2 = strchr(param->value, '~'); +	if (!tilde_ptr2) +		return NULL; +	*tilde_ptr2++ = '\0'; +	acceptor_right_value = simple_strtoul(tilde_ptr2, &end_ptr, 0); + +	return (acceptor_right_value >= proposer_right_value) ? +		tilde_ptr1 : tilde_ptr2; +} + +static char *iscsi_check_valuelist_for_support( +	struct iscsi_param *param, +	char *value) +{ +	char *tmp1 = NULL, *tmp2 = NULL; +	char *acceptor_values = NULL, *proposer_values = NULL; + +	acceptor_values = param->value; +	proposer_values = value; + +	do { +		if (!proposer_values) +			return NULL; +		tmp1 = strchr(proposer_values, ','); +		if (tmp1) +			*tmp1 = '\0'; +		acceptor_values = param->value; +		do { +			if (!acceptor_values) { +				if (tmp1) +					*tmp1 = ','; +				return NULL; +			} +			tmp2 = strchr(acceptor_values, ','); +			if (tmp2) +				*tmp2 = '\0'; +			if (!acceptor_values || !proposer_values) { +				if (tmp1) +					*tmp1 = ','; +				if (tmp2) +					*tmp2 = ','; +				return NULL; +			} +			if (!strcmp(acceptor_values, proposer_values)) { +				if (tmp2) +					*tmp2 = ','; +				goto out; +			} +			if (tmp2) +				*tmp2++ = ','; + +			acceptor_values = tmp2; +			if (!acceptor_values) +				break; +		} while (acceptor_values); +		if (tmp1) +			*tmp1++ = ','; +		proposer_values = tmp1; +	} while (proposer_values); + +out: +	return proposer_values; +} + +static int iscsi_check_acceptor_state(struct iscsi_param *param, char *value) +{ +	u8 acceptor_boolean_value = 0, proposer_boolean_value = 0; +	char *negoitated_value = NULL; + +	if (IS_PSTATE_ACCEPTOR(param)) { +		pr_err("Received key \"%s\" twice, protocol error.\n", +				param->name); +		return -1; +	} + +	if (IS_PSTATE_REJECT(param)) +		return 0; + +	if (IS_TYPE_BOOL_AND(param)) { +		if (!strcmp(value, YES)) +			proposer_boolean_value = 1; +		if (!strcmp(param->value, YES)) +			acceptor_boolean_value = 1; +		if (acceptor_boolean_value && proposer_boolean_value) +			do {} while (0); +		else { +			if (iscsi_update_param_value(param, NO) < 0) +				return -1; +			if (!proposer_boolean_value) +				SET_PSTATE_REPLY_OPTIONAL(param); +		} +	} else if (IS_TYPE_BOOL_OR(param)) { +		if (!strcmp(value, YES)) +			proposer_boolean_value = 1; +		if (!strcmp(param->value, YES)) +			acceptor_boolean_value = 1; +		if (acceptor_boolean_value || proposer_boolean_value) { +			if (iscsi_update_param_value(param, YES) < 0) +				return -1; +			if (proposer_boolean_value) +				SET_PSTATE_REPLY_OPTIONAL(param); +		} +	} else if (IS_TYPE_NUMBER(param)) { +		char *tmpptr, buf[10]; +		u32 acceptor_value = simple_strtoul(param->value, &tmpptr, 0); +		u32 proposer_value = simple_strtoul(value, &tmpptr, 0); + +		memset(buf, 0, 10); + +		if (!strcmp(param->name, MAXCONNECTIONS) || +		    !strcmp(param->name, MAXBURSTLENGTH) || +		    !strcmp(param->name, FIRSTBURSTLENGTH) || +		    !strcmp(param->name, MAXOUTSTANDINGR2T) || +		    !strcmp(param->name, DEFAULTTIME2RETAIN) || +		    !strcmp(param->name, ERRORRECOVERYLEVEL)) { +			if (proposer_value > acceptor_value) { +				sprintf(buf, "%u", acceptor_value); +				if (iscsi_update_param_value(param, +						&buf[0]) < 0) +					return -1; +			} else { +				if (iscsi_update_param_value(param, value) < 0) +					return -1; +			} +		} else if (!strcmp(param->name, DEFAULTTIME2WAIT)) { +			if (acceptor_value > proposer_value) { +				sprintf(buf, "%u", acceptor_value); +				if (iscsi_update_param_value(param, +						&buf[0]) < 0) +					return -1; +			} else { +				if (iscsi_update_param_value(param, value) < 0) +					return -1; +			} +		} else { +			if (iscsi_update_param_value(param, value) < 0) +				return -1; +		} + +		if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) +			SET_PSTATE_REPLY_OPTIONAL(param); +	} else if (IS_TYPE_NUMBER_RANGE(param)) { +		negoitated_value = iscsi_get_value_from_number_range( +					param, value); +		if (!negoitated_value) +			return -1; +		if (iscsi_update_param_value(param, negoitated_value) < 0) +			return -1; +	} else if (IS_TYPE_VALUE_LIST(param)) { +		negoitated_value = iscsi_check_valuelist_for_support( +					param, value); +		if (!negoitated_value) { +			pr_err("Proposer's value list \"%s\" contains" +				" no valid values from Acceptor's value list" +				" \"%s\".\n", value, param->value); +			return -1; +		} +		if (iscsi_update_param_value(param, negoitated_value) < 0) +			return -1; +	} else if (IS_PHASE_DECLARATIVE(param)) { +		if (iscsi_update_param_value(param, value) < 0) +			return -1; +		SET_PSTATE_REPLY_OPTIONAL(param); +	} + +	return 0; +} + +static int iscsi_check_proposer_state(struct iscsi_param *param, char *value) +{ +	if (IS_PSTATE_RESPONSE_GOT(param)) { +		pr_err("Received key \"%s\" twice, protocol error.\n", +				param->name); +		return -1; +	} + +	if (IS_TYPE_NUMBER_RANGE(param)) { +		u32 left_val = 0, right_val = 0, recieved_value = 0; +		char *left_val_ptr = NULL, *right_val_ptr = NULL; +		char *tilde_ptr = NULL, *tmp_ptr = NULL; + +		if (!strcmp(value, IRRELEVANT) || !strcmp(value, REJECT)) { +			if (iscsi_update_param_value(param, value) < 0) +				return -1; +			return 0; +		} + +		tilde_ptr = strchr(value, '~'); +		if (tilde_ptr) { +			pr_err("Illegal \"~\" in response for \"%s\".\n", +					param->name); +			return -1; +		} +		tilde_ptr = strchr(param->value, '~'); +		if (!tilde_ptr) { +			pr_err("Unable to locate numerical range" +				" indicator \"~\" for \"%s\".\n", param->name); +			return -1; +		} +		*tilde_ptr = '\0'; + +		left_val_ptr = param->value; +		right_val_ptr = param->value + strlen(left_val_ptr) + 1; +		left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0); +		right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0); +		recieved_value = simple_strtoul(value, &tmp_ptr, 0); + +		*tilde_ptr = '~'; + +		if ((recieved_value < left_val) || +		    (recieved_value > right_val)) { +			pr_err("Illegal response \"%s=%u\", value must" +				" be between %u and %u.\n", param->name, +				recieved_value, left_val, right_val); +			return -1; +		} +	} else if (IS_TYPE_VALUE_LIST(param)) { +		char *comma_ptr = NULL, *tmp_ptr = NULL; + +		comma_ptr = strchr(value, ','); +		if (comma_ptr) { +			pr_err("Illegal \",\" in response for \"%s\".\n", +					param->name); +			return -1; +		} + +		tmp_ptr = iscsi_check_valuelist_for_support(param, value); +		if (!tmp_ptr) +			return -1; +	} + +	if (iscsi_update_param_value(param, value) < 0) +		return -1; + +	return 0; +} + +static int iscsi_check_value(struct iscsi_param *param, char *value) +{ +	char *comma_ptr = NULL; + +	if (!strcmp(value, REJECT)) { +		if (!strcmp(param->name, IFMARKINT) || +		    !strcmp(param->name, OFMARKINT)) { +			/* +			 * Reject is not fatal for [I,O]FMarkInt,  and causes +			 * [I,O]FMarker to be reset to No. (See iSCSI v20 A.3.2) +			 */ +			SET_PSTATE_REJECT(param); +			return 0; +		} +		pr_err("Received %s=%s\n", param->name, value); +		return -1; +	} +	if (!strcmp(value, IRRELEVANT)) { +		pr_debug("Received %s=%s\n", param->name, value); +		SET_PSTATE_IRRELEVANT(param); +		return 0; +	} +	if (!strcmp(value, NOTUNDERSTOOD)) { +		if (!IS_PSTATE_PROPOSER(param)) { +			pr_err("Received illegal offer %s=%s\n", +				param->name, value); +			return -1; +		} + +/* #warning FIXME: Add check for X-ExtensionKey here */ +		pr_err("Standard iSCSI key \"%s\" cannot be answered" +			" with \"%s\", protocol error.\n", param->name, value); +		return -1; +	} + +	do { +		comma_ptr = NULL; +		comma_ptr = strchr(value, ','); + +		if (comma_ptr && !IS_TYPE_VALUE_LIST(param)) { +			pr_err("Detected value seperator \",\", but" +				" key \"%s\" does not allow a value list," +				" protocol error.\n", param->name); +			return -1; +		} +		if (comma_ptr) +			*comma_ptr = '\0'; + +		if (strlen(value) > VALUE_MAXLEN) { +			pr_err("Value for key \"%s\" exceeds %d," +				" protocol error.\n", param->name, +				VALUE_MAXLEN); +			return -1; +		} + +		if (IS_TYPE_BOOL_AND(param) || IS_TYPE_BOOL_OR(param)) { +			if (iscsi_check_boolean_value(param, value) < 0) +				return -1; +		} else if (IS_TYPE_NUMBER(param)) { +			if (iscsi_check_numerical_value(param, value) < 0) +				return -1; +		} else if (IS_TYPE_NUMBER_RANGE(param)) { +			if (iscsi_check_numerical_range_value(param, value) < 0) +				return -1; +		} else if (IS_TYPE_STRING(param) || IS_TYPE_VALUE_LIST(param)) { +			if (iscsi_check_string_or_list_value(param, value) < 0) +				return -1; +		} else { +			pr_err("Huh? 0x%02x\n", param->type); +			return -1; +		} + +		if (comma_ptr) +			*comma_ptr++ = ','; + +		value = comma_ptr; +	} while (value); + +	return 0; +} + +static struct iscsi_param *__iscsi_check_key( +	char *key, +	int sender, +	struct iscsi_param_list *param_list) +{ +	struct iscsi_param *param; + +	if (strlen(key) > KEY_MAXLEN) { +		pr_err("Length of key name \"%s\" exceeds %d.\n", +			key, KEY_MAXLEN); +		return NULL; +	} + +	param = iscsi_find_param_from_key(key, param_list); +	if (!param) +		return NULL; + +	if ((sender & SENDER_INITIATOR) && !IS_SENDER_INITIATOR(param)) { +		pr_err("Key \"%s\" may not be sent to %s," +			" protocol error.\n", param->name, +			(sender & SENDER_RECEIVER) ? "target" : "initiator"); +		return NULL; +	} + +	if ((sender & SENDER_TARGET) && !IS_SENDER_TARGET(param)) { +		pr_err("Key \"%s\" may not be sent to %s," +			" protocol error.\n", param->name, +			(sender & SENDER_RECEIVER) ? "initiator" : "target"); +		return NULL; +	} + +	return param; +} + +static struct iscsi_param *iscsi_check_key( +	char *key, +	int phase, +	int sender, +	struct iscsi_param_list *param_list) +{ +	struct iscsi_param *param; +	/* +	 * Key name length must not exceed 63 bytes. (See iSCSI v20 5.1) +	 */ +	if (strlen(key) > KEY_MAXLEN) { +		pr_err("Length of key name \"%s\" exceeds %d.\n", +			key, KEY_MAXLEN); +		return NULL; +	} + +	param = iscsi_find_param_from_key(key, param_list); +	if (!param) +		return NULL; + +	if ((sender & SENDER_INITIATOR) && !IS_SENDER_INITIATOR(param)) { +		pr_err("Key \"%s\" may not be sent to %s," +			" protocol error.\n", param->name, +			(sender & SENDER_RECEIVER) ? "target" : "initiator"); +		return NULL; +	} +	if ((sender & SENDER_TARGET) && !IS_SENDER_TARGET(param)) { +		pr_err("Key \"%s\" may not be sent to %s," +				" protocol error.\n", param->name, +			(sender & SENDER_RECEIVER) ? "initiator" : "target"); +		return NULL; +	} + +	if (IS_PSTATE_ACCEPTOR(param)) { +		pr_err("Key \"%s\" received twice, protocol error.\n", +				key); +		return NULL; +	} + +	if (!phase) +		return param; + +	if (!(param->phase & phase)) { +		pr_err("Key \"%s\" may not be negotiated during ", +				param->name); +		switch (phase) { +		case PHASE_SECURITY: +			pr_debug("Security phase.\n"); +			break; +		case PHASE_OPERATIONAL: +			pr_debug("Operational phase.\n"); +		default: +			pr_debug("Unknown phase.\n"); +		} +		return NULL; +	} + +	return param; +} + +static int iscsi_enforce_integrity_rules( +	u8 phase, +	struct iscsi_param_list *param_list) +{ +	char *tmpptr; +	u8 DataSequenceInOrder = 0; +	u8 ErrorRecoveryLevel = 0, SessionType = 0; +	u8 IFMarker = 0, OFMarker = 0; +	u8 IFMarkInt_Reject = 0, OFMarkInt_Reject = 0; +	u32 FirstBurstLength = 0, MaxBurstLength = 0; +	struct iscsi_param *param = NULL; + +	list_for_each_entry(param, ¶m_list->param_list, p_list) { +		if (!(param->phase & phase)) +			continue; +		if (!strcmp(param->name, SESSIONTYPE)) +			if (!strcmp(param->value, NORMAL)) +				SessionType = 1; +		if (!strcmp(param->name, ERRORRECOVERYLEVEL)) +			ErrorRecoveryLevel = simple_strtoul(param->value, +					&tmpptr, 0); +		if (!strcmp(param->name, DATASEQUENCEINORDER)) +			if (!strcmp(param->value, YES)) +				DataSequenceInOrder = 1; +		if (!strcmp(param->name, MAXBURSTLENGTH)) +			MaxBurstLength = simple_strtoul(param->value, +					&tmpptr, 0); +		if (!strcmp(param->name, IFMARKER)) +			if (!strcmp(param->value, YES)) +				IFMarker = 1; +		if (!strcmp(param->name, OFMARKER)) +			if (!strcmp(param->value, YES)) +				OFMarker = 1; +		if (!strcmp(param->name, IFMARKINT)) +			if (!strcmp(param->value, REJECT)) +				IFMarkInt_Reject = 1; +		if (!strcmp(param->name, OFMARKINT)) +			if (!strcmp(param->value, REJECT)) +				OFMarkInt_Reject = 1; +	} + +	list_for_each_entry(param, ¶m_list->param_list, p_list) { +		if (!(param->phase & phase)) +			continue; +		if (!SessionType && (!IS_PSTATE_ACCEPTOR(param) && +		     (strcmp(param->name, IFMARKER) && +		      strcmp(param->name, OFMARKER) && +		      strcmp(param->name, IFMARKINT) && +		      strcmp(param->name, OFMARKINT)))) +			continue; +		if (!strcmp(param->name, MAXOUTSTANDINGR2T) && +		    DataSequenceInOrder && (ErrorRecoveryLevel > 0)) { +			if (strcmp(param->value, "1")) { +				if (iscsi_update_param_value(param, "1") < 0) +					return -1; +				pr_debug("Reset \"%s\" to \"%s\".\n", +					param->name, param->value); +			} +		} +		if (!strcmp(param->name, MAXCONNECTIONS) && !SessionType) { +			if (strcmp(param->value, "1")) { +				if (iscsi_update_param_value(param, "1") < 0) +					return -1; +				pr_debug("Reset \"%s\" to \"%s\".\n", +					param->name, param->value); +			} +		} +		if (!strcmp(param->name, FIRSTBURSTLENGTH)) { +			FirstBurstLength = simple_strtoul(param->value, +					&tmpptr, 0); +			if (FirstBurstLength > MaxBurstLength) { +				char tmpbuf[10]; +				memset(tmpbuf, 0, 10); +				sprintf(tmpbuf, "%u", MaxBurstLength); +				if (iscsi_update_param_value(param, tmpbuf)) +					return -1; +				pr_debug("Reset \"%s\" to \"%s\".\n", +					param->name, param->value); +			} +		} +		if (!strcmp(param->name, IFMARKER) && IFMarkInt_Reject) { +			if (iscsi_update_param_value(param, NO) < 0) +				return -1; +			IFMarker = 0; +			pr_debug("Reset \"%s\" to \"%s\".\n", +					param->name, param->value); +		} +		if (!strcmp(param->name, OFMARKER) && OFMarkInt_Reject) { +			if (iscsi_update_param_value(param, NO) < 0) +				return -1; +			OFMarker = 0; +			pr_debug("Reset \"%s\" to \"%s\".\n", +					 param->name, param->value); +		} +		if (!strcmp(param->name, IFMARKINT) && !IFMarker) { +			if (!strcmp(param->value, REJECT)) +				continue; +			param->state &= ~PSTATE_NEGOTIATE; +			if (iscsi_update_param_value(param, IRRELEVANT) < 0) +				return -1; +			pr_debug("Reset \"%s\" to \"%s\".\n", +					param->name, param->value); +		} +		if (!strcmp(param->name, OFMARKINT) && !OFMarker) { +			if (!strcmp(param->value, REJECT)) +				continue; +			param->state &= ~PSTATE_NEGOTIATE; +			if (iscsi_update_param_value(param, IRRELEVANT) < 0) +				return -1; +			pr_debug("Reset \"%s\" to \"%s\".\n", +					param->name, param->value); +		} +	} + +	return 0; +} + +int iscsi_decode_text_input( +	u8 phase, +	u8 sender, +	char *textbuf, +	u32 length, +	struct iscsi_param_list *param_list) +{ +	char *tmpbuf, *start = NULL, *end = NULL; + +	tmpbuf = kzalloc(length + 1, GFP_KERNEL); +	if (!tmpbuf) { +		pr_err("Unable to allocate memory for tmpbuf.\n"); +		return -1; +	} + +	memcpy(tmpbuf, textbuf, length); +	tmpbuf[length] = '\0'; +	start = tmpbuf; +	end = (start + length); + +	while (start < end) { +		char *key, *value; +		struct iscsi_param *param; + +		if (iscsi_extract_key_value(start, &key, &value) < 0) { +			kfree(tmpbuf); +			return -1; +		} + +		pr_debug("Got key: %s=%s\n", key, value); + +		if (phase & PHASE_SECURITY) { +			if (iscsi_check_for_auth_key(key) > 0) { +				char *tmpptr = key + strlen(key); +				*tmpptr = '='; +				kfree(tmpbuf); +				return 1; +			} +		} + +		param = iscsi_check_key(key, phase, sender, param_list); +		if (!param) { +			if (iscsi_add_notunderstood_response(key, +					value, param_list) < 0) { +				kfree(tmpbuf); +				return -1; +			} +			start += strlen(key) + strlen(value) + 2; +			continue; +		} +		if (iscsi_check_value(param, value) < 0) { +			kfree(tmpbuf); +			return -1; +		} + +		start += strlen(key) + strlen(value) + 2; + +		if (IS_PSTATE_PROPOSER(param)) { +			if (iscsi_check_proposer_state(param, value) < 0) { +				kfree(tmpbuf); +				return -1; +			} +			SET_PSTATE_RESPONSE_GOT(param); +		} else { +			if (iscsi_check_acceptor_state(param, value) < 0) { +				kfree(tmpbuf); +				return -1; +			} +			SET_PSTATE_ACCEPTOR(param); +		} +	} + +	kfree(tmpbuf); +	return 0; +} + +int iscsi_encode_text_output( +	u8 phase, +	u8 sender, +	char *textbuf, +	u32 *length, +	struct iscsi_param_list *param_list) +{ +	char *output_buf = NULL; +	struct iscsi_extra_response *er; +	struct iscsi_param *param; + +	output_buf = textbuf + *length; + +	if (iscsi_enforce_integrity_rules(phase, param_list) < 0) +		return -1; + +	list_for_each_entry(param, ¶m_list->param_list, p_list) { +		if (!(param->sender & sender)) +			continue; +		if (IS_PSTATE_ACCEPTOR(param) && +		    !IS_PSTATE_RESPONSE_SENT(param) && +		    !IS_PSTATE_REPLY_OPTIONAL(param) && +		    (param->phase & phase)) { +			*length += sprintf(output_buf, "%s=%s", +				param->name, param->value); +			*length += 1; +			output_buf = textbuf + *length; +			SET_PSTATE_RESPONSE_SENT(param); +			pr_debug("Sending key: %s=%s\n", +				param->name, param->value); +			continue; +		} +		if (IS_PSTATE_NEGOTIATE(param) && +		    !IS_PSTATE_ACCEPTOR(param) && +		    !IS_PSTATE_PROPOSER(param) && +		    (param->phase & phase)) { +			*length += sprintf(output_buf, "%s=%s", +				param->name, param->value); +			*length += 1; +			output_buf = textbuf + *length; +			SET_PSTATE_PROPOSER(param); +			iscsi_check_proposer_for_optional_reply(param); +			pr_debug("Sending key: %s=%s\n", +				param->name, param->value); +		} +	} + +	list_for_each_entry(er, ¶m_list->extra_response_list, er_list) { +		*length += sprintf(output_buf, "%s=%s", er->key, er->value); +		*length += 1; +		output_buf = textbuf + *length; +		pr_debug("Sending key: %s=%s\n", er->key, er->value); +	} +	iscsi_release_extra_responses(param_list); + +	return 0; +} + +int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list) +{ +	int ret = 0; +	struct iscsi_param *param; + +	list_for_each_entry(param, ¶m_list->param_list, p_list) { +		if (IS_PSTATE_NEGOTIATE(param) && +		    IS_PSTATE_PROPOSER(param) && +		    !IS_PSTATE_RESPONSE_GOT(param) && +		    !IS_PSTATE_REPLY_OPTIONAL(param) && +		    !IS_PHASE_DECLARATIVE(param)) { +			pr_err("No response for proposed key \"%s\".\n", +					param->name); +			ret = -1; +		} +	} + +	return ret; +} + +int iscsi_change_param_value( +	char *keyvalue, +	struct iscsi_param_list *param_list, +	int check_key) +{ +	char *key = NULL, *value = NULL; +	struct iscsi_param *param; +	int sender = 0; + +	if (iscsi_extract_key_value(keyvalue, &key, &value) < 0) +		return -1; + +	if (!check_key) { +		param = __iscsi_check_key(keyvalue, sender, param_list); +		if (!param) +			return -1; +	} else { +		param = iscsi_check_key(keyvalue, 0, sender, param_list); +		if (!param) +			return -1; + +		param->set_param = 1; +		if (iscsi_check_value(param, value) < 0) { +			param->set_param = 0; +			return -1; +		} +		param->set_param = 0; +	} + +	if (iscsi_update_param_value(param, value) < 0) +		return -1; + +	return 0; +} + +void iscsi_set_connection_parameters( +	struct iscsi_conn_ops *ops, +	struct iscsi_param_list *param_list) +{ +	char *tmpptr; +	struct iscsi_param *param; + +	pr_debug("---------------------------------------------------" +			"---------------\n"); +	list_for_each_entry(param, ¶m_list->param_list, p_list) { +		if (!IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param)) +			continue; +		if (!strcmp(param->name, AUTHMETHOD)) { +			pr_debug("AuthMethod:                   %s\n", +				param->value); +		} else if (!strcmp(param->name, HEADERDIGEST)) { +			ops->HeaderDigest = !strcmp(param->value, CRC32C); +			pr_debug("HeaderDigest:                 %s\n", +				param->value); +		} else if (!strcmp(param->name, DATADIGEST)) { +			ops->DataDigest = !strcmp(param->value, CRC32C); +			pr_debug("DataDigest:                   %s\n", +				param->value); +		} else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) { +			ops->MaxRecvDataSegmentLength = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("MaxRecvDataSegmentLength:     %s\n", +				param->value); +		} else if (!strcmp(param->name, OFMARKER)) { +			ops->OFMarker = !strcmp(param->value, YES); +			pr_debug("OFMarker:                     %s\n", +				param->value); +		} else if (!strcmp(param->name, IFMARKER)) { +			ops->IFMarker = !strcmp(param->value, YES); +			pr_debug("IFMarker:                     %s\n", +				param->value); +		} else if (!strcmp(param->name, OFMARKINT)) { +			ops->OFMarkInt = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("OFMarkInt:                    %s\n", +				param->value); +		} else if (!strcmp(param->name, IFMARKINT)) { +			ops->IFMarkInt = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("IFMarkInt:                    %s\n", +				param->value); +		} +	} +	pr_debug("----------------------------------------------------" +			"--------------\n"); +} + +void iscsi_set_session_parameters( +	struct iscsi_sess_ops *ops, +	struct iscsi_param_list *param_list, +	int leading) +{ +	char *tmpptr; +	struct iscsi_param *param; + +	pr_debug("----------------------------------------------------" +			"--------------\n"); +	list_for_each_entry(param, ¶m_list->param_list, p_list) { +		if (!IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param)) +			continue; +		if (!strcmp(param->name, INITIATORNAME)) { +			if (!param->value) +				continue; +			if (leading) +				snprintf(ops->InitiatorName, +						sizeof(ops->InitiatorName), +						"%s", param->value); +			pr_debug("InitiatorName:                %s\n", +				param->value); +		} else if (!strcmp(param->name, INITIATORALIAS)) { +			if (!param->value) +				continue; +			snprintf(ops->InitiatorAlias, +						sizeof(ops->InitiatorAlias), +						"%s", param->value); +			pr_debug("InitiatorAlias:               %s\n", +				param->value); +		} else if (!strcmp(param->name, TARGETNAME)) { +			if (!param->value) +				continue; +			if (leading) +				snprintf(ops->TargetName, +						sizeof(ops->TargetName), +						"%s", param->value); +			pr_debug("TargetName:                   %s\n", +				param->value); +		} else if (!strcmp(param->name, TARGETALIAS)) { +			if (!param->value) +				continue; +			snprintf(ops->TargetAlias, sizeof(ops->TargetAlias), +					"%s", param->value); +			pr_debug("TargetAlias:                  %s\n", +				param->value); +		} else if (!strcmp(param->name, TARGETPORTALGROUPTAG)) { +			ops->TargetPortalGroupTag = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("TargetPortalGroupTag:         %s\n", +				param->value); +		} else if (!strcmp(param->name, MAXCONNECTIONS)) { +			ops->MaxConnections = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("MaxConnections:               %s\n", +				param->value); +		} else if (!strcmp(param->name, INITIALR2T)) { +			ops->InitialR2T = !strcmp(param->value, YES); +			 pr_debug("InitialR2T:                   %s\n", +				param->value); +		} else if (!strcmp(param->name, IMMEDIATEDATA)) { +			ops->ImmediateData = !strcmp(param->value, YES); +			pr_debug("ImmediateData:                %s\n", +				param->value); +		} else if (!strcmp(param->name, MAXBURSTLENGTH)) { +			ops->MaxBurstLength = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("MaxBurstLength:               %s\n", +				param->value); +		} else if (!strcmp(param->name, FIRSTBURSTLENGTH)) { +			ops->FirstBurstLength = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("FirstBurstLength:             %s\n", +				param->value); +		} else if (!strcmp(param->name, DEFAULTTIME2WAIT)) { +			ops->DefaultTime2Wait = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("DefaultTime2Wait:             %s\n", +				param->value); +		} else if (!strcmp(param->name, DEFAULTTIME2RETAIN)) { +			ops->DefaultTime2Retain = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("DefaultTime2Retain:           %s\n", +				param->value); +		} else if (!strcmp(param->name, MAXOUTSTANDINGR2T)) { +			ops->MaxOutstandingR2T = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("MaxOutstandingR2T:            %s\n", +				param->value); +		} else if (!strcmp(param->name, DATAPDUINORDER)) { +			ops->DataPDUInOrder = !strcmp(param->value, YES); +			pr_debug("DataPDUInOrder:               %s\n", +				param->value); +		} else if (!strcmp(param->name, DATASEQUENCEINORDER)) { +			ops->DataSequenceInOrder = !strcmp(param->value, YES); +			pr_debug("DataSequenceInOrder:          %s\n", +				param->value); +		} else if (!strcmp(param->name, ERRORRECOVERYLEVEL)) { +			ops->ErrorRecoveryLevel = +				simple_strtoul(param->value, &tmpptr, 0); +			pr_debug("ErrorRecoveryLevel:           %s\n", +				param->value); +		} else if (!strcmp(param->name, SESSIONTYPE)) { +			ops->SessionType = !strcmp(param->value, DISCOVERY); +			pr_debug("SessionType:                  %s\n", +				param->value); +		} +	} +	pr_debug("----------------------------------------------------" +			"--------------\n"); + +}  |