| /******************************************************************************* | 
 |  * 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 -ENOMEM; | 
 | 	} | 
 |  | 
 | 	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); | 
 |  | 
 | 	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; | 
 | 	u32 left_val, right_val, local_left_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, NULL, 0); | 
 | 	right_val = simple_strtoul(right_val_ptr, NULL, 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, NULL, 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 (!strcmp(acceptor_values, proposer_values)) { | 
 | 				if (tmp2) | 
 | 					*tmp2 = ','; | 
 | 				goto out; | 
 | 			} | 
 | 			if (tmp2) | 
 | 				*tmp2++ = ','; | 
 |  | 
 | 			acceptor_values = tmp2; | 
 | 		} 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; | 
 |  | 
 | 		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, NULL, 0); | 
 | 		right_val = simple_strtoul(right_val_ptr, NULL, 0); | 
 | 		recieved_value = simple_strtoul(value, NULL, 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 = 1, OFMarkInt_Reject = 1; | 
 | 	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"); | 
 |  | 
 | } |