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