Home | History | Annotate | Download | only in iscsi
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2000 by Cisco Systems, Inc.  All rights reserved.
     23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  *
     26  * iSCSI protocol login and enumeration
     27  */
     28 
     29 #include "iscsi.h"
     30 #include <sys/iscsi_protocol.h>
     31 #include <sys/scsi/adapters/iscsi_door.h>
     32 
     33 boolean_t iscsi_login_logging = B_FALSE;
     34 
     35 /* internal login protocol interfaces */
     36 static iscsi_status_t iscsi_login(iscsi_conn_t *icp,
     37     uint8_t *status_class, uint8_t *status_detail);
     38 static int iscsi_add_text(idm_pdu_t *text_pdu,
     39     int max_data_length, char *param, char *value);
     40 static int iscsi_find_key_value(char *param, char *ihp, char *pdu_end,
     41     char **value_start, char **value_end);
     42 static void iscsi_null_callback(void *user_handle, void *message_handle,
     43     int auth_status);
     44 static iscsi_status_t iscsi_process_login_response(iscsi_conn_t *icp,
     45     iscsi_login_rsp_hdr_t *ilrhp, char *data, int max_data_length);
     46 static iscsi_status_t iscsi_make_login_pdu(iscsi_conn_t *icp,
     47     idm_pdu_t *text_pdu, char *data, int max_data_length);
     48 static iscsi_status_t iscsi_update_address(iscsi_conn_t *icp,
     49     char *address);
     50 static char *iscsi_login_failure_str(uchar_t status_class,
     51     uchar_t status_detail);
     52 static void iscsi_login_end(iscsi_conn_t *icp,
     53     iscsi_status_t status, iscsi_task_t *itp);
     54 static iscsi_status_t iscsi_login_connect(iscsi_conn_t *icp);
     55 static void iscsi_login_disconnect(iscsi_conn_t *icp);
     56 static void iscsi_notice_key_values(iscsi_conn_t *icp);
     57 
     58 #define	ISCSI_LOGIN_RETRY_DELAY		5	/* seconds */
     59 
     60 #define	ISCSI_LOGIN_TRANSIT_FFP(flags) \
     61 	(!(flags & ISCSI_FLAG_LOGIN_CONTINUE) && \
     62 	(flags & ISCSI_FLAG_LOGIN_TRANSIT) && \
     63 	(ISCSI_LOGIN_CURRENT_STAGE(flags) == \
     64 	ISCSI_OP_PARMS_NEGOTIATION_STAGE) && \
     65 	(ISCSI_LOGIN_NEXT_STAGE(flags) == \
     66 	ISCSI_FULL_FEATURE_PHASE))
     67 
     68 /*
     69  * +--------------------------------------------------------------------+
     70  * | External Login Interface						|
     71  * +--------------------------------------------------------------------+
     72  */
     73 
     74 /*
     75  * iscsi_login_start - connect and perform iscsi protocol login
     76  */
     77 iscsi_status_t
     78 iscsi_login_start(void *arg)
     79 {
     80 	iscsi_task_t		*itp = (iscsi_task_t *)arg;
     81 	iscsi_status_t		rval	= ISCSI_STATUS_LOGIN_FAILED;
     82 	iscsi_conn_t		*icp;
     83 	iscsi_sess_t		*isp;
     84 	iscsi_hba_t		*ihp;
     85 	unsigned char		status_class;
     86 	unsigned char		status_detail;
     87 
     88 	ASSERT(itp != NULL);
     89 	icp = (iscsi_conn_t *)itp->t_arg;
     90 	ASSERT(icp != NULL);
     91 	isp = icp->conn_sess;
     92 	ASSERT(isp != NULL);
     93 	ihp = isp->sess_hba;
     94 	ASSERT(ihp != NULL);
     95 
     96 login_start:
     97 	ASSERT((icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) ||
     98 	    (icp->conn_state == ISCSI_CONN_STATE_FAILED) ||
     99 	    (icp->conn_state == ISCSI_CONN_STATE_POLLING));
    100 
    101 	icp->conn_state_ffp = B_FALSE;
    102 
    103 	/* reset connection statsn */
    104 	icp->conn_expstatsn = 0;
    105 	icp->conn_laststatsn = 0;
    106 
    107 	/* sync up authentication information */
    108 	(void) iscsi_sess_set_auth(isp);
    109 
    110 	/* sync up login and session parameters */
    111 	if (!ISCSI_SUCCESS(iscsi_conn_sync_params(icp))) {
    112 		/* unable to sync params.  fail connection attempts */
    113 		iscsi_login_end(icp, ISCSI_STATUS_LOGIN_FAILED, itp);
    114 		return (ISCSI_STATUS_LOGIN_FAILED);
    115 	}
    116 
    117 	/*
    118 	 * Attempt to open TCP connection, associated IDM connection will
    119 	 * have a hold on it that must be released after the call to
    120 	 * iscsi_login() below.
    121 	 */
    122 	if (!ISCSI_SUCCESS(iscsi_login_connect(icp))) {
    123 		if ((isp->sess_boot == B_TRUE) &&
    124 		    (ihp->hba_service_status_overwrite == B_TRUE) &&
    125 		    (isp->sess_boot_nic_reset == B_FALSE)) {
    126 			/*
    127 			 * The connection to boot target failed
    128 			 * before the system fully started.
    129 			 * Reset the boot nic to the settings from
    130 			 * firmware before retrying the connect to
    131 			 * save the the system.
    132 			 */
    133 			if (iscsi_net_interface(B_TRUE) ==
    134 			    ISCSI_STATUS_SUCCESS) {
    135 				isp->sess_boot_nic_reset = B_TRUE;
    136 			}
    137 		}
    138 		/* retry this failure */
    139 		goto login_retry;
    140 	}
    141 
    142 	/*
    143 	 * allocate response buffer with based on default max
    144 	 * transfer size.  This size might shift during login.
    145 	 */
    146 	icp->conn_login_max_data_length =
    147 	    icp->conn_params.max_xmit_data_seg_len;
    148 	icp->conn_login_data = kmem_zalloc(icp->conn_login_max_data_length,
    149 	    KM_SLEEP);
    150 
    151 	/*
    152 	 * Start protocol login, upon return we will be either logged in
    153 	 * or disconnected
    154 	 */
    155 	rval = iscsi_login(icp, &status_class, &status_detail);
    156 
    157 	/* done with buffer */
    158 	kmem_free(icp->conn_login_data, icp->conn_login_max_data_length);
    159 
    160 	/* Release connection hold */
    161 	idm_conn_rele(icp->conn_ic);
    162 
    163 	/* hard failure in login */
    164 	if (!ISCSI_SUCCESS(rval)) {
    165 		/*
    166 		 * We should just give up retry if these failures are
    167 		 * detected.
    168 		 */
    169 		switch (rval) {
    170 		/*
    171 		 * We should just give up retry if these
    172 		 * failures are detected.
    173 		 */
    174 		case ISCSI_STATUS_AUTHENTICATION_FAILED:
    175 		case ISCSI_STATUS_INTERNAL_ERROR:
    176 		case ISCSI_STATUS_VERSION_MISMATCH:
    177 		case ISCSI_STATUS_NEGO_FAIL:
    178 			/* we don't want to retry this failure */
    179 			iscsi_login_end(icp, ISCSI_STATUS_LOGIN_FAILED, itp);
    180 			return (ISCSI_STATUS_LOGIN_FAILED);
    181 		default:
    182 			/* retry this failure */
    183 			goto login_retry;
    184 		}
    185 	}
    186 
    187 	/* soft failure with reason */
    188 	switch (status_class) {
    189 	case ISCSI_STATUS_CLASS_SUCCESS:
    190 		/* login was successful */
    191 		iscsi_login_end(icp, ISCSI_STATUS_SUCCESS, itp);
    192 		return (ISCSI_STATUS_SUCCESS);
    193 	case ISCSI_STATUS_CLASS_REDIRECT:
    194 		/* Retry at the redirected address */
    195 		goto login_start;
    196 	case ISCSI_STATUS_CLASS_TARGET_ERR:
    197 		/* retry this failure */
    198 		cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
    199 		    "%s (0x%02x/0x%02x)", icp->conn_oid,
    200 		    iscsi_login_failure_str(status_class, status_detail),
    201 		    status_class, status_detail);
    202 		goto login_retry;
    203 	case ISCSI_STATUS_CLASS_INITIATOR_ERR:
    204 	default:
    205 		/* All other errors are hard failures */
    206 		cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
    207 		    "%s (0x%02x/0x%02x) Target: %s, TPGT: %d",
    208 		    icp->conn_oid,
    209 		    iscsi_login_failure_str(status_class, status_detail),
    210 		    status_class, status_detail, isp->sess_name,
    211 		    isp->sess_tpgt_conf);
    212 
    213 		/* we don't want to retry this failure */
    214 		iscsi_login_end(icp, ISCSI_STATUS_LOGIN_FAILED, itp);
    215 		break;
    216 	}
    217 
    218 	return (ISCSI_STATUS_LOGIN_FAILED);
    219 
    220 login_retry:
    221 	/* retry this failure if we haven't run out of time */
    222 	if (icp->conn_login_max > ddi_get_lbolt()) {
    223 
    224 		if (icp->conn_state == ISCSI_CONN_STATE_POLLING) {
    225 			icp->conn_login_min = ddi_get_lbolt() +
    226 			    SEC_TO_TICK(icp->conn_tunable_params.
    227 			    polling_login_delay);
    228 		} else {
    229 			icp->conn_login_min = ddi_get_lbolt() +
    230 			    SEC_TO_TICK(ISCSI_LOGIN_RETRY_DELAY);
    231 		}
    232 
    233 		if (itp->t_blocking == B_TRUE) {
    234 			goto login_start;
    235 		} else {
    236 			if (ddi_taskq_dispatch(isp->sess_taskq,
    237 			    (void(*)())iscsi_login_start, itp, DDI_SLEEP) !=
    238 			    DDI_SUCCESS) {
    239 				iscsi_login_end(icp,
    240 				    ISCSI_STATUS_LOGIN_TIMED_OUT, itp);
    241 			}
    242 			return (ISCSI_STATUS_SUCCESS);
    243 		}
    244 	} else {
    245 		/* Retries exceeded */
    246 		iscsi_login_end(icp, ISCSI_STATUS_LOGIN_TIMED_OUT, itp);
    247 	}
    248 
    249 	return (ISCSI_STATUS_LOGIN_FAILED);
    250 }
    251 
    252 static void
    253 iscsi_login_end(iscsi_conn_t *icp, iscsi_status_t status, iscsi_task_t *itp)
    254 {
    255 	iscsi_sess_t	*isp;
    256 
    257 	ASSERT(icp != NULL);
    258 	isp = icp->conn_sess;
    259 	ASSERT(isp != NULL);
    260 
    261 	if (status == ISCSI_STATUS_SUCCESS) {
    262 		/* Inform IDM of the relevant negotiated values */
    263 		iscsi_notice_key_values(icp);
    264 
    265 		/* We are now logged in */
    266 		iscsi_conn_update_state(icp, ISCSI_CONN_STATE_LOGGED_IN);
    267 
    268 		/* startup TX thread */
    269 		(void) iscsi_thread_start(icp->conn_tx_thread);
    270 
    271 		/*
    272 		 * Move login state machine to LOGIN_FFP.  This will
    273 		 * release the taskq thread handling the CN_FFP_ENABLED
    274 		 * allowing the IDM connection state machine to resume
    275 		 * processing events
    276 		 */
    277 		iscsi_login_update_state(icp, LOGIN_FFP);
    278 
    279 		/* Notify the session that a connection is logged in */
    280 		mutex_enter(&isp->sess_state_mutex);
    281 		iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N1);
    282 		mutex_exit(&isp->sess_state_mutex);
    283 	} else {
    284 		/* If login failed reset nego tpgt */
    285 		isp->sess_tpgt_nego = ISCSI_DEFAULT_TPGT;
    286 
    287 		mutex_enter(&icp->conn_state_mutex);
    288 		switch (icp->conn_state) {
    289 		case ISCSI_CONN_STATE_IN_LOGIN:
    290 			iscsi_conn_update_state_locked(icp,
    291 			    ISCSI_CONN_STATE_FREE);
    292 			mutex_exit(&icp->conn_state_mutex);
    293 			break;
    294 		case ISCSI_CONN_STATE_FAILED:
    295 			if (status == ISCSI_STATUS_LOGIN_FAILED) {
    296 				iscsi_conn_update_state_locked(icp,
    297 				    ISCSI_CONN_STATE_FREE);
    298 			} else {
    299 				/* ISCSI_STATUS_LOGIN_TIMED_OUT */
    300 				iscsi_conn_update_state_locked(icp,
    301 				    ISCSI_CONN_STATE_POLLING);
    302 			}
    303 			mutex_exit(&icp->conn_state_mutex);
    304 
    305 			mutex_enter(&isp->sess_state_mutex);
    306 			iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6);
    307 			mutex_exit(&isp->sess_state_mutex);
    308 
    309 			if (status == ISCSI_STATUS_LOGIN_TIMED_OUT) {
    310 				iscsi_conn_retry(isp, icp);
    311 			}
    312 			break;
    313 		case ISCSI_CONN_STATE_POLLING:
    314 			if (status == ISCSI_STATUS_LOGIN_FAILED) {
    315 				iscsi_conn_update_state_locked(icp,
    316 				    ISCSI_CONN_STATE_FREE);
    317 				mutex_exit(&icp->conn_state_mutex);
    318 
    319 				mutex_enter(&isp->sess_state_mutex);
    320 				iscsi_sess_state_machine(isp,
    321 				    ISCSI_SESS_EVENT_N6);
    322 				mutex_exit(&isp->sess_state_mutex);
    323 			} else {
    324 				/* ISCSI_STATUS_LOGIN_TIMED_OUT */
    325 				if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
    326 					mutex_exit(&icp->conn_state_mutex);
    327 
    328 					iscsi_conn_retry(isp, icp);
    329 				} else {
    330 					iscsi_conn_update_state_locked(icp,
    331 					    ISCSI_CONN_STATE_FREE);
    332 					mutex_exit(&icp->conn_state_mutex);
    333 				}
    334 			}
    335 			break;
    336 		case ISCSI_CONN_STATE_FREE:
    337 			mutex_exit(&icp->conn_state_mutex);
    338 			break;
    339 		default:
    340 			mutex_exit(&icp->conn_state_mutex);
    341 			ASSERT(0);
    342 			break;
    343 		}
    344 	}
    345 
    346 	if (itp->t_blocking == B_FALSE) {
    347 		kmem_free(itp, sizeof (iscsi_task_t));
    348 	}
    349 
    350 	isp->sess_boot_nic_reset = B_FALSE;
    351 }
    352 
    353 /*
    354  * +--------------------------------------------------------------------+
    355  * | Begin of protocol login routines					|
    356  * +--------------------------------------------------------------------+
    357  */
    358 
    359 /*
    360  * iscsi_login - Attempt to login to the target.  The caller
    361  * must check the status class to determine if the login succeeded.
    362  * A return of 1 does not mean the login succeeded, it just means
    363  * this function worked, and the status class is valid info.  This
    364  * allows the caller to decide whether or not to retry logins, so
    365  * that we don't have any policy logic here.
    366  */
    367 iscsi_status_t
    368 iscsi_login(iscsi_conn_t *icp, uint8_t *status_class, uint8_t *status_detail)
    369 {
    370 	iscsi_status_t		rval		= ISCSI_STATUS_INTERNAL_ERROR;
    371 	struct iscsi_sess	*isp		= NULL;
    372 	IscsiAuthClient		*auth_client	= NULL;
    373 	int			max_data_length	= 0;
    374 	char			*data		= NULL;
    375 	idm_pdu_t		*text_pdu;
    376 	char			*buffer;
    377 	size_t			bufsize;
    378 	iscsi_login_rsp_hdr_t	*ilrhp;
    379 	clock_t			response_timeout, timeout_result;
    380 
    381 	buffer = icp->conn_login_data;
    382 	bufsize = icp->conn_login_max_data_length;
    383 
    384 	ASSERT(icp != NULL);
    385 	ASSERT(buffer != NULL);
    386 	ASSERT(status_class != NULL);
    387 	ASSERT(status_detail != NULL);
    388 	isp = icp->conn_sess;
    389 	ASSERT(isp != NULL);
    390 
    391 	/*
    392 	 * prepare the connection, hold IDM connection until login completes
    393 	 */
    394 	icp->conn_current_stage = ISCSI_INITIAL_LOGIN_STAGE;
    395 	icp->conn_partial_response = 0;
    396 
    397 	if (isp->sess_auth.auth_buffers &&
    398 	    isp->sess_auth.num_auth_buffers) {
    399 
    400 		auth_client = (IscsiAuthClient *)isp->
    401 		    sess_auth.auth_buffers[0].address;
    402 
    403 		/*
    404 		 * prepare for authentication
    405 		 */
    406 		if (iscsiAuthClientInit(iscsiAuthNodeTypeInitiator,
    407 		    isp->sess_auth.num_auth_buffers,
    408 		    isp->sess_auth.auth_buffers) !=
    409 		    iscsiAuthStatusNoError) {
    410 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
    411 			    "unable to initialize authentication",
    412 			    icp->conn_oid);
    413 			iscsi_login_disconnect(icp);
    414 			iscsi_login_update_state(icp, LOGIN_DONE);
    415 			return (ISCSI_STATUS_INTERNAL_ERROR);
    416 		}
    417 
    418 		if (iscsiAuthClientSetVersion(auth_client,
    419 		    iscsiAuthVersionRfc) != iscsiAuthStatusNoError) {
    420 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
    421 			    "unable to set authentication", icp->conn_oid);
    422 			goto iscsi_login_done;
    423 		}
    424 
    425 		if (isp->sess_auth.username &&
    426 		    (iscsiAuthClientSetUsername(auth_client,
    427 		    isp->sess_auth.username) !=
    428 		    iscsiAuthStatusNoError)) {
    429 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
    430 			    "unable to set username", icp->conn_oid);
    431 			goto iscsi_login_done;
    432 		}
    433 
    434 		if (isp->sess_auth.password &&
    435 		    (iscsiAuthClientSetPassword(auth_client,
    436 		    isp->sess_auth.password, isp->sess_auth.password_length) !=
    437 		    iscsiAuthStatusNoError)) {
    438 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
    439 			    "unable to set password", icp->conn_oid);
    440 			goto iscsi_login_done;
    441 		}
    442 
    443 		if (iscsiAuthClientSetIpSec(auth_client, 1) !=
    444 		    iscsiAuthStatusNoError) {
    445 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
    446 			    "unable to set ipsec", icp->conn_oid);
    447 			goto iscsi_login_done;
    448 		}
    449 
    450 		if (iscsiAuthClientSetAuthRemote(auth_client,
    451 		    isp->sess_auth.bidirectional_auth) !=
    452 		    iscsiAuthStatusNoError) {
    453 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
    454 			    "unable to set remote authentication",
    455 			    icp->conn_oid);
    456 			goto iscsi_login_done;
    457 		}
    458 	}
    459 
    460 	/*
    461 	 * exchange PDUs until the login stage is complete, or an error occurs
    462 	 */
    463 	do {
    464 		/* setup */
    465 		bzero(buffer, bufsize);
    466 		data = buffer;
    467 		max_data_length = bufsize;
    468 		rval = ISCSI_STATUS_INTERNAL_ERROR;
    469 
    470 		text_pdu = idm_pdu_alloc(sizeof (iscsi_hdr_t), 0);
    471 		idm_pdu_init(text_pdu, icp->conn_ic, NULL, NULL);
    472 
    473 		/*
    474 		 * fill in the PDU header and text data based on the
    475 		 * login stage that we're in
    476 		 */
    477 		rval = iscsi_make_login_pdu(icp, text_pdu, data,
    478 		    max_data_length);
    479 		if (!ISCSI_SUCCESS(rval)) {
    480 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
    481 			    "unable to make login pdu", icp->conn_oid);
    482 			goto iscsi_login_done;
    483 		}
    484 
    485 		mutex_enter(&icp->conn_login_mutex);
    486 		/*
    487 		 * Make sure we are still in LOGIN_START or LOGIN_RX
    488 		 * state before switching to LOGIN_TX.  It's possible
    489 		 * for a connection failure to move us to LOGIN_ERROR
    490 		 * before we get to this point.
    491 		 */
    492 		if (((icp->conn_login_state != LOGIN_READY) &&
    493 		    (icp->conn_login_state != LOGIN_RX)) ||
    494 		    !icp->conn_state_idm_connected) {
    495 			/* Error occurred */
    496 			mutex_exit(&icp->conn_login_mutex);
    497 			rval = (ISCSI_STATUS_INTERNAL_ERROR);
    498 			goto iscsi_login_done;
    499 		}
    500 
    501 		iscsi_login_update_state_locked(icp, LOGIN_TX);
    502 		icp->conn_login_data = data;
    503 		icp->conn_login_max_data_length = max_data_length;
    504 
    505 		/*
    506 		 * send a PDU to the target.  This is asynchronous but
    507 		 * we don't have any particular need for a TX completion
    508 		 * notification since we are going to block waiting for the
    509 		 * receive.
    510 		 */
    511 		response_timeout = ddi_get_lbolt() +
    512 		    SEC_TO_TICK(icp->conn_tunable_params.
    513 		    recv_login_rsp_timeout);
    514 		idm_pdu_tx(text_pdu);
    515 
    516 		/*
    517 		 * Wait for login failure indication or login RX.
    518 		 * Handler for login response PDU will copy any data into
    519 		 * the buffer pointed to by icp->conn_login_data
    520 		 */
    521 		while (icp->conn_login_state == LOGIN_TX) {
    522 			timeout_result = cv_timedwait(&icp->conn_login_cv,
    523 			    &icp->conn_login_mutex, response_timeout);
    524 			if (timeout_result == -1)
    525 				break;
    526 		}
    527 
    528 		if (icp->conn_login_state != LOGIN_RX) {
    529 			mutex_exit(&icp->conn_login_mutex);
    530 			rval = (ISCSI_STATUS_INTERNAL_ERROR);
    531 			goto iscsi_login_done;
    532 		}
    533 		mutex_exit(&icp->conn_login_mutex);
    534 
    535 		/* check the PDU response type */
    536 		ilrhp = (iscsi_login_rsp_hdr_t *)&icp->conn_login_resp_hdr;
    537 		if (ilrhp->opcode != ISCSI_OP_LOGIN_RSP) {
    538 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
    539 			    "received invalid login response (0x%02x)",
    540 			    icp->conn_oid, ilrhp->opcode);
    541 			rval = (ISCSI_STATUS_PROTOCOL_ERROR);
    542 			goto iscsi_login_done;
    543 		}
    544 
    545 		/*
    546 		 * give the caller the status class and detail from the
    547 		 * last login response PDU received
    548 		 */
    549 		if (status_class) {
    550 			*status_class = ilrhp->status_class;
    551 		}
    552 		if (status_detail) {
    553 			*status_detail = ilrhp->status_detail;
    554 		}
    555 
    556 		switch (ilrhp->status_class) {
    557 		case ISCSI_STATUS_CLASS_SUCCESS:
    558 			/*
    559 			 * process this response and possibly continue
    560 			 * sending PDUs
    561 			 */
    562 			rval = iscsi_process_login_response(icp,
    563 			    ilrhp, (char *)icp->conn_login_data,
    564 			    icp->conn_login_max_data_length);
    565 			/* pass back whatever error we discovered */
    566 			if (!ISCSI_SUCCESS(rval)) {
    567 				if (ISCSI_LOGIN_TRANSIT_FFP(ilrhp->flags)) {
    568 					/*
    569 					 * iSCSI connection transit to next
    570 					 * FFP stage while iscsi params
    571 					 * ngeotiate error, LOGIN_ERROR
    572 					 * marked so CN_FFP_ENABLED can
    573 					 * be fully handled before
    574 					 * CN_FFP_DISABLED can be processed.
    575 					 */
    576 					iscsi_login_update_state(icp,
    577 					    LOGIN_ERROR);
    578 				}
    579 				goto iscsi_login_done;
    580 			}
    581 
    582 			break;
    583 		case ISCSI_STATUS_CLASS_REDIRECT:
    584 			/*
    585 			 * we need to process this response to get the
    586 			 * TargetAddress of the redirect, but we don't
    587 			 * care about the return code.
    588 			 */
    589 			(void) iscsi_process_login_response(icp,
    590 			    ilrhp, (char *)icp->conn_login_data,
    591 			    icp->conn_login_max_data_length);
    592 			rval = ISCSI_STATUS_SUCCESS;
    593 			goto iscsi_login_done;
    594 		case ISCSI_STATUS_CLASS_INITIATOR_ERR:
    595 			if (ilrhp->status_detail ==
    596 			    ISCSI_LOGIN_STATUS_AUTH_FAILED) {
    597 				cmn_err(CE_WARN, "iscsi connection(%u) login "
    598 				    "failed - login failed to authenticate "
    599 				    "with target", icp->conn_oid);
    600 			}
    601 			rval = ISCSI_STATUS_SUCCESS;
    602 			goto iscsi_login_done;
    603 		default:
    604 			/*
    605 			 * some sort of error, login terminated unsuccessfully,
    606 			 * though this function did it's job. the caller must
    607 			 * check the status_class and status_detail and decide
    608 			 * what to do next.
    609 			 */
    610 			rval = ISCSI_STATUS_SUCCESS;
    611 			goto iscsi_login_done;
    612 		}
    613 
    614 	} while (icp->conn_current_stage != ISCSI_FULL_FEATURE_PHASE);
    615 
    616 	rval = ISCSI_STATUS_SUCCESS;
    617 
    618 iscsi_login_done:
    619 	if (auth_client) {
    620 		if (iscsiAuthClientFinish(auth_client) !=
    621 		    iscsiAuthStatusNoError) {
    622 			cmn_err(CE_WARN, "iscsi connection(%u) login "
    623 			    "failed - login failed to authenticate "
    624 			    "with target", icp->conn_oid);
    625 			if (ISCSI_SUCCESS(rval))
    626 				rval = ISCSI_STATUS_INTERNAL_ERROR;
    627 		}
    628 	}
    629 
    630 	if (ISCSI_SUCCESS(rval) &&
    631 	    (*status_class == ISCSI_STATUS_CLASS_SUCCESS)) {
    632 		mutex_enter(&icp->conn_state_mutex);
    633 		while (!icp->conn_state_ffp)
    634 			cv_wait(&icp->conn_state_change,
    635 			    &icp->conn_state_mutex);
    636 		mutex_exit(&icp->conn_state_mutex);
    637 	} else {
    638 		iscsi_login_disconnect(icp);
    639 	}
    640 
    641 	iscsi_login_update_state(icp, LOGIN_DONE);
    642 
    643 	return (rval);
    644 }
    645 
    646 
    647 /*
    648  * iscsi_make_login_pdu -
    649  *
    650  */
    651 static iscsi_status_t
    652 iscsi_make_login_pdu(iscsi_conn_t *icp, idm_pdu_t *text_pdu,
    653     char *data, int max_data_length)
    654 {
    655 	struct iscsi_sess	*isp		= NULL;
    656 	int			transit		= 0;
    657 	iscsi_hdr_t		*ihp		= text_pdu->isp_hdr;
    658 	iscsi_login_hdr_t	*ilhp		=
    659 	    (iscsi_login_hdr_t *)text_pdu->isp_hdr;
    660 	IscsiAuthClient		*auth_client	= NULL;
    661 	int			keytype		= 0;
    662 	int			rc		= 0;
    663 	char			value[iscsiAuthStringMaxLength];
    664 
    665 	ASSERT(icp != NULL);
    666 	ASSERT(text_pdu != NULL);
    667 	isp = icp->conn_sess;
    668 	ASSERT(isp != NULL);
    669 
    670 	auth_client =
    671 	    (isp->sess_auth.auth_buffers && isp->sess_auth.num_auth_buffers) ?
    672 	    (IscsiAuthClient *)isp->sess_auth.auth_buffers[0].address : NULL;
    673 
    674 	/*
    675 	 * initialize the PDU header
    676 	 */
    677 	bzero(ilhp, sizeof (*ilhp));
    678 	ilhp->opcode = ISCSI_OP_LOGIN_CMD | ISCSI_OP_IMMEDIATE;
    679 	ilhp->cid = icp->conn_cid;
    680 	bcopy(&isp->sess_isid[0], &ilhp->isid[0], sizeof (isp->sess_isid));
    681 	ilhp->tsid = 0;
    682 
    683 	/*
    684 	 * Set data buffer pointer.  The calls to iscsi_add_text will update the
    685 	 * data length.
    686 	 */
    687 	text_pdu->isp_data = (uint8_t *)data;
    688 
    689 	/* don't increment on immediate */
    690 	ilhp->cmdsn = htonl(isp->sess_cmdsn);
    691 
    692 	ilhp->min_version = ISCSI_DRAFT20_VERSION;
    693 	ilhp->max_version = ISCSI_DRAFT20_VERSION;
    694 
    695 	/*
    696 	 * we have to send 0 until full-feature stage
    697 	 */
    698 	ilhp->expstatsn = htonl(icp->conn_expstatsn);
    699 
    700 	/*
    701 	 * the very first Login PDU has some additional requirements,
    702 	 * * and we need to decide what stage to start in.
    703 	 */
    704 	if (icp->conn_current_stage == ISCSI_INITIAL_LOGIN_STAGE) {
    705 		if ((isp->sess_hba->hba_name) &&
    706 		    (isp->sess_hba->hba_name[0])) {
    707 			if (!iscsi_add_text(text_pdu, max_data_length,
    708 			    "InitiatorName",
    709 			    (char *)isp->sess_hba->hba_name)) {
    710 				return (ISCSI_STATUS_INTERNAL_ERROR);
    711 			}
    712 		} else {
    713 			cmn_err(CE_WARN, "iscsi connection(%u) login "
    714 			    "failed - initiator name is required",
    715 			    icp->conn_oid);
    716 			return (ISCSI_STATUS_INTERNAL_ERROR);
    717 		}
    718 
    719 		if ((isp->sess_hba->hba_alias) &&
    720 		    (isp->sess_hba->hba_alias[0])) {
    721 			if (!iscsi_add_text(text_pdu, max_data_length,
    722 			    "InitiatorAlias",
    723 			    (char *)isp->sess_hba->hba_alias)) {
    724 				return (ISCSI_STATUS_INTERNAL_ERROR);
    725 			}
    726 		}
    727 
    728 		if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
    729 			if (isp->sess_name[0] != '\0') {
    730 				if (!iscsi_add_text(text_pdu, max_data_length,
    731 				    "TargetName", (char *)isp->sess_name)) {
    732 					return (ISCSI_STATUS_INTERNAL_ERROR);
    733 				}
    734 			}
    735 
    736 			if (!iscsi_add_text(text_pdu, max_data_length,
    737 			    "SessionType", "Normal")) {
    738 				return (ISCSI_STATUS_INTERNAL_ERROR);
    739 			}
    740 		} else if (isp->sess_type == ISCSI_SESS_TYPE_DISCOVERY) {
    741 			if (!iscsi_add_text(text_pdu, max_data_length,
    742 			    "SessionType", "Discovery")) {
    743 				return (ISCSI_STATUS_INTERNAL_ERROR);
    744 			}
    745 		} else {
    746 			return (ISCSI_STATUS_INTERNAL_ERROR);
    747 		}
    748 
    749 		if (auth_client) {
    750 			/* we're prepared to do authentication */
    751 			icp->conn_current_stage =
    752 			    ISCSI_SECURITY_NEGOTIATION_STAGE;
    753 		} else {
    754 			/* can't do any authentication, skip that stage */
    755 			icp->conn_current_stage =
    756 			    ISCSI_OP_PARMS_NEGOTIATION_STAGE;
    757 		}
    758 	}
    759 
    760 	/*
    761 	 * fill in text based on the stage
    762 	 */
    763 	switch (icp->conn_current_stage) {
    764 	case ISCSI_OP_PARMS_NEGOTIATION_STAGE:
    765 		/*
    766 		 * we always try to go from op params to full
    767 		 * feature stage
    768 		 */
    769 		icp->conn_next_stage	= ISCSI_FULL_FEATURE_PHASE;
    770 		transit			= 1;
    771 
    772 		/*
    773 		 * The terminology here may have gotten dated.  A partial
    774 		 * response is a login response that doesn't complete a
    775 		 * login.  If we haven't gotten a partial response, then
    776 		 * either we shouldn't be here, or we just switched to
    777 		 * this stage, and need to start offering keys.
    778 		 */
    779 		if (!icp->conn_partial_response) {
    780 			/*
    781 			 * request the desired settings the first time
    782 			 * we are in this stage
    783 			 */
    784 			switch (icp->conn_params.header_digest) {
    785 			case ISCSI_DIGEST_NONE:
    786 				if (!iscsi_add_text(text_pdu,
    787 				    max_data_length, "HeaderDigest", "None")) {
    788 					return (ISCSI_STATUS_INTERNAL_ERROR);
    789 				}
    790 				break;
    791 			case ISCSI_DIGEST_CRC32C:
    792 				if (!iscsi_add_text(text_pdu,
    793 				    max_data_length,
    794 				    "HeaderDigest", "CRC32C")) {
    795 					return (ISCSI_STATUS_INTERNAL_ERROR);
    796 				}
    797 				break;
    798 			case ISCSI_DIGEST_CRC32C_NONE:
    799 				if (!iscsi_add_text(text_pdu,
    800 				    max_data_length, "HeaderDigest",
    801 				    "CRC32C,None")) {
    802 					return (ISCSI_STATUS_INTERNAL_ERROR);
    803 				}
    804 				break;
    805 			default:
    806 			case ISCSI_DIGEST_NONE_CRC32C:
    807 				if (!iscsi_add_text(text_pdu,
    808 				    max_data_length, "HeaderDigest",
    809 				    "None,CRC32C")) {
    810 					return (ISCSI_STATUS_INTERNAL_ERROR);
    811 				}
    812 				break;
    813 			}
    814 
    815 			switch (icp->conn_params.data_digest) {
    816 			case ISCSI_DIGEST_NONE:
    817 				if (!iscsi_add_text(text_pdu,
    818 				    max_data_length, "DataDigest", "None")) {
    819 					return (ISCSI_STATUS_INTERNAL_ERROR);
    820 				}
    821 				break;
    822 			case ISCSI_DIGEST_CRC32C:
    823 				if (!iscsi_add_text(text_pdu,
    824 				    max_data_length, "DataDigest", "CRC32C")) {
    825 					return (ISCSI_STATUS_INTERNAL_ERROR);
    826 				}
    827 				break;
    828 			case ISCSI_DIGEST_CRC32C_NONE:
    829 				if (!iscsi_add_text(text_pdu,
    830 				    max_data_length, "DataDigest",
    831 				    "CRC32C,None")) {
    832 					return (ISCSI_STATUS_INTERNAL_ERROR);
    833 				}
    834 				break;
    835 			default:
    836 			case ISCSI_DIGEST_NONE_CRC32C:
    837 				if (!iscsi_add_text(text_pdu,
    838 				    max_data_length, "DataDigest",
    839 				    "None,CRC32C")) {
    840 					return (ISCSI_STATUS_INTERNAL_ERROR);
    841 				}
    842 				break;
    843 			}
    844 
    845 			(void) sprintf(value, "%d",
    846 			    icp->conn_params.max_recv_data_seg_len);
    847 			if (!iscsi_add_text(text_pdu, max_data_length,
    848 			    "MaxRecvDataSegmentLength", value)) {
    849 				return (ISCSI_STATUS_INTERNAL_ERROR);
    850 			}
    851 
    852 			(void) sprintf(value, "%d",
    853 			    icp->conn_params.default_time_to_wait);
    854 			if (!iscsi_add_text(text_pdu,
    855 			    max_data_length, "DefaultTime2Wait", value)) {
    856 				return (ISCSI_STATUS_INTERNAL_ERROR);
    857 			}
    858 
    859 			(void) sprintf(value, "%d",
    860 			    icp->conn_params.default_time_to_retain);
    861 			if (!iscsi_add_text(text_pdu,
    862 			    max_data_length, "DefaultTime2Retain", value)) {
    863 				return (ISCSI_STATUS_INTERNAL_ERROR);
    864 			}
    865 
    866 			(void) sprintf(value, "%d",
    867 			    icp->conn_params.error_recovery_level);
    868 			if (!iscsi_add_text(text_pdu,
    869 			    max_data_length, "ErrorRecoveryLevel", "0")) {
    870 				return (ISCSI_STATUS_INTERNAL_ERROR);
    871 			}
    872 
    873 			if (!iscsi_add_text(text_pdu,
    874 			    max_data_length, "IFMarker",
    875 			    icp->conn_params.ifmarker ? "Yes" : "No")) {
    876 				return (ISCSI_STATUS_INTERNAL_ERROR);
    877 			}
    878 
    879 			if (!iscsi_add_text(text_pdu,
    880 			    max_data_length, "OFMarker",
    881 			    icp->conn_params.ofmarker ? "Yes" : "No")) {
    882 				return (ISCSI_STATUS_INTERNAL_ERROR);
    883 			}
    884 
    885 			/*
    886 			 * The following login parameters are "Irrelevant"
    887 			 * for discovery sessions
    888 			 */
    889 			if (isp->sess_type != ISCSI_SESS_TYPE_DISCOVERY) {
    890 
    891 				if (!iscsi_add_text(text_pdu,
    892 				    max_data_length, "InitialR2T",
    893 				    icp->conn_params.initial_r2t ?
    894 				    "Yes" : "No")) {
    895 					return (ISCSI_STATUS_INTERNAL_ERROR);
    896 				}
    897 
    898 				if (!iscsi_add_text(text_pdu,
    899 				    max_data_length, "ImmediateData",
    900 				    icp->conn_params.immediate_data ?
    901 				    "Yes" : "No")) {
    902 					return (ISCSI_STATUS_INTERNAL_ERROR);
    903 				}
    904 
    905 				(void) sprintf(value, "%d",
    906 				    icp->conn_params.max_burst_length);
    907 				if (!iscsi_add_text(text_pdu,
    908 				    max_data_length, "MaxBurstLength", value)) {
    909 					return (ISCSI_STATUS_INTERNAL_ERROR);
    910 				}
    911 
    912 				(void) sprintf(value, "%d",
    913 				    icp->conn_params.first_burst_length);
    914 				if (!iscsi_add_text(text_pdu, max_data_length,
    915 				    "FirstBurstLength", value)) {
    916 					return (ISCSI_STATUS_INTERNAL_ERROR);
    917 				}
    918 
    919 				(void) sprintf(value, "%d",
    920 				    icp->conn_params.max_outstanding_r2t);
    921 				if (!iscsi_add_text(text_pdu, max_data_length,
    922 				    "MaxOutstandingR2T", value)) {
    923 					return (ISCSI_STATUS_INTERNAL_ERROR);
    924 				}
    925 
    926 				(void) sprintf(value, "%d",
    927 				    icp->conn_params.max_connections);
    928 				if (!iscsi_add_text(text_pdu, max_data_length,
    929 				    "MaxConnections", value)) {
    930 					return (ISCSI_STATUS_INTERNAL_ERROR);
    931 				}
    932 
    933 				if (!iscsi_add_text(text_pdu,
    934 				    max_data_length, "DataPDUInOrder",
    935 				    icp->conn_params.data_pdu_in_order ?
    936 				    "Yes" : "No")) {
    937 					return (ISCSI_STATUS_INTERNAL_ERROR);
    938 				}
    939 
    940 				if (!iscsi_add_text(text_pdu,
    941 				    max_data_length, "DataSequenceInOrder",
    942 				    icp->conn_params.data_sequence_in_order ?
    943 				    "Yes" : "No")) {
    944 					return (ISCSI_STATUS_INTERNAL_ERROR);
    945 				}
    946 			}
    947 		}
    948 		break;
    949 
    950 	case ISCSI_SECURITY_NEGOTIATION_STAGE:
    951 		keytype = iscsiAuthKeyTypeNone;
    952 		rc = iscsiAuthClientSendTransitBit(auth_client, &transit);
    953 
    954 		/*
    955 		 * see if we're ready for a stage change
    956 		 */
    957 		if (rc == iscsiAuthStatusNoError) {
    958 			if (transit) {
    959 				icp->conn_next_stage =
    960 				    ISCSI_OP_PARMS_NEGOTIATION_STAGE;
    961 			} else {
    962 				icp->conn_next_stage =
    963 				    ISCSI_SECURITY_NEGOTIATION_STAGE;
    964 			}
    965 		} else {
    966 			return (ISCSI_STATUS_INTERNAL_ERROR);
    967 		}
    968 
    969 		/*
    970 		 * enumerate all the keys the auth code might want to send
    971 		 */
    972 		while (iscsiAuthClientGetNextKeyType(&keytype) ==
    973 		    iscsiAuthStatusNoError) {
    974 			int present = 0;
    975 			char *key = (char *)iscsiAuthClientGetKeyName(keytype);
    976 			int key_length = key ? strlen(key) : 0;
    977 			int pdu_length = text_pdu->isp_datalen;
    978 			char *auth_value = data + pdu_length + key_length + 1;
    979 			unsigned int max_length = max_data_length -
    980 			    (pdu_length + key_length + 1);
    981 
    982 			/*
    983 			 * add the key/value pairs the auth code wants to
    984 			 * send directly to the PDU, since they could in
    985 			 * theory be large.
    986 			 */
    987 			rc = iscsiAuthClientSendKeyValue(auth_client, keytype,
    988 			    &present, auth_value, max_length);
    989 			if ((rc == iscsiAuthStatusNoError) && present) {
    990 				/*
    991 				 * actually fill in the key
    992 				 */
    993 				(void) strncpy(&data[pdu_length], key,
    994 				    key_length);
    995 				pdu_length += key_length;
    996 				data[pdu_length] = '=';
    997 				pdu_length++;
    998 				/*
    999 				 * adjust the PDU's data segment length to
   1000 				 * include the value and trailing NULL
   1001 				 */
   1002 				pdu_length += strlen(auth_value) + 1;
   1003 				text_pdu->isp_datalen = pdu_length;
   1004 				hton24(ihp->dlength, pdu_length);
   1005 			}
   1006 		}
   1007 
   1008 		break;
   1009 	case ISCSI_FULL_FEATURE_PHASE:
   1010 		cmn_err(CE_WARN, "iscsi connection(%u) login "
   1011 		    "failed - can't send login in full feature stage",
   1012 		    icp->conn_oid);
   1013 		return (ISCSI_STATUS_INTERNAL_ERROR);
   1014 	default:
   1015 		cmn_err(CE_WARN, "iscsi connection(%u) login "
   1016 		    "failed - can't send login in unknown stage (%d)",
   1017 		    icp->conn_oid, icp->conn_current_stage);
   1018 		return (ISCSI_STATUS_INTERNAL_ERROR);
   1019 	}
   1020 
   1021 	/* fill in the flags */
   1022 	ilhp->flags = icp->conn_current_stage << 2;
   1023 	if (transit) {
   1024 		/* transit to the next stage */
   1025 		ilhp->flags |= icp->conn_next_stage;
   1026 		ilhp->flags |= ISCSI_FLAG_LOGIN_TRANSIT;
   1027 	} else {
   1028 		/* next == current */
   1029 		ilhp->flags |= icp->conn_current_stage;
   1030 	}
   1031 
   1032 	return (ISCSI_STATUS_SUCCESS);
   1033 }
   1034 
   1035 
   1036 /*
   1037  * iscsi_process_login_response - This assumes the text data is
   1038  * always NUL terminated.  The caller can always arrange for that by
   1039  * using a slightly larger buffer than the max PDU size, and then
   1040  * appending a NUL to the PDU.
   1041  */
   1042 static iscsi_status_t
   1043 iscsi_process_login_response(iscsi_conn_t *icp,
   1044     iscsi_login_rsp_hdr_t *ilrhp, char *data, int max_data_length)
   1045 {
   1046 	iscsi_sess_t		*isp			= NULL;
   1047 	IscsiAuthClient		*auth_client		= NULL;
   1048 	int			transit			= 0;
   1049 	char			*text			= data;
   1050 	char			*end			= NULL;
   1051 	int			pdu_current_stage	= 0;
   1052 	int			pdu_next_stage		= 0;
   1053 	int			debug_status		= 0;
   1054 	unsigned long		tmp;
   1055 	char			*tmpe;
   1056 	boolean_t		fbl_irrelevant		= B_FALSE;
   1057 
   1058 	ASSERT(icp != NULL);
   1059 	ASSERT(ilrhp != NULL);
   1060 	ASSERT(data != NULL);
   1061 	isp = icp->conn_sess;
   1062 	ASSERT(isp != NULL);
   1063 
   1064 	auth_client =
   1065 	    (isp->sess_auth.auth_buffers && isp->sess_auth.num_auth_buffers) ?
   1066 	    (IscsiAuthClient *) isp->sess_auth.auth_buffers[0].address : NULL;
   1067 	transit = ilrhp->flags & ISCSI_FLAG_LOGIN_TRANSIT;
   1068 
   1069 	/* verify the initial buffer was big enough to hold everything */
   1070 	end = text + ntoh24(ilrhp->dlength) + 1;
   1071 	if (end >= (data + max_data_length)) {
   1072 		cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
   1073 		    "buffer too small", icp->conn_oid);
   1074 		return (ISCSI_STATUS_INTERNAL_ERROR);
   1075 	}
   1076 	*end = '\0';
   1077 
   1078 	/* if the response status was success, sanity check the response */
   1079 	if (ilrhp->status_class == ISCSI_STATUS_CLASS_SUCCESS) {
   1080 		/* check the active version */
   1081 		if (ilrhp->active_version != ISCSI_DRAFT20_VERSION) {
   1082 			cmn_err(CE_WARN, "iscsi connection(%u) login "
   1083 			    "failed - target version incompatible "
   1084 			    "received:0x%0x2x expected:0x%02x",
   1085 			    icp->conn_oid, ilrhp->active_version,
   1086 			    ISCSI_DRAFT20_VERSION);
   1087 			return (ISCSI_STATUS_VERSION_MISMATCH);
   1088 		}
   1089 
   1090 		/* make sure the current stage matches */
   1091 		pdu_current_stage = (ilrhp->flags &
   1092 		    ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2;
   1093 		if (pdu_current_stage != icp->conn_current_stage) {
   1094 			cmn_err(CE_WARN, "iscsi connection(%u) login "
   1095 			    "failed - login response contained invalid "
   1096 			    "stage %d", icp->conn_oid, pdu_current_stage);
   1097 			return (ISCSI_STATUS_PROTOCOL_ERROR);
   1098 		}
   1099 
   1100 		/*
   1101 		 * Make sure that we're actually advancing
   1102 		 * if the T-bit is set
   1103 		 */
   1104 		pdu_next_stage = ilrhp->flags &
   1105 		    ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK;
   1106 		if (transit && (pdu_next_stage <= icp->conn_current_stage)) {
   1107 			cmn_err(CE_WARN, "iscsi connection(%u) login "
   1108 			    "failed - login response wants to go to stage "
   1109 			    "%d, but we want stage %d", icp->conn_oid,
   1110 			    pdu_next_stage, icp->conn_next_stage);
   1111 			return (ISCSI_STATUS_PROTOCOL_ERROR);
   1112 		}
   1113 	}
   1114 
   1115 	if (icp->conn_current_stage == ISCSI_SECURITY_NEGOTIATION_STAGE) {
   1116 		if (iscsiAuthClientRecvBegin(auth_client) !=
   1117 		    iscsiAuthStatusNoError) {
   1118 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
   1119 			    "authentication receive failed", icp->conn_oid);
   1120 			return (ISCSI_STATUS_INTERNAL_ERROR);
   1121 		}
   1122 
   1123 		if (iscsiAuthClientRecvTransitBit(auth_client,
   1124 		    transit) != iscsiAuthStatusNoError) {
   1125 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
   1126 			    "authentication transmit failed", icp->conn_oid);
   1127 			return (ISCSI_STATUS_INTERNAL_ERROR);
   1128 		}
   1129 	}
   1130 
   1131 	/*
   1132 	 * scan the text data
   1133 	 */
   1134 more_text:
   1135 	while (text && (text < end)) {
   1136 		char *value = NULL;
   1137 		char *value_end = NULL;
   1138 
   1139 		/*
   1140 		 * skip any NULs separating each text key=value pair
   1141 		 */
   1142 		while ((text < end) && (*text == '\0')) {
   1143 			text++;
   1144 		}
   1145 		if (text >= end) {
   1146 			break;
   1147 		}
   1148 
   1149 		/*
   1150 		 * handle keys appropriate for each stage
   1151 		 */
   1152 		switch (icp->conn_current_stage) {
   1153 		case ISCSI_SECURITY_NEGOTIATION_STAGE:
   1154 			/*
   1155 			 * a few keys are possible in Security stage
   1156 			 * * which the auth code doesn't care about,
   1157 			 * * but which we might want to see, or at
   1158 			 * * least not choke on.
   1159 			 */
   1160 			if (iscsi_find_key_value("TargetAlias",
   1161 			    text, end, &value, &value_end)) {
   1162 				isp->sess_alias_length =
   1163 				    sizeof (isp->sess_alias) - 1;
   1164 
   1165 				if ((value_end - value) <
   1166 				    isp->sess_alias_length) {
   1167 					isp->sess_alias_length =
   1168 					    value_end - value;
   1169 				}
   1170 
   1171 				bcopy(value, isp->sess_alias,
   1172 				    isp->sess_alias_length);
   1173 				isp->sess_alias[isp->sess_alias_length + 1] =
   1174 				    '\0';
   1175 				text = value_end;
   1176 
   1177 			} else if (iscsi_find_key_value("TargetAddress",
   1178 			    text, end, &value, &value_end)) {
   1179 				if (!ISCSI_SUCCESS(iscsi_update_address(
   1180 				    icp, value))) {
   1181 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1182 					    "login failed - login redirection "
   1183 					    "invalid", icp->conn_oid);
   1184 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1185 				}
   1186 				text = value_end;
   1187 			} else if (iscsi_find_key_value("TargetPortalGroupTag",
   1188 			    text, end, &value, &value_end)) {
   1189 				/*
   1190 				 * We should have already obtained this via
   1191 				 * discovery.  We've already picked an isid,
   1192 				 * so the most we can do is confirm we reached
   1193 				 * the portal group we were expecting to.
   1194 				 */
   1195 				if (ddi_strtoul(value, &tmpe, 0, &tmp) != 0) {
   1196 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1197 				}
   1198 				if (isp->sess_tpgt_conf != ISCSI_DEFAULT_TPGT) {
   1199 					if (tmp != isp->sess_tpgt_conf) {
   1200 
   1201 	cmn_err(CE_WARN, "iscsi connection(%u) login failed - target "
   1202 	    "protocol group tag mismatch, expected %d, received %lu",
   1203 	    icp->conn_oid, isp->sess_tpgt_conf, tmp);
   1204 	return (ISCSI_STATUS_PROTOCOL_ERROR);
   1205 
   1206 					}
   1207 				}
   1208 				isp->sess_tpgt_nego = (int)tmp;
   1209 				text = value_end;
   1210 			} else {
   1211 				/*
   1212 				 * any key we don't recognize either goes
   1213 				 * to the auth code, or we choke on it
   1214 				 */
   1215 				int keytype = iscsiAuthKeyTypeNone;
   1216 
   1217 				while (iscsiAuthClientGetNextKeyType(
   1218 				    &keytype) == iscsiAuthStatusNoError) {
   1219 
   1220 					char *key =
   1221 					    (char *)iscsiAuthClientGetKeyName(
   1222 					    keytype);
   1223 
   1224 					if ((key) &&
   1225 					    (iscsi_find_key_value(key,
   1226 					    text, end, &value, &value_end))) {
   1227 
   1228 						if (iscsiAuthClientRecvKeyValue
   1229 						    (auth_client, keytype,
   1230 						    value) !=
   1231 						    iscsiAuthStatusNoError) {
   1232 
   1233 	cmn_err(CE_WARN, "iscsi connection(%u) login failed - can't accept "
   1234 	    "%s in security stage", icp->conn_oid, text);
   1235 	return (ISCSI_STATUS_NEGO_FAIL);
   1236 
   1237 						}
   1238 						text = value_end;
   1239 						goto more_text;
   1240 					}
   1241 				}
   1242 
   1243 	cmn_err(CE_WARN, "iscsi connection(%u) login failed - can't except "
   1244 	    "%s in security stage", icp->conn_oid, text);
   1245 
   1246 				return (ISCSI_STATUS_NEGO_FAIL);
   1247 			}
   1248 			break;
   1249 		case ISCSI_OP_PARMS_NEGOTIATION_STAGE:
   1250 			if (iscsi_find_key_value("TargetAlias", text,
   1251 			    end, &value, &value_end)) {
   1252 				isp->sess_alias_length =
   1253 				    sizeof (isp->sess_alias) - 1;
   1254 
   1255 				if ((value_end - value) <
   1256 				    isp->sess_alias_length) {
   1257 					isp->sess_alias_length =
   1258 					    value_end - value;
   1259 				}
   1260 
   1261 				bcopy(value, isp->sess_alias,
   1262 				    isp->sess_alias_length);
   1263 				isp->sess_alias[isp->sess_alias_length + 1] =
   1264 				    '\0';
   1265 				text = value_end;
   1266 
   1267 			} else if (iscsi_find_key_value("TargetAddress",
   1268 			    text, end, &value, &value_end)) {
   1269 				if (!ISCSI_SUCCESS(iscsi_update_address(
   1270 				    icp, value))) {
   1271 
   1272 	cmn_err(CE_WARN, "iscsi connection(%u) login failed - login "
   1273 	    "redirection invalid", icp->conn_oid);
   1274 
   1275 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1276 				}
   1277 				text = value_end;
   1278 			} else if (iscsi_find_key_value("TargetPortalGroupTag",
   1279 			    text, end, &value, &value_end)) {
   1280 				/*
   1281 				 * We should have already obtained this via
   1282 				 * discovery.  We've already picked an isid,
   1283 				 * so the most we can do is confirm we reached
   1284 				 * the portal group we were expecting to.
   1285 				 */
   1286 				if (ddi_strtoul(value, &tmpe, 0, &tmp) != 0) {
   1287 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1288 				}
   1289 				if (isp->sess_tpgt_conf != ISCSI_DEFAULT_TPGT) {
   1290 					if (tmp != isp->sess_tpgt_conf) {
   1291 
   1292 	cmn_err(CE_WARN, "iscsi connection(%u) login failed - target portal "
   1293 	    "tag mismatch, expected:%d received:%lu", icp->conn_oid,
   1294 	    isp->sess_tpgt_conf, tmp);
   1295 	return (ISCSI_STATUS_PROTOCOL_ERROR);
   1296 
   1297 					}
   1298 				}
   1299 				isp->sess_tpgt_nego = (int)tmp;
   1300 				text = value_end;
   1301 
   1302 			} else if (iscsi_find_key_value("InitialR2T",
   1303 			    text, end, &value, &value_end)) {
   1304 
   1305 				/*
   1306 				 * iSCSI RFC section 12.10 states that
   1307 				 * InitialR2T is Irrelevant for a
   1308 				 * discovery session.
   1309 				 */
   1310 				if (isp->sess_type ==
   1311 				    ISCSI_SESS_TYPE_DISCOVERY) {
   1312 					/* EMPTY */
   1313 				} else if (value == NULL) {
   1314 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1315 					    "login failed - InitialR2T is "
   1316 					    "invalid - protocol error",
   1317 					    icp->conn_oid);
   1318 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1319 				} else if (strcmp(value, "Yes") == 0) {
   1320 					icp->conn_params.initial_r2t = B_TRUE;
   1321 				} else if (strcmp(value, "No") == 0) {
   1322 					icp->conn_params.initial_r2t = B_FALSE;
   1323 				} else {
   1324 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1325 					    "login failed - InitialR2T  is "
   1326 					    "invalid - protocol error",
   1327 					    icp->conn_oid);
   1328 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1329 				}
   1330 				text = value_end;
   1331 
   1332 			} else if (iscsi_find_key_value("ImmediateData",
   1333 			    text, end, &value, &value_end)) {
   1334 
   1335 				/*
   1336 				 * iSCSI RFC section 12.11 states that
   1337 				 * ImmediateData is Irrelevant for a
   1338 				 * discovery session.
   1339 				 */
   1340 				if (isp->sess_type ==
   1341 				    ISCSI_SESS_TYPE_DISCOVERY) {
   1342 					/* EMPTY */
   1343 				} else if (value == NULL) {
   1344 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1345 					    "login failed - ImmediateData is "
   1346 					    "invalid - protocol error",
   1347 					    icp->conn_oid);
   1348 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1349 				} else if (strcmp(value, "Yes") == 0) {
   1350 					icp->conn_params.immediate_data = 1;
   1351 				} else if (strcmp(value, "No") == 0) {
   1352 					icp->conn_params.immediate_data = 0;
   1353 				} else {
   1354 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1355 					    "login failed - ImmediateData is "
   1356 					    "invalid - protocol error",
   1357 					    icp->conn_oid);
   1358 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1359 				}
   1360 				text = value_end;
   1361 
   1362 			} else if (iscsi_find_key_value(
   1363 			    "MaxRecvDataSegmentLength", text, end,
   1364 			    &value, &value_end)) {
   1365 
   1366 				if (ddi_strtoul(value, &tmpe, 0, &tmp) != 0) {
   1367 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1368 					    "login failed - MaxRecvDataSegment"
   1369 					    "Length is invalid - protocol "
   1370 					    "error", icp->conn_oid);
   1371 					return (ISCSI_STATUS_NEGO_FAIL);
   1372 				}
   1373 				icp->conn_params.max_recv_data_seg_len =
   1374 				    icp->conn_params.max_xmit_data_seg_len =
   1375 				    (int)tmp;
   1376 
   1377 				text = value_end;
   1378 			} else if (iscsi_find_key_value("FirstBurstLength",
   1379 			    text, end, &value, &value_end)) {
   1380 
   1381 				/*
   1382 				 * iSCSI RFC section 12.14 states that
   1383 				 * FirstBurstLength is Irrelevant if
   1384 				 * InitialR2T=Yes and ImmediateData=No
   1385 				 * or is this is a discovery session.
   1386 				 */
   1387 				if ((isp->sess_type ==
   1388 				    ISCSI_SESS_TYPE_DISCOVERY)) {
   1389 					/* EMPTY */
   1390 				} else if (value &&
   1391 				    (strcmp(value, "Irrelevant") == 0)) {
   1392 					/* irrelevant */
   1393 					fbl_irrelevant = B_TRUE;
   1394 				} else if (ddi_strtoul(
   1395 				    value, &tmpe, 0, &tmp) != 0) {
   1396 					/* bad value */
   1397 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1398 					    "login failed - FirstBurstLength"
   1399 					    "is invalid - protocol error",
   1400 					    icp->conn_oid);
   1401 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1402 				} else {
   1403 					/* good value */
   1404 					icp->conn_params.first_burst_length =
   1405 					    (int)tmp;
   1406 				}
   1407 				text = value_end;
   1408 			} else if (iscsi_find_key_value("MaxBurstLength",
   1409 			    text, end, &value, &value_end)) {
   1410 				/*
   1411 				 * iSCSI RFC section 12.13 states that
   1412 				 * MaxBurstLength is Irrelevant for a
   1413 				 * discovery session.
   1414 				 */
   1415 				if (isp->sess_type ==
   1416 				    ISCSI_SESS_TYPE_DISCOVERY) {
   1417 					/* EMPTY */
   1418 				} else if (ddi_strtoul(
   1419 				    value, &tmpe, 0, &tmp) != 0) {
   1420 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1421 					    "login failed - MaxBurstLength"
   1422 					    "is invalid - protocol error",
   1423 					    icp->conn_oid);
   1424 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1425 				} else {
   1426 					icp->conn_params.max_burst_length =
   1427 					    (int)tmp;
   1428 				}
   1429 
   1430 				text = value_end;
   1431 
   1432 			} else if (iscsi_find_key_value("HeaderDigest",
   1433 			    text, end, &value, &value_end)) {
   1434 
   1435 				if (strcmp(value, "None") == 0) {
   1436 					if (icp->conn_params.header_digest !=
   1437 					    ISCSI_DIGEST_CRC32C) {
   1438 						icp->conn_params.header_digest =
   1439 						    ISCSI_DIGEST_NONE;
   1440 					} else {
   1441 						cmn_err(CE_WARN, "iscsi "
   1442 						    "connection(%u) login "
   1443 						    "failed - HeaderDigest="
   1444 						    "CRC32 is required, can't "
   1445 						    "accept %s",
   1446 						    icp->conn_oid, text);
   1447 						return (ISCSI_STATUS_NEGO_FAIL);
   1448 					}
   1449 				} else if (strcmp(value, "CRC32C") == 0) {
   1450 					if (icp->conn_params.header_digest !=
   1451 					    ISCSI_DIGEST_NONE) {
   1452 						icp->conn_params.header_digest =
   1453 						    ISCSI_DIGEST_CRC32C;
   1454 					} else {
   1455 						cmn_err(CE_WARN, "iscsi "
   1456 						    "connection(%u) login "
   1457 						    "failed - HeaderDigest="
   1458 						    "None is required, can't "
   1459 						    "accept %s",
   1460 						    icp->conn_oid, text);
   1461 						return (ISCSI_STATUS_NEGO_FAIL);
   1462 					}
   1463 				} else {
   1464 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1465 					    "login failed - HeaderDigest "
   1466 					    "can't accept %s", icp->conn_oid,
   1467 					    text);
   1468 					return (ISCSI_STATUS_NEGO_FAIL);
   1469 				}
   1470 				text = value_end;
   1471 			} else if (iscsi_find_key_value("DataDigest", text,
   1472 			    end, &value, &value_end)) {
   1473 
   1474 				if (strcmp(value, "None") == 0) {
   1475 					if (icp->conn_params.data_digest !=
   1476 					    ISCSI_DIGEST_CRC32C) {
   1477 						icp->conn_params.data_digest =
   1478 						    ISCSI_DIGEST_NONE;
   1479 					} else {
   1480 						cmn_err(CE_WARN, "iscsi "
   1481 						    "connection(%u) login "
   1482 						    "failed - DataDigest="
   1483 						    "CRC32C is required, "
   1484 						    "can't accept %s",
   1485 						    icp->conn_oid, text);
   1486 						return (ISCSI_STATUS_NEGO_FAIL);
   1487 					}
   1488 				} else if (strcmp(value, "CRC32C") == 0) {
   1489 					if (icp->conn_params.data_digest !=
   1490 					    ISCSI_DIGEST_NONE) {
   1491 						icp->conn_params.data_digest =
   1492 						    ISCSI_DIGEST_CRC32C;
   1493 					} else {
   1494 						cmn_err(CE_WARN, "iscsi "
   1495 						    "connection(%u) login "
   1496 						    "failed - DataDigest=None "
   1497 						    "is required, can't "
   1498 						    "accept %s",
   1499 						    icp->conn_oid, text);
   1500 						return (ISCSI_STATUS_NEGO_FAIL);
   1501 					}
   1502 				} else {
   1503 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1504 					    "login failed - can't accept %s",
   1505 					    icp->conn_oid, text);
   1506 					return (ISCSI_STATUS_NEGO_FAIL);
   1507 				}
   1508 				text = value_end;
   1509 
   1510 			} else if (iscsi_find_key_value("DefaultTime2Wait",
   1511 			    text, end, &value, &value_end)) {
   1512 
   1513 				if (ddi_strtoul(value, &tmpe, 0, &tmp) != 0) {
   1514 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1515 					    "login failed - DefaultTime2Wait "
   1516 					    "is invalid - protocol error",
   1517 					    icp->conn_oid);
   1518 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1519 				}
   1520 				icp->conn_params.default_time_to_wait =
   1521 				    (int)tmp;
   1522 
   1523 				text = value_end;
   1524 
   1525 			} else if (iscsi_find_key_value("DefaultTime2Retain",
   1526 			    text, end, &value, &value_end)) {
   1527 
   1528 				if (ddi_strtoul(value, &tmpe, 0, &tmp) != 0) {
   1529 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1530 					    "login failed - DefaultTime2Retain "
   1531 					    "is invalid - protocol error",
   1532 					    icp->conn_oid);
   1533 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1534 				}
   1535 				icp->conn_params.default_time_to_retain =
   1536 				    (int)tmp;
   1537 
   1538 				text = value_end;
   1539 
   1540 			} else if (iscsi_find_key_value("OFMarker", text,
   1541 			    end, &value, &value_end)) {
   1542 
   1543 				/*
   1544 				 * result function is AND, target must
   1545 				 * honor our No
   1546 				 */
   1547 				text = value_end;
   1548 
   1549 			} else if (iscsi_find_key_value("OFMarkInt", text,
   1550 			    end, &value, &value_end)) {
   1551 
   1552 				/*
   1553 				 * we don't do markers, so we don't care
   1554 				 */
   1555 				text = value_end;
   1556 
   1557 			} else if (iscsi_find_key_value("IFMarker", text,
   1558 			    end, &value, &value_end)) {
   1559 
   1560 				/*
   1561 				 * result function is AND, target must
   1562 				 * honor our No
   1563 				 */
   1564 				text = value_end;
   1565 
   1566 			} else if (iscsi_find_key_value("IFMarkInt", text,
   1567 			    end, &value, &value_end)) {
   1568 
   1569 				/*
   1570 				 * we don't do markers, so we don't care
   1571 				 */
   1572 				text = value_end;
   1573 
   1574 			} else if (iscsi_find_key_value("DataPDUInOrder",
   1575 			    text, end, &value, &value_end)) {
   1576 
   1577 				/*
   1578 				 * iSCSI RFC section 12.18 states that
   1579 				 * DataPDUInOrder is Irrelevant for a
   1580 				 * discovery session.
   1581 				 */
   1582 				if (isp->sess_type ==
   1583 				    ISCSI_SESS_TYPE_DISCOVERY) {
   1584 					/* EMPTY */
   1585 				} else if (value == NULL) {
   1586 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1587 					    "login failed - InitialR2T is "
   1588 					    "invalid - protocol error",
   1589 					    icp->conn_oid);
   1590 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1591 				} else if (strcmp(value, "Yes") == 0) {
   1592 					icp->conn_params.data_pdu_in_order =
   1593 					    B_TRUE;
   1594 				} else if (strcmp(value, "No") == 0) {
   1595 					icp->conn_params.data_pdu_in_order =
   1596 					    B_FALSE;
   1597 				} else {
   1598 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1599 					    "login failed - InitialR2T is "
   1600 					    "invalid - protocol error",
   1601 					    icp->conn_oid);
   1602 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1603 				}
   1604 				text = value_end;
   1605 
   1606 			} else if (iscsi_find_key_value("DataSequenceInOrder",
   1607 			    text, end, &value, &value_end)) {
   1608 
   1609 				/*
   1610 				 * iSCSI RFC section 12.19 states that
   1611 				 * DataSequenceInOrder is Irrelevant for a
   1612 				 * discovery session.
   1613 				 */
   1614 				if (isp->sess_type ==
   1615 				    ISCSI_SESS_TYPE_DISCOVERY) {
   1616 					/* EMPTY */
   1617 				} else if (value == NULL) {
   1618 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1619 					    "login failed - InitialR2T is "
   1620 					    "invalid - protocol error",
   1621 					    icp->conn_oid);
   1622 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1623 				} else if (strcmp(value, "Yes") == 0) {
   1624 					icp->conn_params.
   1625 					    data_sequence_in_order = B_TRUE;
   1626 				} else if (strcmp(value, "No") == 0) {
   1627 					icp->conn_params.
   1628 					    data_sequence_in_order = B_FALSE;
   1629 				} else {
   1630 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1631 					    "login failed - InitialR2T is "
   1632 					    "invalid - protocol error",
   1633 					    icp->conn_oid);
   1634 					return (ISCSI_STATUS_PROTOCOL_ERROR);
   1635 				}
   1636 				text = value_end;
   1637 
   1638 			} else if (iscsi_find_key_value("MaxOutstandingR2T",
   1639 			    text, end, &value, &value_end)) {
   1640 
   1641 				/*
   1642 				 * iSCSI RFC section 12.17 states that
   1643 				 * MaxOutstandingR2T is Irrelevant for a
   1644 				 * discovery session.
   1645 				 */
   1646 				if (isp->sess_type ==
   1647 				    ISCSI_SESS_TYPE_DISCOVERY) {
   1648 					/* EMPTY */
   1649 				} else if (strcmp(value, "1")) {
   1650 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1651 					    "login failed - can't accept "
   1652 					    "MaxOutstandingR2T %s",
   1653 					    icp->conn_oid, value);
   1654 					return (ISCSI_STATUS_NEGO_FAIL);
   1655 				}
   1656 				text = value_end;
   1657 
   1658 			} else if (iscsi_find_key_value("MaxConnections",
   1659 			    text, end, &value, &value_end)) {
   1660 
   1661 				/*
   1662 				 * iSCSI RFC section 12.2 states that
   1663 				 * MaxConnections is Irrelevant for a
   1664 				 * discovery session.
   1665 				 */
   1666 				if (isp->sess_type ==
   1667 				    ISCSI_SESS_TYPE_DISCOVERY) {
   1668 					/* EMPTY */
   1669 				} else if (strcmp(value, "1")) {
   1670 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1671 					    "login failed - can't accept "
   1672 					    "MaxConnections %s",
   1673 					    icp->conn_oid, value);
   1674 					return (ISCSI_STATUS_NEGO_FAIL);
   1675 				}
   1676 				text = value_end;
   1677 
   1678 			} else if (iscsi_find_key_value("ErrorRecoveryLevel",
   1679 			    text, end, &value, &value_end)) {
   1680 
   1681 				if (strcmp(value, "0")) {
   1682 
   1683 					cmn_err(CE_WARN, "iscsi connection(%u) "
   1684 					    "login failed - can't accept "
   1685 					    "ErrorRecoveryLevel %s",
   1686 					    icp->conn_oid, value);
   1687 					return (ISCSI_STATUS_NEGO_FAIL);
   1688 				}
   1689 				text = value_end;
   1690 
   1691 			} else {
   1692 				cmn_err(CE_WARN, "iscsi connection(%u) "
   1693 				    "login failed - ignoring login "
   1694 				    "parameter %s", icp->conn_oid, value);
   1695 				text = value_end;
   1696 			}
   1697 			break;
   1698 		default:
   1699 			return (ISCSI_STATUS_INTERNAL_ERROR);
   1700 		}
   1701 	}
   1702 
   1703 	/*
   1704 	 * iSCSI RFC section 12.14 states that
   1705 	 * FirstBurstLength is Irrelevant if
   1706 	 * InitialR2T=Yes and ImmediateData=No.
   1707 	 * This is a final check to make sure
   1708 	 * the array didn't make a protocol
   1709 	 * violation.
   1710 	 */
   1711 	if ((fbl_irrelevant == B_TRUE) &&
   1712 	    ((icp->conn_params.initial_r2t != B_TRUE) ||
   1713 	    (icp->conn_params.immediate_data != B_FALSE))) {
   1714 		cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
   1715 		    "FirstBurstLength=Irrelevant and (InitialR2T!=Yes or "
   1716 		    "ImmediateData!=No) - protocol error", icp->conn_oid);
   1717 		return (ISCSI_STATUS_PROTOCOL_ERROR);
   1718 	}
   1719 
   1720 	if (icp->conn_current_stage == ISCSI_SECURITY_NEGOTIATION_STAGE) {
   1721 		switch (iscsiAuthClientRecvEnd(auth_client, iscsi_null_callback,
   1722 		    (void *)isp, NULL)) {
   1723 		case iscsiAuthStatusContinue:
   1724 			/*
   1725 			 * continue sending PDUs
   1726 			 */
   1727 			break;
   1728 
   1729 		case iscsiAuthStatusPass:
   1730 			break;
   1731 
   1732 		case iscsiAuthStatusInProgress:
   1733 			/*
   1734 			 * this should only occur if we were authenticating the
   1735 			 * target, which we don't do yet, so treat this as an
   1736 			 * error.
   1737 			 */
   1738 		case iscsiAuthStatusNoError:
   1739 			/*
   1740 			 * treat this as an error, since we should get a
   1741 			 * different code
   1742 			 */
   1743 		case iscsiAuthStatusError:
   1744 		case iscsiAuthStatusFail:
   1745 		default:
   1746 			debug_status = 0;
   1747 
   1748 			if (iscsiAuthClientGetDebugStatus(auth_client,
   1749 			    &debug_status) != iscsiAuthStatusNoError) {
   1750 
   1751 				cmn_err(CE_WARN, "iscsi connection(%u) login "
   1752 				    "failed - authentication failed with "
   1753 				    "target (%s)", icp->conn_oid,
   1754 				    iscsiAuthClientDebugStatusToText(
   1755 				    debug_status));
   1756 
   1757 			} else {
   1758 
   1759 				cmn_err(CE_WARN, "iscsi connection(%u) login "
   1760 				    "failed - authentication failed with "
   1761 				    "target", icp->conn_oid);
   1762 
   1763 			}
   1764 			return (ISCSI_STATUS_AUTHENTICATION_FAILED);
   1765 		}
   1766 	}
   1767 
   1768 	/*
   1769 	 * record some of the PDU fields for later use
   1770 	 */
   1771 	isp->sess_tsid = ntohs(ilrhp->tsid);
   1772 	isp->sess_expcmdsn = ntohl(ilrhp->expcmdsn);
   1773 	isp->sess_maxcmdsn = ntohl(ilrhp->maxcmdsn);
   1774 	if (ilrhp->status_class == ISCSI_STATUS_CLASS_SUCCESS) {
   1775 		icp->conn_expstatsn = ntohl(ilrhp->statsn) + 1;
   1776 	}
   1777 
   1778 	if (transit) {
   1779 		/*
   1780 		 * advance to the next stage
   1781 		 */
   1782 		icp->conn_partial_response = 0;
   1783 		icp->conn_current_stage =
   1784 		    ilrhp->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK;
   1785 	} else {
   1786 		/*
   1787 		 * we got a partial response, don't advance, more
   1788 		 * negotiation to do
   1789 		 */
   1790 		icp->conn_partial_response = 1;
   1791 	}
   1792 
   1793 	/*
   1794 	 * this PDU is ok, though the login process
   1795 	 * may not be done yet
   1796 	 */
   1797 	return (ISCSI_STATUS_SUCCESS);
   1798 }
   1799 
   1800 /*
   1801  * iscsi_add_text - caller is assumed to be well-behaved and passing NUL
   1802  * terminated strings
   1803  */
   1804 int
   1805 iscsi_add_text(idm_pdu_t *text_pdu, int max_data_length,
   1806     char *param, char *value)
   1807 {
   1808 	int	param_len	= 0;
   1809 	int	value_len	= 0;
   1810 	int	length		= 0;
   1811 	int	pdu_length	= 0;
   1812 	char	*text		= NULL;
   1813 	char	*end		= NULL;
   1814 
   1815 	ASSERT(text_pdu != NULL);
   1816 	ASSERT(param != NULL);
   1817 	ASSERT(value != NULL);
   1818 
   1819 	param_len = strlen(param);
   1820 	value_len = strlen(value);
   1821 	/* param, separator, value, and trailing NULL */
   1822 	length		= param_len + 1 + value_len + 1;
   1823 	pdu_length	= text_pdu->isp_datalen;
   1824 	text		= (char *)text_pdu->isp_data + pdu_length;
   1825 	end		= (char *)text_pdu->isp_data + max_data_length;
   1826 	pdu_length	+= length;
   1827 
   1828 	if (text + length >= end) {
   1829 		return (0);
   1830 	}
   1831 
   1832 	/* param */
   1833 	(void) strncpy(text, param, param_len);
   1834 	text += param_len;
   1835 
   1836 	/* separator */
   1837 	*text++ = ISCSI_TEXT_SEPARATOR;
   1838 
   1839 	/* value */
   1840 	(void) strncpy(text, value, value_len);
   1841 	text += value_len;
   1842 
   1843 	/* NULL */
   1844 	*text++ = '\0';
   1845 
   1846 	/* update the length in the PDU header */
   1847 	text_pdu->isp_datalen = pdu_length;
   1848 	hton24(text_pdu->isp_hdr->dlength, pdu_length);
   1849 
   1850 	return (1);
   1851 }
   1852 
   1853 /*
   1854  * iscsi_get_next_text - get the next line of text from the given data
   1855  * buffer.  This function searches from the address given for the
   1856  * curr_text parameter.  If curr_text_parameter is NULL return first
   1857  * line in buffer.  The return value is the address of the next line
   1858  * based upon where curr_text is located.
   1859  *
   1860  */
   1861 char *
   1862 iscsi_get_next_text(char *data, int max_data_length, char *curr_text)
   1863 {
   1864 	char *curr_data;
   1865 
   1866 	ASSERT(data != NULL);
   1867 
   1868 	/* check if any data exists, if not return */
   1869 	if (max_data_length == 0) {
   1870 		return (NULL);
   1871 	}
   1872 
   1873 	/* handle first call to this function */
   1874 	if (curr_text == NULL) {
   1875 		return (data);
   1876 	}
   1877 
   1878 	/* move to next text string */
   1879 	curr_data = curr_text;
   1880 	while ((curr_data < (data + max_data_length)) && *curr_data) {
   1881 		curr_data++;
   1882 	}
   1883 	curr_data++;		/* go past the NULL to the next entry */
   1884 
   1885 	/* check whether data end reached */
   1886 	if (curr_data >= (data + max_data_length)) {
   1887 		return (NULL);
   1888 	}
   1889 
   1890 	return (curr_data);
   1891 }
   1892 
   1893 
   1894 /*
   1895  * iscsi_find_key_value -
   1896  *
   1897  */
   1898 static int
   1899 iscsi_find_key_value(char *param, char *ihp, char *pdu_end,
   1900     char **value_start, char **value_end)
   1901 {
   1902 	char *str = param;
   1903 	char *text = ihp;
   1904 	char *value = NULL;
   1905 
   1906 	if (value_start)
   1907 		*value_start = NULL;
   1908 	if (value_end)
   1909 		*value_end = NULL;
   1910 
   1911 	/*
   1912 	 * make sure they contain the same bytes
   1913 	 */
   1914 	while (*str) {
   1915 		if (text >= pdu_end) {
   1916 			return (0);
   1917 		}
   1918 		if (*text == '\0') {
   1919 			return (0);
   1920 		}
   1921 		if (*str != *text) {
   1922 			return (0);
   1923 		}
   1924 		str++;
   1925 		text++;
   1926 	}
   1927 
   1928 	if ((text >= pdu_end) ||
   1929 	    (*text == '\0') ||
   1930 	    (*text != ISCSI_TEXT_SEPARATOR)) {
   1931 		return (0);
   1932 	}
   1933 
   1934 	/*
   1935 	 * find the value
   1936 	 */
   1937 	value = text + 1;
   1938 
   1939 	/*
   1940 	 * find the end of the value
   1941 	 */
   1942 	while ((text < pdu_end) && (*text))
   1943 		text++;
   1944 
   1945 	if (value_start)
   1946 		*value_start = value;
   1947 	if (value_end)
   1948 		*value_end = text;
   1949 
   1950 	return (1);
   1951 }
   1952 
   1953 
   1954 /*
   1955  * iscsi_update_address - This function is used on a login redirection.
   1956  * During the login redirection we are asked to switch to an IP address
   1957  * port different than the one we were logging into.
   1958  */
   1959 static iscsi_status_t
   1960 iscsi_update_address(iscsi_conn_t *icp, char *in)
   1961 {
   1962 	char		*addr_str, *port_str, *tpgt_str;
   1963 	int		type;
   1964 	struct hostent	*hptr;
   1965 	unsigned long	tmp;
   1966 	int		error_num;
   1967 	int		port;
   1968 
   1969 	ASSERT(icp != NULL);
   1970 	ASSERT(in != NULL);
   1971 
   1972 	/* parse login redirection response */
   1973 	if (parse_addr_port_tpgt(in, &addr_str, &type,
   1974 	    &port_str, &tpgt_str) == B_FALSE) {
   1975 		return (ISCSI_STATUS_PROTOCOL_ERROR);
   1976 	}
   1977 
   1978 	/* convert addr_str */
   1979 	hptr = kgetipnodebyname(addr_str, type, AI_ALL, &error_num);
   1980 	if (!hptr) {
   1981 		return (ISCSI_STATUS_PROTOCOL_ERROR);
   1982 	}
   1983 
   1984 	/* convert port_str */
   1985 	if (port_str != NULL) {
   1986 		(void) ddi_strtoul(port_str, NULL, 0, &tmp);
   1987 		port = (int)tmp;
   1988 	} else {
   1989 		port = ISCSI_LISTEN_PORT;
   1990 	}
   1991 
   1992 	iscsid_addr_to_sockaddr(hptr->h_length, *hptr->h_addr_list,
   1993 	    port, &icp->conn_curr_addr.sin);
   1994 
   1995 	kfreehostent(hptr);
   1996 	return (ISCSI_STATUS_SUCCESS);
   1997 }
   1998 
   1999 void
   2000 iscsi_login_update_state(iscsi_conn_t *icp, iscsi_login_state_t next_state)
   2001 {
   2002 	mutex_enter(&icp->conn_login_mutex);
   2003 	(void) iscsi_login_update_state_locked(icp, next_state);
   2004 	mutex_exit(&icp->conn_login_mutex);
   2005 }
   2006 
   2007 void
   2008 iscsi_login_update_state_locked(iscsi_conn_t *icp,
   2009     iscsi_login_state_t next_state)
   2010 {
   2011 	ASSERT(mutex_owned(&icp->conn_login_mutex));
   2012 	next_state = (next_state > LOGIN_MAX) ? LOGIN_MAX : next_state;
   2013 	idm_sm_audit_state_change(&icp->conn_state_audit,
   2014 	    SAS_ISCSI_LOGIN, icp->conn_login_state, next_state);
   2015 
   2016 	ISCSI_LOGIN_LOG(CE_NOTE, "iscsi_login_update_state conn %p %d -> %d",
   2017 	    (void *)icp, icp->conn_login_state, next_state);
   2018 
   2019 	icp->conn_login_state = next_state;
   2020 	cv_broadcast(&icp->conn_login_cv);
   2021 }
   2022 
   2023 
   2024 
   2025 /*
   2026  * iscsi_null_callback - This callback may be used under certain
   2027  * conditions when authenticating a target, but I'm not sure what
   2028  * we need to do here.
   2029  */
   2030 /* ARGSUSED */
   2031 static void
   2032 iscsi_null_callback(void *user_handle, void *message_handle, int auth_status)
   2033 {
   2034 }
   2035 
   2036 
   2037 /*
   2038  * iscsi_login_failure_str -
   2039  *
   2040  */
   2041 static char *
   2042 iscsi_login_failure_str(uchar_t status_class, uchar_t status_detail)
   2043 {
   2044 	switch (status_class) {
   2045 	case 0x00:
   2046 		switch (status_detail) {
   2047 		case 0x00:
   2048 			return ("Login is proceeding okay.");
   2049 		default:
   2050 			break;
   2051 		}
   2052 	case 0x01:
   2053 		switch (status_detail) {
   2054 		case 0x01:
   2055 			return ("Requested ITN has moved temporarily to "
   2056 			    "the address provided.");
   2057 		case 0x02:
   2058 			return ("Requested ITN has moved permanently to "
   2059 			    "the address provided.");
   2060 		default:
   2061 			break;
   2062 		}
   2063 	case 0x02:
   2064 		switch (status_detail) {
   2065 		case 0x00:
   2066 			return ("Miscellaneous iSCSI initiator errors.");
   2067 		case 0x01:
   2068 			return ("Initiator could not be successfully "
   2069 			    "authenticated.");
   2070 		case 0x02:
   2071 			return ("Initiator is not allowed access to the "
   2072 			    "given target.");
   2073 		case 0x03:
   2074 			return ("Requested ITN does not exist at this "
   2075 			    "address.");
   2076 		case 0x04:
   2077 			return ("Requested ITN has been removed and no "
   2078 			    "forwarding address is provided.");
   2079 		case 0x05:
   2080 			return ("Requested iSCSI version range is not "
   2081 			    "supported by the target.");
   2082 		case 0x06:
   2083 			return ("No more connections can be accepted on "
   2084 			    "this Session ID (SSID).");
   2085 		case 0x07:
   2086 			return ("Missing parameters (e.g., iSCSI initiator "
   2087 			    "and/or target name).");
   2088 		case 0x08:
   2089 			return ("Target does not support session spanning "
   2090 			    "to this connection (address).");
   2091 		case 0x09:
   2092 			return ("Target does not support this type of "
   2093 			    "session or not from this initiator.");
   2094 		case 0x0A:
   2095 			return ("Attempt to add a connection to a "
   2096 			    "nonexistent session.");
   2097 		case 0x0B:
   2098 			return ("Invalid request type during login.");
   2099 		default:
   2100 			break;
   2101 		}
   2102 	case 0x03:
   2103 		switch (status_detail) {
   2104 		case 0x00:
   2105 			return ("Target hardware or software error.");
   2106 		case 0x01:
   2107 			return ("iSCSI service or target is not currently "
   2108 			    "operational.");
   2109 		case 0x02:
   2110 			return ("Target has insufficient session, connection "
   2111 			    "or other resources.");
   2112 		default:
   2113 			break;
   2114 		}
   2115 	}
   2116 	return ("Unknown login response received.");
   2117 }
   2118 
   2119 
   2120 /*
   2121  * iscsi_login_connect -
   2122  */
   2123 static iscsi_status_t
   2124 iscsi_login_connect(iscsi_conn_t *icp)
   2125 {
   2126 	iscsi_hba_t		*ihp;
   2127 	iscsi_sess_t		*isp;
   2128 	struct sockaddr		*addr;
   2129 	idm_conn_req_t		cr;
   2130 	idm_status_t		rval;
   2131 	clock_t			lbolt;
   2132 
   2133 	ASSERT(icp != NULL);
   2134 	isp = icp->conn_sess;
   2135 	ASSERT(isp != NULL);
   2136 	ihp = isp->sess_hba;
   2137 	ASSERT(ihp != NULL);
   2138 	addr = &icp->conn_curr_addr.sin;
   2139 
   2140 	/* Make sure that scope_id is zero if it is an IPv6 address */
   2141 	if (addr->sa_family == AF_INET6) {
   2142 		((struct sockaddr_in6 *)addr)->sin6_scope_id = 0;
   2143 	}
   2144 
   2145 	/* delay the connect process if required */
   2146 	lbolt = ddi_get_lbolt();
   2147 	if (lbolt < icp->conn_login_min) {
   2148 		if (icp->conn_login_max < icp->conn_login_min) {
   2149 			delay(icp->conn_login_max - lbolt);
   2150 		} else {
   2151 			delay(icp->conn_login_min - lbolt);
   2152 		}
   2153 	}
   2154 
   2155 	/* Create IDM connection context */
   2156 	cr.cr_domain = addr->sa_family;
   2157 	cr.cr_type = SOCK_STREAM;
   2158 	cr.cr_protocol = 0;
   2159 	cr.cr_bound = icp->conn_bound;
   2160 	cr.cr_li = icp->conn_sess->sess_hba->hba_li;
   2161 	cr.icr_conn_ops.icb_rx_misc = &iscsi_rx_misc_pdu;
   2162 	cr.icr_conn_ops.icb_rx_error = &iscsi_rx_error_pdu;
   2163 	cr.icr_conn_ops.icb_rx_scsi_rsp = &iscsi_rx_scsi_rsp;
   2164 	cr.icr_conn_ops.icb_client_notify = &iscsi_client_notify;
   2165 	cr.icr_conn_ops.icb_build_hdr = &iscsi_build_hdr;
   2166 	cr.icr_conn_ops.icb_task_aborted = &iscsi_task_aborted;
   2167 	bcopy(addr, &cr.cr_ini_dst_addr,
   2168 	    sizeof (cr.cr_ini_dst_addr));
   2169 	bcopy(&icp->conn_bound_addr, &cr.cr_bound_addr,
   2170 	    sizeof (cr.cr_bound_addr));
   2171 	if (isp->sess_boot == B_TRUE) {
   2172 		cr.cr_boot_conn = B_TRUE;
   2173 	} else {
   2174 		cr.cr_boot_conn = B_FALSE;
   2175 	}
   2176 
   2177 	/*
   2178 	 * Allocate IDM connection context
   2179 	 */
   2180 	rval = idm_ini_conn_create(&cr, &icp->conn_ic);
   2181 	if (rval != IDM_STATUS_SUCCESS) {
   2182 		return (ISCSI_STATUS_LOGIN_FAILED);
   2183 	}
   2184 
   2185 	icp->conn_ic->ic_handle = icp;
   2186 
   2187 	/*
   2188 	 * About to initiate connect, reset login state.
   2189 	 */
   2190 	iscsi_login_update_state(icp, LOGIN_START);
   2191 
   2192 	/*
   2193 	 * Make sure the connection doesn't go away until we are done with it.
   2194 	 * This hold will prevent us from receiving a CN_CONNECT_DESTROY
   2195 	 * notification on this connection until we are ready.
   2196 	 */
   2197 	idm_conn_hold(icp->conn_ic);
   2198 
   2199 	/*
   2200 	 * When iSCSI initiator to target IO timeout or connection failure
   2201 	 * Connection retry is needed for normal operational session.
   2202 	 */
   2203 	if ((icp->conn_sess->sess_type == ISCSI_SESS_TYPE_NORMAL) &&
   2204 	    ((icp->conn_state == ISCSI_CONN_STATE_FAILED) ||
   2205 	    (icp->conn_state == ISCSI_CONN_STATE_POLLING))) {
   2206 		icp->conn_ic->ic_conn_params.nonblock_socket = B_TRUE;
   2207 		icp->conn_ic->ic_conn_params.conn_login_max =
   2208 		    icp->conn_login_max;
   2209 		if (icp->conn_state == ISCSI_CONN_STATE_POLLING) {
   2210 			icp->conn_ic->ic_conn_params.conn_login_interval =
   2211 			    icp->conn_tunable_params.polling_login_delay;
   2212 		} else {
   2213 			icp->conn_ic->ic_conn_params.conn_login_interval =
   2214 			    ISCSI_LOGIN_RETRY_DELAY;
   2215 		}
   2216 
   2217 	} else {
   2218 		icp->conn_ic->ic_conn_params.nonblock_socket = B_FALSE;
   2219 		icp->conn_ic->ic_conn_params.conn_login_max = 0;
   2220 		icp->conn_ic->ic_conn_params.conn_login_interval = 0;
   2221 	}
   2222 	/*
   2223 	 * Attempt connection.  Upon return we will either be ready to
   2224 	 * login or disconnected.  If idm_ini_conn_connect fails we
   2225 	 * will eventually receive a CN_CONNECT_DESTROY at which point
   2226 	 * we will destroy the connection allocated above (so there
   2227 	 * is no need to explicitly free it here).
   2228 	 */
   2229 	rval = idm_ini_conn_connect(icp->conn_ic);
   2230 
   2231 	if (rval != IDM_STATUS_SUCCESS) {
   2232 		cmn_err(CE_NOTE, "iscsi connection(%u) unable to "
   2233 		    "connect to target %s", icp->conn_oid,
   2234 		    icp->conn_sess->sess_name);
   2235 		idm_conn_rele(icp->conn_ic);
   2236 	}
   2237 
   2238 	return (rval == IDM_STATUS_SUCCESS ?
   2239 	    ISCSI_STATUS_SUCCESS : ISCSI_STATUS_INTERNAL_ERROR);
   2240 }
   2241 
   2242 /*
   2243  * iscsi_login_disconnect
   2244  */
   2245 static void
   2246 iscsi_login_disconnect(iscsi_conn_t *icp)
   2247 {
   2248 	/* Tell IDM to disconnect is if we are not already disconnect */
   2249 	idm_ini_conn_disconnect_sync(icp->conn_ic);
   2250 
   2251 	/*
   2252 	 * The function above may return before the CN_CONNECT_LOST
   2253 	 * notification.  Wait for it.
   2254 	 */
   2255 	mutex_enter(&icp->conn_state_mutex);
   2256 	while (icp->conn_state_idm_connected)
   2257 		cv_wait(&icp->conn_state_change,
   2258 		    &icp->conn_state_mutex);
   2259 	mutex_exit(&icp->conn_state_mutex);
   2260 }
   2261 
   2262 /*
   2263  * iscsi_notice_key_values - Create an nvlist containing the values
   2264  * that have been negotiated for this connection and pass them down to
   2265  * IDM so it can pick up any values that are important.
   2266  */
   2267 static void
   2268 iscsi_notice_key_values(iscsi_conn_t *icp)
   2269 {
   2270 	nvlist_t	*neg_nvl;
   2271 	int		rc;
   2272 
   2273 	rc = nvlist_alloc(&neg_nvl, NV_UNIQUE_NAME, KM_SLEEP);
   2274 	ASSERT(rc == 0);
   2275 
   2276 	/* Only crc32c is supported so the digest logic is simple */
   2277 	if (icp->conn_params.header_digest) {
   2278 		rc = nvlist_add_string(neg_nvl, "HeaderDigest", "crc32c");
   2279 	} else {
   2280 		rc = nvlist_add_string(neg_nvl, "HeaderDigest", "none");
   2281 	}
   2282 	ASSERT(rc == 0);
   2283 
   2284 	if (icp->conn_params.data_digest) {
   2285 		rc = nvlist_add_string(neg_nvl, "DataDigest", "crc32c");
   2286 	} else {
   2287 		rc = nvlist_add_string(neg_nvl, "DataDigest", "none");
   2288 	}
   2289 	ASSERT(rc == 0);
   2290 
   2291 	rc = nvlist_add_uint64(neg_nvl, "MaxRecvDataSegmentLength",
   2292 	    (uint64_t)icp->conn_params.max_recv_data_seg_len);
   2293 	ASSERT(rc == 0);
   2294 
   2295 	rc = nvlist_add_uint64(neg_nvl, "MaxBurstLength",
   2296 	    (uint64_t)icp->conn_params.max_burst_length);
   2297 	ASSERT(rc == 0);
   2298 
   2299 	rc = nvlist_add_uint64(neg_nvl, "MaxOutstandingR2T",
   2300 	    (uint64_t)icp->conn_params.max_outstanding_r2t);
   2301 	ASSERT(rc == 0);
   2302 
   2303 	rc = nvlist_add_uint64(neg_nvl, "ErrorRecoveryLevel",
   2304 	    (uint64_t)icp->conn_params.error_recovery_level);
   2305 	ASSERT(rc == 0);
   2306 
   2307 	rc = nvlist_add_uint64(neg_nvl, "DefaultTime2Wait",
   2308 	    (uint64_t)icp->conn_params.default_time_to_wait);
   2309 	ASSERT(rc == 0);
   2310 
   2311 	rc = nvlist_add_uint64(neg_nvl, "DefaultTime2Retain",
   2312 	    (uint64_t)icp->conn_params.default_time_to_retain);
   2313 	ASSERT(rc == 0);
   2314 
   2315 	/* Pass the list to IDM to examine, then free it */
   2316 	idm_notice_key_values(icp->conn_ic, neg_nvl);
   2317 	nvlist_free(neg_nvl);
   2318 }
   2319