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