numam-spdk/lib/iscsi/param.c
Shuhei Matsumoto 893e02a54f iscsi: Remove the prefix spdk_ from names of private functions
iSCSI library had used the prefix spdk_ for most functions regardless
of private or public. Using the prefix spdk_ only for public functions
will be helpful to distinguish private and public functions, and
will be helpful to investigate issues or do further improvement.

Besides in iscsi.c static variable spdk_arc4random_initialized had
the prefix spdk_, and change it to g_arc4random_initialized according
to the SPDK's good practice.

iSCSI library still have some issues but is more stable than before
and now will be the good time to adjust the naming rule to other
libraries.

This patch doesn't change any behavior.

Change-Id: Ia0b8585a7ce6662cabc0e6f57b7ccb8a40342297
Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/449396
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
2019-04-01 00:52:56 +00:00

1216 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
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
iscsi_parse_param(struct iscsi_param **params, const uint8_t *data, uint32_t data_len)
{
int rc;
uint8_t *key_copy, *val_copy;
const uint8_t *key_end;
int key_len, val_len;
int max_len;
data_len = strnlen(data, data_len);
/* No such thing as strnchr so use memchr instead. */
key_end = memchr(data, '=', data_len);
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_len = strnlen(key_end + 1, data_len - key_len - 1);
/*
* 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 = 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;
}
val_copy = calloc(1, val_len + 1);
if (val_copy == NULL) {
SPDK_ERRLOG("Could not allocate value string\n");
free(key_copy);
return -1;
}
memcpy(val_copy, key_end + 1, val_len);
rc = spdk_iscsi_param_add(params, key_copy, val_copy, NULL, 0);
free(val_copy);
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 = iscsi_parse_param(params, p, i + strlen(*partial_parameter));
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'
*/
for (i = len - 1; data[i] != '\0' && i > 0; i--) {
;
}
if (i != 0) {
/* We found a NULL character - don't copy it into the
* partial parameter.
*/
i++;
}
*partial_parameter = calloc(1, len - i + 1);
if (*partial_parameter == NULL) {
SPDK_ERRLOG("could not allocate partial parameter\n");
return -1;
}
memcpy(*partial_parameter, &data[i], len - i);
if (i == 0) {
/* No full parameters to parse - so return now. */
return 0;
} else {
len = i - 1;
}
}
while (offset < len && data[offset] != '\0') {
rc = iscsi_parse_param(params, data + offset, len - 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
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 iscsi_params_init_internal(params, &conn_param_table[0]);
}
int
spdk_iscsi_sess_params_init(struct iscsi_param **params)
{
return 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
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;
}
/**
* 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
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 *
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 *
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 *
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 *
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 = 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 = iscsi_negotiate_param_numerical(add_param_value,
param,
valid_list,
in_val,
cur_val);
break;
case ISPT_BOOLEAN_OR:
new_val = iscsi_negotiate_param_boolean(add_param_value,
param,
in_val,
cur_val,
"Yes");
break;
case ISPT_BOOLEAN_AND:
new_val = 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
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] &&
!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] &&
!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 (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 &&
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 = 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 < SPDK_ISCSI_MAX_FIRST_BURST_LENGTH &&
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 (iscsi_find_key_in_array(param->key, target_declarative_params)) {
add_param_value = 1;
}
new_val = 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 = iscsi_construct_data_from_param(param,
new_val,
data,
alloc_len,
total);
if (total < 0) {
goto final_return;
}
total = 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_BDEV_LARGE_BUF_MAX_SIZE) {
conn->MaxRecvDataSegmentLength = SPDK_BDEV_LARGE_BUF_MAX_SIZE;
}
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;
}