/*-
* Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
#define MAX_STRING 255 /* Maximum length of parameter value */
#define MAX_LIST 4 /* Maximum number of list elements we'll ever send */
/* Maximum number of negotiation parameters in the operational negotiation phase */
/* 48 should be more than enough even with the target defining its own keys */
#define MAX_NEG 48
#define CHAP_CHALLENGE_LEN 32 /* Number of bytes to send in challenge */
#define CHAP_MD5_SIZE 16 /* Number of bytes in MD5 hash */
/* a negotiation parameter: key and values (there may be more than 1 for lists) */
typedef struct
{
text_key_t key; /* the key */
int list_num; /* number of elements in list, doubles as */
/* data size for large numeric values */
bool hex_bignums; /* whether to encode in hex or base64 */
union
{
uint32_t nval[MAX_LIST];/* numeric or enumeration values */
uint8_t *sval; /* string or data pointer */
} val;
} negotiation_parameter_t;
/* Negotiation state flags */
#define NS_SENT 0x01 /* key was sent to target */
#define NS_RECEIVED 0x02 /* key was received from target */
typedef struct
{
negotiation_parameter_t pars[MAX_NEG]; /* the parameters to send */
negotiation_parameter_t *cpar; /* the last parameter set */
uint16_t num_pars; /* number of parameters to send */
auth_state_t auth_state; /* authentication state */
iscsi_auth_types_t auth_alg; /* authentication algorithm */
uint8_t kflags[MAX_KEY + 2]; /* negotiation flags for each key */
uint8_t password[MAX_STRING + 1]; /* authentication secret */
uint8_t target_password[MAX_STRING + 1]; /* target authentication secret */
uint8_t user_name[MAX_STRING + 1]; /* authentication user ID */
uint8_t temp_buf[MAX_STRING + 1]; /* scratch buffer */
/*
* hexdig:
* Return value of hex digit.
* Note: a null character is acceptable, and returns 0.
*
* Parameter:
* c The character
*
* Returns: The value, -1 on error.
*/
static __inline int
hexdig(uint8_t c)
{
if (!c) {
return 0;
}
if (isdigit(c)) {
return c - '0';
}
c = toupper(c);
if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
}
return -1;
}
/*
* skiptozero:
* Skip to next zero character in buffer.
*
* Parameter:
* buf The buffer pointer
*
* Returns: The pointer to the character after the zero character.
*/
/*
* get_bignumval:
* Get a large numeric value.
* NOTE: Overwrites source string.
*
* Parameter:
* buf The buffer pointer
* par The parameter
*
* Returns: The pointer to the next parameter, NULL on error.
*/
/*
* get_numval:
* Get a numeric value.
*
* Parameter:
* buf The buffer pointer
* pval The pointer to the result.
* sep Separator to next value.
*
* Returns: The pointer to the next parameter, NULL on error.
*/
if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) {
buf += 2;
while (*buf && *buf != '~') {
int n;
if ((n = hexdig(*buf++)) < 0)
return NULL;
val = (val << 4) | n;
}
} else
while (*buf && *buf != '~') {
c = *buf++;
if (!isdigit(c))
return NULL;
val = val * 10 + (c - '0');
}
*pval = val;
return buf + 1;
}
/*
* get_range:
* Get a numeric range.
*
* Parameter:
* buf The buffer pointer
* pval1 The pointer to the first result.
* pval2 The pointer to the second result.
*
* Returns: The pointer to the next parameter, NULL on error.
*/
if ((buf = get_numval(buf, pval1, '~')) == NULL)
return NULL;
if (!*buf)
return NULL;
if ((buf = get_numval(buf, pval2, '~')) == NULL)
return NULL;
return buf;
}
/*
* get_ynval:
* Get a yes/no selection.
*
* Parameter:
* buf The buffer pointer
* pval The pointer to the result.
*
* Returns: The pointer to the next parameter, NULL on error.
*/
if (strcmp(buf, "Yes") == 0)
*pval = 1;
else if (strcmp(buf, "No") == 0)
*pval = 0;
else
return NULL;
return skiptozero(buf);
}
/*
* get_digestval:
* Get a digest selection.
*
* Parameter:
* buf The buffer pointer
* pval The pointer to the result.
*
* Returns: The pointer to the next parameter, NULL on error.
*/
if (strcmp(buf, "CRC32C") == 0)
*pval = 1;
else if (strcmp(buf, "None") == 0)
*pval = 0;
else
return NULL;
return skiptozero(buf);
}
/*
* get_authval:
* Get an authentication method.
*
* Parameter:
* buf The buffer pointer
* pval The pointer to the result.
*
* Returns: The pointer to the next parameter, NULL on error.
*/
if (strcmp(buf, "None") == 0)
*pval = ISCSI_AUTH_None;
else if (strcmp(buf, "CHAP") == 0)
*pval = ISCSI_AUTH_CHAP;
else if (strcmp(buf, "KRB5") == 0)
*pval = ISCSI_AUTH_KRB5;
else if (strcmp(buf, "SRP") == 0)
*pval = ISCSI_AUTH_SRP;
else
return NULL;
return skiptozero(buf);
}
/*
* get_strval:
* Get a string value (returns pointer to original buffer, not a copy).
*
* Parameter:
* buf The buffer pointer
* pval The pointer to the result pointer.
*
* Returns: The pointer to the next parameter, NULL on error.
*/
/*
* get_parameter:
* Analyze a key=value string.
* NOTE: The string is modified in the process.
*
* Parameter:
* buf The buffer pointer
* par The parameter descriptor to be filled in
*
* Returns: The pointer to the next parameter, NULL on error.
*/
/*
* my_strcpy:
* Replacement for strcpy that returns the end of the result string
*
* Parameter:
* dest The destination buffer pointer
* src The source string
*
* Returns: A pointer to the terminating zero of the result.
*/
/*
* put_bignumval:
* Write a large numeric value.
* NOTE: Overwrites source string.
*
* Parameter:
* buf The buffer pointer
* par The parameter
*
* Returns: The pointer to the next parameter, NULL on error.
*/
STATIC unsigned
put_bignumval(negotiation_parameter_t *par, uint8_t *buf)
{
int k, c;
if (par->hex_bignums) {
my_strcpy(buf, "0x");
for (k=0; k<par->list_num; ++k) {
c = par->val.sval[k] >> 4;
buf[2+2*k] = c < 10 ? '0' + c : 'a' + (c-10);
c = par->val.sval[k] & 0xf;
buf[2+2*k+1] = c < 10 ? '0' + c : 'a' + (c-10);
}
buf[2+2*k] = '\0';
/*
* put_parameter:
* Create a key=value string.
*
* Parameter:
* buf The buffer pointer
* par The parameter descriptor
*
* Returns: The pointer to the next free buffer space, NULL on error.
*/
default:
cl = 0;
/* We shouldn't be here... */
DEBOUT(("Invalid type %d in put_parameter!\n",
entries[par->key].val));
break;
}
DEB(10, ("put_par: value '%s'\n",&buf[cc]));
cc += cl;
if (cc >= len)
return len;
if ((i + 1) < par->list_num) {
if (cc >= len)
return len;
buf[cc++] = ',';
}
}
if (cc >= len)
return len;
buf[cc] = 0x0; /* make sure it's terminated */
return cc + 1; /* return next place in list */
}
/*
* put_par_block:
* Fill a parameter block
*
* Parameter:
* buf The buffer pointer
* pars The parameter descriptor array
* n The number of elements
*
* Returns: result from put_parameter (ptr to buffer, NULL on error)
*/
static __inline unsigned
put_par_block(uint8_t *buf, unsigned len, negotiation_parameter_t *pars, int n)
{
unsigned cc;
int i;
for (cc = 0, i = 0; i < n; i++) {
cc += put_parameter(&buf[cc], len - cc, pars++);
if (cc >= len) {
break;
}
}
return cc;
}
/*
* parameter_size:
* Determine the size of a key=value string.
*
* Parameter:
* par The parameter descriptor
*
* Returns: The size of the resulting string.
*/
STATIC int
parameter_size(negotiation_parameter_t *par)
{
int i, size;
char buf[24]; /* max. 2 10-digit numbers + sep. */
if (par->key > MAX_KEY) {
return strlen(par->val.sval) + 15;
}
/* count '=' and terminal zero */
size = strlen(entries[par->key].name) + 2;
for (i = 0; i < par->list_num; i++) {
switch (entries[par->key].val) {
case T_NUM:
size += snprintf(buf, sizeof(buf), "%d",
par->val.nval[i]);
break;
case T_BIGNUM:
/* list_num holds value size */
if (par->hex_bignums)
size += 2 + 2*par->list_num;
else
size += base64_enclen(par->list_num);
i = par->list_num;
break;
case T_STRING:
case T_SENDT:
size += strlen(par->val.sval);
break;
case T_YESNO:
size += (par->val.nval[i]) ? 3 : 2;
break;
case T_DIGEST:
size += (par->val.nval[i]) ? 6 : 4;
break;
case T_RANGE:
if (i+1 < par->list_num) {
size += snprintf(buf, sizeof(buf), "%d~%d",
par->val.nval[i],
par->val.nval[i + 1]);
i++;
} else
DEBOUT(("Incomplete range parameter\n"));
break;
case T_SESS:
size += (par->val.nval[i]) ? 6 : 9;
break;
default:
/* We shouldn't be here... */
DEBOUT(("Invalid type %d in parameter_size!\n",
entries[par->key].val));
break;
}
if ((i + 1) < par->list_num) {
size++;
}
}
return size;
}
/*
* total_size:
* Determine the size of a negotiation data block
*
* Parameter:
* pars The parameter descriptor array
* n The number of elements
*
* Returns: The size of the block
*/
static __inline int
total_size(negotiation_parameter_t *pars, int n)
{
int i, size;
for (i = 0, size = 0; i < n; i++) {
size += parameter_size(pars++);
}
return size;
}
/*
* complete_pars:
* Allocate space for text parameters, translate parameter values into
* text.
*
* Parameter:
* state Negotiation state
* pdu The transmit PDU
*
* Returns: 0 On success
* > 0 (an ISCSI error code) if an error occurred.
*/
STATIC int
complete_pars(negotiation_state_t *state, pdu_t *pdu)
{
int len;
uint8_t *bp;
/*
* eval_parameter:
* Evaluate a received negotiation value.
*
* Parameter:
* conn The connection
* state The negotiation state
* par The parameter
*
* Returns: 0 on success, else an ISCSI status value.
*/
STATIC int
eval_parameter(connection_t *conn, negotiation_state_t *state,
negotiation_parameter_t *par)
{
uint32_t n = par->val.nval[0];
size_t sz;
text_key_t key = par->key;
bool sent = (state->kflags[key] & NS_SENT) != 0;
state->kflags[key] |= NS_RECEIVED;
switch (key) {
/*
* keys connected to security negotiation
*/
case K_AuthMethod:
if (n) {
DEBOUT(("eval_par: AuthMethod nonzero (%d)\n", n));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
break;
case K_Auth_CHAP_Algorithm:
case K_Auth_CHAP_Challenge:
case K_Auth_CHAP_Identifier:
case K_Auth_CHAP_Name:
case K_Auth_CHAP_Response:
DEBOUT(("eval_par: Authorization Key in Operational Phase\n"));
return ISCSI_STATUS_NEGOTIATION_ERROR;
/*
* keys we always send
*/
case K_DataDigest:
state->DataDigest = n;
if (!sent)
set_key_n(state, key, n);
break;
case K_HeaderDigest:
state->HeaderDigest = n;
if (!sent)
set_key_n(state, key, n);
break;
case K_ErrorRecoveryLevel:
state->ErrorRecoveryLevel = n;
if (!sent)
set_key_n(state, key, n);
break;
case K_ImmediateData:
state->ImmediateData = n;
if (!sent)
set_key_n(state, key, n);
break;
case K_InitialR2T:
state->InitialR2T = n;
if (!sent)
set_key_n(state, key, n);
break;
case K_MaxRecvDataSegmentLength:
state->MaxRecvDataSegmentLength = n;
/* this is basically declarative, not negotiated */
/* (each side has its own value) */
break;
/*
* keys we don't always send, so we may have to reflect the value
*/
case K_DefaultTime2Retain:
state->DefaultTime2Retain = n = min(state->DefaultTime2Retain, n);
if (!sent)
set_key_n(state, key, n);
break;
case K_DefaultTime2Wait:
state->DefaultTime2Wait = n = min(state->DefaultTime2Wait, n);
if (!sent)
set_key_n(state, key, n);
break;
case K_MaxConnections:
if (state->MaxConnections)
state->MaxConnections = n = min(state->MaxConnections, n);
else
state->MaxConnections = n;
if (!sent)
set_key_n(state, key, n);
break;
case K_MaxOutstandingR2T:
state->MaxOutstandingR2T = n;
if (!sent)
set_key_n(state, key, n);
break;
case K_FirstBurstLength:
state->FirstBurstLength = n;
if (!sent)
set_key_n(state, key, n);
break;
case K_MaxBurstLength:
state->MaxBurstLength = n;
if (!sent)
set_key_n(state, key, n);
break;
case K_IFMarker:
case K_OFMarker:
/* not (yet) supported */
if (!sent)
set_key_n(state, key, 0);
break;
case K_IFMarkInt:
case K_OFMarkInt:
/* it's a range, and list_num will be 1, so this will reply "Reject" */
if (!sent)
set_key_n(state, key, 0);
break;
case K_DataPDUInOrder:
case K_DataSequenceInOrder:
/* values are don't care */
if (!sent)
set_key_n(state, key, n);
break;
case K_NotUnderstood:
/* return "NotUnderstood" */
set_key_s(state, key, par->val.sval);
break;
/*
* Declarative keys (no response required)
*/
case K_TargetAddress:
/* ignore for now... */
break;
case K_TargetAlias:
if (conn->c_login_par->is_present.TargetAlias) {
copyoutstr(par->val.sval, conn->c_login_par->TargetAlias,
ISCSI_STRING_LENGTH - 1, &sz);
/* do anything with return code?? */
}
break;
case K_TargetPortalGroupTag:
/* ignore for now... */
break;
/*
* init_session_parameters:
* Initialize session-related negotiation parameters from existing session
*
* Parameter:
* sess The session
* state The negotiation state
*/
/*
* assemble_login_parameters:
* Assemble the initial login negotiation parameters.
*
* Parameter:
* conn The connection
* ccb The CCB for the login exchange
* pdu The PDU to use for sending
*
* Returns: < 0 if more security negotiation is required
* 0 if this is the last security negotiation block
* > 0 (an ISCSI error code) if an error occurred.
*/
int
assemble_login_parameters(connection_t *conn, ccb_t *ccb, pdu_t *pdu)
{
iscsi_login_parameters_t *par = conn->c_login_par;
size_t sz;
int rc, i, next;
negotiation_state_t *state;
negotiation_parameter_t *cpar;
state = malloc(sizeof(*state), M_TEMP, M_WAITOK | M_ZERO);
if (state == NULL) {
DEBOUT(("*** Out of memory in assemble_login_params\n"));
return ISCSI_STATUS_NO_RESOURCES;
}
ccb->ccb_temp_data = state;
if (cpar != NULL && par->is_present.auth_info &&
par->auth_info.auth_number > 0) {
if (par->auth_info.auth_number > ISCSI_AUTH_OPTIONS) {
DEBOUT(("Auth number too big in asm_login\n"));
return ISCSI_STATUS_PARAMETER_INVALID;
}
cpar->list_num = par->auth_info.auth_number;
for (i = 0; i < cpar->list_num; i++) {
cpar->val.nval[i] = par->auth_info.auth_type[i];
if (par->auth_info.auth_type[i])
next = FALSE;
}
}
if (state->temp_buf[0]) {
set_key_s(state, K_TargetName, state->temp_buf);
}
if ((rc = complete_pars(state, pdu)) != 0)
return rc;
return (next) ? 0 : -1;
}
/*
* assemble_security_parameters:
* Assemble the security negotiation parameters.
*
* Parameter:
* conn The connection
* rx_pdu The received login response PDU
* tx_pdu The transmit PDU
*
* Returns: < 0 if more security negotiation is required
* 0 if this is the last security negotiation block
* > 0 (an ISCSI error code) if an error occurred.
*/
rxpars = (uint8_t *) rx_pdu->pdu_temp_data;
if (rxpars == NULL) {
DEBOUT(("No received parameters!\n"));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
/* Note: There are always at least 2 extra bytes past temp_data_len */
rxpars[rx_pdu->pdu_temp_data_len] = '\0';
rxpars[rx_pdu->pdu_temp_data_len + 1] = '\0';
while (*rxpars) {
if ((rxpars = get_parameter(rxpars, &rxp)) == NULL) {
DEBOUT(("get_parameter returned error\n"));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
state->kflags[rxp.key] |= NS_RECEIVED;
switch (rxp.key) {
case K_AuthMethod:
if (state->auth_state != AUTH_INITIAL) {
DEBOUT(("AuthMethod received, auth_state = %d\n",
state->auth_state));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
/* Note: if the selection is None, we shouldn't be here,
* the target should have transited the state to op-neg.
*/
if (rxp.val.nval[0] != ISCSI_AUTH_CHAP) {
DEBOUT(("AuthMethod isn't CHAP (%d)\n", rxp.val.nval[0]));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
case K_Auth_CHAP_Challenge:
if (state->auth_state != AUTH_CHAP_ALG_SENT || !rxp.list_num) {
DEBOUT(("Bad Challenge, auth_state = %d, len %d\n",
state->auth_state, rxp.list_num));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
challenge = rxp.val.sval;
challenge_size = rxp.list_num;
/* respond in the same format as the challenge */
challenge_hex = rxp.hex_bignums;
break;
case K_Auth_CHAP_Identifier:
if (state->auth_state != AUTH_CHAP_ALG_SENT) {
DEBOUT(("Bad ID, auth_state = %d, id %d\n",
state->auth_state, rxp.val.nval[0]));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
identifier = (uint8_t) rxp.val.nval[0];
break;
case K_Auth_CHAP_Name:
if (state->auth_state != AUTH_CHAP_RSP_SENT) {
DEBOUT(("Bad Name, auth_state = %d, name <%s>\n",
state->auth_state, rxp.val.sval));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
/* what do we do with the name?? */
break;
case K_Auth_CHAP_Response:
if (state->auth_state != AUTH_CHAP_RSP_SENT) {
DEBOUT(("Bad Response, auth_state = %d, size %d\n",
state->auth_state, rxp.list_num));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
response = rxp.val.sval;
response_size = rxp.list_num;
if (response_size != CHAP_MD5_SIZE) {
DEBOUT(("CHAP Response, bad size %d\n",
response_size));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
break;
cpar = set_key_s(state, K_Auth_CHAP_Response, state->temp_buf);
if (cpar != NULL) {
cpar->list_num = CHAP_MD5_SIZE;
/* respond in same format as challenge */
cpar->hex_bignums = challenge_hex;
}
if (par->auth_info.mutual_auth) {
if (!state->target_password[0]) {
DEBOUT(("No target password with mutual authentication!\n"));
return ISCSI_STATUS_PARAMETER_MISSING;
}
cprng_strong(kern_cprng,
&state->temp_buf[CHAP_MD5_SIZE],
CHAP_CHALLENGE_LEN + 1, 0);
set_key_n(state, K_Auth_CHAP_Identifier,
state->temp_buf[CHAP_MD5_SIZE]);
cpar = set_key_s(state, K_Auth_CHAP_Challenge,
&state->temp_buf[CHAP_MD5_SIZE + 1]);
if (cpar != NULL) {
cpar->list_num = CHAP_CHALLENGE_LEN;
/* use same format as target challenge */
cpar->hex_bignums = challenge_hex;
}
case AUTH_CHAP_RSP_SENT:
/* we can only be here for mutual authentication */
if (!par->auth_info.mutual_auth || response == NULL) {
DEBOUT(("Mutual authentication not requested\n"));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
/*
* set_first_opnegs:
* Set the operational negotiation parameters we want to negotiate in
* the first login request in op_neg phase.
*
* Parameter:
* conn The connection
* state Negotiation state
*/
set_key_n(state, K_MaxRecvDataSegmentLength,
conn->c_Our_MaxRecvDataSegmentLength);
/* This is direction-specific, we may have a different default */
state->MaxRecvDataSegmentLength =
entries[K_MaxRecvDataSegmentLength].defval;
/* First connection only */
if (!conn->c_session->s_TSIH) {
state->ErrorRecoveryLevel =
(lpar->is_present.ErrorRecoveryLevel) ?
lpar->ErrorRecoveryLevel : 2;
/*
* Negotiate InitialR2T to FALSE and ImmediateData to
* TRUE, should be slightly more efficient than the
* default InitialR2T=TRUE.
*/
state->InitialR2T = FALSE;
state->ImmediateData = TRUE;
/* We don't really care about this, so don't negotiate
* by default
*/
state->MaxBurstLength = entries[K_MaxBurstLength].defval;
state->FirstBurstLength = entries[K_FirstBurstLength].defval;
state->MaxOutstandingR2T = entries[K_MaxOutstandingR2T].defval;
/*
* assemble_negotiation_parameters:
* Assemble any negotiation parameters requested by the other side.
*
* Parameter:
* conn The connection
* ccb The login ccb
* rx_pdu The received login response PDU
* tx_pdu The transmit PDU
*
* Returns: 0 On success
* > 0 (an ISCSI error code) if an error occurred.
*/
rxpars = (uint8_t *) rx_pdu->pdu_temp_data;
if (rxpars != NULL) {
/* Note: There are always at least 2 extra bytes past temp_data_len */
rxpars[rx_pdu->pdu_temp_data_len] = '\0';
rxpars[rx_pdu->pdu_temp_data_len + 1] = '\0';
while (*rxpars) {
if ((rxpars = get_parameter(rxpars, &rxp)) == NULL)
return ISCSI_STATUS_NEGOTIATION_ERROR;
/*
* init_text_parameters:
* Initialize text negotiation.
*
* Parameter:
* conn The connection
* tx_pdu The transmit PDU
*
* Returns: 0 On success
* > 0 (an ISCSI error code) if an error occurred.
*/
int
init_text_parameters(connection_t *conn, ccb_t *ccb)
{
negotiation_state_t *state;
state = malloc(sizeof(*state), M_TEMP, M_WAITOK | M_ZERO);
if (state == NULL) {
DEBOUT(("*** Out of memory in init_text_params\n"));
return ISCSI_STATUS_NO_RESOURCES;
}
ccb->ccb_temp_data = state;
if ((buf = malloc(len, M_TEMP, M_WAITOK)) == NULL) {
DEBOUT(("*** Out of memory in assemble_send_targets\n"));
return ISCSI_STATUS_NO_RESOURCES;
}
pdu->pdu_temp_data = buf;
pdu->pdu_temp_data_len = len;
if (put_parameter(buf, len, &par) == 0) {
DEBOUT(("trying to put zero sized buffer\n"));
return ISCSI_STATUS_PARAMETER_INVALID;
}
return 0;
}
/*
* set_negotiated_parameters:
* Copy the negotiated parameters into the connection and session structure.
*
* Parameter:
* ccb The ccb containing the state information
*/