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