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