numam-spdk/lib/iscsi/param.c
Tomasz Zawadzki f7561e31de iscsi: check provided data_len for negative value during param negotiation
Change-Id: Icb7184a88d93a55aa53e94bf50dab645785a6d9c
Signed-off-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
Reviewed-on: https://review.gerrithub.io/434178
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
2018-11-29 19:31:11 +00:00

1187 lines
30 KiB
C

/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "spdk/stdinc.h"
#include "spdk/string.h"
#include "iscsi/iscsi.h"
#include "iscsi/param.h"
#include "iscsi/conn.h"
#include "spdk/string.h"
#include "spdk_internal/log.h"
#define MAX_TMPBUF 1024
/* whose value may be bigger than 255 */
static const char *non_simple_value_params[] = {
"CHAP_C",
"CHAP_R",
NULL,
};
void
spdk_iscsi_param_free(struct iscsi_param *params)
{
struct iscsi_param *param, *next_param;
if (params == NULL) {
return;
}
for (param = params; param != NULL; param = next_param) {
next_param = param->next;
if (param->list) {
free(param->list);
}
free(param->val);
free(param->key);
free(param);
}
}
static int
spdk_iscsi_find_key_in_array(const char *key, const char *array[])
{
int i;
for (i = 0; array[i] != NULL; i++) {
if (strcasecmp(key, array[i]) == 0) {
return 1;
}
}
return 0;
}
struct iscsi_param *
spdk_iscsi_param_find(struct iscsi_param *params, const char *key)
{
struct iscsi_param *param;
if (params == NULL || key == NULL) {
return NULL;
}
for (param = params; param != NULL; param = param->next) {
if (param->key != NULL && param->key[0] == key[0]
&& strcasecmp(param->key, key) == 0) {
return param;
}
}
return NULL;
}
int
spdk_iscsi_param_del(struct iscsi_param **params, const char *key)
{
struct iscsi_param *param, *prev_param = NULL;
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "del %s\n", key);
if (params == NULL || key == NULL) {
return 0;
}
for (param = *params; param != NULL; param = param->next) {
if (param->key != NULL && param->key[0] == key[0]
&& strcasecmp(param->key, key) == 0) {
if (prev_param != NULL) {
prev_param->next = param->next;
} else {
*params = param->next;
}
param->next = NULL;
spdk_iscsi_param_free(param);
return 0;
}
prev_param = param;
}
return -1;
}
int
spdk_iscsi_param_add(struct iscsi_param **params, const char *key,
const char *val, const char *list, int type)
{
struct iscsi_param *param, *last_param;
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "add %s=%s, list=[%s], type=%d\n",
key, val, list, type);
if (key == NULL) {
return -1;
}
param = spdk_iscsi_param_find(*params, key);
if (param != NULL) {
spdk_iscsi_param_del(params, key);
}
param = calloc(1, sizeof(*param));
if (!param) {
SPDK_ERRLOG("calloc() failed for parameter\n");
return -ENOMEM;
}
param->next = NULL;
param->key = xstrdup(key);
param->val = xstrdup(val);
param->list = xstrdup(list);
param->type = type;
last_param = *params;
if (last_param != NULL) {
while (last_param->next != NULL) {
last_param = last_param->next;
}
last_param->next = param;
} else {
*params = param;
}
return 0;
}
int
spdk_iscsi_param_set(struct iscsi_param *params, const char *key,
const char *val)
{
struct iscsi_param *param;
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set %s=%s\n", key, val);
param = spdk_iscsi_param_find(params, key);
if (param == NULL) {
SPDK_ERRLOG("no key %s\n", key);
return -1;
}
free(param->val);
param->val = xstrdup(val);
return 0;
}
int
spdk_iscsi_param_set_int(struct iscsi_param *params, const char *key, uint32_t val)
{
char buf[MAX_TMPBUF];
struct iscsi_param *param;
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set %s=%d\n", key, val);
param = spdk_iscsi_param_find(params, key);
if (param == NULL) {
SPDK_ERRLOG("no key %s\n", key);
return -1;
}
free(param->val);
snprintf(buf, sizeof buf, "%d", val);
param->val = strdup(buf);
return 0;
}
/**
* Parse a single KEY=VAL pair
*
* data = "KEY=VAL<NUL>"
*/
static int
spdk_iscsi_parse_param(struct iscsi_param **params, const uint8_t *data)
{
int rc;
uint8_t *key_copy;
const uint8_t *key_end, *val;
int key_len, val_len;
int max_len;
key_end = strchr(data, '=');
if (!key_end) {
SPDK_ERRLOG("'=' not found\n");
return -1;
}
key_len = key_end - data;
if (key_len == 0) {
SPDK_ERRLOG("Empty key\n");
return -1;
}
/*
* RFC 7143 6.1
*/
if (key_len > ISCSI_TEXT_MAX_KEY_LEN) {
SPDK_ERRLOG("Key name length is bigger than 63\n");
return -1;
}
key_copy = malloc(key_len + 1);
if (!key_copy) {
SPDK_ERRLOG("malloc() failed for key_copy\n");
return -ENOMEM;
}
memcpy(key_copy, data, key_len);
key_copy[key_len] = '\0';
/* check whether this key is duplicated */
if (NULL != spdk_iscsi_param_find(*params, key_copy)) {
SPDK_ERRLOG("Duplicated Key %s\n", key_copy);
free(key_copy);
return -1;
}
val = key_end + 1; /* +1 to skip over the '=' */
val_len = strlen(val);
/*
* RFC 3720 5.1
* If not otherwise specified, the maximum length of a simple-value
* (not its encoded representation) is 255 bytes, not including the delimiter
* (comma or zero byte).
*/
/*
* comma or zero is counted in, otherwise we need to iterate each parameter
* value
*/
max_len = spdk_iscsi_find_key_in_array(key_copy, non_simple_value_params) ?
ISCSI_TEXT_MAX_VAL_LEN : ISCSI_TEXT_MAX_SIMPLE_VAL_LEN;
if (val_len > max_len) {
SPDK_ERRLOG("Overflow Val %d\n", val_len);
free(key_copy);
return -1;
}
rc = spdk_iscsi_param_add(params, key_copy, val, NULL, 0);
free(key_copy);
if (rc < 0) {
SPDK_ERRLOG("iscsi_param_add() failed\n");
return -1;
}
/* return number of bytes consumed
* +1 for '=' and +1 for NUL
*/
return key_len + 1 + val_len + 1;
}
/**
* Parse a sequence of KEY=VAL pairs.
*
* \param data "KEY=VAL<NUL>KEY=VAL<NUL>..."
* \param len length of data in bytes
*/
int
spdk_iscsi_parse_params(struct iscsi_param **params, const uint8_t *data,
int len, bool cbit_enabled, char **partial_parameter)
{
int rc, offset = 0;
char *p;
int i;
/* strip the partial text parameters if previous PDU have C enabled */
if (partial_parameter && *partial_parameter) {
for (i = 0; i < len && data[i] != '\0'; i++) {
;
}
p = spdk_sprintf_alloc("%s%s", *partial_parameter, (const char *)data);
if (!p) {
return -1;
}
rc = spdk_iscsi_parse_param(params, p);
free(p);
if (rc < 0) {
return -1;
}
free(*partial_parameter);
*partial_parameter = NULL;
data = data + i + 1;
len = len - (i + 1);
}
/* strip the partial text parameters if C bit is enabled */
if (cbit_enabled) {
if (partial_parameter == NULL) {
SPDK_ERRLOG("C bit set but no partial parameters provided\n");
return -1;
}
/*
* reverse iterate the string from the tail not including '\0'
* index of last '\0' is len -1.
*/
for (i = len - 2; data[i] != '\0' && i > 0; i--) {
;
}
*partial_parameter = xstrdup(&data[i == 0 ? 0 : i + 1]);
len = (i == 0 ? 0 : i + 1);
}
while (offset < len && data[offset] != '\0') {
rc = spdk_iscsi_parse_param(params, data + offset);
if (rc < 0) {
return -1;
}
offset += rc;
}
return 0;
}
char *
spdk_iscsi_param_get_val(struct iscsi_param *params, const char *key)
{
struct iscsi_param *param;
param = spdk_iscsi_param_find(params, key);
if (param == NULL) {
return NULL;
}
return param->val;
}
int
spdk_iscsi_param_eq_val(struct iscsi_param *params, const char *key,
const char *val)
{
struct iscsi_param *param;
param = spdk_iscsi_param_find(params, key);
if (param == NULL) {
return 0;
}
if (strcasecmp(param->val, val) == 0) {
return 1;
}
return 0;
}
struct iscsi_param_table {
const char *key;
const char *val;
const char *list;
int type;
};
static const struct iscsi_param_table conn_param_table[] = {
{ "HeaderDigest", "None", "CRC32C,None", ISPT_LIST },
{ "DataDigest", "None", "CRC32C,None", ISPT_LIST },
{ "MaxRecvDataSegmentLength", "8192", "512,16777215", ISPT_NUMERICAL_DECLARATIVE },
{ "OFMarker", "No", "Yes,No", ISPT_BOOLEAN_AND },
{ "IFMarker", "No", "Yes,No", ISPT_BOOLEAN_AND },
{ "OFMarkInt", "1", "1,65535", ISPT_NUMERICAL_MIN },
{ "IFMarkInt", "1", "1,65535", ISPT_NUMERICAL_MIN },
{ "AuthMethod", "None", "CHAP,None", ISPT_LIST },
{ "CHAP_A", "5", "5", ISPT_LIST },
{ "CHAP_N", "", "", ISPT_DECLARATIVE },
{ "CHAP_R", "", "", ISPT_DECLARATIVE },
{ "CHAP_I", "", "", ISPT_DECLARATIVE },
{ "CHAP_C", "", "", ISPT_DECLARATIVE },
{ NULL, NULL, NULL, ISPT_INVALID },
};
static const struct iscsi_param_table sess_param_table[] = {
{ "MaxConnections", "1", "1,65535", ISPT_NUMERICAL_MIN },
#if 0
/* need special handling */
{ "SendTargets", "", "", ISPT_DECLARATIVE },
#endif
{ "TargetName", "", "", ISPT_DECLARATIVE },
{ "InitiatorName", "", "", ISPT_DECLARATIVE },
{ "TargetAlias", "", "", ISPT_DECLARATIVE },
{ "InitiatorAlias", "", "", ISPT_DECLARATIVE },
{ "TargetAddress", "", "", ISPT_DECLARATIVE },
{ "TargetPortalGroupTag", "1", "1,65535", ISPT_NUMERICAL_DECLARATIVE },
{ "InitialR2T", "Yes", "Yes,No", ISPT_BOOLEAN_OR },
{ "ImmediateData", "Yes", "Yes,No", ISPT_BOOLEAN_AND },
{ "MaxBurstLength", "262144", "512,16777215", ISPT_NUMERICAL_MIN },
{ "FirstBurstLength", "65536", "512,16777215", ISPT_NUMERICAL_MIN },
{ "DefaultTime2Wait", "2", "0,3600", ISPT_NUMERICAL_MAX },
{ "DefaultTime2Retain", "20", "0,3600", ISPT_NUMERICAL_MIN },
{ "MaxOutstandingR2T", "1", "1,65536", ISPT_NUMERICAL_MIN },
{ "DataPDUInOrder", "Yes", "Yes,No", ISPT_BOOLEAN_OR },
{ "DataSequenceInOrder", "Yes", "Yes,No", ISPT_BOOLEAN_OR },
{ "ErrorRecoveryLevel", "0", "0,2", ISPT_NUMERICAL_MIN },
{ "SessionType", "Normal", "Normal,Discovery", ISPT_DECLARATIVE },
{ NULL, NULL, NULL, ISPT_INVALID },
};
static int
spdk_iscsi_params_init_internal(struct iscsi_param **params,
const struct iscsi_param_table *table)
{
int rc;
int i;
struct iscsi_param *param;
for (i = 0; table[i].key != NULL; i++) {
rc = spdk_iscsi_param_add(params, table[i].key, table[i].val,
table[i].list, table[i].type);
if (rc < 0) {
SPDK_ERRLOG("iscsi_param_add() failed\n");
return -1;
}
param = spdk_iscsi_param_find(*params, table[i].key);
if (param != NULL) {
param->state_index = i;
} else {
SPDK_ERRLOG("spdk_iscsi_param_find() failed\n");
return -1;
}
}
return 0;
}
int
spdk_iscsi_conn_params_init(struct iscsi_param **params)
{
return spdk_iscsi_params_init_internal(params, &conn_param_table[0]);
}
int
spdk_iscsi_sess_params_init(struct iscsi_param **params)
{
return spdk_iscsi_params_init_internal(params, &sess_param_table[0]);
}
static const char *chap_type[] = {
"CHAP_A",
"CHAP_N",
"CHAP_R",
"CHAP_I",
"CHAP_C",
NULL,
};
static const char *discovery_ignored_param[] = {
"MaxConnections",
"InitialR2T",
"ImmediateData",
"MaxBurstLength",
"FirstBurstLength"
"MaxOutstandingR2T",
"DataPDUInOrder",
NULL,
};
static const char *multi_negot_conn_params[] = {
"MaxRecvDataSegmentLength",
NULL,
};
/* The following params should be declared by target */
static const char *target_declarative_params[] = {
"TargetAlias",
"TargetAddress",
"TargetPortalGroupTag",
NULL,
};
/* This function is used to construct the data from the special param (e.g.,
* MaxRecvDataSegmentLength)
* return:
* normal: the total len of the data
* error: -1
*/
static int
spdk_iscsi_special_param_construction(struct spdk_iscsi_conn *conn,
struct iscsi_param *param,
bool FirstBurstLength_flag, char *data,
int alloc_len, int total)
{
int len;
struct iscsi_param *param_first;
struct iscsi_param *param_max;
uint32_t FirstBurstLength;
uint32_t MaxBurstLength;
char *val;
val = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
if (!val) {
SPDK_ERRLOG("malloc() failed for temporary buffer\n");
return -ENOMEM;
}
if (strcasecmp(param->key, "MaxRecvDataSegmentLength") == 0) {
/*
* MaxRecvDataSegmentLength is sent by both
* initiator and target, but is declarative - meaning
* each direction can have different values.
* So when MaxRecvDataSegmentLength is found in the
* the parameter set sent from the initiator, add SPDK
* iscsi target's MaxRecvDataSegmentLength value to
* the returned parameter list.
*/
if (alloc_len - total < 1) {
SPDK_ERRLOG("data space small %d\n", alloc_len);
free(val);
return -1;
}
SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
"returning MaxRecvDataSegmentLength=%d\n",
SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
len = snprintf((char *)data + total, alloc_len - total,
"MaxRecvDataSegmentLength=%d",
SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
total += len + 1;
}
if (strcasecmp(param->key, "MaxBurstLength") == 0 &&
!FirstBurstLength_flag) {
if (alloc_len - total < 1) {
SPDK_ERRLOG("data space small %d\n", alloc_len);
free(val);
return -1;
}
param_first = spdk_iscsi_param_find(conn->sess->params,
"FirstBurstLength");
if (param_first != NULL) {
FirstBurstLength = (uint32_t)strtol(param_first->val, NULL, 10);
} else {
FirstBurstLength = SPDK_ISCSI_FIRST_BURST_LENGTH;
}
param_max = spdk_iscsi_param_find(conn->sess->params,
"MaxBurstLength");
if (param_max != NULL) {
MaxBurstLength = (uint32_t)strtol(param_max->val, NULL, 10);
} else {
MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH;
}
if (FirstBurstLength > MaxBurstLength) {
FirstBurstLength = MaxBurstLength;
if (param_first != NULL) {
free(param_first->val);
snprintf(val, ISCSI_TEXT_MAX_VAL_LEN, "%d",
FirstBurstLength);
param_first->val = xstrdup(val);
}
}
len = snprintf((char *)data + total, alloc_len - total,
"FirstBurstLength=%d", FirstBurstLength);
total += len + 1;
}
free(val);
return total;
}
/**
* spdk_iscsi_construct_data_from_param:
* To construct the data which will be returned to the initiator
* return: length of the negotiated data, -1 indicates error;
*/
static int
spdk_iscsi_construct_data_from_param(struct iscsi_param *param, char *new_val,
char *data, int alloc_len, int total)
{
int len;
if (param->type != ISPT_DECLARATIVE &&
param->type != ISPT_NUMERICAL_DECLARATIVE) {
if (alloc_len - total < 1) {
SPDK_ERRLOG("data space small %d\n", alloc_len);
return -1;
}
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "negotiated %s=%s\n",
param->key, new_val);
len = snprintf((char *)data + total, alloc_len - total, "%s=%s",
param->key, new_val);
total += len + 1;
}
return total;
}
/**
* To negotiate param with
* type = ISPT_LIST
* return: the negotiated value of the key
*/
static char *spdk_iscsi_negotiate_param_list(int *add_param_value,
struct iscsi_param *param,
char *valid_list, char *in_val,
char *cur_val)
{
char *val_start, *val_end;
char *in_start, *in_end;
int flag = 0;
if (add_param_value == NULL) {
return NULL;
}
in_start = in_val;
do {
if ((in_end = strchr(in_start, (int)',')) != NULL) {
*in_end = '\0';
}
val_start = valid_list;
do {
if ((val_end = strchr(val_start, (int)',')) != NULL) {
*val_end = '\0';
}
if (strcasecmp(in_start, val_start) == 0) {
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "match %s\n",
val_start);
flag = 1;
break;
}
if (val_end) {
*val_end = ',';
val_start = val_end + 1;
}
} while (val_end);
if (flag) {
break;
}
if (in_end) {
*in_end = ',';
in_start = in_end + 1;
}
} while (in_end);
return flag ? val_start : NULL;
}
/**
* To negotiate param with
* type = ISPT_NUMERICAL_MIN/MAX, ISPT_NUMERICAL_DECLARATIVE
* return: the negotiated value of the key
*/
static char *spdk_iscsi_negotiate_param_numerical(int *add_param_value,
struct iscsi_param *param,
char *valid_list, char *in_val,
char *cur_val)
{
char *valid_next;
char *new_val = NULL;
char *min_val, *max_val;
int val_i, cur_val_i;
int min_i, max_i;
if (add_param_value == NULL) {
return NULL;
}
val_i = (int)strtol(param->val, NULL, 10);
/* check whether the key is FirstBurstLength, if that we use in_val */
if (strcasecmp(param->key, "FirstBurstLength") == 0) {
val_i = (int)strtol(in_val, NULL, 10);
}
cur_val_i = (int)strtol(cur_val, NULL, 10);
valid_next = valid_list;
min_val = spdk_strsepq(&valid_next, ",");
max_val = spdk_strsepq(&valid_next, ",");
min_i = (min_val != NULL) ? (int)strtol(min_val, NULL, 10) : 0;
max_i = (max_val != NULL) ? (int)strtol(max_val, NULL, 10) : 0;
if (val_i < min_i || val_i > max_i) {
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "key %.64s reject\n", param->key);
new_val = NULL;
} else {
switch (param->type) {
case ISPT_NUMERICAL_MIN:
if (val_i > cur_val_i) {
val_i = cur_val_i;
}
break;
case ISPT_NUMERICAL_MAX:
if (val_i < cur_val_i) {
val_i = cur_val_i;
}
break;
default:
break;
}
snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN, "%d", val_i);
new_val = in_val;
}
return new_val;
}
/**
* To negotiate param with
* type = ISPT_BOOLEAN_OR, ISPT_BOOLEAN_AND
* return: the negotiated value of the key
*/
static char *spdk_iscsi_negotiate_param_boolean(int *add_param_value,
struct iscsi_param *param,
char *in_val, char *cur_val,
const char *value)
{
char *new_val = NULL;
if (add_param_value == NULL) {
return NULL;
}
/* Make sure the val is Yes or No */
if (!((strcasecmp(in_val, "Yes") == 0) ||
(strcasecmp(in_val, "No") == 0))) {
/* unknown value */
snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", "Reject");
new_val = in_val;
*add_param_value = 1;
return new_val;
}
if (strcasecmp(cur_val, value) == 0) {
snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", value);
new_val = in_val;
} else {
new_val = param->val;
}
return new_val;
}
/**
* The entry function to handle each type of the param
* return value: the new negotiated value
*/
static char *
spdk_iscsi_negotiate_param_all(int *add_param_value, struct iscsi_param *param,
char *valid_list, char *in_val, char *cur_val)
{
char *new_val;
switch (param->type) {
case ISPT_LIST:
new_val = spdk_iscsi_negotiate_param_list(add_param_value,
param,
valid_list,
in_val,
cur_val);
break;
case ISPT_NUMERICAL_MIN:
case ISPT_NUMERICAL_MAX:
case ISPT_NUMERICAL_DECLARATIVE:
new_val = spdk_iscsi_negotiate_param_numerical(add_param_value,
param,
valid_list,
in_val,
cur_val);
break;
case ISPT_BOOLEAN_OR:
new_val = spdk_iscsi_negotiate_param_boolean(add_param_value,
param,
in_val,
cur_val,
"Yes");
break;
case ISPT_BOOLEAN_AND:
new_val = spdk_iscsi_negotiate_param_boolean(add_param_value,
param,
in_val,
cur_val,
"No");
break;
default:
snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", param->val);
new_val = in_val;
break;
}
return new_val;
}
/**
* This function is used to judge whether the param is in session's params or
* connection's params
*/
static int
spdk_iscsi_negotiate_param_init(struct spdk_iscsi_conn *conn,
struct iscsi_param **cur_param_p,
struct iscsi_param **params_dst_p,
struct iscsi_param *param)
{
int index;
*cur_param_p = spdk_iscsi_param_find(*params_dst_p, param->key);
if (*cur_param_p == NULL) {
*params_dst_p = conn->sess->params;
*cur_param_p = spdk_iscsi_param_find(*params_dst_p, param->key);
if (*cur_param_p == NULL) {
if ((strncasecmp(param->key, "X-", 2) == 0) ||
(strncasecmp(param->key, "X#", 2) == 0)) {
/* Extension Key */
SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
"extension key %.64s\n",
param->key);
} else {
SPDK_ERRLOG("unknown key %.64s\n", param->key);
}
return 1;
} else {
index = (*cur_param_p)->state_index;
if (conn->sess_param_state_negotiated[index] &&
!spdk_iscsi_find_key_in_array(param->key,
target_declarative_params)) {
return SPDK_ISCSI_PARAMETER_EXCHANGE_NOT_ONCE;
}
conn->sess_param_state_negotiated[index] = true;
}
} else {
index = (*cur_param_p)->state_index;
if (conn->conn_param_state_negotiated[index] &&
!spdk_iscsi_find_key_in_array(param->key,
multi_negot_conn_params)) {
return SPDK_ISCSI_PARAMETER_EXCHANGE_NOT_ONCE;
}
conn->conn_param_state_negotiated[index] = true;
}
return 0;
}
int
spdk_iscsi_negotiate_params(struct spdk_iscsi_conn *conn,
struct iscsi_param **params, uint8_t *data, int alloc_len,
int data_len)
{
struct iscsi_param *param;
struct iscsi_param *cur_param;
char *valid_list, *in_val;
char *cur_val;
char *new_val;
int discovery;
int total;
int rc;
uint32_t FirstBurstLength;
uint32_t MaxBurstLength;
bool FirstBurstLength_flag = false;
int type;
total = data_len;
if (data_len < 0) {
assert(false);
return -EINVAL;
}
if (alloc_len < 1) {
return 0;
}
if (total > alloc_len) {
total = alloc_len;
data[total - 1] = '\0';
return total;
}
if (*params == NULL) {
/* no input */
return total;
}
/* discovery? */
discovery = 0;
cur_param = spdk_iscsi_param_find(*params, "SessionType");
if (cur_param == NULL) {
cur_param = spdk_iscsi_param_find(conn->sess->params, "SessionType");
if (cur_param == NULL) {
/* no session type */
} else {
if (strcasecmp(cur_param->val, "Discovery") == 0) {
discovery = 1;
}
}
} else {
if (strcasecmp(cur_param->val, "Discovery") == 0) {
discovery = 1;
}
}
/* for temporary store */
valid_list = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
if (!valid_list) {
SPDK_ERRLOG("malloc() failed for valid_list\n");
return -ENOMEM;
}
in_val = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
if (!in_val) {
SPDK_ERRLOG("malloc() failed for in_val\n");
free(valid_list);
return -ENOMEM;
}
cur_val = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
if (!cur_val) {
SPDK_ERRLOG("malloc() failed for cur_val\n");
free(valid_list);
free(in_val);
return -ENOMEM;
}
/* To adjust the location of FirstBurstLength location and put it to
* the end, then we can always firstly determine the MaxBurstLength
*/
param = spdk_iscsi_param_find(*params, "MaxBurstLength");
if (param != NULL) {
param = spdk_iscsi_param_find(*params, "FirstBurstLength");
/* check the existence of FirstBurstLength */
if (param != NULL) {
FirstBurstLength_flag = true;
if (param->next != NULL) {
snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", param->val);
type = param->type;
spdk_iscsi_param_add(params, "FirstBurstLength",
in_val, NULL, type);
}
}
}
for (param = *params; param != NULL; param = param->next) {
struct iscsi_param *params_dst = conn->params;
int add_param_value = 0;
new_val = NULL;
param->type = ISPT_INVALID;
/* sendtargets is special */
if (strcasecmp(param->key, "SendTargets") == 0) {
continue;
}
/* CHAP keys */
if (spdk_iscsi_find_key_in_array(param->key, chap_type)) {
continue;
}
/* 12.2, 12.10, 12.11, 12.13, 12.14, 12.17, 12.18, 12.19 */
if (discovery &&
spdk_iscsi_find_key_in_array(param->key,
discovery_ignored_param)) {
snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", "Irrelevant");
new_val = in_val;
add_param_value = 1;
} else {
rc = spdk_iscsi_negotiate_param_init(conn,
&cur_param,
&params_dst,
param);
if (rc < 0) {
free(valid_list);
free(in_val);
free(cur_val);
return rc;
} else if (rc > 0) {
snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", "NotUnderstood");
new_val = in_val;
add_param_value = 1;
} else {
snprintf(valid_list, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", cur_param->list);
snprintf(cur_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", cur_param->val);
param->type = cur_param->type;
}
}
if (param->type > 0) {
snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", param->val);
/* "NotUnderstood" value shouldn't be assigned to "Understood" key */
if (strcasecmp(in_val, "NotUnderstood") == 0) {
free(in_val);
free(valid_list);
free(cur_val);
return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
}
if (strcasecmp(param->key, "FirstBurstLength") == 0) {
FirstBurstLength = (uint32_t)strtol(param->val, NULL,
10);
new_val = spdk_iscsi_param_get_val(conn->sess->params,
"MaxBurstLength");
if (new_val != NULL) {
MaxBurstLength = (uint32_t) strtol(new_val, NULL,
10);
} else {
MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH;
}
if (FirstBurstLength < MAX_FIRSTBURSTLENGTH &&
FirstBurstLength > MaxBurstLength) {
FirstBurstLength = MaxBurstLength;
snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN, "%d",
FirstBurstLength);
}
}
/* prevent target's declarative params from being changed by initiator */
if (spdk_iscsi_find_key_in_array(param->key, target_declarative_params)) {
add_param_value = 1;
}
new_val = spdk_iscsi_negotiate_param_all(&add_param_value,
param,
valid_list,
in_val,
cur_val);
}
/* check the negotiated value of the key */
if (new_val != NULL) {
/* add_param_value = 0 means updating the value of
* existed key in the connection's parameters
*/
if (add_param_value == 0) {
spdk_iscsi_param_set(params_dst, param->key, new_val);
}
total = spdk_iscsi_construct_data_from_param(param,
new_val,
data,
alloc_len,
total);
if (total < 0) {
goto final_return;
}
total = spdk_iscsi_special_param_construction(conn,
param,
FirstBurstLength_flag,
data,
alloc_len,
total);
if (total < 0) {
goto final_return;
}
} else {
total = -1;
break;
}
}
final_return:
free(valid_list);
free(in_val);
free(cur_val);
return total;
}
int
spdk_iscsi_copy_param2var(struct spdk_iscsi_conn *conn)
{
const char *val;
val = spdk_iscsi_param_get_val(conn->params, "MaxRecvDataSegmentLength");
if (val == NULL) {
SPDK_ERRLOG("Getval MaxRecvDataSegmentLength failed\n");
return -1;
}
SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
"copy MaxRecvDataSegmentLength=%s\n", val);
conn->MaxRecvDataSegmentLength = (int)strtol(val, NULL, 10);
if (conn->MaxRecvDataSegmentLength > SPDK_ISCSI_MAX_SEND_DATA_SEGMENT_LENGTH) {
conn->MaxRecvDataSegmentLength = SPDK_ISCSI_MAX_SEND_DATA_SEGMENT_LENGTH;
}
val = spdk_iscsi_param_get_val(conn->params, "HeaderDigest");
if (val == NULL) {
SPDK_ERRLOG("Getval HeaderDigest failed\n");
return -1;
}
if (strcasecmp(val, "CRC32C") == 0) {
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set HeaderDigest=1\n");
conn->header_digest = 1;
} else {
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set HeaderDigest=0\n");
conn->header_digest = 0;
}
val = spdk_iscsi_param_get_val(conn->params, "DataDigest");
if (val == NULL) {
SPDK_ERRLOG("Getval DataDigest failed\n");
return -1;
}
if (strcasecmp(val, "CRC32C") == 0) {
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set DataDigest=1\n");
conn->data_digest = 1;
} else {
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set DataDigest=0\n");
conn->data_digest = 0;
}
val = spdk_iscsi_param_get_val(conn->sess->params, "MaxConnections");
if (val == NULL) {
SPDK_ERRLOG("Getval MaxConnections failed\n");
return -1;
}
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "copy MaxConnections=%s\n", val);
conn->sess->MaxConnections = (uint32_t) strtol(val, NULL, 10);
val = spdk_iscsi_param_get_val(conn->sess->params, "MaxOutstandingR2T");
if (val == NULL) {
SPDK_ERRLOG("Getval MaxOutstandingR2T failed\n");
return -1;
}
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "copy MaxOutstandingR2T=%s\n", val);
conn->sess->MaxOutstandingR2T = (uint32_t) strtol(val, NULL, 10);
val = spdk_iscsi_param_get_val(conn->sess->params, "FirstBurstLength");
if (val == NULL) {
SPDK_ERRLOG("Getval FirstBurstLength failed\n");
return -1;
}
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "copy FirstBurstLength=%s\n", val);
conn->sess->FirstBurstLength = (uint32_t) strtol(val, NULL, 10);
val = spdk_iscsi_param_get_val(conn->sess->params, "MaxBurstLength");
if (val == NULL) {
SPDK_ERRLOG("Getval MaxBurstLength failed\n");
return -1;
}
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "copy MaxBurstLength=%s\n", val);
conn->sess->MaxBurstLength = (uint32_t) strtol(val, NULL, 10);
val = spdk_iscsi_param_get_val(conn->sess->params, "InitialR2T");
if (val == NULL) {
SPDK_ERRLOG("Getval InitialR2T failed\n");
return -1;
}
if (strcasecmp(val, "Yes") == 0) {
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set InitialR2T=1\n");
conn->sess->InitialR2T = true;
} else {
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set InitialR2T=0\n");
conn->sess->InitialR2T = false;
}
val = spdk_iscsi_param_get_val(conn->sess->params, "ImmediateData");
if (val == NULL) {
SPDK_ERRLOG("Getval ImmediateData failed\n");
return -1;
}
if (strcasecmp(val, "Yes") == 0) {
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set ImmediateData=1\n");
conn->sess->ImmediateData = true;
} else {
SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set ImmediateData=0\n");
conn->sess->ImmediateData = false;
}
return 0;
}