xref: /onnv-gate/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_login.c (revision 12697:794116a9929f)
17836SJohn.Forte@Sun.COM /*
27836SJohn.Forte@Sun.COM  * CDDL HEADER START
37836SJohn.Forte@Sun.COM  *
47836SJohn.Forte@Sun.COM  * The contents of this file are subject to the terms of the
57836SJohn.Forte@Sun.COM  * Common Development and Distribution License (the "License").
67836SJohn.Forte@Sun.COM  * You may not use this file except in compliance with the License.
77836SJohn.Forte@Sun.COM  *
87836SJohn.Forte@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97836SJohn.Forte@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107836SJohn.Forte@Sun.COM  * See the License for the specific language governing permissions
117836SJohn.Forte@Sun.COM  * and limitations under the License.
127836SJohn.Forte@Sun.COM  *
137836SJohn.Forte@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147836SJohn.Forte@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157836SJohn.Forte@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167836SJohn.Forte@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177836SJohn.Forte@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187836SJohn.Forte@Sun.COM  *
197836SJohn.Forte@Sun.COM  * CDDL HEADER END
207836SJohn.Forte@Sun.COM  */
217836SJohn.Forte@Sun.COM /*
227836SJohn.Forte@Sun.COM  * Copyright 2000 by Cisco Systems, Inc.  All rights reserved.
2312161SJack.Meng@Sun.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
247836SJohn.Forte@Sun.COM  *
257836SJohn.Forte@Sun.COM  * iSCSI protocol login and enumeration
267836SJohn.Forte@Sun.COM  */
277836SJohn.Forte@Sun.COM 
287836SJohn.Forte@Sun.COM #include "iscsi.h"
298656SPeter.Dunlap@Sun.COM #include <sys/iscsi_protocol.h>
307836SJohn.Forte@Sun.COM #include <sys/scsi/adapters/iscsi_door.h>
317836SJohn.Forte@Sun.COM 
329162SPeter.Dunlap@Sun.COM boolean_t iscsi_login_logging = B_FALSE;
339162SPeter.Dunlap@Sun.COM 
347836SJohn.Forte@Sun.COM /* internal login protocol interfaces */
357836SJohn.Forte@Sun.COM static iscsi_status_t iscsi_login(iscsi_conn_t *icp,
369162SPeter.Dunlap@Sun.COM     uint8_t *status_class, uint8_t *status_detail);
379162SPeter.Dunlap@Sun.COM static int iscsi_add_text(idm_pdu_t *text_pdu,
387836SJohn.Forte@Sun.COM     int max_data_length, char *param, char *value);
397836SJohn.Forte@Sun.COM static int iscsi_find_key_value(char *param, char *ihp, char *pdu_end,
407836SJohn.Forte@Sun.COM     char **value_start, char **value_end);
417836SJohn.Forte@Sun.COM static void iscsi_null_callback(void *user_handle, void *message_handle,
427836SJohn.Forte@Sun.COM     int auth_status);
437836SJohn.Forte@Sun.COM static iscsi_status_t iscsi_process_login_response(iscsi_conn_t *icp,
447836SJohn.Forte@Sun.COM     iscsi_login_rsp_hdr_t *ilrhp, char *data, int max_data_length);
457836SJohn.Forte@Sun.COM static iscsi_status_t iscsi_make_login_pdu(iscsi_conn_t *icp,
469162SPeter.Dunlap@Sun.COM     idm_pdu_t *text_pdu, char *data, int max_data_length);
477836SJohn.Forte@Sun.COM static iscsi_status_t iscsi_update_address(iscsi_conn_t *icp,
487836SJohn.Forte@Sun.COM     char *address);
497836SJohn.Forte@Sun.COM static char *iscsi_login_failure_str(uchar_t status_class,
507836SJohn.Forte@Sun.COM     uchar_t status_detail);
517836SJohn.Forte@Sun.COM static void iscsi_login_end(iscsi_conn_t *icp,
529162SPeter.Dunlap@Sun.COM     iscsi_status_t status, iscsi_task_t *itp);
537836SJohn.Forte@Sun.COM static iscsi_status_t iscsi_login_connect(iscsi_conn_t *icp);
549162SPeter.Dunlap@Sun.COM static void iscsi_login_disconnect(iscsi_conn_t *icp);
559162SPeter.Dunlap@Sun.COM static void iscsi_notice_key_values(iscsi_conn_t *icp);
567836SJohn.Forte@Sun.COM 
577836SJohn.Forte@Sun.COM #define	ISCSI_LOGIN_RETRY_DELAY		5	/* seconds */
587836SJohn.Forte@Sun.COM 
5910317SZhang.Yi@Sun.COM #define	ISCSI_LOGIN_TRANSIT_FFP(flags) \
6010317SZhang.Yi@Sun.COM 	(!(flags & ISCSI_FLAG_LOGIN_CONTINUE) && \
6110317SZhang.Yi@Sun.COM 	(flags & ISCSI_FLAG_LOGIN_TRANSIT) && \
6210317SZhang.Yi@Sun.COM 	(ISCSI_LOGIN_CURRENT_STAGE(flags) == \
6310317SZhang.Yi@Sun.COM 	ISCSI_OP_PARMS_NEGOTIATION_STAGE) && \
6410317SZhang.Yi@Sun.COM 	(ISCSI_LOGIN_NEXT_STAGE(flags) == \
6510317SZhang.Yi@Sun.COM 	ISCSI_FULL_FEATURE_PHASE))
6610317SZhang.Yi@Sun.COM 
677836SJohn.Forte@Sun.COM /*
687836SJohn.Forte@Sun.COM  * +--------------------------------------------------------------------+
697836SJohn.Forte@Sun.COM  * | External Login Interface						|
707836SJohn.Forte@Sun.COM  * +--------------------------------------------------------------------+
717836SJohn.Forte@Sun.COM  */
727836SJohn.Forte@Sun.COM 
737836SJohn.Forte@Sun.COM /*
747836SJohn.Forte@Sun.COM  * iscsi_login_start - connect and perform iscsi protocol login
757836SJohn.Forte@Sun.COM  */
767836SJohn.Forte@Sun.COM iscsi_status_t
iscsi_login_start(void * arg)777836SJohn.Forte@Sun.COM iscsi_login_start(void *arg)
787836SJohn.Forte@Sun.COM {
797836SJohn.Forte@Sun.COM 	iscsi_task_t		*itp = (iscsi_task_t *)arg;
807836SJohn.Forte@Sun.COM 	iscsi_status_t		rval	= ISCSI_STATUS_LOGIN_FAILED;
817836SJohn.Forte@Sun.COM 	iscsi_conn_t		*icp;
827836SJohn.Forte@Sun.COM 	iscsi_sess_t		*isp;
837836SJohn.Forte@Sun.COM 	iscsi_hba_t		*ihp;
847836SJohn.Forte@Sun.COM 	unsigned char		status_class;
857836SJohn.Forte@Sun.COM 	unsigned char		status_detail;
867836SJohn.Forte@Sun.COM 
877836SJohn.Forte@Sun.COM 	ASSERT(itp != NULL);
887836SJohn.Forte@Sun.COM 	icp = (iscsi_conn_t *)itp->t_arg;
897836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
907836SJohn.Forte@Sun.COM 	isp = icp->conn_sess;
917836SJohn.Forte@Sun.COM 	ASSERT(isp != NULL);
927836SJohn.Forte@Sun.COM 	ihp = isp->sess_hba;
937836SJohn.Forte@Sun.COM 	ASSERT(ihp != NULL);
947836SJohn.Forte@Sun.COM 
957836SJohn.Forte@Sun.COM login_start:
969162SPeter.Dunlap@Sun.COM 	ASSERT((icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) ||
979162SPeter.Dunlap@Sun.COM 	    (icp->conn_state == ISCSI_CONN_STATE_FAILED) ||
989162SPeter.Dunlap@Sun.COM 	    (icp->conn_state == ISCSI_CONN_STATE_POLLING));
999162SPeter.Dunlap@Sun.COM 
1009162SPeter.Dunlap@Sun.COM 	icp->conn_state_ffp = B_FALSE;
10112001SZhang.Yi@Sun.COM 	icp->conn_login_status = ISCSI_INITIAL_LOGIN_STAGE;
1029162SPeter.Dunlap@Sun.COM 
1037836SJohn.Forte@Sun.COM 	/* reset connection statsn */
1047836SJohn.Forte@Sun.COM 	icp->conn_expstatsn = 0;
1057836SJohn.Forte@Sun.COM 	icp->conn_laststatsn = 0;
1067836SJohn.Forte@Sun.COM 
1077836SJohn.Forte@Sun.COM 	/* sync up authentication information */
1087836SJohn.Forte@Sun.COM 	(void) iscsi_sess_set_auth(isp);
1097836SJohn.Forte@Sun.COM 
1107836SJohn.Forte@Sun.COM 	/* sync up login and session parameters */
1117836SJohn.Forte@Sun.COM 	if (!ISCSI_SUCCESS(iscsi_conn_sync_params(icp))) {
1127836SJohn.Forte@Sun.COM 		/* unable to sync params.  fail connection attempts */
1139162SPeter.Dunlap@Sun.COM 		iscsi_login_end(icp, ISCSI_STATUS_LOGIN_FAILED, itp);
1147836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_LOGIN_FAILED);
1157836SJohn.Forte@Sun.COM 	}
1167836SJohn.Forte@Sun.COM 
1179162SPeter.Dunlap@Sun.COM 	/*
1189162SPeter.Dunlap@Sun.COM 	 * Attempt to open TCP connection, associated IDM connection will
1199162SPeter.Dunlap@Sun.COM 	 * have a hold on it that must be released after the call to
1209162SPeter.Dunlap@Sun.COM 	 * iscsi_login() below.
1219162SPeter.Dunlap@Sun.COM 	 */
1227836SJohn.Forte@Sun.COM 	if (!ISCSI_SUCCESS(iscsi_login_connect(icp))) {
12311424SJack.Meng@Sun.COM 		if ((isp->sess_boot == B_TRUE) &&
12411424SJack.Meng@Sun.COM 		    (ihp->hba_service_status_overwrite == B_TRUE) &&
12511424SJack.Meng@Sun.COM 		    (isp->sess_boot_nic_reset == B_FALSE)) {
12611424SJack.Meng@Sun.COM 			/*
12711424SJack.Meng@Sun.COM 			 * The connection to boot target failed
12811424SJack.Meng@Sun.COM 			 * before the system fully started.
12911424SJack.Meng@Sun.COM 			 * Reset the boot nic to the settings from
13011424SJack.Meng@Sun.COM 			 * firmware before retrying the connect to
13111424SJack.Meng@Sun.COM 			 * save the the system.
13211424SJack.Meng@Sun.COM 			 */
13311424SJack.Meng@Sun.COM 			if (iscsi_net_interface(B_TRUE) ==
13411424SJack.Meng@Sun.COM 			    ISCSI_STATUS_SUCCESS) {
13511424SJack.Meng@Sun.COM 				isp->sess_boot_nic_reset = B_TRUE;
13611424SJack.Meng@Sun.COM 			}
13711424SJack.Meng@Sun.COM 		}
1387836SJohn.Forte@Sun.COM 		/* retry this failure */
1397836SJohn.Forte@Sun.COM 		goto login_retry;
1407836SJohn.Forte@Sun.COM 	}
1417836SJohn.Forte@Sun.COM 
1427836SJohn.Forte@Sun.COM 	/*
1437836SJohn.Forte@Sun.COM 	 * allocate response buffer with based on default max
1447836SJohn.Forte@Sun.COM 	 * transfer size.  This size might shift during login.
1457836SJohn.Forte@Sun.COM 	 */
1469162SPeter.Dunlap@Sun.COM 	icp->conn_login_max_data_length =
1479162SPeter.Dunlap@Sun.COM 	    icp->conn_params.max_xmit_data_seg_len;
1489162SPeter.Dunlap@Sun.COM 	icp->conn_login_data = kmem_zalloc(icp->conn_login_max_data_length,
1499162SPeter.Dunlap@Sun.COM 	    KM_SLEEP);
1507836SJohn.Forte@Sun.COM 
1519162SPeter.Dunlap@Sun.COM 	/*
1529162SPeter.Dunlap@Sun.COM 	 * Start protocol login, upon return we will be either logged in
1539162SPeter.Dunlap@Sun.COM 	 * or disconnected
1549162SPeter.Dunlap@Sun.COM 	 */
1559162SPeter.Dunlap@Sun.COM 	rval = iscsi_login(icp, &status_class, &status_detail);
1567836SJohn.Forte@Sun.COM 
1577836SJohn.Forte@Sun.COM 	/* done with buffer */
1589162SPeter.Dunlap@Sun.COM 	kmem_free(icp->conn_login_data, icp->conn_login_max_data_length);
1599162SPeter.Dunlap@Sun.COM 
1609162SPeter.Dunlap@Sun.COM 	/* Release connection hold */
1619162SPeter.Dunlap@Sun.COM 	idm_conn_rele(icp->conn_ic);
1627836SJohn.Forte@Sun.COM 
1637836SJohn.Forte@Sun.COM 	/* hard failure in login */
1647836SJohn.Forte@Sun.COM 	if (!ISCSI_SUCCESS(rval)) {
1657836SJohn.Forte@Sun.COM 		/*
1667836SJohn.Forte@Sun.COM 		 * We should just give up retry if these failures are
1677836SJohn.Forte@Sun.COM 		 * detected.
1687836SJohn.Forte@Sun.COM 		 */
1697836SJohn.Forte@Sun.COM 		switch (rval) {
1707836SJohn.Forte@Sun.COM 		/*
1717836SJohn.Forte@Sun.COM 		 * We should just give up retry if these
1727836SJohn.Forte@Sun.COM 		 * failures are detected.
1737836SJohn.Forte@Sun.COM 		 */
1747836SJohn.Forte@Sun.COM 		case ISCSI_STATUS_AUTHENTICATION_FAILED:
1757836SJohn.Forte@Sun.COM 		case ISCSI_STATUS_INTERNAL_ERROR:
1767836SJohn.Forte@Sun.COM 		case ISCSI_STATUS_VERSION_MISMATCH:
1777836SJohn.Forte@Sun.COM 		case ISCSI_STATUS_NEGO_FAIL:
17812001SZhang.Yi@Sun.COM 		case ISCSI_STATUS_LOGIN_TPGT_NEGO_FAIL:
1797836SJohn.Forte@Sun.COM 			/* we don't want to retry this failure */
1809162SPeter.Dunlap@Sun.COM 			iscsi_login_end(icp, ISCSI_STATUS_LOGIN_FAILED, itp);
1817836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_LOGIN_FAILED);
1827836SJohn.Forte@Sun.COM 		default:
1837836SJohn.Forte@Sun.COM 			/* retry this failure */
1847836SJohn.Forte@Sun.COM 			goto login_retry;
1857836SJohn.Forte@Sun.COM 		}
1867836SJohn.Forte@Sun.COM 	}
1877836SJohn.Forte@Sun.COM 
1887836SJohn.Forte@Sun.COM 	/* soft failure with reason */
1897836SJohn.Forte@Sun.COM 	switch (status_class) {
1907836SJohn.Forte@Sun.COM 	case ISCSI_STATUS_CLASS_SUCCESS:
1917836SJohn.Forte@Sun.COM 		/* login was successful */
1929162SPeter.Dunlap@Sun.COM 		iscsi_login_end(icp, ISCSI_STATUS_SUCCESS, itp);
1937836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_SUCCESS);
1947836SJohn.Forte@Sun.COM 	case ISCSI_STATUS_CLASS_REDIRECT:
1957836SJohn.Forte@Sun.COM 		/* Retry at the redirected address */
1967836SJohn.Forte@Sun.COM 		goto login_start;
1977836SJohn.Forte@Sun.COM 	case ISCSI_STATUS_CLASS_TARGET_ERR:
1987836SJohn.Forte@Sun.COM 		/* retry this failure */
1997836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
2007836SJohn.Forte@Sun.COM 		    "%s (0x%02x/0x%02x)", icp->conn_oid,
2017836SJohn.Forte@Sun.COM 		    iscsi_login_failure_str(status_class, status_detail),
2027836SJohn.Forte@Sun.COM 		    status_class, status_detail);
2037836SJohn.Forte@Sun.COM 		goto login_retry;
2047836SJohn.Forte@Sun.COM 	case ISCSI_STATUS_CLASS_INITIATOR_ERR:
2057836SJohn.Forte@Sun.COM 	default:
2067836SJohn.Forte@Sun.COM 		/* All other errors are hard failures */
2077836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
2088265SBing.Zhao@Sun.COM 		    "%s (0x%02x/0x%02x) Target: %s, TPGT: %d",
2098265SBing.Zhao@Sun.COM 		    icp->conn_oid,
2107836SJohn.Forte@Sun.COM 		    iscsi_login_failure_str(status_class, status_detail),
2118265SBing.Zhao@Sun.COM 		    status_class, status_detail, isp->sess_name,
2128265SBing.Zhao@Sun.COM 		    isp->sess_tpgt_conf);
2137836SJohn.Forte@Sun.COM 
2147836SJohn.Forte@Sun.COM 		/* we don't want to retry this failure */
2159162SPeter.Dunlap@Sun.COM 		iscsi_login_end(icp, ISCSI_STATUS_LOGIN_FAILED, itp);
2167836SJohn.Forte@Sun.COM 		break;
2177836SJohn.Forte@Sun.COM 	}
2189162SPeter.Dunlap@Sun.COM 
2197836SJohn.Forte@Sun.COM 	return (ISCSI_STATUS_LOGIN_FAILED);
2207836SJohn.Forte@Sun.COM 
2217836SJohn.Forte@Sun.COM login_retry:
2227836SJohn.Forte@Sun.COM 	/* retry this failure if we haven't run out of time */
2237836SJohn.Forte@Sun.COM 	if (icp->conn_login_max > ddi_get_lbolt()) {
2247836SJohn.Forte@Sun.COM 
2257836SJohn.Forte@Sun.COM 		if (icp->conn_state == ISCSI_CONN_STATE_POLLING) {
2267836SJohn.Forte@Sun.COM 			icp->conn_login_min = ddi_get_lbolt() +
22710156SZhang.Yi@Sun.COM 			    SEC_TO_TICK(icp->conn_tunable_params.
22810156SZhang.Yi@Sun.COM 			    polling_login_delay);
2297836SJohn.Forte@Sun.COM 		} else {
2307836SJohn.Forte@Sun.COM 			icp->conn_login_min = ddi_get_lbolt() +
2317836SJohn.Forte@Sun.COM 			    SEC_TO_TICK(ISCSI_LOGIN_RETRY_DELAY);
2327836SJohn.Forte@Sun.COM 		}
2337836SJohn.Forte@Sun.COM 
2347836SJohn.Forte@Sun.COM 		if (itp->t_blocking == B_TRUE) {
2357836SJohn.Forte@Sun.COM 			goto login_start;
2367836SJohn.Forte@Sun.COM 		} else {
23712161SJack.Meng@Sun.COM 			if (ddi_taskq_dispatch(isp->sess_login_taskq,
2387836SJohn.Forte@Sun.COM 			    (void(*)())iscsi_login_start, itp, DDI_SLEEP) !=
2397836SJohn.Forte@Sun.COM 			    DDI_SUCCESS) {
2407836SJohn.Forte@Sun.COM 				iscsi_login_end(icp,
2419162SPeter.Dunlap@Sun.COM 				    ISCSI_STATUS_LOGIN_TIMED_OUT, itp);
2427836SJohn.Forte@Sun.COM 			}
2437836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_SUCCESS);
2447836SJohn.Forte@Sun.COM 		}
2457836SJohn.Forte@Sun.COM 	} else {
2467836SJohn.Forte@Sun.COM 		/* Retries exceeded */
2479162SPeter.Dunlap@Sun.COM 		iscsi_login_end(icp, ISCSI_STATUS_LOGIN_TIMED_OUT, itp);
2487836SJohn.Forte@Sun.COM 	}
2499162SPeter.Dunlap@Sun.COM 
2507836SJohn.Forte@Sun.COM 	return (ISCSI_STATUS_LOGIN_FAILED);
2517836SJohn.Forte@Sun.COM }
2527836SJohn.Forte@Sun.COM 
2537836SJohn.Forte@Sun.COM static void
iscsi_login_end(iscsi_conn_t * icp,iscsi_status_t status,iscsi_task_t * itp)2549162SPeter.Dunlap@Sun.COM iscsi_login_end(iscsi_conn_t *icp, iscsi_status_t status, iscsi_task_t *itp)
2557836SJohn.Forte@Sun.COM {
2567836SJohn.Forte@Sun.COM 	iscsi_sess_t	*isp;
25712161SJack.Meng@Sun.COM 	uint32_t	event_count;
2587836SJohn.Forte@Sun.COM 
2597836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
2607836SJohn.Forte@Sun.COM 	isp = icp->conn_sess;
2617836SJohn.Forte@Sun.COM 	ASSERT(isp != NULL);
2627836SJohn.Forte@Sun.COM 
2639162SPeter.Dunlap@Sun.COM 	if (status == ISCSI_STATUS_SUCCESS) {
2649162SPeter.Dunlap@Sun.COM 		/* Inform IDM of the relevant negotiated values */
2659162SPeter.Dunlap@Sun.COM 		iscsi_notice_key_values(icp);
2669162SPeter.Dunlap@Sun.COM 
2679162SPeter.Dunlap@Sun.COM 		/* We are now logged in */
2689162SPeter.Dunlap@Sun.COM 		iscsi_conn_update_state(icp, ISCSI_CONN_STATE_LOGGED_IN);
2699162SPeter.Dunlap@Sun.COM 
2709162SPeter.Dunlap@Sun.COM 		/* startup TX thread */
2719162SPeter.Dunlap@Sun.COM 		(void) iscsi_thread_start(icp->conn_tx_thread);
2729162SPeter.Dunlap@Sun.COM 
2739162SPeter.Dunlap@Sun.COM 		/*
2749162SPeter.Dunlap@Sun.COM 		 * Move login state machine to LOGIN_FFP.  This will
2759162SPeter.Dunlap@Sun.COM 		 * release the taskq thread handling the CN_FFP_ENABLED
2769162SPeter.Dunlap@Sun.COM 		 * allowing the IDM connection state machine to resume
2779162SPeter.Dunlap@Sun.COM 		 * processing events
2789162SPeter.Dunlap@Sun.COM 		 */
2799162SPeter.Dunlap@Sun.COM 		iscsi_login_update_state(icp, LOGIN_FFP);
2809162SPeter.Dunlap@Sun.COM 
2819162SPeter.Dunlap@Sun.COM 		/* Notify the session that a connection is logged in */
28212161SJack.Meng@Sun.COM 		event_count = atomic_inc_32_nv(&isp->sess_state_event_count);
28312161SJack.Meng@Sun.COM 		iscsi_sess_enter_state_zone(isp);
28412161SJack.Meng@Sun.COM 		iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N1, event_count);
28512161SJack.Meng@Sun.COM 		iscsi_sess_exit_state_zone(isp);
2869162SPeter.Dunlap@Sun.COM 	} else {
2879162SPeter.Dunlap@Sun.COM 		/* If login failed reset nego tpgt */
2889162SPeter.Dunlap@Sun.COM 		isp->sess_tpgt_nego = ISCSI_DEFAULT_TPGT;
2897836SJohn.Forte@Sun.COM 
2909162SPeter.Dunlap@Sun.COM 		mutex_enter(&icp->conn_state_mutex);
2919162SPeter.Dunlap@Sun.COM 		switch (icp->conn_state) {
2929162SPeter.Dunlap@Sun.COM 		case ISCSI_CONN_STATE_IN_LOGIN:
2939162SPeter.Dunlap@Sun.COM 			iscsi_conn_update_state_locked(icp,
2949162SPeter.Dunlap@Sun.COM 			    ISCSI_CONN_STATE_FREE);
2959162SPeter.Dunlap@Sun.COM 			mutex_exit(&icp->conn_state_mutex);
2969162SPeter.Dunlap@Sun.COM 			break;
2979162SPeter.Dunlap@Sun.COM 		case ISCSI_CONN_STATE_FAILED:
2989162SPeter.Dunlap@Sun.COM 			if (status == ISCSI_STATUS_LOGIN_FAILED) {
2999162SPeter.Dunlap@Sun.COM 				iscsi_conn_update_state_locked(icp,
3009162SPeter.Dunlap@Sun.COM 				    ISCSI_CONN_STATE_FREE);
3019162SPeter.Dunlap@Sun.COM 			} else {
3029162SPeter.Dunlap@Sun.COM 				/* ISCSI_STATUS_LOGIN_TIMED_OUT */
3039162SPeter.Dunlap@Sun.COM 				iscsi_conn_update_state_locked(icp,
3049162SPeter.Dunlap@Sun.COM 				    ISCSI_CONN_STATE_POLLING);
3059162SPeter.Dunlap@Sun.COM 			}
3069162SPeter.Dunlap@Sun.COM 			mutex_exit(&icp->conn_state_mutex);
30712161SJack.Meng@Sun.COM 			event_count = atomic_inc_32_nv(
30812161SJack.Meng@Sun.COM 			    &isp->sess_state_event_count);
30912161SJack.Meng@Sun.COM 			iscsi_sess_enter_state_zone(isp);
31012161SJack.Meng@Sun.COM 			iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6,
31112161SJack.Meng@Sun.COM 			    event_count);
31212161SJack.Meng@Sun.COM 			iscsi_sess_exit_state_zone(isp);
3139162SPeter.Dunlap@Sun.COM 
3149162SPeter.Dunlap@Sun.COM 			if (status == ISCSI_STATUS_LOGIN_TIMED_OUT) {
3159162SPeter.Dunlap@Sun.COM 				iscsi_conn_retry(isp, icp);
3169162SPeter.Dunlap@Sun.COM 			}
3179162SPeter.Dunlap@Sun.COM 			break;
3189162SPeter.Dunlap@Sun.COM 		case ISCSI_CONN_STATE_POLLING:
3199162SPeter.Dunlap@Sun.COM 			if (status == ISCSI_STATUS_LOGIN_FAILED) {
3209162SPeter.Dunlap@Sun.COM 				iscsi_conn_update_state_locked(icp,
3219162SPeter.Dunlap@Sun.COM 				    ISCSI_CONN_STATE_FREE);
3229162SPeter.Dunlap@Sun.COM 				mutex_exit(&icp->conn_state_mutex);
32312161SJack.Meng@Sun.COM 				event_count = atomic_inc_32_nv(
32412161SJack.Meng@Sun.COM 				    &isp->sess_state_event_count);
32512161SJack.Meng@Sun.COM 				iscsi_sess_enter_state_zone(isp);
3269162SPeter.Dunlap@Sun.COM 
3279162SPeter.Dunlap@Sun.COM 				iscsi_sess_state_machine(isp,
32812161SJack.Meng@Sun.COM 				    ISCSI_SESS_EVENT_N6, event_count);
32912161SJack.Meng@Sun.COM 
33012161SJack.Meng@Sun.COM 				iscsi_sess_exit_state_zone(isp);
3319162SPeter.Dunlap@Sun.COM 			} else {
3329162SPeter.Dunlap@Sun.COM 				/* ISCSI_STATUS_LOGIN_TIMED_OUT */
3339162SPeter.Dunlap@Sun.COM 				if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
3349162SPeter.Dunlap@Sun.COM 					mutex_exit(&icp->conn_state_mutex);
3359162SPeter.Dunlap@Sun.COM 
3369162SPeter.Dunlap@Sun.COM 					iscsi_conn_retry(isp, icp);
3379162SPeter.Dunlap@Sun.COM 				} else {
3389162SPeter.Dunlap@Sun.COM 					iscsi_conn_update_state_locked(icp,
3399162SPeter.Dunlap@Sun.COM 					    ISCSI_CONN_STATE_FREE);
3409162SPeter.Dunlap@Sun.COM 					mutex_exit(&icp->conn_state_mutex);
3419162SPeter.Dunlap@Sun.COM 				}
3429162SPeter.Dunlap@Sun.COM 			}
3439162SPeter.Dunlap@Sun.COM 			break;
34410317SZhang.Yi@Sun.COM 		case ISCSI_CONN_STATE_FREE:
34510317SZhang.Yi@Sun.COM 			mutex_exit(&icp->conn_state_mutex);
34610317SZhang.Yi@Sun.COM 			break;
3479162SPeter.Dunlap@Sun.COM 		default:
34810317SZhang.Yi@Sun.COM 			mutex_exit(&icp->conn_state_mutex);
3499162SPeter.Dunlap@Sun.COM 			ASSERT(0);
3509162SPeter.Dunlap@Sun.COM 			break;
3519162SPeter.Dunlap@Sun.COM 		}
3527836SJohn.Forte@Sun.COM 	}
3537836SJohn.Forte@Sun.COM 
3547836SJohn.Forte@Sun.COM 	if (itp->t_blocking == B_FALSE) {
3557836SJohn.Forte@Sun.COM 		kmem_free(itp, sizeof (iscsi_task_t));
3567836SJohn.Forte@Sun.COM 	}
35711424SJack.Meng@Sun.COM 
35811424SJack.Meng@Sun.COM 	isp->sess_boot_nic_reset = B_FALSE;
3597836SJohn.Forte@Sun.COM }
3607836SJohn.Forte@Sun.COM 
3617836SJohn.Forte@Sun.COM /*
3627836SJohn.Forte@Sun.COM  * +--------------------------------------------------------------------+
3637836SJohn.Forte@Sun.COM  * | Begin of protocol login routines					|
3647836SJohn.Forte@Sun.COM  * +--------------------------------------------------------------------+
3657836SJohn.Forte@Sun.COM  */
3667836SJohn.Forte@Sun.COM 
3677836SJohn.Forte@Sun.COM /*
3687836SJohn.Forte@Sun.COM  * iscsi_login - Attempt to login to the target.  The caller
3697836SJohn.Forte@Sun.COM  * must check the status class to determine if the login succeeded.
3707836SJohn.Forte@Sun.COM  * A return of 1 does not mean the login succeeded, it just means
3717836SJohn.Forte@Sun.COM  * this function worked, and the status class is valid info.  This
3727836SJohn.Forte@Sun.COM  * allows the caller to decide whether or not to retry logins, so
3737836SJohn.Forte@Sun.COM  * that we don't have any policy logic here.
3747836SJohn.Forte@Sun.COM  */
3759162SPeter.Dunlap@Sun.COM iscsi_status_t
iscsi_login(iscsi_conn_t * icp,uint8_t * status_class,uint8_t * status_detail)3769162SPeter.Dunlap@Sun.COM iscsi_login(iscsi_conn_t *icp, uint8_t *status_class, uint8_t *status_detail)
3777836SJohn.Forte@Sun.COM {
3787836SJohn.Forte@Sun.COM 	iscsi_status_t		rval		= ISCSI_STATUS_INTERNAL_ERROR;
3797836SJohn.Forte@Sun.COM 	struct iscsi_sess	*isp		= NULL;
3807836SJohn.Forte@Sun.COM 	IscsiAuthClient		*auth_client	= NULL;
3817836SJohn.Forte@Sun.COM 	int			max_data_length	= 0;
3827836SJohn.Forte@Sun.COM 	char			*data		= NULL;
3839162SPeter.Dunlap@Sun.COM 	idm_pdu_t		*text_pdu;
3849162SPeter.Dunlap@Sun.COM 	char			*buffer;
3859162SPeter.Dunlap@Sun.COM 	size_t			bufsize;
3869162SPeter.Dunlap@Sun.COM 	iscsi_login_rsp_hdr_t	*ilrhp;
3879162SPeter.Dunlap@Sun.COM 	clock_t			response_timeout, timeout_result;
3889162SPeter.Dunlap@Sun.COM 
3899162SPeter.Dunlap@Sun.COM 	buffer = icp->conn_login_data;
3909162SPeter.Dunlap@Sun.COM 	bufsize = icp->conn_login_max_data_length;
3917836SJohn.Forte@Sun.COM 
3927836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
3937836SJohn.Forte@Sun.COM 	ASSERT(buffer != NULL);
3947836SJohn.Forte@Sun.COM 	ASSERT(status_class != NULL);
3957836SJohn.Forte@Sun.COM 	ASSERT(status_detail != NULL);
3967836SJohn.Forte@Sun.COM 	isp = icp->conn_sess;
3977836SJohn.Forte@Sun.COM 	ASSERT(isp != NULL);
3987836SJohn.Forte@Sun.COM 
3997836SJohn.Forte@Sun.COM 	/*
4009162SPeter.Dunlap@Sun.COM 	 * prepare the connection, hold IDM connection until login completes
4017836SJohn.Forte@Sun.COM 	 */
4027836SJohn.Forte@Sun.COM 	icp->conn_current_stage = ISCSI_INITIAL_LOGIN_STAGE;
4037836SJohn.Forte@Sun.COM 	icp->conn_partial_response = 0;
4047836SJohn.Forte@Sun.COM 
4057836SJohn.Forte@Sun.COM 	if (isp->sess_auth.auth_buffers &&
4067836SJohn.Forte@Sun.COM 	    isp->sess_auth.num_auth_buffers) {
4077836SJohn.Forte@Sun.COM 
4087836SJohn.Forte@Sun.COM 		auth_client = (IscsiAuthClient *)isp->
4097836SJohn.Forte@Sun.COM 		    sess_auth.auth_buffers[0].address;
4107836SJohn.Forte@Sun.COM 
4117836SJohn.Forte@Sun.COM 		/*
4127836SJohn.Forte@Sun.COM 		 * prepare for authentication
4137836SJohn.Forte@Sun.COM 		 */
4147836SJohn.Forte@Sun.COM 		if (iscsiAuthClientInit(iscsiAuthNodeTypeInitiator,
4157836SJohn.Forte@Sun.COM 		    isp->sess_auth.num_auth_buffers,
4167836SJohn.Forte@Sun.COM 		    isp->sess_auth.auth_buffers) !=
4177836SJohn.Forte@Sun.COM 		    iscsiAuthStatusNoError) {
4187836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
4197836SJohn.Forte@Sun.COM 			    "unable to initialize authentication",
4207836SJohn.Forte@Sun.COM 			    icp->conn_oid);
42112001SZhang.Yi@Sun.COM 			icp->conn_login_status = ISCSI_STATUS_INTERNAL_ERROR;
4229162SPeter.Dunlap@Sun.COM 			iscsi_login_disconnect(icp);
4239162SPeter.Dunlap@Sun.COM 			iscsi_login_update_state(icp, LOGIN_DONE);
4247836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_INTERNAL_ERROR);
4257836SJohn.Forte@Sun.COM 		}
4267836SJohn.Forte@Sun.COM 
4277836SJohn.Forte@Sun.COM 		if (iscsiAuthClientSetVersion(auth_client,
4287836SJohn.Forte@Sun.COM 		    iscsiAuthVersionRfc) != iscsiAuthStatusNoError) {
4297836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
4307836SJohn.Forte@Sun.COM 			    "unable to set authentication", icp->conn_oid);
4317836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
4327836SJohn.Forte@Sun.COM 		}
4337836SJohn.Forte@Sun.COM 
4347836SJohn.Forte@Sun.COM 		if (isp->sess_auth.username &&
4357836SJohn.Forte@Sun.COM 		    (iscsiAuthClientSetUsername(auth_client,
4367836SJohn.Forte@Sun.COM 		    isp->sess_auth.username) !=
4377836SJohn.Forte@Sun.COM 		    iscsiAuthStatusNoError)) {
4387836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
4397836SJohn.Forte@Sun.COM 			    "unable to set username", icp->conn_oid);
4407836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
4417836SJohn.Forte@Sun.COM 		}
4427836SJohn.Forte@Sun.COM 
4437836SJohn.Forte@Sun.COM 		if (isp->sess_auth.password &&
4447836SJohn.Forte@Sun.COM 		    (iscsiAuthClientSetPassword(auth_client,
4457836SJohn.Forte@Sun.COM 		    isp->sess_auth.password, isp->sess_auth.password_length) !=
4467836SJohn.Forte@Sun.COM 		    iscsiAuthStatusNoError)) {
4477836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
4487836SJohn.Forte@Sun.COM 			    "unable to set password", icp->conn_oid);
4497836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
4507836SJohn.Forte@Sun.COM 		}
4517836SJohn.Forte@Sun.COM 
4527836SJohn.Forte@Sun.COM 		if (iscsiAuthClientSetIpSec(auth_client, 1) !=
4537836SJohn.Forte@Sun.COM 		    iscsiAuthStatusNoError) {
4547836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
4557836SJohn.Forte@Sun.COM 			    "unable to set ipsec", icp->conn_oid);
4567836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
4577836SJohn.Forte@Sun.COM 		}
4587836SJohn.Forte@Sun.COM 
4597836SJohn.Forte@Sun.COM 		if (iscsiAuthClientSetAuthRemote(auth_client,
4607836SJohn.Forte@Sun.COM 		    isp->sess_auth.bidirectional_auth) !=
4617836SJohn.Forte@Sun.COM 		    iscsiAuthStatusNoError) {
4627836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
4637836SJohn.Forte@Sun.COM 			    "unable to set remote authentication",
4647836SJohn.Forte@Sun.COM 			    icp->conn_oid);
4657836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
4667836SJohn.Forte@Sun.COM 		}
4677836SJohn.Forte@Sun.COM 	}
4687836SJohn.Forte@Sun.COM 
4697836SJohn.Forte@Sun.COM 	/*
4707836SJohn.Forte@Sun.COM 	 * exchange PDUs until the login stage is complete, or an error occurs
4717836SJohn.Forte@Sun.COM 	 */
4727836SJohn.Forte@Sun.COM 	do {
4737836SJohn.Forte@Sun.COM 		/* setup */
4747836SJohn.Forte@Sun.COM 		bzero(buffer, bufsize);
4757836SJohn.Forte@Sun.COM 		data = buffer;
4767836SJohn.Forte@Sun.COM 		max_data_length = bufsize;
4777836SJohn.Forte@Sun.COM 		rval = ISCSI_STATUS_INTERNAL_ERROR;
4787836SJohn.Forte@Sun.COM 
4799162SPeter.Dunlap@Sun.COM 		text_pdu = idm_pdu_alloc(sizeof (iscsi_hdr_t), 0);
4809162SPeter.Dunlap@Sun.COM 		idm_pdu_init(text_pdu, icp->conn_ic, NULL, NULL);
4819162SPeter.Dunlap@Sun.COM 
4827836SJohn.Forte@Sun.COM 		/*
4837836SJohn.Forte@Sun.COM 		 * fill in the PDU header and text data based on the
4847836SJohn.Forte@Sun.COM 		 * login stage that we're in
4857836SJohn.Forte@Sun.COM 		 */
4869162SPeter.Dunlap@Sun.COM 		rval = iscsi_make_login_pdu(icp, text_pdu, data,
4879162SPeter.Dunlap@Sun.COM 		    max_data_length);
4887836SJohn.Forte@Sun.COM 		if (!ISCSI_SUCCESS(rval)) {
4897836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
4907836SJohn.Forte@Sun.COM 			    "unable to make login pdu", icp->conn_oid);
4917836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
4927836SJohn.Forte@Sun.COM 		}
4937836SJohn.Forte@Sun.COM 
4949162SPeter.Dunlap@Sun.COM 		mutex_enter(&icp->conn_login_mutex);
4959162SPeter.Dunlap@Sun.COM 		/*
4969162SPeter.Dunlap@Sun.COM 		 * Make sure we are still in LOGIN_START or LOGIN_RX
4979162SPeter.Dunlap@Sun.COM 		 * state before switching to LOGIN_TX.  It's possible
4989162SPeter.Dunlap@Sun.COM 		 * for a connection failure to move us to LOGIN_ERROR
4999162SPeter.Dunlap@Sun.COM 		 * before we get to this point.
5009162SPeter.Dunlap@Sun.COM 		 */
5019162SPeter.Dunlap@Sun.COM 		if (((icp->conn_login_state != LOGIN_READY) &&
5029162SPeter.Dunlap@Sun.COM 		    (icp->conn_login_state != LOGIN_RX)) ||
5039162SPeter.Dunlap@Sun.COM 		    !icp->conn_state_idm_connected) {
5049162SPeter.Dunlap@Sun.COM 			/* Error occurred */
5059162SPeter.Dunlap@Sun.COM 			mutex_exit(&icp->conn_login_mutex);
5069162SPeter.Dunlap@Sun.COM 			rval = (ISCSI_STATUS_INTERNAL_ERROR);
5077836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
5087836SJohn.Forte@Sun.COM 		}
5097836SJohn.Forte@Sun.COM 
510*12697SPeter.Gill@Sun.COM 		icp->conn_login_resp_hdr.opcode = 0;
5119162SPeter.Dunlap@Sun.COM 		iscsi_login_update_state_locked(icp, LOGIN_TX);
5129162SPeter.Dunlap@Sun.COM 		icp->conn_login_data = data;
5139162SPeter.Dunlap@Sun.COM 		icp->conn_login_max_data_length = max_data_length;
5149162SPeter.Dunlap@Sun.COM 
5159162SPeter.Dunlap@Sun.COM 		/*
5169162SPeter.Dunlap@Sun.COM 		 * send a PDU to the target.  This is asynchronous but
5179162SPeter.Dunlap@Sun.COM 		 * we don't have any particular need for a TX completion
5189162SPeter.Dunlap@Sun.COM 		 * notification since we are going to block waiting for the
5199162SPeter.Dunlap@Sun.COM 		 * receive.
5209162SPeter.Dunlap@Sun.COM 		 */
5219162SPeter.Dunlap@Sun.COM 		response_timeout = ddi_get_lbolt() +
52210156SZhang.Yi@Sun.COM 		    SEC_TO_TICK(icp->conn_tunable_params.
52310156SZhang.Yi@Sun.COM 		    recv_login_rsp_timeout);
5249162SPeter.Dunlap@Sun.COM 		idm_pdu_tx(text_pdu);
5259162SPeter.Dunlap@Sun.COM 
5269162SPeter.Dunlap@Sun.COM 		/*
5279162SPeter.Dunlap@Sun.COM 		 * Wait for login failure indication or login RX.
5289162SPeter.Dunlap@Sun.COM 		 * Handler for login response PDU will copy any data into
5299162SPeter.Dunlap@Sun.COM 		 * the buffer pointed to by icp->conn_login_data
5309162SPeter.Dunlap@Sun.COM 		 */
5319162SPeter.Dunlap@Sun.COM 		while (icp->conn_login_state == LOGIN_TX) {
5329162SPeter.Dunlap@Sun.COM 			timeout_result = cv_timedwait(&icp->conn_login_cv,
5339162SPeter.Dunlap@Sun.COM 			    &icp->conn_login_mutex, response_timeout);
5349162SPeter.Dunlap@Sun.COM 			if (timeout_result == -1)
5359162SPeter.Dunlap@Sun.COM 				break;
5369162SPeter.Dunlap@Sun.COM 		}
5379162SPeter.Dunlap@Sun.COM 
538*12697SPeter.Gill@Sun.COM 		/*
539*12697SPeter.Gill@Sun.COM 		 * We have either received a login response or the connection
540*12697SPeter.Gill@Sun.COM 		 * has gone down or both.  If a login response is present,
541*12697SPeter.Gill@Sun.COM 		 * then process it.
542*12697SPeter.Gill@Sun.COM 		 */
543*12697SPeter.Gill@Sun.COM 		ilrhp = (iscsi_login_rsp_hdr_t *)&icp->conn_login_resp_hdr;
544*12697SPeter.Gill@Sun.COM 		if (icp->conn_login_state != LOGIN_RX && ilrhp->opcode == 0) {
545*12697SPeter.Gill@Sun.COM 			/* connection down, with no login response */
5469162SPeter.Dunlap@Sun.COM 			mutex_exit(&icp->conn_login_mutex);
5479162SPeter.Dunlap@Sun.COM 			rval = (ISCSI_STATUS_INTERNAL_ERROR);
5487836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
5497836SJohn.Forte@Sun.COM 		}
5509162SPeter.Dunlap@Sun.COM 		mutex_exit(&icp->conn_login_mutex);
5517836SJohn.Forte@Sun.COM 
5527836SJohn.Forte@Sun.COM 		/* check the PDU response type */
5539162SPeter.Dunlap@Sun.COM 		if (ilrhp->opcode != ISCSI_OP_LOGIN_RSP) {
5547836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
5557836SJohn.Forte@Sun.COM 			    "received invalid login response (0x%02x)",
5569162SPeter.Dunlap@Sun.COM 			    icp->conn_oid, ilrhp->opcode);
5577836SJohn.Forte@Sun.COM 			rval = (ISCSI_STATUS_PROTOCOL_ERROR);
5587836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
5597836SJohn.Forte@Sun.COM 		}
5607836SJohn.Forte@Sun.COM 
5617836SJohn.Forte@Sun.COM 		/*
5627836SJohn.Forte@Sun.COM 		 * give the caller the status class and detail from the
5637836SJohn.Forte@Sun.COM 		 * last login response PDU received
5647836SJohn.Forte@Sun.COM 		 */
5657836SJohn.Forte@Sun.COM 		if (status_class) {
5667836SJohn.Forte@Sun.COM 			*status_class = ilrhp->status_class;
5677836SJohn.Forte@Sun.COM 		}
5687836SJohn.Forte@Sun.COM 		if (status_detail) {
5697836SJohn.Forte@Sun.COM 			*status_detail = ilrhp->status_detail;
5707836SJohn.Forte@Sun.COM 		}
5717836SJohn.Forte@Sun.COM 
5727836SJohn.Forte@Sun.COM 		switch (ilrhp->status_class) {
5737836SJohn.Forte@Sun.COM 		case ISCSI_STATUS_CLASS_SUCCESS:
5747836SJohn.Forte@Sun.COM 			/*
5757836SJohn.Forte@Sun.COM 			 * process this response and possibly continue
5767836SJohn.Forte@Sun.COM 			 * sending PDUs
5777836SJohn.Forte@Sun.COM 			 */
5787836SJohn.Forte@Sun.COM 			rval = iscsi_process_login_response(icp,
5799162SPeter.Dunlap@Sun.COM 			    ilrhp, (char *)icp->conn_login_data,
5809162SPeter.Dunlap@Sun.COM 			    icp->conn_login_max_data_length);
5817836SJohn.Forte@Sun.COM 			/* pass back whatever error we discovered */
5827836SJohn.Forte@Sun.COM 			if (!ISCSI_SUCCESS(rval)) {
58310317SZhang.Yi@Sun.COM 				if (ISCSI_LOGIN_TRANSIT_FFP(ilrhp->flags)) {
58410317SZhang.Yi@Sun.COM 					/*
58510317SZhang.Yi@Sun.COM 					 * iSCSI connection transit to next
58610317SZhang.Yi@Sun.COM 					 * FFP stage while iscsi params
58710317SZhang.Yi@Sun.COM 					 * ngeotiate error, LOGIN_ERROR
58810317SZhang.Yi@Sun.COM 					 * marked so CN_FFP_ENABLED can
58910317SZhang.Yi@Sun.COM 					 * be fully handled before
59010317SZhang.Yi@Sun.COM 					 * CN_FFP_DISABLED can be processed.
59110317SZhang.Yi@Sun.COM 					 */
59210317SZhang.Yi@Sun.COM 					iscsi_login_update_state(icp,
59310317SZhang.Yi@Sun.COM 					    LOGIN_ERROR);
59410317SZhang.Yi@Sun.COM 				}
5957836SJohn.Forte@Sun.COM 				goto iscsi_login_done;
5967836SJohn.Forte@Sun.COM 			}
5977836SJohn.Forte@Sun.COM 
5987836SJohn.Forte@Sun.COM 			break;
5997836SJohn.Forte@Sun.COM 		case ISCSI_STATUS_CLASS_REDIRECT:
6007836SJohn.Forte@Sun.COM 			/*
6017836SJohn.Forte@Sun.COM 			 * we need to process this response to get the
6027836SJohn.Forte@Sun.COM 			 * TargetAddress of the redirect, but we don't
6037836SJohn.Forte@Sun.COM 			 * care about the return code.
6047836SJohn.Forte@Sun.COM 			 */
6059162SPeter.Dunlap@Sun.COM 			(void) iscsi_process_login_response(icp,
6069162SPeter.Dunlap@Sun.COM 			    ilrhp, (char *)icp->conn_login_data,
6079162SPeter.Dunlap@Sun.COM 			    icp->conn_login_max_data_length);
6087836SJohn.Forte@Sun.COM 			rval = ISCSI_STATUS_SUCCESS;
6097836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
6107836SJohn.Forte@Sun.COM 		case ISCSI_STATUS_CLASS_INITIATOR_ERR:
6117836SJohn.Forte@Sun.COM 			if (ilrhp->status_detail ==
6127836SJohn.Forte@Sun.COM 			    ISCSI_LOGIN_STATUS_AUTH_FAILED) {
6137836SJohn.Forte@Sun.COM 				cmn_err(CE_WARN, "iscsi connection(%u) login "
6147836SJohn.Forte@Sun.COM 				    "failed - login failed to authenticate "
6157836SJohn.Forte@Sun.COM 				    "with target", icp->conn_oid);
6167836SJohn.Forte@Sun.COM 			}
6177836SJohn.Forte@Sun.COM 			rval = ISCSI_STATUS_SUCCESS;
6187836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
6197836SJohn.Forte@Sun.COM 		default:
6207836SJohn.Forte@Sun.COM 			/*
6217836SJohn.Forte@Sun.COM 			 * some sort of error, login terminated unsuccessfully,
6227836SJohn.Forte@Sun.COM 			 * though this function did it's job. the caller must
6237836SJohn.Forte@Sun.COM 			 * check the status_class and status_detail and decide
6247836SJohn.Forte@Sun.COM 			 * what to do next.
6257836SJohn.Forte@Sun.COM 			 */
6267836SJohn.Forte@Sun.COM 			rval = ISCSI_STATUS_SUCCESS;
6277836SJohn.Forte@Sun.COM 			goto iscsi_login_done;
6287836SJohn.Forte@Sun.COM 		}
6297836SJohn.Forte@Sun.COM 
6307836SJohn.Forte@Sun.COM 	} while (icp->conn_current_stage != ISCSI_FULL_FEATURE_PHASE);
6317836SJohn.Forte@Sun.COM 
6327836SJohn.Forte@Sun.COM 	rval = ISCSI_STATUS_SUCCESS;
6337836SJohn.Forte@Sun.COM 
6347836SJohn.Forte@Sun.COM iscsi_login_done:
6357836SJohn.Forte@Sun.COM 	if (auth_client) {
6367836SJohn.Forte@Sun.COM 		if (iscsiAuthClientFinish(auth_client) !=
6377836SJohn.Forte@Sun.COM 		    iscsiAuthStatusNoError) {
6387836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login "
6397836SJohn.Forte@Sun.COM 			    "failed - login failed to authenticate "
6407836SJohn.Forte@Sun.COM 			    "with target", icp->conn_oid);
6417836SJohn.Forte@Sun.COM 			if (ISCSI_SUCCESS(rval))
6427836SJohn.Forte@Sun.COM 				rval = ISCSI_STATUS_INTERNAL_ERROR;
6437836SJohn.Forte@Sun.COM 		}
6447836SJohn.Forte@Sun.COM 	}
6459162SPeter.Dunlap@Sun.COM 
64612001SZhang.Yi@Sun.COM 	icp->conn_login_status = rval;
6479162SPeter.Dunlap@Sun.COM 	if (ISCSI_SUCCESS(rval) &&
6489162SPeter.Dunlap@Sun.COM 	    (*status_class == ISCSI_STATUS_CLASS_SUCCESS)) {
6499162SPeter.Dunlap@Sun.COM 		mutex_enter(&icp->conn_state_mutex);
6509162SPeter.Dunlap@Sun.COM 		while (!icp->conn_state_ffp)
6519162SPeter.Dunlap@Sun.COM 			cv_wait(&icp->conn_state_change,
6529162SPeter.Dunlap@Sun.COM 			    &icp->conn_state_mutex);
6539162SPeter.Dunlap@Sun.COM 		mutex_exit(&icp->conn_state_mutex);
6549162SPeter.Dunlap@Sun.COM 	} else {
6559162SPeter.Dunlap@Sun.COM 		iscsi_login_disconnect(icp);
6569162SPeter.Dunlap@Sun.COM 	}
6579162SPeter.Dunlap@Sun.COM 
6589162SPeter.Dunlap@Sun.COM 	iscsi_login_update_state(icp, LOGIN_DONE);
6599162SPeter.Dunlap@Sun.COM 
6607836SJohn.Forte@Sun.COM 	return (rval);
6617836SJohn.Forte@Sun.COM }
6627836SJohn.Forte@Sun.COM 
6637836SJohn.Forte@Sun.COM 
6647836SJohn.Forte@Sun.COM /*
6657836SJohn.Forte@Sun.COM  * iscsi_make_login_pdu -
6667836SJohn.Forte@Sun.COM  *
6677836SJohn.Forte@Sun.COM  */
6687836SJohn.Forte@Sun.COM static iscsi_status_t
iscsi_make_login_pdu(iscsi_conn_t * icp,idm_pdu_t * text_pdu,char * data,int max_data_length)6699162SPeter.Dunlap@Sun.COM iscsi_make_login_pdu(iscsi_conn_t *icp, idm_pdu_t *text_pdu,
6707836SJohn.Forte@Sun.COM     char *data, int max_data_length)
6717836SJohn.Forte@Sun.COM {
6727836SJohn.Forte@Sun.COM 	struct iscsi_sess	*isp		= NULL;
6737836SJohn.Forte@Sun.COM 	int			transit		= 0;
6749162SPeter.Dunlap@Sun.COM 	iscsi_hdr_t		*ihp		= text_pdu->isp_hdr;
6759162SPeter.Dunlap@Sun.COM 	iscsi_login_hdr_t	*ilhp		=
6769162SPeter.Dunlap@Sun.COM 	    (iscsi_login_hdr_t *)text_pdu->isp_hdr;
6777836SJohn.Forte@Sun.COM 	IscsiAuthClient		*auth_client	= NULL;
6787836SJohn.Forte@Sun.COM 	int			keytype		= 0;
6797836SJohn.Forte@Sun.COM 	int			rc		= 0;
6807836SJohn.Forte@Sun.COM 	char			value[iscsiAuthStringMaxLength];
6817836SJohn.Forte@Sun.COM 
6827836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
6839162SPeter.Dunlap@Sun.COM 	ASSERT(text_pdu != NULL);
6847836SJohn.Forte@Sun.COM 	isp = icp->conn_sess;
6857836SJohn.Forte@Sun.COM 	ASSERT(isp != NULL);
6867836SJohn.Forte@Sun.COM 
6877836SJohn.Forte@Sun.COM 	auth_client =
6887836SJohn.Forte@Sun.COM 	    (isp->sess_auth.auth_buffers && isp->sess_auth.num_auth_buffers) ?
6897836SJohn.Forte@Sun.COM 	    (IscsiAuthClient *)isp->sess_auth.auth_buffers[0].address : NULL;
6907836SJohn.Forte@Sun.COM 
6917836SJohn.Forte@Sun.COM 	/*
6927836SJohn.Forte@Sun.COM 	 * initialize the PDU header
6937836SJohn.Forte@Sun.COM 	 */
6947836SJohn.Forte@Sun.COM 	bzero(ilhp, sizeof (*ilhp));
6957836SJohn.Forte@Sun.COM 	ilhp->opcode = ISCSI_OP_LOGIN_CMD | ISCSI_OP_IMMEDIATE;
6967836SJohn.Forte@Sun.COM 	ilhp->cid = icp->conn_cid;
6977836SJohn.Forte@Sun.COM 	bcopy(&isp->sess_isid[0], &ilhp->isid[0], sizeof (isp->sess_isid));
6987836SJohn.Forte@Sun.COM 	ilhp->tsid = 0;
6997836SJohn.Forte@Sun.COM 
7009162SPeter.Dunlap@Sun.COM 	/*
7019162SPeter.Dunlap@Sun.COM 	 * Set data buffer pointer.  The calls to iscsi_add_text will update the
7029162SPeter.Dunlap@Sun.COM 	 * data length.
7039162SPeter.Dunlap@Sun.COM 	 */
7049162SPeter.Dunlap@Sun.COM 	text_pdu->isp_data = (uint8_t *)data;
7059162SPeter.Dunlap@Sun.COM 
7067836SJohn.Forte@Sun.COM 	/* don't increment on immediate */
7077836SJohn.Forte@Sun.COM 	ilhp->cmdsn = htonl(isp->sess_cmdsn);
7087836SJohn.Forte@Sun.COM 
7097836SJohn.Forte@Sun.COM 	ilhp->min_version = ISCSI_DRAFT20_VERSION;
7107836SJohn.Forte@Sun.COM 	ilhp->max_version = ISCSI_DRAFT20_VERSION;
7117836SJohn.Forte@Sun.COM 
7127836SJohn.Forte@Sun.COM 	/*
7137836SJohn.Forte@Sun.COM 	 * we have to send 0 until full-feature stage
7147836SJohn.Forte@Sun.COM 	 */
7157836SJohn.Forte@Sun.COM 	ilhp->expstatsn = htonl(icp->conn_expstatsn);
7167836SJohn.Forte@Sun.COM 
7177836SJohn.Forte@Sun.COM 	/*
7187836SJohn.Forte@Sun.COM 	 * the very first Login PDU has some additional requirements,
7197836SJohn.Forte@Sun.COM 	 * * and we need to decide what stage to start in.
7207836SJohn.Forte@Sun.COM 	 */
7217836SJohn.Forte@Sun.COM 	if (icp->conn_current_stage == ISCSI_INITIAL_LOGIN_STAGE) {
7227836SJohn.Forte@Sun.COM 		if ((isp->sess_hba->hba_name) &&
7237836SJohn.Forte@Sun.COM 		    (isp->sess_hba->hba_name[0])) {
7249162SPeter.Dunlap@Sun.COM 			if (!iscsi_add_text(text_pdu, max_data_length,
7257836SJohn.Forte@Sun.COM 			    "InitiatorName",
7267836SJohn.Forte@Sun.COM 			    (char *)isp->sess_hba->hba_name)) {
7277836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_INTERNAL_ERROR);
7287836SJohn.Forte@Sun.COM 			}
7297836SJohn.Forte@Sun.COM 		} else {
7307836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login "
7317836SJohn.Forte@Sun.COM 			    "failed - initiator name is required",
7327836SJohn.Forte@Sun.COM 			    icp->conn_oid);
7337836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_INTERNAL_ERROR);
7347836SJohn.Forte@Sun.COM 		}
7357836SJohn.Forte@Sun.COM 
7367836SJohn.Forte@Sun.COM 		if ((isp->sess_hba->hba_alias) &&
7377836SJohn.Forte@Sun.COM 		    (isp->sess_hba->hba_alias[0])) {
7389162SPeter.Dunlap@Sun.COM 			if (!iscsi_add_text(text_pdu, max_data_length,
7397836SJohn.Forte@Sun.COM 			    "InitiatorAlias",
7407836SJohn.Forte@Sun.COM 			    (char *)isp->sess_hba->hba_alias)) {
7417836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_INTERNAL_ERROR);
7427836SJohn.Forte@Sun.COM 			}
7437836SJohn.Forte@Sun.COM 		}
7447836SJohn.Forte@Sun.COM 
7457836SJohn.Forte@Sun.COM 		if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
7467836SJohn.Forte@Sun.COM 			if (isp->sess_name[0] != '\0') {
7479162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu, max_data_length,
7487836SJohn.Forte@Sun.COM 				    "TargetName", (char *)isp->sess_name)) {
7497836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
7507836SJohn.Forte@Sun.COM 				}
7517836SJohn.Forte@Sun.COM 			}
7527836SJohn.Forte@Sun.COM 
7539162SPeter.Dunlap@Sun.COM 			if (!iscsi_add_text(text_pdu, max_data_length,
7547836SJohn.Forte@Sun.COM 			    "SessionType", "Normal")) {
7557836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_INTERNAL_ERROR);
7567836SJohn.Forte@Sun.COM 			}
7577836SJohn.Forte@Sun.COM 		} else if (isp->sess_type == ISCSI_SESS_TYPE_DISCOVERY) {
7589162SPeter.Dunlap@Sun.COM 			if (!iscsi_add_text(text_pdu, max_data_length,
7597836SJohn.Forte@Sun.COM 			    "SessionType", "Discovery")) {
7607836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_INTERNAL_ERROR);
7617836SJohn.Forte@Sun.COM 			}
7627836SJohn.Forte@Sun.COM 		} else {
7637836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_INTERNAL_ERROR);
7647836SJohn.Forte@Sun.COM 		}
7657836SJohn.Forte@Sun.COM 
7667836SJohn.Forte@Sun.COM 		if (auth_client) {
7677836SJohn.Forte@Sun.COM 			/* we're prepared to do authentication */
7687836SJohn.Forte@Sun.COM 			icp->conn_current_stage =
7697836SJohn.Forte@Sun.COM 			    ISCSI_SECURITY_NEGOTIATION_STAGE;
7707836SJohn.Forte@Sun.COM 		} else {
7717836SJohn.Forte@Sun.COM 			/* can't do any authentication, skip that stage */
7727836SJohn.Forte@Sun.COM 			icp->conn_current_stage =
7737836SJohn.Forte@Sun.COM 			    ISCSI_OP_PARMS_NEGOTIATION_STAGE;
7747836SJohn.Forte@Sun.COM 		}
7757836SJohn.Forte@Sun.COM 	}
7767836SJohn.Forte@Sun.COM 
7777836SJohn.Forte@Sun.COM 	/*
7787836SJohn.Forte@Sun.COM 	 * fill in text based on the stage
7797836SJohn.Forte@Sun.COM 	 */
7807836SJohn.Forte@Sun.COM 	switch (icp->conn_current_stage) {
7817836SJohn.Forte@Sun.COM 	case ISCSI_OP_PARMS_NEGOTIATION_STAGE:
7827836SJohn.Forte@Sun.COM 		/*
7837836SJohn.Forte@Sun.COM 		 * we always try to go from op params to full
7847836SJohn.Forte@Sun.COM 		 * feature stage
7857836SJohn.Forte@Sun.COM 		 */
7867836SJohn.Forte@Sun.COM 		icp->conn_next_stage	= ISCSI_FULL_FEATURE_PHASE;
7877836SJohn.Forte@Sun.COM 		transit			= 1;
7887836SJohn.Forte@Sun.COM 
7897836SJohn.Forte@Sun.COM 		/*
7907836SJohn.Forte@Sun.COM 		 * The terminology here may have gotten dated.  A partial
7917836SJohn.Forte@Sun.COM 		 * response is a login response that doesn't complete a
7927836SJohn.Forte@Sun.COM 		 * login.  If we haven't gotten a partial response, then
7937836SJohn.Forte@Sun.COM 		 * either we shouldn't be here, or we just switched to
7947836SJohn.Forte@Sun.COM 		 * this stage, and need to start offering keys.
7957836SJohn.Forte@Sun.COM 		 */
7967836SJohn.Forte@Sun.COM 		if (!icp->conn_partial_response) {
7977836SJohn.Forte@Sun.COM 			/*
7987836SJohn.Forte@Sun.COM 			 * request the desired settings the first time
7997836SJohn.Forte@Sun.COM 			 * we are in this stage
8007836SJohn.Forte@Sun.COM 			 */
8017836SJohn.Forte@Sun.COM 			switch (icp->conn_params.header_digest) {
8027836SJohn.Forte@Sun.COM 			case ISCSI_DIGEST_NONE:
8039162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
8047836SJohn.Forte@Sun.COM 				    max_data_length, "HeaderDigest", "None")) {
8057836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
8067836SJohn.Forte@Sun.COM 				}
8077836SJohn.Forte@Sun.COM 				break;
8087836SJohn.Forte@Sun.COM 			case ISCSI_DIGEST_CRC32C:
8099162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
8107836SJohn.Forte@Sun.COM 				    max_data_length,
8117836SJohn.Forte@Sun.COM 				    "HeaderDigest", "CRC32C")) {
8127836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
8137836SJohn.Forte@Sun.COM 				}
8147836SJohn.Forte@Sun.COM 				break;
8157836SJohn.Forte@Sun.COM 			case ISCSI_DIGEST_CRC32C_NONE:
8169162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
8177836SJohn.Forte@Sun.COM 				    max_data_length, "HeaderDigest",
8187836SJohn.Forte@Sun.COM 				    "CRC32C,None")) {
8197836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
8207836SJohn.Forte@Sun.COM 				}
8217836SJohn.Forte@Sun.COM 				break;
8227836SJohn.Forte@Sun.COM 			default:
8237836SJohn.Forte@Sun.COM 			case ISCSI_DIGEST_NONE_CRC32C:
8249162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
8257836SJohn.Forte@Sun.COM 				    max_data_length, "HeaderDigest",
8267836SJohn.Forte@Sun.COM 				    "None,CRC32C")) {
8277836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
8287836SJohn.Forte@Sun.COM 				}
8297836SJohn.Forte@Sun.COM 				break;
8307836SJohn.Forte@Sun.COM 			}
8317836SJohn.Forte@Sun.COM 
8327836SJohn.Forte@Sun.COM 			switch (icp->conn_params.data_digest) {
8337836SJohn.Forte@Sun.COM 			case ISCSI_DIGEST_NONE:
8349162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
8357836SJohn.Forte@Sun.COM 				    max_data_length, "DataDigest", "None")) {
8367836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
8377836SJohn.Forte@Sun.COM 				}
8387836SJohn.Forte@Sun.COM 				break;
8397836SJohn.Forte@Sun.COM 			case ISCSI_DIGEST_CRC32C:
8409162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
8417836SJohn.Forte@Sun.COM 				    max_data_length, "DataDigest", "CRC32C")) {
8427836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
8437836SJohn.Forte@Sun.COM 				}
8447836SJohn.Forte@Sun.COM 				break;
8457836SJohn.Forte@Sun.COM 			case ISCSI_DIGEST_CRC32C_NONE:
8469162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
8477836SJohn.Forte@Sun.COM 				    max_data_length, "DataDigest",
8487836SJohn.Forte@Sun.COM 				    "CRC32C,None")) {
8497836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
8507836SJohn.Forte@Sun.COM 				}
8517836SJohn.Forte@Sun.COM 				break;
8527836SJohn.Forte@Sun.COM 			default:
8537836SJohn.Forte@Sun.COM 			case ISCSI_DIGEST_NONE_CRC32C:
8549162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
8557836SJohn.Forte@Sun.COM 				    max_data_length, "DataDigest",
8567836SJohn.Forte@Sun.COM 				    "None,CRC32C")) {
8577836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
8587836SJohn.Forte@Sun.COM 				}
8597836SJohn.Forte@Sun.COM 				break;
8607836SJohn.Forte@Sun.COM 			}
8617836SJohn.Forte@Sun.COM 
8627836SJohn.Forte@Sun.COM 			(void) sprintf(value, "%d",
8637836SJohn.Forte@Sun.COM 			    icp->conn_params.max_recv_data_seg_len);
8649162SPeter.Dunlap@Sun.COM 			if (!iscsi_add_text(text_pdu, max_data_length,
8657836SJohn.Forte@Sun.COM 			    "MaxRecvDataSegmentLength", value)) {
8667836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_INTERNAL_ERROR);
8677836SJohn.Forte@Sun.COM 			}
8687836SJohn.Forte@Sun.COM 
8697836SJohn.Forte@Sun.COM 			(void) sprintf(value, "%d",
8707836SJohn.Forte@Sun.COM 			    icp->conn_params.default_time_to_wait);
8719162SPeter.Dunlap@Sun.COM 			if (!iscsi_add_text(text_pdu,
8727836SJohn.Forte@Sun.COM 			    max_data_length, "DefaultTime2Wait", value)) {
8737836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_INTERNAL_ERROR);
8747836SJohn.Forte@Sun.COM 			}
8757836SJohn.Forte@Sun.COM 
8767836SJohn.Forte@Sun.COM 			(void) sprintf(value, "%d",
8777836SJohn.Forte@Sun.COM 			    icp->conn_params.default_time_to_retain);
8789162SPeter.Dunlap@Sun.COM 			if (!iscsi_add_text(text_pdu,
8797836SJohn.Forte@Sun.COM 			    max_data_length, "DefaultTime2Retain", value)) {
8807836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_INTERNAL_ERROR);
8817836SJohn.Forte@Sun.COM 			}
8827836SJohn.Forte@Sun.COM 
8837836SJohn.Forte@Sun.COM 			(void) sprintf(value, "%d",
8847836SJohn.Forte@Sun.COM 			    icp->conn_params.error_recovery_level);
8859162SPeter.Dunlap@Sun.COM 			if (!iscsi_add_text(text_pdu,
8867836SJohn.Forte@Sun.COM 			    max_data_length, "ErrorRecoveryLevel", "0")) {
8877836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_INTERNAL_ERROR);
8887836SJohn.Forte@Sun.COM 			}
8897836SJohn.Forte@Sun.COM 
8909162SPeter.Dunlap@Sun.COM 			if (!iscsi_add_text(text_pdu,
8917836SJohn.Forte@Sun.COM 			    max_data_length, "IFMarker",
8927836SJohn.Forte@Sun.COM 			    icp->conn_params.ifmarker ? "Yes" : "No")) {
8937836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_INTERNAL_ERROR);
8947836SJohn.Forte@Sun.COM 			}
8957836SJohn.Forte@Sun.COM 
8969162SPeter.Dunlap@Sun.COM 			if (!iscsi_add_text(text_pdu,
8977836SJohn.Forte@Sun.COM 			    max_data_length, "OFMarker",
8987836SJohn.Forte@Sun.COM 			    icp->conn_params.ofmarker ? "Yes" : "No")) {
8997836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_INTERNAL_ERROR);
9007836SJohn.Forte@Sun.COM 			}
9017836SJohn.Forte@Sun.COM 
9027836SJohn.Forte@Sun.COM 			/*
9037836SJohn.Forte@Sun.COM 			 * The following login parameters are "Irrelevant"
9047836SJohn.Forte@Sun.COM 			 * for discovery sessions
9057836SJohn.Forte@Sun.COM 			 */
9067836SJohn.Forte@Sun.COM 			if (isp->sess_type != ISCSI_SESS_TYPE_DISCOVERY) {
9077836SJohn.Forte@Sun.COM 
9089162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
9097836SJohn.Forte@Sun.COM 				    max_data_length, "InitialR2T",
9107836SJohn.Forte@Sun.COM 				    icp->conn_params.initial_r2t ?
9117836SJohn.Forte@Sun.COM 				    "Yes" : "No")) {
9127836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
9137836SJohn.Forte@Sun.COM 				}
9147836SJohn.Forte@Sun.COM 
9159162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
9167836SJohn.Forte@Sun.COM 				    max_data_length, "ImmediateData",
9177836SJohn.Forte@Sun.COM 				    icp->conn_params.immediate_data ?
9187836SJohn.Forte@Sun.COM 				    "Yes" : "No")) {
9197836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
9207836SJohn.Forte@Sun.COM 				}
9217836SJohn.Forte@Sun.COM 
9227836SJohn.Forte@Sun.COM 				(void) sprintf(value, "%d",
9237836SJohn.Forte@Sun.COM 				    icp->conn_params.max_burst_length);
9249162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
9257836SJohn.Forte@Sun.COM 				    max_data_length, "MaxBurstLength", value)) {
9267836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
9277836SJohn.Forte@Sun.COM 				}
9287836SJohn.Forte@Sun.COM 
9297836SJohn.Forte@Sun.COM 				(void) sprintf(value, "%d",
9307836SJohn.Forte@Sun.COM 				    icp->conn_params.first_burst_length);
9319162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu, max_data_length,
9327836SJohn.Forte@Sun.COM 				    "FirstBurstLength", value)) {
9337836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
9347836SJohn.Forte@Sun.COM 				}
9357836SJohn.Forte@Sun.COM 
9367836SJohn.Forte@Sun.COM 				(void) sprintf(value, "%d",
9377836SJohn.Forte@Sun.COM 				    icp->conn_params.max_outstanding_r2t);
9389162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu, max_data_length,
9397836SJohn.Forte@Sun.COM 				    "MaxOutstandingR2T", value)) {
9407836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
9417836SJohn.Forte@Sun.COM 				}
9427836SJohn.Forte@Sun.COM 
9437836SJohn.Forte@Sun.COM 				(void) sprintf(value, "%d",
9447836SJohn.Forte@Sun.COM 				    icp->conn_params.max_connections);
9459162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu, max_data_length,
9467836SJohn.Forte@Sun.COM 				    "MaxConnections", value)) {
9477836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
9487836SJohn.Forte@Sun.COM 				}
9497836SJohn.Forte@Sun.COM 
9509162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
9517836SJohn.Forte@Sun.COM 				    max_data_length, "DataPDUInOrder",
9527836SJohn.Forte@Sun.COM 				    icp->conn_params.data_pdu_in_order ?
9537836SJohn.Forte@Sun.COM 				    "Yes" : "No")) {
9547836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
9557836SJohn.Forte@Sun.COM 				}
9567836SJohn.Forte@Sun.COM 
9579162SPeter.Dunlap@Sun.COM 				if (!iscsi_add_text(text_pdu,
9587836SJohn.Forte@Sun.COM 				    max_data_length, "DataSequenceInOrder",
9597836SJohn.Forte@Sun.COM 				    icp->conn_params.data_sequence_in_order ?
9607836SJohn.Forte@Sun.COM 				    "Yes" : "No")) {
9617836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_INTERNAL_ERROR);
9627836SJohn.Forte@Sun.COM 				}
9637836SJohn.Forte@Sun.COM 			}
9647836SJohn.Forte@Sun.COM 		}
9657836SJohn.Forte@Sun.COM 		break;
9667836SJohn.Forte@Sun.COM 
9677836SJohn.Forte@Sun.COM 	case ISCSI_SECURITY_NEGOTIATION_STAGE:
9687836SJohn.Forte@Sun.COM 		keytype = iscsiAuthKeyTypeNone;
9697836SJohn.Forte@Sun.COM 		rc = iscsiAuthClientSendTransitBit(auth_client, &transit);
9707836SJohn.Forte@Sun.COM 
9717836SJohn.Forte@Sun.COM 		/*
9727836SJohn.Forte@Sun.COM 		 * see if we're ready for a stage change
9737836SJohn.Forte@Sun.COM 		 */
9747836SJohn.Forte@Sun.COM 		if (rc == iscsiAuthStatusNoError) {
9757836SJohn.Forte@Sun.COM 			if (transit) {
9767836SJohn.Forte@Sun.COM 				icp->conn_next_stage =
9777836SJohn.Forte@Sun.COM 				    ISCSI_OP_PARMS_NEGOTIATION_STAGE;
9787836SJohn.Forte@Sun.COM 			} else {
9797836SJohn.Forte@Sun.COM 				icp->conn_next_stage =
9807836SJohn.Forte@Sun.COM 				    ISCSI_SECURITY_NEGOTIATION_STAGE;
9817836SJohn.Forte@Sun.COM 			}
9827836SJohn.Forte@Sun.COM 		} else {
9837836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_INTERNAL_ERROR);
9847836SJohn.Forte@Sun.COM 		}
9857836SJohn.Forte@Sun.COM 
9867836SJohn.Forte@Sun.COM 		/*
9877836SJohn.Forte@Sun.COM 		 * enumerate all the keys the auth code might want to send
9887836SJohn.Forte@Sun.COM 		 */
9897836SJohn.Forte@Sun.COM 		while (iscsiAuthClientGetNextKeyType(&keytype) ==
9907836SJohn.Forte@Sun.COM 		    iscsiAuthStatusNoError) {
9917836SJohn.Forte@Sun.COM 			int present = 0;
9927836SJohn.Forte@Sun.COM 			char *key = (char *)iscsiAuthClientGetKeyName(keytype);
9937836SJohn.Forte@Sun.COM 			int key_length = key ? strlen(key) : 0;
9949162SPeter.Dunlap@Sun.COM 			int pdu_length = text_pdu->isp_datalen;
9957836SJohn.Forte@Sun.COM 			char *auth_value = data + pdu_length + key_length + 1;
9967836SJohn.Forte@Sun.COM 			unsigned int max_length = max_data_length -
9977836SJohn.Forte@Sun.COM 			    (pdu_length + key_length + 1);
9987836SJohn.Forte@Sun.COM 
9997836SJohn.Forte@Sun.COM 			/*
10007836SJohn.Forte@Sun.COM 			 * add the key/value pairs the auth code wants to
10017836SJohn.Forte@Sun.COM 			 * send directly to the PDU, since they could in
10027836SJohn.Forte@Sun.COM 			 * theory be large.
10037836SJohn.Forte@Sun.COM 			 */
10047836SJohn.Forte@Sun.COM 			rc = iscsiAuthClientSendKeyValue(auth_client, keytype,
10057836SJohn.Forte@Sun.COM 			    &present, auth_value, max_length);
10067836SJohn.Forte@Sun.COM 			if ((rc == iscsiAuthStatusNoError) && present) {
10077836SJohn.Forte@Sun.COM 				/*
10087836SJohn.Forte@Sun.COM 				 * actually fill in the key
10097836SJohn.Forte@Sun.COM 				 */
10107836SJohn.Forte@Sun.COM 				(void) strncpy(&data[pdu_length], key,
10117836SJohn.Forte@Sun.COM 				    key_length);
10127836SJohn.Forte@Sun.COM 				pdu_length += key_length;
10137836SJohn.Forte@Sun.COM 				data[pdu_length] = '=';
10147836SJohn.Forte@Sun.COM 				pdu_length++;
10157836SJohn.Forte@Sun.COM 				/*
10167836SJohn.Forte@Sun.COM 				 * adjust the PDU's data segment length to
10177836SJohn.Forte@Sun.COM 				 * include the value and trailing NULL
10187836SJohn.Forte@Sun.COM 				 */
10197836SJohn.Forte@Sun.COM 				pdu_length += strlen(auth_value) + 1;
10209162SPeter.Dunlap@Sun.COM 				text_pdu->isp_datalen = pdu_length;
10217836SJohn.Forte@Sun.COM 				hton24(ihp->dlength, pdu_length);
10227836SJohn.Forte@Sun.COM 			}
10237836SJohn.Forte@Sun.COM 		}
10247836SJohn.Forte@Sun.COM 
10257836SJohn.Forte@Sun.COM 		break;
10267836SJohn.Forte@Sun.COM 	case ISCSI_FULL_FEATURE_PHASE:
10277836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN, "iscsi connection(%u) login "
10287836SJohn.Forte@Sun.COM 		    "failed - can't send login in full feature stage",
10297836SJohn.Forte@Sun.COM 		    icp->conn_oid);
10307836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_INTERNAL_ERROR);
10317836SJohn.Forte@Sun.COM 	default:
10327836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN, "iscsi connection(%u) login "
10337836SJohn.Forte@Sun.COM 		    "failed - can't send login in unknown stage (%d)",
10347836SJohn.Forte@Sun.COM 		    icp->conn_oid, icp->conn_current_stage);
10357836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_INTERNAL_ERROR);
10367836SJohn.Forte@Sun.COM 	}
10377836SJohn.Forte@Sun.COM 
10387836SJohn.Forte@Sun.COM 	/* fill in the flags */
10397836SJohn.Forte@Sun.COM 	ilhp->flags = icp->conn_current_stage << 2;
10407836SJohn.Forte@Sun.COM 	if (transit) {
10417836SJohn.Forte@Sun.COM 		/* transit to the next stage */
10427836SJohn.Forte@Sun.COM 		ilhp->flags |= icp->conn_next_stage;
10437836SJohn.Forte@Sun.COM 		ilhp->flags |= ISCSI_FLAG_LOGIN_TRANSIT;
10447836SJohn.Forte@Sun.COM 	} else {
10457836SJohn.Forte@Sun.COM 		/* next == current */
10467836SJohn.Forte@Sun.COM 		ilhp->flags |= icp->conn_current_stage;
10477836SJohn.Forte@Sun.COM 	}
10487836SJohn.Forte@Sun.COM 
10497836SJohn.Forte@Sun.COM 	return (ISCSI_STATUS_SUCCESS);
10507836SJohn.Forte@Sun.COM }
10517836SJohn.Forte@Sun.COM 
10527836SJohn.Forte@Sun.COM 
10537836SJohn.Forte@Sun.COM /*
10547836SJohn.Forte@Sun.COM  * iscsi_process_login_response - This assumes the text data is
10557836SJohn.Forte@Sun.COM  * always NUL terminated.  The caller can always arrange for that by
10567836SJohn.Forte@Sun.COM  * using a slightly larger buffer than the max PDU size, and then
10577836SJohn.Forte@Sun.COM  * appending a NUL to the PDU.
10587836SJohn.Forte@Sun.COM  */
10597836SJohn.Forte@Sun.COM static iscsi_status_t
iscsi_process_login_response(iscsi_conn_t * icp,iscsi_login_rsp_hdr_t * ilrhp,char * data,int max_data_length)10607836SJohn.Forte@Sun.COM iscsi_process_login_response(iscsi_conn_t *icp,
10617836SJohn.Forte@Sun.COM     iscsi_login_rsp_hdr_t *ilrhp, char *data, int max_data_length)
10627836SJohn.Forte@Sun.COM {
10637836SJohn.Forte@Sun.COM 	iscsi_sess_t		*isp			= NULL;
10647836SJohn.Forte@Sun.COM 	IscsiAuthClient		*auth_client		= NULL;
10657836SJohn.Forte@Sun.COM 	int			transit			= 0;
10667836SJohn.Forte@Sun.COM 	char			*text			= data;
10677836SJohn.Forte@Sun.COM 	char			*end			= NULL;
10687836SJohn.Forte@Sun.COM 	int			pdu_current_stage	= 0;
10697836SJohn.Forte@Sun.COM 	int			pdu_next_stage		= 0;
10707836SJohn.Forte@Sun.COM 	int			debug_status		= 0;
10717836SJohn.Forte@Sun.COM 	unsigned long		tmp;
10727836SJohn.Forte@Sun.COM 	char			*tmpe;
10737836SJohn.Forte@Sun.COM 	boolean_t		fbl_irrelevant		= B_FALSE;
10747836SJohn.Forte@Sun.COM 
10757836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
10767836SJohn.Forte@Sun.COM 	ASSERT(ilrhp != NULL);
10777836SJohn.Forte@Sun.COM 	ASSERT(data != NULL);
10787836SJohn.Forte@Sun.COM 	isp = icp->conn_sess;
10797836SJohn.Forte@Sun.COM 	ASSERT(isp != NULL);
10807836SJohn.Forte@Sun.COM 
10817836SJohn.Forte@Sun.COM 	auth_client =
10827836SJohn.Forte@Sun.COM 	    (isp->sess_auth.auth_buffers && isp->sess_auth.num_auth_buffers) ?
10837836SJohn.Forte@Sun.COM 	    (IscsiAuthClient *) isp->sess_auth.auth_buffers[0].address : NULL;
10847836SJohn.Forte@Sun.COM 	transit = ilrhp->flags & ISCSI_FLAG_LOGIN_TRANSIT;
10857836SJohn.Forte@Sun.COM 
10867836SJohn.Forte@Sun.COM 	/* verify the initial buffer was big enough to hold everything */
10877836SJohn.Forte@Sun.COM 	end = text + ntoh24(ilrhp->dlength) + 1;
10887836SJohn.Forte@Sun.COM 	if (end >= (data + max_data_length)) {
10897836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
10907836SJohn.Forte@Sun.COM 		    "buffer too small", icp->conn_oid);
10917836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_INTERNAL_ERROR);
10927836SJohn.Forte@Sun.COM 	}
10937836SJohn.Forte@Sun.COM 	*end = '\0';
10947836SJohn.Forte@Sun.COM 
10957836SJohn.Forte@Sun.COM 	/* if the response status was success, sanity check the response */
10967836SJohn.Forte@Sun.COM 	if (ilrhp->status_class == ISCSI_STATUS_CLASS_SUCCESS) {
10977836SJohn.Forte@Sun.COM 		/* check the active version */
10987836SJohn.Forte@Sun.COM 		if (ilrhp->active_version != ISCSI_DRAFT20_VERSION) {
10997836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login "
11007836SJohn.Forte@Sun.COM 			    "failed - target version incompatible "
11017836SJohn.Forte@Sun.COM 			    "received:0x%0x2x expected:0x%02x",
11027836SJohn.Forte@Sun.COM 			    icp->conn_oid, ilrhp->active_version,
11037836SJohn.Forte@Sun.COM 			    ISCSI_DRAFT20_VERSION);
11047836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_VERSION_MISMATCH);
11057836SJohn.Forte@Sun.COM 		}
11067836SJohn.Forte@Sun.COM 
11077836SJohn.Forte@Sun.COM 		/* make sure the current stage matches */
11087836SJohn.Forte@Sun.COM 		pdu_current_stage = (ilrhp->flags &
11097836SJohn.Forte@Sun.COM 		    ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2;
11107836SJohn.Forte@Sun.COM 		if (pdu_current_stage != icp->conn_current_stage) {
11117836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login "
11127836SJohn.Forte@Sun.COM 			    "failed - login response contained invalid "
11137836SJohn.Forte@Sun.COM 			    "stage %d", icp->conn_oid, pdu_current_stage);
11147836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_PROTOCOL_ERROR);
11157836SJohn.Forte@Sun.COM 		}
11167836SJohn.Forte@Sun.COM 
11177836SJohn.Forte@Sun.COM 		/*
11187836SJohn.Forte@Sun.COM 		 * Make sure that we're actually advancing
11197836SJohn.Forte@Sun.COM 		 * if the T-bit is set
11207836SJohn.Forte@Sun.COM 		 */
11217836SJohn.Forte@Sun.COM 		pdu_next_stage = ilrhp->flags &
11227836SJohn.Forte@Sun.COM 		    ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK;
11237836SJohn.Forte@Sun.COM 		if (transit && (pdu_next_stage <= icp->conn_current_stage)) {
11247836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login "
11257836SJohn.Forte@Sun.COM 			    "failed - login response wants to go to stage "
11267836SJohn.Forte@Sun.COM 			    "%d, but we want stage %d", icp->conn_oid,
11277836SJohn.Forte@Sun.COM 			    pdu_next_stage, icp->conn_next_stage);
11287836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_PROTOCOL_ERROR);
11297836SJohn.Forte@Sun.COM 		}
11307836SJohn.Forte@Sun.COM 	}
11317836SJohn.Forte@Sun.COM 
11327836SJohn.Forte@Sun.COM 	if (icp->conn_current_stage == ISCSI_SECURITY_NEGOTIATION_STAGE) {
11337836SJohn.Forte@Sun.COM 		if (iscsiAuthClientRecvBegin(auth_client) !=
11347836SJohn.Forte@Sun.COM 		    iscsiAuthStatusNoError) {
11357836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
11367836SJohn.Forte@Sun.COM 			    "authentication receive failed", icp->conn_oid);
11377836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_INTERNAL_ERROR);
11387836SJohn.Forte@Sun.COM 		}
11397836SJohn.Forte@Sun.COM 
11407836SJohn.Forte@Sun.COM 		if (iscsiAuthClientRecvTransitBit(auth_client,
11417836SJohn.Forte@Sun.COM 		    transit) != iscsiAuthStatusNoError) {
11427836SJohn.Forte@Sun.COM 			cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
11437836SJohn.Forte@Sun.COM 			    "authentication transmit failed", icp->conn_oid);
11447836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_INTERNAL_ERROR);
11457836SJohn.Forte@Sun.COM 		}
11467836SJohn.Forte@Sun.COM 	}
11477836SJohn.Forte@Sun.COM 
11487836SJohn.Forte@Sun.COM 	/*
11497836SJohn.Forte@Sun.COM 	 * scan the text data
11507836SJohn.Forte@Sun.COM 	 */
11517836SJohn.Forte@Sun.COM more_text:
11527836SJohn.Forte@Sun.COM 	while (text && (text < end)) {
11537836SJohn.Forte@Sun.COM 		char *value = NULL;
11547836SJohn.Forte@Sun.COM 		char *value_end = NULL;
11557836SJohn.Forte@Sun.COM 
11567836SJohn.Forte@Sun.COM 		/*
11577836SJohn.Forte@Sun.COM 		 * skip any NULs separating each text key=value pair
11587836SJohn.Forte@Sun.COM 		 */
11597836SJohn.Forte@Sun.COM 		while ((text < end) && (*text == '\0')) {
11607836SJohn.Forte@Sun.COM 			text++;
11617836SJohn.Forte@Sun.COM 		}
11627836SJohn.Forte@Sun.COM 		if (text >= end) {
11637836SJohn.Forte@Sun.COM 			break;
11647836SJohn.Forte@Sun.COM 		}
11657836SJohn.Forte@Sun.COM 
11667836SJohn.Forte@Sun.COM 		/*
11677836SJohn.Forte@Sun.COM 		 * handle keys appropriate for each stage
11687836SJohn.Forte@Sun.COM 		 */
11697836SJohn.Forte@Sun.COM 		switch (icp->conn_current_stage) {
11707836SJohn.Forte@Sun.COM 		case ISCSI_SECURITY_NEGOTIATION_STAGE:
11717836SJohn.Forte@Sun.COM 			/*
11727836SJohn.Forte@Sun.COM 			 * a few keys are possible in Security stage
11737836SJohn.Forte@Sun.COM 			 * * which the auth code doesn't care about,
11747836SJohn.Forte@Sun.COM 			 * * but which we might want to see, or at
11757836SJohn.Forte@Sun.COM 			 * * least not choke on.
11767836SJohn.Forte@Sun.COM 			 */
11777836SJohn.Forte@Sun.COM 			if (iscsi_find_key_value("TargetAlias",
11787836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
11797836SJohn.Forte@Sun.COM 				isp->sess_alias_length =
11807836SJohn.Forte@Sun.COM 				    sizeof (isp->sess_alias) - 1;
11817836SJohn.Forte@Sun.COM 
11827836SJohn.Forte@Sun.COM 				if ((value_end - value) <
11837836SJohn.Forte@Sun.COM 				    isp->sess_alias_length) {
11847836SJohn.Forte@Sun.COM 					isp->sess_alias_length =
11857836SJohn.Forte@Sun.COM 					    value_end - value;
11867836SJohn.Forte@Sun.COM 				}
11877836SJohn.Forte@Sun.COM 
11887836SJohn.Forte@Sun.COM 				bcopy(value, isp->sess_alias,
11897836SJohn.Forte@Sun.COM 				    isp->sess_alias_length);
11907836SJohn.Forte@Sun.COM 				isp->sess_alias[isp->sess_alias_length + 1] =
11917836SJohn.Forte@Sun.COM 				    '\0';
11927836SJohn.Forte@Sun.COM 				text = value_end;
11937836SJohn.Forte@Sun.COM 
11947836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("TargetAddress",
11957836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
11967836SJohn.Forte@Sun.COM 				if (!ISCSI_SUCCESS(iscsi_update_address(
11977836SJohn.Forte@Sun.COM 				    icp, value))) {
11987836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
11997836SJohn.Forte@Sun.COM 					    "login failed - login redirection "
12007836SJohn.Forte@Sun.COM 					    "invalid", icp->conn_oid);
12017836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
12027836SJohn.Forte@Sun.COM 				}
12037836SJohn.Forte@Sun.COM 				text = value_end;
12047836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("TargetPortalGroupTag",
12057836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
12067836SJohn.Forte@Sun.COM 				/*
12077836SJohn.Forte@Sun.COM 				 * We should have already obtained this via
12087836SJohn.Forte@Sun.COM 				 * discovery.  We've already picked an isid,
12097836SJohn.Forte@Sun.COM 				 * so the most we can do is confirm we reached
12107836SJohn.Forte@Sun.COM 				 * the portal group we were expecting to.
12117836SJohn.Forte@Sun.COM 				 */
12127836SJohn.Forte@Sun.COM 				if (ddi_strtoul(value, &tmpe, 0, &tmp) != 0) {
12137836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
12147836SJohn.Forte@Sun.COM 				}
12157836SJohn.Forte@Sun.COM 				if (isp->sess_tpgt_conf != ISCSI_DEFAULT_TPGT) {
12167836SJohn.Forte@Sun.COM 					if (tmp != isp->sess_tpgt_conf) {
12177836SJohn.Forte@Sun.COM 
12187836SJohn.Forte@Sun.COM 	cmn_err(CE_WARN, "iscsi connection(%u) login failed - target "
12197836SJohn.Forte@Sun.COM 	    "protocol group tag mismatch, expected %d, received %lu",
12207836SJohn.Forte@Sun.COM 	    icp->conn_oid, isp->sess_tpgt_conf, tmp);
122112001SZhang.Yi@Sun.COM 	return (ISCSI_STATUS_LOGIN_TPGT_NEGO_FAIL);
12227836SJohn.Forte@Sun.COM 
12237836SJohn.Forte@Sun.COM 					}
12247836SJohn.Forte@Sun.COM 				}
12257836SJohn.Forte@Sun.COM 				isp->sess_tpgt_nego = (int)tmp;
12267836SJohn.Forte@Sun.COM 				text = value_end;
12277836SJohn.Forte@Sun.COM 			} else {
12287836SJohn.Forte@Sun.COM 				/*
12297836SJohn.Forte@Sun.COM 				 * any key we don't recognize either goes
12307836SJohn.Forte@Sun.COM 				 * to the auth code, or we choke on it
12317836SJohn.Forte@Sun.COM 				 */
12327836SJohn.Forte@Sun.COM 				int keytype = iscsiAuthKeyTypeNone;
12337836SJohn.Forte@Sun.COM 
12347836SJohn.Forte@Sun.COM 				while (iscsiAuthClientGetNextKeyType(
12357836SJohn.Forte@Sun.COM 				    &keytype) == iscsiAuthStatusNoError) {
12367836SJohn.Forte@Sun.COM 
12377836SJohn.Forte@Sun.COM 					char *key =
12387836SJohn.Forte@Sun.COM 					    (char *)iscsiAuthClientGetKeyName(
12397836SJohn.Forte@Sun.COM 					    keytype);
12407836SJohn.Forte@Sun.COM 
12417836SJohn.Forte@Sun.COM 					if ((key) &&
12427836SJohn.Forte@Sun.COM 					    (iscsi_find_key_value(key,
12437836SJohn.Forte@Sun.COM 					    text, end, &value, &value_end))) {
12447836SJohn.Forte@Sun.COM 
12457836SJohn.Forte@Sun.COM 						if (iscsiAuthClientRecvKeyValue
12467836SJohn.Forte@Sun.COM 						    (auth_client, keytype,
12477836SJohn.Forte@Sun.COM 						    value) !=
12487836SJohn.Forte@Sun.COM 						    iscsiAuthStatusNoError) {
12497836SJohn.Forte@Sun.COM 
12507836SJohn.Forte@Sun.COM 	cmn_err(CE_WARN, "iscsi connection(%u) login failed - can't accept "
12517836SJohn.Forte@Sun.COM 	    "%s in security stage", icp->conn_oid, text);
12527836SJohn.Forte@Sun.COM 	return (ISCSI_STATUS_NEGO_FAIL);
12537836SJohn.Forte@Sun.COM 
12547836SJohn.Forte@Sun.COM 						}
12557836SJohn.Forte@Sun.COM 						text = value_end;
12567836SJohn.Forte@Sun.COM 						goto more_text;
12577836SJohn.Forte@Sun.COM 					}
12587836SJohn.Forte@Sun.COM 				}
12597836SJohn.Forte@Sun.COM 
12607836SJohn.Forte@Sun.COM 	cmn_err(CE_WARN, "iscsi connection(%u) login failed - can't except "
12617836SJohn.Forte@Sun.COM 	    "%s in security stage", icp->conn_oid, text);
12627836SJohn.Forte@Sun.COM 
12637836SJohn.Forte@Sun.COM 				return (ISCSI_STATUS_NEGO_FAIL);
12647836SJohn.Forte@Sun.COM 			}
12657836SJohn.Forte@Sun.COM 			break;
12667836SJohn.Forte@Sun.COM 		case ISCSI_OP_PARMS_NEGOTIATION_STAGE:
12677836SJohn.Forte@Sun.COM 			if (iscsi_find_key_value("TargetAlias", text,
12687836SJohn.Forte@Sun.COM 			    end, &value, &value_end)) {
12697836SJohn.Forte@Sun.COM 				isp->sess_alias_length =
12707836SJohn.Forte@Sun.COM 				    sizeof (isp->sess_alias) - 1;
12717836SJohn.Forte@Sun.COM 
12727836SJohn.Forte@Sun.COM 				if ((value_end - value) <
12737836SJohn.Forte@Sun.COM 				    isp->sess_alias_length) {
12747836SJohn.Forte@Sun.COM 					isp->sess_alias_length =
12757836SJohn.Forte@Sun.COM 					    value_end - value;
12767836SJohn.Forte@Sun.COM 				}
12777836SJohn.Forte@Sun.COM 
12787836SJohn.Forte@Sun.COM 				bcopy(value, isp->sess_alias,
12797836SJohn.Forte@Sun.COM 				    isp->sess_alias_length);
12807836SJohn.Forte@Sun.COM 				isp->sess_alias[isp->sess_alias_length + 1] =
12817836SJohn.Forte@Sun.COM 				    '\0';
12827836SJohn.Forte@Sun.COM 				text = value_end;
12837836SJohn.Forte@Sun.COM 
12847836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("TargetAddress",
12857836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
12867836SJohn.Forte@Sun.COM 				if (!ISCSI_SUCCESS(iscsi_update_address(
12877836SJohn.Forte@Sun.COM 				    icp, value))) {
12887836SJohn.Forte@Sun.COM 
12897836SJohn.Forte@Sun.COM 	cmn_err(CE_WARN, "iscsi connection(%u) login failed - login "
12907836SJohn.Forte@Sun.COM 	    "redirection invalid", icp->conn_oid);
12917836SJohn.Forte@Sun.COM 
12927836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
12937836SJohn.Forte@Sun.COM 				}
12947836SJohn.Forte@Sun.COM 				text = value_end;
12957836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("TargetPortalGroupTag",
12967836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
12977836SJohn.Forte@Sun.COM 				/*
12987836SJohn.Forte@Sun.COM 				 * We should have already obtained this via
12997836SJohn.Forte@Sun.COM 				 * discovery.  We've already picked an isid,
13007836SJohn.Forte@Sun.COM 				 * so the most we can do is confirm we reached
13017836SJohn.Forte@Sun.COM 				 * the portal group we were expecting to.
13027836SJohn.Forte@Sun.COM 				 */
13037836SJohn.Forte@Sun.COM 				if (ddi_strtoul(value, &tmpe, 0, &tmp) != 0) {
13047836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
13057836SJohn.Forte@Sun.COM 				}
13067836SJohn.Forte@Sun.COM 				if (isp->sess_tpgt_conf != ISCSI_DEFAULT_TPGT) {
13077836SJohn.Forte@Sun.COM 					if (tmp != isp->sess_tpgt_conf) {
13087836SJohn.Forte@Sun.COM 
13097836SJohn.Forte@Sun.COM 	cmn_err(CE_WARN, "iscsi connection(%u) login failed - target portal "
13107836SJohn.Forte@Sun.COM 	    "tag mismatch, expected:%d received:%lu", icp->conn_oid,
13117836SJohn.Forte@Sun.COM 	    isp->sess_tpgt_conf, tmp);
131212001SZhang.Yi@Sun.COM 	return (ISCSI_STATUS_LOGIN_TPGT_NEGO_FAIL);
13137836SJohn.Forte@Sun.COM 
13147836SJohn.Forte@Sun.COM 					}
13157836SJohn.Forte@Sun.COM 				}
13167836SJohn.Forte@Sun.COM 				isp->sess_tpgt_nego = (int)tmp;
13177836SJohn.Forte@Sun.COM 				text = value_end;
13187836SJohn.Forte@Sun.COM 
13197836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("InitialR2T",
13207836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
13217836SJohn.Forte@Sun.COM 
13227836SJohn.Forte@Sun.COM 				/*
13237836SJohn.Forte@Sun.COM 				 * iSCSI RFC section 12.10 states that
13247836SJohn.Forte@Sun.COM 				 * InitialR2T is Irrelevant for a
13257836SJohn.Forte@Sun.COM 				 * discovery session.
13267836SJohn.Forte@Sun.COM 				 */
13277836SJohn.Forte@Sun.COM 				if (isp->sess_type ==
13287836SJohn.Forte@Sun.COM 				    ISCSI_SESS_TYPE_DISCOVERY) {
13297836SJohn.Forte@Sun.COM 					/* EMPTY */
13307836SJohn.Forte@Sun.COM 				} else if (value == NULL) {
13317836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
13327836SJohn.Forte@Sun.COM 					    "login failed - InitialR2T is "
13337836SJohn.Forte@Sun.COM 					    "invalid - protocol error",
13347836SJohn.Forte@Sun.COM 					    icp->conn_oid);
13357836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
13367836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "Yes") == 0) {
13377836SJohn.Forte@Sun.COM 					icp->conn_params.initial_r2t = B_TRUE;
13387836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "No") == 0) {
13397836SJohn.Forte@Sun.COM 					icp->conn_params.initial_r2t = B_FALSE;
13407836SJohn.Forte@Sun.COM 				} else {
13417836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
13427836SJohn.Forte@Sun.COM 					    "login failed - InitialR2T  is "
13437836SJohn.Forte@Sun.COM 					    "invalid - protocol error",
13447836SJohn.Forte@Sun.COM 					    icp->conn_oid);
13457836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
13467836SJohn.Forte@Sun.COM 				}
13477836SJohn.Forte@Sun.COM 				text = value_end;
13487836SJohn.Forte@Sun.COM 
13497836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("ImmediateData",
13507836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
13517836SJohn.Forte@Sun.COM 
13527836SJohn.Forte@Sun.COM 				/*
13537836SJohn.Forte@Sun.COM 				 * iSCSI RFC section 12.11 states that
13547836SJohn.Forte@Sun.COM 				 * ImmediateData is Irrelevant for a
13557836SJohn.Forte@Sun.COM 				 * discovery session.
13567836SJohn.Forte@Sun.COM 				 */
13577836SJohn.Forte@Sun.COM 				if (isp->sess_type ==
13587836SJohn.Forte@Sun.COM 				    ISCSI_SESS_TYPE_DISCOVERY) {
13597836SJohn.Forte@Sun.COM 					/* EMPTY */
13607836SJohn.Forte@Sun.COM 				} else if (value == NULL) {
13617836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
13627836SJohn.Forte@Sun.COM 					    "login failed - ImmediateData is "
13637836SJohn.Forte@Sun.COM 					    "invalid - protocol error",
13647836SJohn.Forte@Sun.COM 					    icp->conn_oid);
13657836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
13667836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "Yes") == 0) {
13677836SJohn.Forte@Sun.COM 					icp->conn_params.immediate_data = 1;
13687836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "No") == 0) {
13697836SJohn.Forte@Sun.COM 					icp->conn_params.immediate_data = 0;
13707836SJohn.Forte@Sun.COM 				} else {
13717836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
13727836SJohn.Forte@Sun.COM 					    "login failed - ImmediateData is "
13737836SJohn.Forte@Sun.COM 					    "invalid - protocol error",
13747836SJohn.Forte@Sun.COM 					    icp->conn_oid);
13757836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
13767836SJohn.Forte@Sun.COM 				}
13777836SJohn.Forte@Sun.COM 				text = value_end;
13787836SJohn.Forte@Sun.COM 
13797836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value(
13807836SJohn.Forte@Sun.COM 			    "MaxRecvDataSegmentLength", text, end,
13817836SJohn.Forte@Sun.COM 			    &value, &value_end)) {
13827836SJohn.Forte@Sun.COM 
13837836SJohn.Forte@Sun.COM 				if (ddi_strtoul(value, &tmpe, 0, &tmp) != 0) {
13847836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
13857836SJohn.Forte@Sun.COM 					    "login failed - MaxRecvDataSegment"
13867836SJohn.Forte@Sun.COM 					    "Length is invalid - protocol "
13877836SJohn.Forte@Sun.COM 					    "error", icp->conn_oid);
13887836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_NEGO_FAIL);
13897836SJohn.Forte@Sun.COM 				}
13907836SJohn.Forte@Sun.COM 				icp->conn_params.max_recv_data_seg_len =
13917836SJohn.Forte@Sun.COM 				    icp->conn_params.max_xmit_data_seg_len =
13927836SJohn.Forte@Sun.COM 				    (int)tmp;
13937836SJohn.Forte@Sun.COM 
13947836SJohn.Forte@Sun.COM 				text = value_end;
13957836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("FirstBurstLength",
13967836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
13977836SJohn.Forte@Sun.COM 
13987836SJohn.Forte@Sun.COM 				/*
13997836SJohn.Forte@Sun.COM 				 * iSCSI RFC section 12.14 states that
14007836SJohn.Forte@Sun.COM 				 * FirstBurstLength is Irrelevant if
14017836SJohn.Forte@Sun.COM 				 * InitialR2T=Yes and ImmediateData=No
14027836SJohn.Forte@Sun.COM 				 * or is this is a discovery session.
14037836SJohn.Forte@Sun.COM 				 */
14047836SJohn.Forte@Sun.COM 				if ((isp->sess_type ==
14057836SJohn.Forte@Sun.COM 				    ISCSI_SESS_TYPE_DISCOVERY)) {
14067836SJohn.Forte@Sun.COM 					/* EMPTY */
14077836SJohn.Forte@Sun.COM 				} else if (value &&
14087836SJohn.Forte@Sun.COM 				    (strcmp(value, "Irrelevant") == 0)) {
14097836SJohn.Forte@Sun.COM 					/* irrelevant */
14107836SJohn.Forte@Sun.COM 					fbl_irrelevant = B_TRUE;
14117836SJohn.Forte@Sun.COM 				} else if (ddi_strtoul(
14127836SJohn.Forte@Sun.COM 				    value, &tmpe, 0, &tmp) != 0) {
14137836SJohn.Forte@Sun.COM 					/* bad value */
14147836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
14157836SJohn.Forte@Sun.COM 					    "login failed - FirstBurstLength"
14167836SJohn.Forte@Sun.COM 					    "is invalid - protocol error",
14177836SJohn.Forte@Sun.COM 					    icp->conn_oid);
14187836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
14197836SJohn.Forte@Sun.COM 				} else {
14207836SJohn.Forte@Sun.COM 					/* good value */
14217836SJohn.Forte@Sun.COM 					icp->conn_params.first_burst_length =
14227836SJohn.Forte@Sun.COM 					    (int)tmp;
14237836SJohn.Forte@Sun.COM 				}
14247836SJohn.Forte@Sun.COM 				text = value_end;
14257836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("MaxBurstLength",
14267836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
14277836SJohn.Forte@Sun.COM 				/*
14287836SJohn.Forte@Sun.COM 				 * iSCSI RFC section 12.13 states that
14297836SJohn.Forte@Sun.COM 				 * MaxBurstLength is Irrelevant for a
14307836SJohn.Forte@Sun.COM 				 * discovery session.
14317836SJohn.Forte@Sun.COM 				 */
14327836SJohn.Forte@Sun.COM 				if (isp->sess_type ==
14337836SJohn.Forte@Sun.COM 				    ISCSI_SESS_TYPE_DISCOVERY) {
14347836SJohn.Forte@Sun.COM 					/* EMPTY */
14357836SJohn.Forte@Sun.COM 				} else if (ddi_strtoul(
14367836SJohn.Forte@Sun.COM 				    value, &tmpe, 0, &tmp) != 0) {
14377836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
14387836SJohn.Forte@Sun.COM 					    "login failed - MaxBurstLength"
14397836SJohn.Forte@Sun.COM 					    "is invalid - protocol error",
14407836SJohn.Forte@Sun.COM 					    icp->conn_oid);
14417836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
14427836SJohn.Forte@Sun.COM 				} else {
14437836SJohn.Forte@Sun.COM 					icp->conn_params.max_burst_length =
14447836SJohn.Forte@Sun.COM 					    (int)tmp;
14457836SJohn.Forte@Sun.COM 				}
14467836SJohn.Forte@Sun.COM 
14477836SJohn.Forte@Sun.COM 				text = value_end;
14487836SJohn.Forte@Sun.COM 
14497836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("HeaderDigest",
14507836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
14517836SJohn.Forte@Sun.COM 
14527836SJohn.Forte@Sun.COM 				if (strcmp(value, "None") == 0) {
14537836SJohn.Forte@Sun.COM 					if (icp->conn_params.header_digest !=
14547836SJohn.Forte@Sun.COM 					    ISCSI_DIGEST_CRC32C) {
14557836SJohn.Forte@Sun.COM 						icp->conn_params.header_digest =
14567836SJohn.Forte@Sun.COM 						    ISCSI_DIGEST_NONE;
14577836SJohn.Forte@Sun.COM 					} else {
14587836SJohn.Forte@Sun.COM 						cmn_err(CE_WARN, "iscsi "
14597836SJohn.Forte@Sun.COM 						    "connection(%u) login "
14607836SJohn.Forte@Sun.COM 						    "failed - HeaderDigest="
14617836SJohn.Forte@Sun.COM 						    "CRC32 is required, can't "
14627836SJohn.Forte@Sun.COM 						    "accept %s",
14637836SJohn.Forte@Sun.COM 						    icp->conn_oid, text);
14647836SJohn.Forte@Sun.COM 						return (ISCSI_STATUS_NEGO_FAIL);
14657836SJohn.Forte@Sun.COM 					}
14667836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "CRC32C") == 0) {
14677836SJohn.Forte@Sun.COM 					if (icp->conn_params.header_digest !=
14687836SJohn.Forte@Sun.COM 					    ISCSI_DIGEST_NONE) {
14697836SJohn.Forte@Sun.COM 						icp->conn_params.header_digest =
14707836SJohn.Forte@Sun.COM 						    ISCSI_DIGEST_CRC32C;
14717836SJohn.Forte@Sun.COM 					} else {
14727836SJohn.Forte@Sun.COM 						cmn_err(CE_WARN, "iscsi "
14737836SJohn.Forte@Sun.COM 						    "connection(%u) login "
14747836SJohn.Forte@Sun.COM 						    "failed - HeaderDigest="
14757836SJohn.Forte@Sun.COM 						    "None is required, can't "
14767836SJohn.Forte@Sun.COM 						    "accept %s",
14777836SJohn.Forte@Sun.COM 						    icp->conn_oid, text);
14787836SJohn.Forte@Sun.COM 						return (ISCSI_STATUS_NEGO_FAIL);
14797836SJohn.Forte@Sun.COM 					}
14807836SJohn.Forte@Sun.COM 				} else {
14817836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
14827836SJohn.Forte@Sun.COM 					    "login failed - HeaderDigest "
14837836SJohn.Forte@Sun.COM 					    "can't accept %s", icp->conn_oid,
14847836SJohn.Forte@Sun.COM 					    text);
14857836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_NEGO_FAIL);
14867836SJohn.Forte@Sun.COM 				}
14877836SJohn.Forte@Sun.COM 				text = value_end;
14887836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("DataDigest", text,
14897836SJohn.Forte@Sun.COM 			    end, &value, &value_end)) {
14907836SJohn.Forte@Sun.COM 
14917836SJohn.Forte@Sun.COM 				if (strcmp(value, "None") == 0) {
14927836SJohn.Forte@Sun.COM 					if (icp->conn_params.data_digest !=
14937836SJohn.Forte@Sun.COM 					    ISCSI_DIGEST_CRC32C) {
14947836SJohn.Forte@Sun.COM 						icp->conn_params.data_digest =
14957836SJohn.Forte@Sun.COM 						    ISCSI_DIGEST_NONE;
14967836SJohn.Forte@Sun.COM 					} else {
14977836SJohn.Forte@Sun.COM 						cmn_err(CE_WARN, "iscsi "
14987836SJohn.Forte@Sun.COM 						    "connection(%u) login "
14997836SJohn.Forte@Sun.COM 						    "failed - DataDigest="
15007836SJohn.Forte@Sun.COM 						    "CRC32C is required, "
15017836SJohn.Forte@Sun.COM 						    "can't accept %s",
15027836SJohn.Forte@Sun.COM 						    icp->conn_oid, text);
15037836SJohn.Forte@Sun.COM 						return (ISCSI_STATUS_NEGO_FAIL);
15047836SJohn.Forte@Sun.COM 					}
15057836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "CRC32C") == 0) {
15067836SJohn.Forte@Sun.COM 					if (icp->conn_params.data_digest !=
15077836SJohn.Forte@Sun.COM 					    ISCSI_DIGEST_NONE) {
15087836SJohn.Forte@Sun.COM 						icp->conn_params.data_digest =
15097836SJohn.Forte@Sun.COM 						    ISCSI_DIGEST_CRC32C;
15107836SJohn.Forte@Sun.COM 					} else {
15117836SJohn.Forte@Sun.COM 						cmn_err(CE_WARN, "iscsi "
15127836SJohn.Forte@Sun.COM 						    "connection(%u) login "
15137836SJohn.Forte@Sun.COM 						    "failed - DataDigest=None "
15147836SJohn.Forte@Sun.COM 						    "is required, can't "
15157836SJohn.Forte@Sun.COM 						    "accept %s",
15167836SJohn.Forte@Sun.COM 						    icp->conn_oid, text);
15177836SJohn.Forte@Sun.COM 						return (ISCSI_STATUS_NEGO_FAIL);
15187836SJohn.Forte@Sun.COM 					}
15197836SJohn.Forte@Sun.COM 				} else {
15207836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
15217836SJohn.Forte@Sun.COM 					    "login failed - can't accept %s",
15227836SJohn.Forte@Sun.COM 					    icp->conn_oid, text);
15237836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_NEGO_FAIL);
15247836SJohn.Forte@Sun.COM 				}
15257836SJohn.Forte@Sun.COM 				text = value_end;
15267836SJohn.Forte@Sun.COM 
15277836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("DefaultTime2Wait",
15287836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
15297836SJohn.Forte@Sun.COM 
15307836SJohn.Forte@Sun.COM 				if (ddi_strtoul(value, &tmpe, 0, &tmp) != 0) {
15317836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
15327836SJohn.Forte@Sun.COM 					    "login failed - DefaultTime2Wait "
15337836SJohn.Forte@Sun.COM 					    "is invalid - protocol error",
15347836SJohn.Forte@Sun.COM 					    icp->conn_oid);
15357836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
15367836SJohn.Forte@Sun.COM 				}
15377836SJohn.Forte@Sun.COM 				icp->conn_params.default_time_to_wait =
15387836SJohn.Forte@Sun.COM 				    (int)tmp;
15397836SJohn.Forte@Sun.COM 
15407836SJohn.Forte@Sun.COM 				text = value_end;
15417836SJohn.Forte@Sun.COM 
15427836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("DefaultTime2Retain",
15437836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
15447836SJohn.Forte@Sun.COM 
15457836SJohn.Forte@Sun.COM 				if (ddi_strtoul(value, &tmpe, 0, &tmp) != 0) {
15467836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
15477836SJohn.Forte@Sun.COM 					    "login failed - DefaultTime2Retain "
15487836SJohn.Forte@Sun.COM 					    "is invalid - protocol error",
15497836SJohn.Forte@Sun.COM 					    icp->conn_oid);
15507836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
15517836SJohn.Forte@Sun.COM 				}
15527836SJohn.Forte@Sun.COM 				icp->conn_params.default_time_to_retain =
15537836SJohn.Forte@Sun.COM 				    (int)tmp;
15547836SJohn.Forte@Sun.COM 
15557836SJohn.Forte@Sun.COM 				text = value_end;
15567836SJohn.Forte@Sun.COM 
15577836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("OFMarker", text,
15587836SJohn.Forte@Sun.COM 			    end, &value, &value_end)) {
15597836SJohn.Forte@Sun.COM 
15607836SJohn.Forte@Sun.COM 				/*
15617836SJohn.Forte@Sun.COM 				 * result function is AND, target must
15627836SJohn.Forte@Sun.COM 				 * honor our No
15637836SJohn.Forte@Sun.COM 				 */
15647836SJohn.Forte@Sun.COM 				text = value_end;
15657836SJohn.Forte@Sun.COM 
15667836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("OFMarkInt", text,
15677836SJohn.Forte@Sun.COM 			    end, &value, &value_end)) {
15687836SJohn.Forte@Sun.COM 
15697836SJohn.Forte@Sun.COM 				/*
15707836SJohn.Forte@Sun.COM 				 * we don't do markers, so we don't care
15717836SJohn.Forte@Sun.COM 				 */
15727836SJohn.Forte@Sun.COM 				text = value_end;
15737836SJohn.Forte@Sun.COM 
15747836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("IFMarker", text,
15757836SJohn.Forte@Sun.COM 			    end, &value, &value_end)) {
15767836SJohn.Forte@Sun.COM 
15777836SJohn.Forte@Sun.COM 				/*
15787836SJohn.Forte@Sun.COM 				 * result function is AND, target must
15797836SJohn.Forte@Sun.COM 				 * honor our No
15807836SJohn.Forte@Sun.COM 				 */
15817836SJohn.Forte@Sun.COM 				text = value_end;
15827836SJohn.Forte@Sun.COM 
15837836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("IFMarkInt", text,
15847836SJohn.Forte@Sun.COM 			    end, &value, &value_end)) {
15857836SJohn.Forte@Sun.COM 
15867836SJohn.Forte@Sun.COM 				/*
15877836SJohn.Forte@Sun.COM 				 * we don't do markers, so we don't care
15887836SJohn.Forte@Sun.COM 				 */
15897836SJohn.Forte@Sun.COM 				text = value_end;
15907836SJohn.Forte@Sun.COM 
15917836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("DataPDUInOrder",
15927836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
15937836SJohn.Forte@Sun.COM 
15947836SJohn.Forte@Sun.COM 				/*
15957836SJohn.Forte@Sun.COM 				 * iSCSI RFC section 12.18 states that
15967836SJohn.Forte@Sun.COM 				 * DataPDUInOrder is Irrelevant for a
15977836SJohn.Forte@Sun.COM 				 * discovery session.
15987836SJohn.Forte@Sun.COM 				 */
15997836SJohn.Forte@Sun.COM 				if (isp->sess_type ==
16007836SJohn.Forte@Sun.COM 				    ISCSI_SESS_TYPE_DISCOVERY) {
16017836SJohn.Forte@Sun.COM 					/* EMPTY */
16027836SJohn.Forte@Sun.COM 				} else if (value == NULL) {
16037836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
16047836SJohn.Forte@Sun.COM 					    "login failed - InitialR2T is "
16057836SJohn.Forte@Sun.COM 					    "invalid - protocol error",
16067836SJohn.Forte@Sun.COM 					    icp->conn_oid);
16077836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
16087836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "Yes") == 0) {
16097836SJohn.Forte@Sun.COM 					icp->conn_params.data_pdu_in_order =
16107836SJohn.Forte@Sun.COM 					    B_TRUE;
16117836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "No") == 0) {
16127836SJohn.Forte@Sun.COM 					icp->conn_params.data_pdu_in_order =
16137836SJohn.Forte@Sun.COM 					    B_FALSE;
16147836SJohn.Forte@Sun.COM 				} else {
16157836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
16167836SJohn.Forte@Sun.COM 					    "login failed - InitialR2T is "
16177836SJohn.Forte@Sun.COM 					    "invalid - protocol error",
16187836SJohn.Forte@Sun.COM 					    icp->conn_oid);
16197836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
16207836SJohn.Forte@Sun.COM 				}
16217836SJohn.Forte@Sun.COM 				text = value_end;
16227836SJohn.Forte@Sun.COM 
16237836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("DataSequenceInOrder",
16247836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
16257836SJohn.Forte@Sun.COM 
16267836SJohn.Forte@Sun.COM 				/*
16277836SJohn.Forte@Sun.COM 				 * iSCSI RFC section 12.19 states that
16287836SJohn.Forte@Sun.COM 				 * DataSequenceInOrder is Irrelevant for a
16297836SJohn.Forte@Sun.COM 				 * discovery session.
16307836SJohn.Forte@Sun.COM 				 */
16317836SJohn.Forte@Sun.COM 				if (isp->sess_type ==
16327836SJohn.Forte@Sun.COM 				    ISCSI_SESS_TYPE_DISCOVERY) {
16337836SJohn.Forte@Sun.COM 					/* EMPTY */
16347836SJohn.Forte@Sun.COM 				} else if (value == NULL) {
16357836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
16367836SJohn.Forte@Sun.COM 					    "login failed - InitialR2T is "
16377836SJohn.Forte@Sun.COM 					    "invalid - protocol error",
16387836SJohn.Forte@Sun.COM 					    icp->conn_oid);
16397836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
16407836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "Yes") == 0) {
16417836SJohn.Forte@Sun.COM 					icp->conn_params.
16427836SJohn.Forte@Sun.COM 					    data_sequence_in_order = B_TRUE;
16437836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "No") == 0) {
16447836SJohn.Forte@Sun.COM 					icp->conn_params.
16457836SJohn.Forte@Sun.COM 					    data_sequence_in_order = B_FALSE;
16467836SJohn.Forte@Sun.COM 				} else {
16477836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
16487836SJohn.Forte@Sun.COM 					    "login failed - InitialR2T is "
16497836SJohn.Forte@Sun.COM 					    "invalid - protocol error",
16507836SJohn.Forte@Sun.COM 					    icp->conn_oid);
16517836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_PROTOCOL_ERROR);
16527836SJohn.Forte@Sun.COM 				}
16537836SJohn.Forte@Sun.COM 				text = value_end;
16547836SJohn.Forte@Sun.COM 
16557836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("MaxOutstandingR2T",
16567836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
16577836SJohn.Forte@Sun.COM 
16587836SJohn.Forte@Sun.COM 				/*
16597836SJohn.Forte@Sun.COM 				 * iSCSI RFC section 12.17 states that
16607836SJohn.Forte@Sun.COM 				 * MaxOutstandingR2T is Irrelevant for a
16617836SJohn.Forte@Sun.COM 				 * discovery session.
16627836SJohn.Forte@Sun.COM 				 */
16637836SJohn.Forte@Sun.COM 				if (isp->sess_type ==
16647836SJohn.Forte@Sun.COM 				    ISCSI_SESS_TYPE_DISCOVERY) {
16657836SJohn.Forte@Sun.COM 					/* EMPTY */
16667836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "1")) {
16677836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
16687836SJohn.Forte@Sun.COM 					    "login failed - can't accept "
16697836SJohn.Forte@Sun.COM 					    "MaxOutstandingR2T %s",
16707836SJohn.Forte@Sun.COM 					    icp->conn_oid, value);
16717836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_NEGO_FAIL);
16727836SJohn.Forte@Sun.COM 				}
16737836SJohn.Forte@Sun.COM 				text = value_end;
16747836SJohn.Forte@Sun.COM 
16757836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("MaxConnections",
16767836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
16777836SJohn.Forte@Sun.COM 
16787836SJohn.Forte@Sun.COM 				/*
16797836SJohn.Forte@Sun.COM 				 * iSCSI RFC section 12.2 states that
16807836SJohn.Forte@Sun.COM 				 * MaxConnections is Irrelevant for a
16817836SJohn.Forte@Sun.COM 				 * discovery session.
16827836SJohn.Forte@Sun.COM 				 */
16837836SJohn.Forte@Sun.COM 				if (isp->sess_type ==
16847836SJohn.Forte@Sun.COM 				    ISCSI_SESS_TYPE_DISCOVERY) {
16857836SJohn.Forte@Sun.COM 					/* EMPTY */
16867836SJohn.Forte@Sun.COM 				} else if (strcmp(value, "1")) {
16877836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
16887836SJohn.Forte@Sun.COM 					    "login failed - can't accept "
16897836SJohn.Forte@Sun.COM 					    "MaxConnections %s",
16907836SJohn.Forte@Sun.COM 					    icp->conn_oid, value);
16917836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_NEGO_FAIL);
16927836SJohn.Forte@Sun.COM 				}
16937836SJohn.Forte@Sun.COM 				text = value_end;
16947836SJohn.Forte@Sun.COM 
16957836SJohn.Forte@Sun.COM 			} else if (iscsi_find_key_value("ErrorRecoveryLevel",
16967836SJohn.Forte@Sun.COM 			    text, end, &value, &value_end)) {
16977836SJohn.Forte@Sun.COM 
16987836SJohn.Forte@Sun.COM 				if (strcmp(value, "0")) {
16997836SJohn.Forte@Sun.COM 
17007836SJohn.Forte@Sun.COM 					cmn_err(CE_WARN, "iscsi connection(%u) "
17017836SJohn.Forte@Sun.COM 					    "login failed - can't accept "
17027836SJohn.Forte@Sun.COM 					    "ErrorRecoveryLevel %s",
17037836SJohn.Forte@Sun.COM 					    icp->conn_oid, value);
17047836SJohn.Forte@Sun.COM 					return (ISCSI_STATUS_NEGO_FAIL);
17057836SJohn.Forte@Sun.COM 				}
17067836SJohn.Forte@Sun.COM 				text = value_end;
17077836SJohn.Forte@Sun.COM 
17087836SJohn.Forte@Sun.COM 			} else {
17097836SJohn.Forte@Sun.COM 				cmn_err(CE_WARN, "iscsi connection(%u) "
17107836SJohn.Forte@Sun.COM 				    "login failed - ignoring login "
17117836SJohn.Forte@Sun.COM 				    "parameter %s", icp->conn_oid, value);
17127836SJohn.Forte@Sun.COM 				text = value_end;
17137836SJohn.Forte@Sun.COM 			}
17147836SJohn.Forte@Sun.COM 			break;
17157836SJohn.Forte@Sun.COM 		default:
17167836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_INTERNAL_ERROR);
17177836SJohn.Forte@Sun.COM 		}
17187836SJohn.Forte@Sun.COM 	}
17197836SJohn.Forte@Sun.COM 
17207836SJohn.Forte@Sun.COM 	/*
17217836SJohn.Forte@Sun.COM 	 * iSCSI RFC section 12.14 states that
17227836SJohn.Forte@Sun.COM 	 * FirstBurstLength is Irrelevant if
17237836SJohn.Forte@Sun.COM 	 * InitialR2T=Yes and ImmediateData=No.
17247836SJohn.Forte@Sun.COM 	 * This is a final check to make sure
17257836SJohn.Forte@Sun.COM 	 * the array didn't make a protocol
17267836SJohn.Forte@Sun.COM 	 * violation.
17277836SJohn.Forte@Sun.COM 	 */
17287836SJohn.Forte@Sun.COM 	if ((fbl_irrelevant == B_TRUE) &&
17297836SJohn.Forte@Sun.COM 	    ((icp->conn_params.initial_r2t != B_TRUE) ||
17307836SJohn.Forte@Sun.COM 	    (icp->conn_params.immediate_data != B_FALSE))) {
17317836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN, "iscsi connection(%u) login failed - "
17327836SJohn.Forte@Sun.COM 		    "FirstBurstLength=Irrelevant and (InitialR2T!=Yes or "
17337836SJohn.Forte@Sun.COM 		    "ImmediateData!=No) - protocol error", icp->conn_oid);
17347836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_PROTOCOL_ERROR);
17357836SJohn.Forte@Sun.COM 	}
17367836SJohn.Forte@Sun.COM 
17377836SJohn.Forte@Sun.COM 	if (icp->conn_current_stage == ISCSI_SECURITY_NEGOTIATION_STAGE) {
17387836SJohn.Forte@Sun.COM 		switch (iscsiAuthClientRecvEnd(auth_client, iscsi_null_callback,
17397836SJohn.Forte@Sun.COM 		    (void *)isp, NULL)) {
17407836SJohn.Forte@Sun.COM 		case iscsiAuthStatusContinue:
17417836SJohn.Forte@Sun.COM 			/*
17427836SJohn.Forte@Sun.COM 			 * continue sending PDUs
17437836SJohn.Forte@Sun.COM 			 */
17447836SJohn.Forte@Sun.COM 			break;
17457836SJohn.Forte@Sun.COM 
17467836SJohn.Forte@Sun.COM 		case iscsiAuthStatusPass:
17477836SJohn.Forte@Sun.COM 			break;
17487836SJohn.Forte@Sun.COM 
17497836SJohn.Forte@Sun.COM 		case iscsiAuthStatusInProgress:
17507836SJohn.Forte@Sun.COM 			/*
17517836SJohn.Forte@Sun.COM 			 * this should only occur if we were authenticating the
17527836SJohn.Forte@Sun.COM 			 * target, which we don't do yet, so treat this as an
17537836SJohn.Forte@Sun.COM 			 * error.
17547836SJohn.Forte@Sun.COM 			 */
17557836SJohn.Forte@Sun.COM 		case iscsiAuthStatusNoError:
17567836SJohn.Forte@Sun.COM 			/*
17577836SJohn.Forte@Sun.COM 			 * treat this as an error, since we should get a
17587836SJohn.Forte@Sun.COM 			 * different code
17597836SJohn.Forte@Sun.COM 			 */
17607836SJohn.Forte@Sun.COM 		case iscsiAuthStatusError:
17617836SJohn.Forte@Sun.COM 		case iscsiAuthStatusFail:
17627836SJohn.Forte@Sun.COM 		default:
17637836SJohn.Forte@Sun.COM 			debug_status = 0;
17647836SJohn.Forte@Sun.COM 
17657836SJohn.Forte@Sun.COM 			if (iscsiAuthClientGetDebugStatus(auth_client,
17667836SJohn.Forte@Sun.COM 			    &debug_status) != iscsiAuthStatusNoError) {
17677836SJohn.Forte@Sun.COM 
17687836SJohn.Forte@Sun.COM 				cmn_err(CE_WARN, "iscsi connection(%u) login "
17697836SJohn.Forte@Sun.COM 				    "failed - authentication failed with "
17707836SJohn.Forte@Sun.COM 				    "target (%s)", icp->conn_oid,
17717836SJohn.Forte@Sun.COM 				    iscsiAuthClientDebugStatusToText(
17727836SJohn.Forte@Sun.COM 				    debug_status));
17737836SJohn.Forte@Sun.COM 
17747836SJohn.Forte@Sun.COM 			} else {
17757836SJohn.Forte@Sun.COM 
17767836SJohn.Forte@Sun.COM 				cmn_err(CE_WARN, "iscsi connection(%u) login "
17777836SJohn.Forte@Sun.COM 				    "failed - authentication failed with "
17787836SJohn.Forte@Sun.COM 				    "target", icp->conn_oid);
17797836SJohn.Forte@Sun.COM 
17807836SJohn.Forte@Sun.COM 			}
17817836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_AUTHENTICATION_FAILED);
17827836SJohn.Forte@Sun.COM 		}
17837836SJohn.Forte@Sun.COM 	}
17847836SJohn.Forte@Sun.COM 
17857836SJohn.Forte@Sun.COM 	/*
17867836SJohn.Forte@Sun.COM 	 * record some of the PDU fields for later use
17877836SJohn.Forte@Sun.COM 	 */
17887836SJohn.Forte@Sun.COM 	isp->sess_tsid = ntohs(ilrhp->tsid);
17897836SJohn.Forte@Sun.COM 	isp->sess_expcmdsn = ntohl(ilrhp->expcmdsn);
17907836SJohn.Forte@Sun.COM 	isp->sess_maxcmdsn = ntohl(ilrhp->maxcmdsn);
17917836SJohn.Forte@Sun.COM 	if (ilrhp->status_class == ISCSI_STATUS_CLASS_SUCCESS) {
17927836SJohn.Forte@Sun.COM 		icp->conn_expstatsn = ntohl(ilrhp->statsn) + 1;
17937836SJohn.Forte@Sun.COM 	}
17947836SJohn.Forte@Sun.COM 
17957836SJohn.Forte@Sun.COM 	if (transit) {
17967836SJohn.Forte@Sun.COM 		/*
17977836SJohn.Forte@Sun.COM 		 * advance to the next stage
17987836SJohn.Forte@Sun.COM 		 */
17997836SJohn.Forte@Sun.COM 		icp->conn_partial_response = 0;
18007836SJohn.Forte@Sun.COM 		icp->conn_current_stage =
18017836SJohn.Forte@Sun.COM 		    ilrhp->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK;
18027836SJohn.Forte@Sun.COM 	} else {
18037836SJohn.Forte@Sun.COM 		/*
18047836SJohn.Forte@Sun.COM 		 * we got a partial response, don't advance, more
18057836SJohn.Forte@Sun.COM 		 * negotiation to do
18067836SJohn.Forte@Sun.COM 		 */
18077836SJohn.Forte@Sun.COM 		icp->conn_partial_response = 1;
18087836SJohn.Forte@Sun.COM 	}
18097836SJohn.Forte@Sun.COM 
18107836SJohn.Forte@Sun.COM 	/*
18117836SJohn.Forte@Sun.COM 	 * this PDU is ok, though the login process
18127836SJohn.Forte@Sun.COM 	 * may not be done yet
18137836SJohn.Forte@Sun.COM 	 */
18147836SJohn.Forte@Sun.COM 	return (ISCSI_STATUS_SUCCESS);
18157836SJohn.Forte@Sun.COM }
18167836SJohn.Forte@Sun.COM 
18177836SJohn.Forte@Sun.COM /*
18187836SJohn.Forte@Sun.COM  * iscsi_add_text - caller is assumed to be well-behaved and passing NUL
18197836SJohn.Forte@Sun.COM  * terminated strings
18207836SJohn.Forte@Sun.COM  */
18217836SJohn.Forte@Sun.COM int
iscsi_add_text(idm_pdu_t * text_pdu,int max_data_length,char * param,char * value)18229162SPeter.Dunlap@Sun.COM iscsi_add_text(idm_pdu_t *text_pdu, int max_data_length,
18237836SJohn.Forte@Sun.COM     char *param, char *value)
18247836SJohn.Forte@Sun.COM {
18257836SJohn.Forte@Sun.COM 	int	param_len	= 0;
18267836SJohn.Forte@Sun.COM 	int	value_len	= 0;
18277836SJohn.Forte@Sun.COM 	int	length		= 0;
18287836SJohn.Forte@Sun.COM 	int	pdu_length	= 0;
18297836SJohn.Forte@Sun.COM 	char	*text		= NULL;
18307836SJohn.Forte@Sun.COM 	char	*end		= NULL;
18317836SJohn.Forte@Sun.COM 
18329162SPeter.Dunlap@Sun.COM 	ASSERT(text_pdu != NULL);
18337836SJohn.Forte@Sun.COM 	ASSERT(param != NULL);
18347836SJohn.Forte@Sun.COM 	ASSERT(value != NULL);
18357836SJohn.Forte@Sun.COM 
18367836SJohn.Forte@Sun.COM 	param_len = strlen(param);
18377836SJohn.Forte@Sun.COM 	value_len = strlen(value);
18387836SJohn.Forte@Sun.COM 	/* param, separator, value, and trailing NULL */
18397836SJohn.Forte@Sun.COM 	length		= param_len + 1 + value_len + 1;
18409162SPeter.Dunlap@Sun.COM 	pdu_length	= text_pdu->isp_datalen;
18419162SPeter.Dunlap@Sun.COM 	text		= (char *)text_pdu->isp_data + pdu_length;
18429162SPeter.Dunlap@Sun.COM 	end		= (char *)text_pdu->isp_data + max_data_length;
18437836SJohn.Forte@Sun.COM 	pdu_length	+= length;
18447836SJohn.Forte@Sun.COM 
18457836SJohn.Forte@Sun.COM 	if (text + length >= end) {
18467836SJohn.Forte@Sun.COM 		return (0);
18477836SJohn.Forte@Sun.COM 	}
18487836SJohn.Forte@Sun.COM 
18497836SJohn.Forte@Sun.COM 	/* param */
18507836SJohn.Forte@Sun.COM 	(void) strncpy(text, param, param_len);
18517836SJohn.Forte@Sun.COM 	text += param_len;
18527836SJohn.Forte@Sun.COM 
18537836SJohn.Forte@Sun.COM 	/* separator */
18547836SJohn.Forte@Sun.COM 	*text++ = ISCSI_TEXT_SEPARATOR;
18557836SJohn.Forte@Sun.COM 
18567836SJohn.Forte@Sun.COM 	/* value */
18577836SJohn.Forte@Sun.COM 	(void) strncpy(text, value, value_len);
18587836SJohn.Forte@Sun.COM 	text += value_len;
18597836SJohn.Forte@Sun.COM 
18607836SJohn.Forte@Sun.COM 	/* NULL */
18617836SJohn.Forte@Sun.COM 	*text++ = '\0';
18627836SJohn.Forte@Sun.COM 
18637836SJohn.Forte@Sun.COM 	/* update the length in the PDU header */
18649162SPeter.Dunlap@Sun.COM 	text_pdu->isp_datalen = pdu_length;
18659162SPeter.Dunlap@Sun.COM 	hton24(text_pdu->isp_hdr->dlength, pdu_length);
18667836SJohn.Forte@Sun.COM 
18677836SJohn.Forte@Sun.COM 	return (1);
18687836SJohn.Forte@Sun.COM }
18697836SJohn.Forte@Sun.COM 
18707836SJohn.Forte@Sun.COM /*
18717836SJohn.Forte@Sun.COM  * iscsi_get_next_text - get the next line of text from the given data
18727836SJohn.Forte@Sun.COM  * buffer.  This function searches from the address given for the
18737836SJohn.Forte@Sun.COM  * curr_text parameter.  If curr_text_parameter is NULL return first
18747836SJohn.Forte@Sun.COM  * line in buffer.  The return value is the address of the next line
18757836SJohn.Forte@Sun.COM  * based upon where curr_text is located.
18767836SJohn.Forte@Sun.COM  *
18777836SJohn.Forte@Sun.COM  */
18787836SJohn.Forte@Sun.COM char *
iscsi_get_next_text(char * data,int max_data_length,char * curr_text)18797836SJohn.Forte@Sun.COM iscsi_get_next_text(char *data, int max_data_length, char *curr_text)
18807836SJohn.Forte@Sun.COM {
18817836SJohn.Forte@Sun.COM 	char *curr_data;
18827836SJohn.Forte@Sun.COM 
18837836SJohn.Forte@Sun.COM 	ASSERT(data != NULL);
18847836SJohn.Forte@Sun.COM 
18857836SJohn.Forte@Sun.COM 	/* check if any data exists, if not return */
18867836SJohn.Forte@Sun.COM 	if (max_data_length == 0) {
18877836SJohn.Forte@Sun.COM 		return (NULL);
18887836SJohn.Forte@Sun.COM 	}
18897836SJohn.Forte@Sun.COM 
18907836SJohn.Forte@Sun.COM 	/* handle first call to this function */
18917836SJohn.Forte@Sun.COM 	if (curr_text == NULL) {
18927836SJohn.Forte@Sun.COM 		return (data);
18937836SJohn.Forte@Sun.COM 	}
18947836SJohn.Forte@Sun.COM 
18957836SJohn.Forte@Sun.COM 	/* move to next text string */
18967836SJohn.Forte@Sun.COM 	curr_data = curr_text;
18977836SJohn.Forte@Sun.COM 	while ((curr_data < (data + max_data_length)) && *curr_data) {
18987836SJohn.Forte@Sun.COM 		curr_data++;
18997836SJohn.Forte@Sun.COM 	}
19007836SJohn.Forte@Sun.COM 	curr_data++;		/* go past the NULL to the next entry */
19017836SJohn.Forte@Sun.COM 
19027836SJohn.Forte@Sun.COM 	/* check whether data end reached */
19037836SJohn.Forte@Sun.COM 	if (curr_data >= (data + max_data_length)) {
19047836SJohn.Forte@Sun.COM 		return (NULL);
19057836SJohn.Forte@Sun.COM 	}
19067836SJohn.Forte@Sun.COM 
19077836SJohn.Forte@Sun.COM 	return (curr_data);
19087836SJohn.Forte@Sun.COM }
19097836SJohn.Forte@Sun.COM 
19107836SJohn.Forte@Sun.COM 
19117836SJohn.Forte@Sun.COM /*
19127836SJohn.Forte@Sun.COM  * iscsi_find_key_value -
19137836SJohn.Forte@Sun.COM  *
19147836SJohn.Forte@Sun.COM  */
19157836SJohn.Forte@Sun.COM static int
iscsi_find_key_value(char * param,char * ihp,char * pdu_end,char ** value_start,char ** value_end)19167836SJohn.Forte@Sun.COM iscsi_find_key_value(char *param, char *ihp, char *pdu_end,
19177836SJohn.Forte@Sun.COM     char **value_start, char **value_end)
19187836SJohn.Forte@Sun.COM {
19197836SJohn.Forte@Sun.COM 	char *str = param;
19207836SJohn.Forte@Sun.COM 	char *text = ihp;
19217836SJohn.Forte@Sun.COM 	char *value = NULL;
19227836SJohn.Forte@Sun.COM 
19237836SJohn.Forte@Sun.COM 	if (value_start)
19247836SJohn.Forte@Sun.COM 		*value_start = NULL;
19257836SJohn.Forte@Sun.COM 	if (value_end)
19267836SJohn.Forte@Sun.COM 		*value_end = NULL;
19277836SJohn.Forte@Sun.COM 
19287836SJohn.Forte@Sun.COM 	/*
19297836SJohn.Forte@Sun.COM 	 * make sure they contain the same bytes
19307836SJohn.Forte@Sun.COM 	 */
19317836SJohn.Forte@Sun.COM 	while (*str) {
19327836SJohn.Forte@Sun.COM 		if (text >= pdu_end) {
19337836SJohn.Forte@Sun.COM 			return (0);
19347836SJohn.Forte@Sun.COM 		}
19357836SJohn.Forte@Sun.COM 		if (*text == '\0') {
19367836SJohn.Forte@Sun.COM 			return (0);
19377836SJohn.Forte@Sun.COM 		}
19387836SJohn.Forte@Sun.COM 		if (*str != *text) {
19397836SJohn.Forte@Sun.COM 			return (0);
19407836SJohn.Forte@Sun.COM 		}
19417836SJohn.Forte@Sun.COM 		str++;
19427836SJohn.Forte@Sun.COM 		text++;
19437836SJohn.Forte@Sun.COM 	}
19447836SJohn.Forte@Sun.COM 
19457836SJohn.Forte@Sun.COM 	if ((text >= pdu_end) ||
19467836SJohn.Forte@Sun.COM 	    (*text == '\0') ||
19477836SJohn.Forte@Sun.COM 	    (*text != ISCSI_TEXT_SEPARATOR)) {
19487836SJohn.Forte@Sun.COM 		return (0);
19497836SJohn.Forte@Sun.COM 	}
19507836SJohn.Forte@Sun.COM 
19517836SJohn.Forte@Sun.COM 	/*
19527836SJohn.Forte@Sun.COM 	 * find the value
19537836SJohn.Forte@Sun.COM 	 */
19547836SJohn.Forte@Sun.COM 	value = text + 1;
19557836SJohn.Forte@Sun.COM 
19567836SJohn.Forte@Sun.COM 	/*
19577836SJohn.Forte@Sun.COM 	 * find the end of the value
19587836SJohn.Forte@Sun.COM 	 */
19597836SJohn.Forte@Sun.COM 	while ((text < pdu_end) && (*text))
19607836SJohn.Forte@Sun.COM 		text++;
19617836SJohn.Forte@Sun.COM 
19627836SJohn.Forte@Sun.COM 	if (value_start)
19637836SJohn.Forte@Sun.COM 		*value_start = value;
19647836SJohn.Forte@Sun.COM 	if (value_end)
19657836SJohn.Forte@Sun.COM 		*value_end = text;
19667836SJohn.Forte@Sun.COM 
19677836SJohn.Forte@Sun.COM 	return (1);
19687836SJohn.Forte@Sun.COM }
19697836SJohn.Forte@Sun.COM 
19707836SJohn.Forte@Sun.COM 
19717836SJohn.Forte@Sun.COM /*
19727836SJohn.Forte@Sun.COM  * iscsi_update_address - This function is used on a login redirection.
19737836SJohn.Forte@Sun.COM  * During the login redirection we are asked to switch to an IP address
19747836SJohn.Forte@Sun.COM  * port different than the one we were logging into.
19757836SJohn.Forte@Sun.COM  */
19767836SJohn.Forte@Sun.COM static iscsi_status_t
iscsi_update_address(iscsi_conn_t * icp,char * in)19777836SJohn.Forte@Sun.COM iscsi_update_address(iscsi_conn_t *icp, char *in)
19787836SJohn.Forte@Sun.COM {
19797836SJohn.Forte@Sun.COM 	char		*addr_str, *port_str, *tpgt_str;
19807836SJohn.Forte@Sun.COM 	int		type;
19817836SJohn.Forte@Sun.COM 	struct hostent	*hptr;
19827836SJohn.Forte@Sun.COM 	unsigned long	tmp;
19837836SJohn.Forte@Sun.COM 	int		error_num;
19847836SJohn.Forte@Sun.COM 	int		port;
19857836SJohn.Forte@Sun.COM 
19867836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
19877836SJohn.Forte@Sun.COM 	ASSERT(in != NULL);
19887836SJohn.Forte@Sun.COM 
19897836SJohn.Forte@Sun.COM 	/* parse login redirection response */
19907836SJohn.Forte@Sun.COM 	if (parse_addr_port_tpgt(in, &addr_str, &type,
19917836SJohn.Forte@Sun.COM 	    &port_str, &tpgt_str) == B_FALSE) {
19927836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_PROTOCOL_ERROR);
19937836SJohn.Forte@Sun.COM 	}
19947836SJohn.Forte@Sun.COM 
19957836SJohn.Forte@Sun.COM 	/* convert addr_str */
19967836SJohn.Forte@Sun.COM 	hptr = kgetipnodebyname(addr_str, type, AI_ALL, &error_num);
19977836SJohn.Forte@Sun.COM 	if (!hptr) {
19987836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_PROTOCOL_ERROR);
19997836SJohn.Forte@Sun.COM 	}
20007836SJohn.Forte@Sun.COM 
20017836SJohn.Forte@Sun.COM 	/* convert port_str */
20027836SJohn.Forte@Sun.COM 	if (port_str != NULL) {
20037836SJohn.Forte@Sun.COM 		(void) ddi_strtoul(port_str, NULL, 0, &tmp);
20047836SJohn.Forte@Sun.COM 		port = (int)tmp;
20057836SJohn.Forte@Sun.COM 	} else {
20067836SJohn.Forte@Sun.COM 		port = ISCSI_LISTEN_PORT;
20077836SJohn.Forte@Sun.COM 	}
20087836SJohn.Forte@Sun.COM 
20097836SJohn.Forte@Sun.COM 	iscsid_addr_to_sockaddr(hptr->h_length, *hptr->h_addr_list,
20107836SJohn.Forte@Sun.COM 	    port, &icp->conn_curr_addr.sin);
20117836SJohn.Forte@Sun.COM 
20127836SJohn.Forte@Sun.COM 	kfreehostent(hptr);
20137836SJohn.Forte@Sun.COM 	return (ISCSI_STATUS_SUCCESS);
20147836SJohn.Forte@Sun.COM }
20157836SJohn.Forte@Sun.COM 
20169162SPeter.Dunlap@Sun.COM void
iscsi_login_update_state(iscsi_conn_t * icp,iscsi_login_state_t next_state)20179162SPeter.Dunlap@Sun.COM iscsi_login_update_state(iscsi_conn_t *icp, iscsi_login_state_t next_state)
20189162SPeter.Dunlap@Sun.COM {
20199162SPeter.Dunlap@Sun.COM 	mutex_enter(&icp->conn_login_mutex);
20209162SPeter.Dunlap@Sun.COM 	(void) iscsi_login_update_state_locked(icp, next_state);
20219162SPeter.Dunlap@Sun.COM 	mutex_exit(&icp->conn_login_mutex);
20229162SPeter.Dunlap@Sun.COM }
20239162SPeter.Dunlap@Sun.COM 
20249162SPeter.Dunlap@Sun.COM void
iscsi_login_update_state_locked(iscsi_conn_t * icp,iscsi_login_state_t next_state)20259162SPeter.Dunlap@Sun.COM iscsi_login_update_state_locked(iscsi_conn_t *icp,
20269162SPeter.Dunlap@Sun.COM     iscsi_login_state_t next_state)
20279162SPeter.Dunlap@Sun.COM {
20289162SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&icp->conn_login_mutex));
20299162SPeter.Dunlap@Sun.COM 	next_state = (next_state > LOGIN_MAX) ? LOGIN_MAX : next_state;
20309162SPeter.Dunlap@Sun.COM 	idm_sm_audit_state_change(&icp->conn_state_audit,
20319162SPeter.Dunlap@Sun.COM 	    SAS_ISCSI_LOGIN, icp->conn_login_state, next_state);
20329162SPeter.Dunlap@Sun.COM 
20339162SPeter.Dunlap@Sun.COM 	ISCSI_LOGIN_LOG(CE_NOTE, "iscsi_login_update_state conn %p %d -> %d",
20349162SPeter.Dunlap@Sun.COM 	    (void *)icp, icp->conn_login_state, next_state);
20359162SPeter.Dunlap@Sun.COM 
20369162SPeter.Dunlap@Sun.COM 	icp->conn_login_state = next_state;
20379162SPeter.Dunlap@Sun.COM 	cv_broadcast(&icp->conn_login_cv);
20389162SPeter.Dunlap@Sun.COM }
20399162SPeter.Dunlap@Sun.COM 
20409162SPeter.Dunlap@Sun.COM 
20417836SJohn.Forte@Sun.COM 
20427836SJohn.Forte@Sun.COM /*
20437836SJohn.Forte@Sun.COM  * iscsi_null_callback - This callback may be used under certain
20447836SJohn.Forte@Sun.COM  * conditions when authenticating a target, but I'm not sure what
20457836SJohn.Forte@Sun.COM  * we need to do here.
20467836SJohn.Forte@Sun.COM  */
20477836SJohn.Forte@Sun.COM /* ARGSUSED */
20487836SJohn.Forte@Sun.COM static void
iscsi_null_callback(void * user_handle,void * message_handle,int auth_status)20497836SJohn.Forte@Sun.COM iscsi_null_callback(void *user_handle, void *message_handle, int auth_status)
20507836SJohn.Forte@Sun.COM {
20517836SJohn.Forte@Sun.COM }
20527836SJohn.Forte@Sun.COM 
20537836SJohn.Forte@Sun.COM 
20547836SJohn.Forte@Sun.COM /*
20557836SJohn.Forte@Sun.COM  * iscsi_login_failure_str -
20567836SJohn.Forte@Sun.COM  *
20577836SJohn.Forte@Sun.COM  */
20587836SJohn.Forte@Sun.COM static char *
iscsi_login_failure_str(uchar_t status_class,uchar_t status_detail)20597836SJohn.Forte@Sun.COM iscsi_login_failure_str(uchar_t status_class, uchar_t status_detail)
20607836SJohn.Forte@Sun.COM {
20617836SJohn.Forte@Sun.COM 	switch (status_class) {
20627836SJohn.Forte@Sun.COM 	case 0x00:
20637836SJohn.Forte@Sun.COM 		switch (status_detail) {
20647836SJohn.Forte@Sun.COM 		case 0x00:
20657836SJohn.Forte@Sun.COM 			return ("Login is proceeding okay.");
20667836SJohn.Forte@Sun.COM 		default:
20677836SJohn.Forte@Sun.COM 			break;
20687836SJohn.Forte@Sun.COM 		}
20697836SJohn.Forte@Sun.COM 	case 0x01:
20707836SJohn.Forte@Sun.COM 		switch (status_detail) {
20717836SJohn.Forte@Sun.COM 		case 0x01:
20727836SJohn.Forte@Sun.COM 			return ("Requested ITN has moved temporarily to "
20737836SJohn.Forte@Sun.COM 			    "the address provided.");
20747836SJohn.Forte@Sun.COM 		case 0x02:
20757836SJohn.Forte@Sun.COM 			return ("Requested ITN has moved permanently to "
20767836SJohn.Forte@Sun.COM 			    "the address provided.");
20777836SJohn.Forte@Sun.COM 		default:
20787836SJohn.Forte@Sun.COM 			break;
20797836SJohn.Forte@Sun.COM 		}
20807836SJohn.Forte@Sun.COM 	case 0x02:
20817836SJohn.Forte@Sun.COM 		switch (status_detail) {
20827836SJohn.Forte@Sun.COM 		case 0x00:
20837836SJohn.Forte@Sun.COM 			return ("Miscellaneous iSCSI initiator errors.");
20847836SJohn.Forte@Sun.COM 		case 0x01:
20857836SJohn.Forte@Sun.COM 			return ("Initiator could not be successfully "
20867836SJohn.Forte@Sun.COM 			    "authenticated.");
20877836SJohn.Forte@Sun.COM 		case 0x02:
20887836SJohn.Forte@Sun.COM 			return ("Initiator is not allowed access to the "
20897836SJohn.Forte@Sun.COM 			    "given target.");
20907836SJohn.Forte@Sun.COM 		case 0x03:
20917836SJohn.Forte@Sun.COM 			return ("Requested ITN does not exist at this "
20927836SJohn.Forte@Sun.COM 			    "address.");
20937836SJohn.Forte@Sun.COM 		case 0x04:
20947836SJohn.Forte@Sun.COM 			return ("Requested ITN has been removed and no "
20957836SJohn.Forte@Sun.COM 			    "forwarding address is provided.");
20967836SJohn.Forte@Sun.COM 		case 0x05:
20977836SJohn.Forte@Sun.COM 			return ("Requested iSCSI version range is not "
20987836SJohn.Forte@Sun.COM 			    "supported by the target.");
20997836SJohn.Forte@Sun.COM 		case 0x06:
21007836SJohn.Forte@Sun.COM 			return ("No more connections can be accepted on "
21017836SJohn.Forte@Sun.COM 			    "this Session ID (SSID).");
21027836SJohn.Forte@Sun.COM 		case 0x07:
21037836SJohn.Forte@Sun.COM 			return ("Missing parameters (e.g., iSCSI initiator "
21047836SJohn.Forte@Sun.COM 			    "and/or target name).");
21057836SJohn.Forte@Sun.COM 		case 0x08:
21067836SJohn.Forte@Sun.COM 			return ("Target does not support session spanning "
21077836SJohn.Forte@Sun.COM 			    "to this connection (address).");
21087836SJohn.Forte@Sun.COM 		case 0x09:
21097836SJohn.Forte@Sun.COM 			return ("Target does not support this type of "
21107836SJohn.Forte@Sun.COM 			    "session or not from this initiator.");
21117836SJohn.Forte@Sun.COM 		case 0x0A:
21127836SJohn.Forte@Sun.COM 			return ("Attempt to add a connection to a "
21137836SJohn.Forte@Sun.COM 			    "nonexistent session.");
21147836SJohn.Forte@Sun.COM 		case 0x0B:
21157836SJohn.Forte@Sun.COM 			return ("Invalid request type during login.");
21167836SJohn.Forte@Sun.COM 		default:
21177836SJohn.Forte@Sun.COM 			break;
21187836SJohn.Forte@Sun.COM 		}
21197836SJohn.Forte@Sun.COM 	case 0x03:
21207836SJohn.Forte@Sun.COM 		switch (status_detail) {
21217836SJohn.Forte@Sun.COM 		case 0x00:
21227836SJohn.Forte@Sun.COM 			return ("Target hardware or software error.");
21237836SJohn.Forte@Sun.COM 		case 0x01:
21247836SJohn.Forte@Sun.COM 			return ("iSCSI service or target is not currently "
21257836SJohn.Forte@Sun.COM 			    "operational.");
21267836SJohn.Forte@Sun.COM 		case 0x02:
21277836SJohn.Forte@Sun.COM 			return ("Target has insufficient session, connection "
21287836SJohn.Forte@Sun.COM 			    "or other resources.");
21297836SJohn.Forte@Sun.COM 		default:
21307836SJohn.Forte@Sun.COM 			break;
21317836SJohn.Forte@Sun.COM 		}
21327836SJohn.Forte@Sun.COM 	}
21337836SJohn.Forte@Sun.COM 	return ("Unknown login response received.");
21347836SJohn.Forte@Sun.COM }
21357836SJohn.Forte@Sun.COM 
21367836SJohn.Forte@Sun.COM 
21377836SJohn.Forte@Sun.COM /*
21387836SJohn.Forte@Sun.COM  * iscsi_login_connect -
21397836SJohn.Forte@Sun.COM  */
21407836SJohn.Forte@Sun.COM static iscsi_status_t
iscsi_login_connect(iscsi_conn_t * icp)21417836SJohn.Forte@Sun.COM iscsi_login_connect(iscsi_conn_t *icp)
21427836SJohn.Forte@Sun.COM {
21438348SEric.Yu@Sun.COM 	iscsi_hba_t		*ihp;
21448348SEric.Yu@Sun.COM 	iscsi_sess_t		*isp;
21458348SEric.Yu@Sun.COM 	struct sockaddr		*addr;
21469162SPeter.Dunlap@Sun.COM 	idm_conn_req_t		cr;
21479162SPeter.Dunlap@Sun.COM 	idm_status_t		rval;
214811066Srafael.vanoni@sun.com 	clock_t			lbolt;
21497836SJohn.Forte@Sun.COM 
21507836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
21517836SJohn.Forte@Sun.COM 	isp = icp->conn_sess;
21527836SJohn.Forte@Sun.COM 	ASSERT(isp != NULL);
21537836SJohn.Forte@Sun.COM 	ihp = isp->sess_hba;
21547836SJohn.Forte@Sun.COM 	ASSERT(ihp != NULL);
21557836SJohn.Forte@Sun.COM 	addr = &icp->conn_curr_addr.sin;
21567836SJohn.Forte@Sun.COM 
21577836SJohn.Forte@Sun.COM 	/* Make sure that scope_id is zero if it is an IPv6 address */
21587836SJohn.Forte@Sun.COM 	if (addr->sa_family == AF_INET6) {
21597836SJohn.Forte@Sun.COM 		((struct sockaddr_in6 *)addr)->sin6_scope_id = 0;
21607836SJohn.Forte@Sun.COM 	}
21617836SJohn.Forte@Sun.COM 
21629162SPeter.Dunlap@Sun.COM 	/* delay the connect process if required */
21639162SPeter.Dunlap@Sun.COM 	lbolt = ddi_get_lbolt();
21649162SPeter.Dunlap@Sun.COM 	if (lbolt < icp->conn_login_min) {
216510156SZhang.Yi@Sun.COM 		if (icp->conn_login_max < icp->conn_login_min) {
216610156SZhang.Yi@Sun.COM 			delay(icp->conn_login_max - lbolt);
216710156SZhang.Yi@Sun.COM 		} else {
216810156SZhang.Yi@Sun.COM 			delay(icp->conn_login_min - lbolt);
216910156SZhang.Yi@Sun.COM 		}
21709162SPeter.Dunlap@Sun.COM 	}
21719162SPeter.Dunlap@Sun.COM 
21729162SPeter.Dunlap@Sun.COM 	/* Create IDM connection context */
21739162SPeter.Dunlap@Sun.COM 	cr.cr_domain = addr->sa_family;
21749162SPeter.Dunlap@Sun.COM 	cr.cr_type = SOCK_STREAM;
21759162SPeter.Dunlap@Sun.COM 	cr.cr_protocol = 0;
21769162SPeter.Dunlap@Sun.COM 	cr.cr_bound = icp->conn_bound;
21779162SPeter.Dunlap@Sun.COM 	cr.cr_li = icp->conn_sess->sess_hba->hba_li;
21789162SPeter.Dunlap@Sun.COM 	cr.icr_conn_ops.icb_rx_misc = &iscsi_rx_misc_pdu;
21799162SPeter.Dunlap@Sun.COM 	cr.icr_conn_ops.icb_rx_error = &iscsi_rx_error_pdu;
21809162SPeter.Dunlap@Sun.COM 	cr.icr_conn_ops.icb_rx_scsi_rsp = &iscsi_rx_scsi_rsp;
21819162SPeter.Dunlap@Sun.COM 	cr.icr_conn_ops.icb_client_notify = &iscsi_client_notify;
21829162SPeter.Dunlap@Sun.COM 	cr.icr_conn_ops.icb_build_hdr = &iscsi_build_hdr;
21839162SPeter.Dunlap@Sun.COM 	cr.icr_conn_ops.icb_task_aborted = &iscsi_task_aborted;
21849162SPeter.Dunlap@Sun.COM 	bcopy(addr, &cr.cr_ini_dst_addr,
21859162SPeter.Dunlap@Sun.COM 	    sizeof (cr.cr_ini_dst_addr));
21869162SPeter.Dunlap@Sun.COM 	bcopy(&icp->conn_bound_addr, &cr.cr_bound_addr,
21879162SPeter.Dunlap@Sun.COM 	    sizeof (cr.cr_bound_addr));
218810822SJack.Meng@Sun.COM 	if (isp->sess_boot == B_TRUE) {
218910822SJack.Meng@Sun.COM 		cr.cr_boot_conn = B_TRUE;
219010822SJack.Meng@Sun.COM 	} else {
219110822SJack.Meng@Sun.COM 		cr.cr_boot_conn = B_FALSE;
219210822SJack.Meng@Sun.COM 	}
21937836SJohn.Forte@Sun.COM 
21949162SPeter.Dunlap@Sun.COM 	/*
21959162SPeter.Dunlap@Sun.COM 	 * Allocate IDM connection context
21969162SPeter.Dunlap@Sun.COM 	 */
21979162SPeter.Dunlap@Sun.COM 	rval = idm_ini_conn_create(&cr, &icp->conn_ic);
21989162SPeter.Dunlap@Sun.COM 	if (rval != IDM_STATUS_SUCCESS) {
21999162SPeter.Dunlap@Sun.COM 		return (ISCSI_STATUS_LOGIN_FAILED);
22009162SPeter.Dunlap@Sun.COM 	}
22019162SPeter.Dunlap@Sun.COM 
22029162SPeter.Dunlap@Sun.COM 	icp->conn_ic->ic_handle = icp;
22039162SPeter.Dunlap@Sun.COM 
22049162SPeter.Dunlap@Sun.COM 	/*
22059162SPeter.Dunlap@Sun.COM 	 * About to initiate connect, reset login state.
22069162SPeter.Dunlap@Sun.COM 	 */
22079162SPeter.Dunlap@Sun.COM 	iscsi_login_update_state(icp, LOGIN_START);
22089162SPeter.Dunlap@Sun.COM 
22099162SPeter.Dunlap@Sun.COM 	/*
22109162SPeter.Dunlap@Sun.COM 	 * Make sure the connection doesn't go away until we are done with it.
22119162SPeter.Dunlap@Sun.COM 	 * This hold will prevent us from receiving a CN_CONNECT_DESTROY
22129162SPeter.Dunlap@Sun.COM 	 * notification on this connection until we are ready.
22139162SPeter.Dunlap@Sun.COM 	 */
22149162SPeter.Dunlap@Sun.COM 	idm_conn_hold(icp->conn_ic);
22159162SPeter.Dunlap@Sun.COM 
22169162SPeter.Dunlap@Sun.COM 	/*
221710156SZhang.Yi@Sun.COM 	 * When iSCSI initiator to target IO timeout or connection failure
221810156SZhang.Yi@Sun.COM 	 * Connection retry is needed for normal operational session.
221910156SZhang.Yi@Sun.COM 	 */
222010156SZhang.Yi@Sun.COM 	if ((icp->conn_sess->sess_type == ISCSI_SESS_TYPE_NORMAL) &&
222110156SZhang.Yi@Sun.COM 	    ((icp->conn_state == ISCSI_CONN_STATE_FAILED) ||
222210156SZhang.Yi@Sun.COM 	    (icp->conn_state == ISCSI_CONN_STATE_POLLING))) {
222310156SZhang.Yi@Sun.COM 		icp->conn_ic->ic_conn_params.nonblock_socket = B_TRUE;
222410156SZhang.Yi@Sun.COM 		icp->conn_ic->ic_conn_params.conn_login_max =
222510156SZhang.Yi@Sun.COM 		    icp->conn_login_max;
222610156SZhang.Yi@Sun.COM 		if (icp->conn_state == ISCSI_CONN_STATE_POLLING) {
222710156SZhang.Yi@Sun.COM 			icp->conn_ic->ic_conn_params.conn_login_interval =
222810156SZhang.Yi@Sun.COM 			    icp->conn_tunable_params.polling_login_delay;
222910156SZhang.Yi@Sun.COM 		} else {
223010156SZhang.Yi@Sun.COM 			icp->conn_ic->ic_conn_params.conn_login_interval =
223110156SZhang.Yi@Sun.COM 			    ISCSI_LOGIN_RETRY_DELAY;
223210156SZhang.Yi@Sun.COM 		}
223310156SZhang.Yi@Sun.COM 
223410156SZhang.Yi@Sun.COM 	} else {
223510156SZhang.Yi@Sun.COM 		icp->conn_ic->ic_conn_params.nonblock_socket = B_FALSE;
223610156SZhang.Yi@Sun.COM 		icp->conn_ic->ic_conn_params.conn_login_max = 0;
223710156SZhang.Yi@Sun.COM 		icp->conn_ic->ic_conn_params.conn_login_interval = 0;
223810156SZhang.Yi@Sun.COM 	}
223910156SZhang.Yi@Sun.COM 	/*
22409162SPeter.Dunlap@Sun.COM 	 * Attempt connection.  Upon return we will either be ready to
22419162SPeter.Dunlap@Sun.COM 	 * login or disconnected.  If idm_ini_conn_connect fails we
22429162SPeter.Dunlap@Sun.COM 	 * will eventually receive a CN_CONNECT_DESTROY at which point
22439162SPeter.Dunlap@Sun.COM 	 * we will destroy the connection allocated above (so there
22449162SPeter.Dunlap@Sun.COM 	 * is no need to explicitly free it here).
22459162SPeter.Dunlap@Sun.COM 	 */
22469162SPeter.Dunlap@Sun.COM 	rval = idm_ini_conn_connect(icp->conn_ic);
22479162SPeter.Dunlap@Sun.COM 
22489162SPeter.Dunlap@Sun.COM 	if (rval != IDM_STATUS_SUCCESS) {
22497836SJohn.Forte@Sun.COM 		cmn_err(CE_NOTE, "iscsi connection(%u) unable to "
22507836SJohn.Forte@Sun.COM 		    "connect to target %s", icp->conn_oid,
22517836SJohn.Forte@Sun.COM 		    icp->conn_sess->sess_name);
22529162SPeter.Dunlap@Sun.COM 		idm_conn_rele(icp->conn_ic);
22537836SJohn.Forte@Sun.COM 	}
22547836SJohn.Forte@Sun.COM 
22559162SPeter.Dunlap@Sun.COM 	return (rval == IDM_STATUS_SUCCESS ?
22569162SPeter.Dunlap@Sun.COM 	    ISCSI_STATUS_SUCCESS : ISCSI_STATUS_INTERNAL_ERROR);
22579162SPeter.Dunlap@Sun.COM }
22589162SPeter.Dunlap@Sun.COM 
22599162SPeter.Dunlap@Sun.COM /*
22609162SPeter.Dunlap@Sun.COM  * iscsi_login_disconnect
22619162SPeter.Dunlap@Sun.COM  */
22629162SPeter.Dunlap@Sun.COM static void
iscsi_login_disconnect(iscsi_conn_t * icp)22639162SPeter.Dunlap@Sun.COM iscsi_login_disconnect(iscsi_conn_t *icp)
22649162SPeter.Dunlap@Sun.COM {
22659162SPeter.Dunlap@Sun.COM 	/* Tell IDM to disconnect is if we are not already disconnect */
22669162SPeter.Dunlap@Sun.COM 	idm_ini_conn_disconnect_sync(icp->conn_ic);
22679162SPeter.Dunlap@Sun.COM 
22689162SPeter.Dunlap@Sun.COM 	/*
22699162SPeter.Dunlap@Sun.COM 	 * The function above may return before the CN_CONNECT_LOST
22709162SPeter.Dunlap@Sun.COM 	 * notification.  Wait for it.
22719162SPeter.Dunlap@Sun.COM 	 */
22729162SPeter.Dunlap@Sun.COM 	mutex_enter(&icp->conn_state_mutex);
22739162SPeter.Dunlap@Sun.COM 	while (icp->conn_state_idm_connected)
22749162SPeter.Dunlap@Sun.COM 		cv_wait(&icp->conn_state_change,
22759162SPeter.Dunlap@Sun.COM 		    &icp->conn_state_mutex);
22769162SPeter.Dunlap@Sun.COM 	mutex_exit(&icp->conn_state_mutex);
22779162SPeter.Dunlap@Sun.COM }
22789162SPeter.Dunlap@Sun.COM 
22799162SPeter.Dunlap@Sun.COM /*
22809162SPeter.Dunlap@Sun.COM  * iscsi_notice_key_values - Create an nvlist containing the values
22819162SPeter.Dunlap@Sun.COM  * that have been negotiated for this connection and pass them down to
22829162SPeter.Dunlap@Sun.COM  * IDM so it can pick up any values that are important.
22839162SPeter.Dunlap@Sun.COM  */
22849162SPeter.Dunlap@Sun.COM static void
iscsi_notice_key_values(iscsi_conn_t * icp)22859162SPeter.Dunlap@Sun.COM iscsi_notice_key_values(iscsi_conn_t *icp)
22869162SPeter.Dunlap@Sun.COM {
22879162SPeter.Dunlap@Sun.COM 	nvlist_t	*neg_nvl;
22889162SPeter.Dunlap@Sun.COM 	int		rc;
22899162SPeter.Dunlap@Sun.COM 
22909162SPeter.Dunlap@Sun.COM 	rc = nvlist_alloc(&neg_nvl, NV_UNIQUE_NAME, KM_SLEEP);
22919162SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
22929162SPeter.Dunlap@Sun.COM 
22939162SPeter.Dunlap@Sun.COM 	/* Only crc32c is supported so the digest logic is simple */
22949162SPeter.Dunlap@Sun.COM 	if (icp->conn_params.header_digest) {
22959162SPeter.Dunlap@Sun.COM 		rc = nvlist_add_string(neg_nvl, "HeaderDigest", "crc32c");
22969162SPeter.Dunlap@Sun.COM 	} else {
22979162SPeter.Dunlap@Sun.COM 		rc = nvlist_add_string(neg_nvl, "HeaderDigest", "none");
22987836SJohn.Forte@Sun.COM 	}
22999162SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
23007836SJohn.Forte@Sun.COM 
23019162SPeter.Dunlap@Sun.COM 	if (icp->conn_params.data_digest) {
23029162SPeter.Dunlap@Sun.COM 		rc = nvlist_add_string(neg_nvl, "DataDigest", "crc32c");
23039162SPeter.Dunlap@Sun.COM 	} else {
23049162SPeter.Dunlap@Sun.COM 		rc = nvlist_add_string(neg_nvl, "DataDigest", "none");
23059162SPeter.Dunlap@Sun.COM 	}
23069162SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
23079162SPeter.Dunlap@Sun.COM 
23089162SPeter.Dunlap@Sun.COM 	rc = nvlist_add_uint64(neg_nvl, "MaxRecvDataSegmentLength",
23099162SPeter.Dunlap@Sun.COM 	    (uint64_t)icp->conn_params.max_recv_data_seg_len);
23109162SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
23119162SPeter.Dunlap@Sun.COM 
23129162SPeter.Dunlap@Sun.COM 	rc = nvlist_add_uint64(neg_nvl, "MaxBurstLength",
23139162SPeter.Dunlap@Sun.COM 	    (uint64_t)icp->conn_params.max_burst_length);
23149162SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
23159162SPeter.Dunlap@Sun.COM 
23169162SPeter.Dunlap@Sun.COM 	rc = nvlist_add_uint64(neg_nvl, "MaxOutstandingR2T",
23179162SPeter.Dunlap@Sun.COM 	    (uint64_t)icp->conn_params.max_outstanding_r2t);
23189162SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
23199162SPeter.Dunlap@Sun.COM 
23209162SPeter.Dunlap@Sun.COM 	rc = nvlist_add_uint64(neg_nvl, "ErrorRecoveryLevel",
23219162SPeter.Dunlap@Sun.COM 	    (uint64_t)icp->conn_params.error_recovery_level);
23229162SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
23239162SPeter.Dunlap@Sun.COM 
23249162SPeter.Dunlap@Sun.COM 	rc = nvlist_add_uint64(neg_nvl, "DefaultTime2Wait",
23259162SPeter.Dunlap@Sun.COM 	    (uint64_t)icp->conn_params.default_time_to_wait);
23269162SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
23279162SPeter.Dunlap@Sun.COM 
23289162SPeter.Dunlap@Sun.COM 	rc = nvlist_add_uint64(neg_nvl, "DefaultTime2Retain",
23299162SPeter.Dunlap@Sun.COM 	    (uint64_t)icp->conn_params.default_time_to_retain);
23309162SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
23319162SPeter.Dunlap@Sun.COM 
23329162SPeter.Dunlap@Sun.COM 	/* Pass the list to IDM to examine, then free it */
23339162SPeter.Dunlap@Sun.COM 	idm_notice_key_values(icp->conn_ic, neg_nvl);
23349162SPeter.Dunlap@Sun.COM 	nvlist_free(neg_nvl);
23357836SJohn.Forte@Sun.COM }
2336