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