Home | History | Annotate | Download | only in iscsitgtd
      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 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <stdio.h>
     28 #include <assert.h>
     29 #include <sys/types.h>
     30 #include <stdlib.h>
     31 #include <strings.h>
     32 #include <syslog.h>
     33 
     34 #include <iscsitgt_impl.h>
     35 #include "iscsi_conn.h"
     36 #include "iscsi_sess.h"
     37 #include "t10.h"
     38 #include "utility.h"
     39 #include "target.h"
     40 
     41 pthread_mutex_t	sess_mutex;
     42 /*
     43  * This value is used as the TSIH which must be non-zero.
     44  */
     45 int		sess_num	= 1;
     46 iscsi_sess_t	*sess_head;
     47 
     48 static void session_free(struct iscsi_sess *s);
     49 static void sess_set_auth(iscsi_sess_t *isp);
     50 static void *sess_from_t10(void *v);
     51 static void *sess_process(void *v);
     52 
     53 /*
     54  * []----
     55  * | session_init -- initialize global variables and mutexs
     56  * []----
     57  */
     58 void
     59 session_init()
     60 {
     61 	(void) pthread_mutex_init(&sess_mutex, NULL);
     62 }
     63 
     64 /*
     65  * []----
     66  * | session_alloc -- create a new session attached to the lead connection
     67  * []----
     68  */
     69 Boolean_t
     70 session_alloc(iscsi_conn_t *c, uint8_t *isid)
     71 {
     72 	iscsi_sess_t	*s, *n;
     73 
     74 	if (c->c_sess != NULL)
     75 		return (True);
     76 
     77 	s = (iscsi_sess_t *)calloc(sizeof (iscsi_sess_t), 1);
     78 	if (s == NULL)
     79 		return (False);
     80 
     81 	bcopy(isid, s->s_isid, 6);
     82 
     83 	(void) pthread_mutex_init(&s->s_mutex, NULL);
     84 	c->c_sess	= s;
     85 	s->s_conn_head	= c;
     86 	s->s_sessq	= queue_alloc();
     87 	s->s_t10q	= queue_alloc();
     88 	c->c_sessq	= s->s_sessq;
     89 	s->s_mgmtq	= c->c_mgmtq;
     90 	s->s_type	= SessionNormal;
     91 
     92 	sess_set_auth(s);
     93 
     94 	(void) pthread_mutex_lock(&sess_mutex);
     95 	s->s_num	= sess_num++;
     96 	s->s_tsid	= s->s_num;
     97 	s->s_state	= SS_STARTED;
     98 
     99 	if (sess_head == NULL)
    100 		sess_head = s;
    101 	else {
    102 		for (n = sess_head; n->s_next; n = n->s_next)
    103 			;
    104 		n->s_next = s;
    105 	}
    106 	(void) pthread_mutex_unlock(&sess_mutex);
    107 
    108 	(void) pthread_create(&s->s_thr_id_t10, NULL, sess_from_t10, s);
    109 	(void) pthread_create(&s->s_thr_id_conn, NULL, sess_process, s);
    110 
    111 	util_title(s->s_mgmtq, Q_SESS_LOGIN, s->s_num, "Start Session");
    112 
    113 	return (True);
    114 }
    115 
    116 /*
    117  * []----
    118  * | session_free -- remove connection from session
    119  * []----
    120  */
    121 static void
    122 session_free(iscsi_sess_t *s)
    123 {
    124 	iscsi_sess_t	*n;
    125 
    126 	/*
    127 	 * Early errors in connection setup can still call this routine
    128 	 * which means the session hasn't been called.
    129 	 */
    130 	if (s == NULL)
    131 		return;
    132 
    133 	(void) pthread_mutex_lock(&sess_mutex);
    134 	if (s->s_i_name)
    135 		free(s->s_i_name);
    136 	if (s->s_t_name)
    137 		free(s->s_t_name);
    138 	if (s->s_i_alias)
    139 		free(s->s_i_alias);
    140 
    141 	if (sess_head == s)
    142 		sess_head = s->s_next;
    143 	else {
    144 		for (n = sess_head; n; n = n->s_next) {
    145 			if (n->s_next == s) {
    146 				n->s_next = s->s_next;
    147 				break;
    148 			}
    149 		}
    150 		if (n == NULL) {
    151 			queue_prt(s->s_mgmtq, Q_SESS_ERRS,
    152 			    "SES%x  NOT IN SESSION LIST!\n", s->s_num);
    153 		}
    154 	}
    155 	(void) pthread_mutex_unlock(&sess_mutex);
    156 }
    157 
    158 /*
    159  * []----
    160  * | session_remove_connection -- removes conn from sess list
    161  * |
    162  * | Returns True if this was the last connection which is always the case
    163  * | for now. In the future with multiple connections per session it'll be
    164  * | different.
    165  * []----
    166  */
    167 /*ARGSUSED*/
    168 static Boolean_t
    169 session_remove_connection(iscsi_sess_t *s, iscsi_conn_t *c)
    170 {
    171 	bzero(s->s_isid, 6);
    172 	return (True);
    173 }
    174 
    175 /*
    176  * []----
    177  * | convert_i_local -- Return a local name for the initiator if avilable.
    178  * |
    179  * | NOTE: If this routine returns true, it's the callers responsibility
    180  * | to free the memory.
    181  * []----
    182  */
    183 Boolean_t
    184 convert_i_local(char *ip, char **rtn)
    185 {
    186 	tgt_node_t	*inode = NULL;
    187 	char		*iname, *name;
    188 
    189 	while ((inode = tgt_node_next_child(main_config, XML_ELEMENT_INIT,
    190 	    inode)) != NULL) {
    191 		if (tgt_find_value_str(inode, XML_ELEMENT_INAME, &iname) ==
    192 		    False) {
    193 			continue;
    194 		}
    195 		if (strcmp(iname, ip) == 0) {
    196 			if (tgt_find_value_str(inode, XML_ELEMENT_INIT,
    197 			    &name) == False) {
    198 				free(iname);
    199 				return (False);
    200 			} else
    201 				free(iname);
    202 			*rtn = name;
    203 			return (True);
    204 		} else
    205 			free(iname);
    206 	}
    207 	return (False);
    208 }
    209 
    210 /*
    211  * []----
    212  * | sess_from_t10 -- handle messages from the T10 layer
    213  * []----
    214  */
    215 void *
    216 sess_from_t10(void *v)
    217 {
    218 	iscsi_sess_t	*s	= (iscsi_sess_t *)v;
    219 	msg_t		*m;
    220 	Boolean_t	process	= True;
    221 	t10_conn_shutdown_t t_c_s;
    222 	Boolean_t	sent_wait_for_destroy = False;
    223 
    224 	t_c_s.t10_to_conn_q = NULL;
    225 	t_c_s.conn_to_t10_q = NULL;
    226 
    227 	while (process == True) {
    228 		m = queue_message_get(s->s_t10q);
    229 		switch (m->msg_type) {
    230 		case msg_cmd_data_rqst:
    231 			queue_message_set(s->s_conn_head->c_dataq, 0,
    232 			    msg_cmd_data_rqst, m->msg_data);
    233 			break;
    234 
    235 		case msg_cmd_data_in:
    236 			queue_message_set(s->s_conn_head->c_dataq, 0,
    237 			    msg_cmd_data_in, m->msg_data);
    238 			break;
    239 
    240 		case msg_cmd_cmplt:
    241 			queue_message_set(s->s_conn_head->c_dataq, 0,
    242 			    msg_cmd_cmplt, m->msg_data);
    243 			break;
    244 
    245 		case msg_shutdown_rsp:
    246 
    247 			if (s->s_t10) {
    248 				if (!sent_wait_for_destroy) {
    249 					if (t_c_s.t10_to_conn_q == NULL) {
    250 						t_c_s.t10_to_conn_q =
    251 						    queue_alloc();
    252 						if (t_c_s.t10_to_conn_q
    253 						    == NULL) {
    254 							queue_message_set(
    255 							    s->s_t10q, 0,
    256 							    msg_shutdown_rsp,
    257 							    m->msg_data);
    258 							break;
    259 						}
    260 					}
    261 					if (t_c_s.conn_to_t10_q == NULL) {
    262 						t_c_s.conn_to_t10_q =
    263 						    queue_alloc();
    264 						if (t_c_s.conn_to_t10_q ==
    265 						    NULL) {
    266 							queue_message_set(
    267 							    s->s_t10q, 0,
    268 							    msg_shutdown_rsp,
    269 							    m->msg_data);
    270 							break;
    271 						}
    272 					}
    273 					queue_message_set(
    274 					    s->s_conn_head->c_dataq, 0,
    275 					    msg_wait_for_destroy,
    276 					    (void *)&t_c_s);
    277 					queue_message_free(queue_message_get(
    278 					    t_c_s.conn_to_t10_q));
    279 					sent_wait_for_destroy = True;
    280 				}
    281 
    282 				if (t10_handle_destroy(s->s_t10, False) != 0) {
    283 					/*
    284 					 * Destroy couldn't complete,
    285 					 * put the message back on our
    286 					 * own queue to be picked up
    287 					 * later and tried again.
    288 					 */
    289 					queue_message_set(s->s_t10q, 0,
    290 					    msg_shutdown_rsp,
    291 					    m->msg_data);
    292 					break;
    293 				}
    294 				s->s_t10 = NULL;
    295 				queue_message_set(t_c_s.t10_to_conn_q,
    296 				    0, 1, (void *)NULL);
    297 				queue_message_free(queue_message_get(
    298 				    t_c_s.conn_to_t10_q));
    299 				queue_free(t_c_s.t10_to_conn_q, NULL);
    300 				queue_free(t_c_s.conn_to_t10_q, NULL);
    301 				sent_wait_for_destroy = False;
    302 			}
    303 
    304 			(void) pthread_mutex_lock(&s->s_mutex);
    305 			s->s_state = SS_SHUTDOWN_CMPLT;
    306 			(void) pthread_mutex_unlock(&s->s_mutex);
    307 
    308 			session_free(s);
    309 
    310 			/*
    311 			 * Let the connection, which is the last, know
    312 			 * about our completion of the shutdown.
    313 			 */
    314 			queue_message_set(s->s_conn_head->c_dataq, 0,
    315 			    msg_shutdown_rsp, (void *)True);
    316 			process		= False;
    317 			s->s_state	= SS_FREE;
    318 			break;
    319 
    320 		default:
    321 			queue_prt(s->s_mgmtq, Q_SESS_ERRS,
    322 			    "SES%x  Unknown msg type (%d) from T10\n",
    323 			    s->s_num, m->msg_type);
    324 			queue_message_set(s->s_conn_head->c_dataq, 0,
    325 			    m->msg_type, m->msg_data);
    326 				break;
    327 		}
    328 		queue_message_free(m);
    329 	}
    330 	queue_message_set(s->s_mgmtq, 0, msg_pthread_join,
    331 	    (void *)(uintptr_t)pthread_self());
    332 	queue_free(s->s_t10q, NULL);
    333 	util_title(s->s_mgmtq, Q_SESS_LOGIN, s->s_num, "End Session");
    334 	free(s);
    335 	return (NULL);
    336 }
    337 
    338 /*
    339  * []----
    340  * | sess_process -- handle messages from the connection(s)
    341  * []----
    342  */
    343 static void *
    344 sess_process(void *v)
    345 {
    346 	iscsi_sess_t	*s = (iscsi_sess_t *)v;
    347 	iscsi_conn_t	*c;
    348 	iscsi_cmd_t	*cmd;
    349 	msg_t		*m;
    350 	Boolean_t	process = True;
    351 	mgmt_request_t	*mgmt;
    352 	name_request_t	*nr;
    353 	t10_cmd_t	*t10_cmd;
    354 	char		**buf, local_buf[16];
    355 	int		lun;
    356 	extern void dataout_callback(t10_cmd_t *t, char *data, size_t *xfer);
    357 
    358 	(void) pthread_mutex_lock(&s->s_mutex);
    359 	s->s_state = SS_RUNNING;
    360 	(void) pthread_mutex_unlock(&s->s_mutex);
    361 	do {
    362 		m = queue_message_get(s->s_sessq);
    363 		switch (m->msg_type) {
    364 		case msg_cmd_send:
    365 			cmd = (iscsi_cmd_t *)m->msg_data;
    366 			if (s->s_t10 == NULL) {
    367 
    368 				/*
    369 				 * The value of 0x960 comes from T10.
    370 				 * See SPC-4, revision 1a, section 6.4.2,
    371 				 * table 87
    372 				 *
    373 				 * XXX Need to rethink how I should do
    374 				 * the callback.
    375 				 */
    376 				s->s_t10 = t10_handle_create(
    377 				    s->s_t_name, s->s_i_name, T10_TRANS_ISCSI,
    378 				    s->s_conn_head->c_tpgt,
    379 				    s->s_conn_head->c_max_burst_len,
    380 				    s->s_t10q, dataout_callback);
    381 			}
    382 			if (t10_cmd_create(s->s_t10, cmd->c_lun, cmd->c_scb,
    383 			    cmd->c_scb_len, (transport_t)cmd,
    384 			    &t10_cmd) == False) {
    385 
    386 				/*
    387 				 * If the command create failed, the T10 layer
    388 				 * will attempt to create a sense buffer
    389 				 * telling the initiator what went wrong. If
    390 				 * that layer was unable to accomplish that
    391 				 * things are really bad and we need to just
    392 				 * close the connection.
    393 				 */
    394 				if (t10_cmd != NULL) {
    395 					queue_message_set(
    396 					    cmd->c_allegiance->c_dataq,
    397 					    0, msg_cmd_cmplt, t10_cmd);
    398 				} else {
    399 					queue_prt(s->s_mgmtq, Q_SESS_ERRS,
    400 					    "SES%x  FAILED to create cmd\n",
    401 					    s->s_num);
    402 					conn_state(cmd->c_allegiance, T11);
    403 				}
    404 			} else {
    405 				(void) pthread_mutex_lock(
    406 				    &cmd->c_allegiance->c_mutex);
    407 				if (cmd->c_state != CmdCanceled) {
    408 					cmd->c_t10_cmd = t10_cmd;
    409 					(void) t10_cmd_send(s->s_t10,
    410 					    cmd->c_t10_cmd, cmd->c_data,
    411 					    cmd->c_data_len);
    412 				} else {
    413 					t10_cmd_shoot_event(t10_cmd,
    414 					    T10_Cmd_T6);
    415 				}
    416 				(void) pthread_mutex_unlock(
    417 				    &cmd->c_allegiance->c_mutex);
    418 			}
    419 			break;
    420 
    421 		case msg_cmd_data_out:
    422 			cmd = (iscsi_cmd_t *)m->msg_data;
    423 			if (s->s_t10 != NULL)
    424 				(void) t10_cmd_data(s->s_t10, cmd->c_t10_cmd,
    425 				    cmd->c_offset_out, cmd->c_data,
    426 				    cmd->c_data_len);
    427 			break;
    428 
    429 		case msg_targ_inventory_change:
    430 			if (s->s_t10 != NULL)
    431 				(void) t10_task_mgmt(s->s_t10, InventoryChange,
    432 				    0, 0);
    433 			break;
    434 
    435 		case msg_lu_capacity_change:
    436 			lun = (int)(uintptr_t)m->msg_data;
    437 			if (s->s_t10 != NULL)
    438 				(void) t10_task_mgmt(s->s_t10, CapacityChange,
    439 				    lun, 0);
    440 			break;
    441 
    442 		case msg_reset_targ:
    443 			if (s->s_t10 != NULL)
    444 				(void) t10_task_mgmt(s->s_t10, ResetTarget,
    445 				    0, 0);
    446 			break;
    447 
    448 		case msg_reset_lu:
    449 			if (s->s_t10 != NULL)
    450 				(void) t10_task_mgmt(s->s_t10, ResetLun,
    451 				    (int)(uintptr_t)m->msg_data, 0);
    452 			break;
    453 
    454 		case msg_shutdown:
    455 			(void) pthread_mutex_lock(&s->s_mutex);
    456 			s->s_state = SS_SHUTDOWN_START;
    457 			(void) pthread_mutex_unlock(&s->s_mutex);
    458 
    459 			/*
    460 			 * Shutdown rquest comming from a connection. Only
    461 			 * shutdown the STE if this is the last connection
    462 			 * for this session.
    463 			 */
    464 			c = (iscsi_conn_t *)m->msg_data;
    465 			if (session_remove_connection(s, c) == True) {
    466 				queue_prt(s->s_mgmtq, Q_SESS_NONIO,
    467 				    "SES%x  Starting shutdown\n", s->s_num);
    468 
    469 				/*
    470 				 * If this is the last connection for this
    471 				 * session send a message to the SAM-3 layer to
    472 				 * shutdown.
    473 				 */
    474 				if (s->s_t10 != NULL) {
    475 					t10_handle_disable(s->s_t10);
    476 				}
    477 				/*
    478 				 * Do all work using the session pointer before
    479 				 * sending the shutdown response msg. The
    480 				 * session struct can get freed by the thread
    481 				 * that picks up and handles the shutdown
    482 				 * response.
    483 				 */
    484 				queue_message_set(s->s_mgmtq, 0,
    485 				    msg_pthread_join,
    486 				    (void *)(uintptr_t)pthread_self());
    487 				queue_message_set(s->s_t10q, 0,
    488 				    msg_shutdown_rsp, 0);
    489 				process = False;
    490 			} else {
    491 
    492 				/*
    493 				 * Since this isn't the last connection for
    494 				 * the session, acknowledge the connection
    495 				 * request now since it's references from
    496 				 * this session have been removed.
    497 				 */
    498 				queue_message_set(c->c_dataq, 0,
    499 				    msg_shutdown_rsp, (void *)False);
    500 			}
    501 			break;
    502 
    503 		case msg_initiator_name:
    504 			nr = (name_request_t *)m->msg_data;
    505 			s->s_i_name = strdup(nr->nr_name);
    506 
    507 			/*
    508 			 * Acknowledge the request by sending back an empty
    509 			 * message.
    510 			 */
    511 			queue_message_set(nr->nr_q, 0, msg_initiator_name, 0);
    512 			break;
    513 
    514 		case msg_initiator_alias:
    515 			nr = (name_request_t *)m->msg_data;
    516 			s->s_i_alias = strdup(nr->nr_name);
    517 
    518 			/*
    519 			 * Acknowledge the request by sending back an empty
    520 			 * message.
    521 			 */
    522 			queue_message_set(nr->nr_q, 0, msg_initiator_alias, 0);
    523 			break;
    524 
    525 		case msg_target_name:
    526 			nr = (name_request_t *)m->msg_data;
    527 			s->s_t_name = strdup(nr->nr_name);
    528 
    529 			/*
    530 			 * Acknowledge the request by sending back an empty
    531 			 * message.
    532 			 */
    533 			queue_message_set(nr->nr_q, 0, msg_target_name, 0);
    534 			break;
    535 
    536 		case msg_mgmt_rqst:
    537 			mgmt		= (mgmt_request_t *)m->msg_data;
    538 			m->msg_data	= NULL;
    539 
    540 			(void) pthread_mutex_lock(&mgmt->m_resp_mutex);
    541 			buf = mgmt->m_u.m_resp;
    542 
    543 			if ((s->s_type == SessionNormal) &&
    544 			    (mgmt->m_request == mgmt_full_phase_statistics) &&
    545 			    (strcmp(s->s_t_name, mgmt->m_targ_name) == 0)) {
    546 
    547 				tgt_buf_add_tag(buf, XML_ELEMENT_CONN,
    548 				    Tag_Start);
    549 				tgt_buf_add_tag(buf, s->s_i_name, Tag_String);
    550 				if (s->s_i_alias != NULL) {
    551 					tgt_buf_add(buf, XML_ELEMENT_ALIAS,
    552 					    s->s_i_alias);
    553 				}
    554 
    555 				/*
    556 				 * Need to loop through the connections
    557 				 * and create one time_connected tag for
    558 				 * each. This will be needed once MC/S support
    559 				 * is added.
    560 				 */
    561 				(void) snprintf(local_buf, sizeof (local_buf),
    562 				    "%d",
    563 				    mgmt->m_time - s->s_conn_head->c_up_at);
    564 				tgt_buf_add(buf, XML_ELEMENT_TIMECON,
    565 				    local_buf);
    566 
    567 				tgt_buf_add_tag(buf, XML_ELEMENT_STATS,
    568 				    Tag_Start);
    569 
    570 				t10_targ_stat(s->s_t10, buf);
    571 
    572 				tgt_buf_add_tag(buf, XML_ELEMENT_STATS,
    573 				    Tag_End);
    574 				tgt_buf_add_tag(buf, XML_ELEMENT_CONN, Tag_End);
    575 			}
    576 
    577 			(void) pthread_mutex_unlock(&mgmt->m_resp_mutex);
    578 
    579 			queue_message_set(mgmt->m_q, 0, msg_mgmt_rply, 0);
    580 
    581 			break;
    582 
    583 		default:
    584 			queue_prt(s->s_mgmtq, Q_SESS_ERRS,
    585 			    "SES%x  Unknown msg type (%d) from Connection\n",
    586 			    s->s_num, m->msg_type);
    587 			break;
    588 		}
    589 		queue_message_free(m);
    590 	} while (process == True);
    591 
    592 	queue_message_set(mgmtq, 0, msg_pthread_join,
    593 	    (void *)(uintptr_t)pthread_self());
    594 	return (NULL);
    595 }
    596 
    597 /*
    598  * []----
    599  * | session_validate -- do what the name says
    600  * |
    601  * | At this point the connection has processed the login command so that
    602  * | we have InitiatorName and ISID at a minimum. Check to see if there
    603  * | are other sessions which match. If so, log that one out and proceed with
    604  * | this session. If nothing matches, then link this into a global list.
    605  * |
    606  * | Once we support multiple connections per session need to scan list
    607  * | to see if other connection have the same CID. If so, log out that
    608  * | connection.
    609  * []----
    610  */
    611 Boolean_t
    612 session_validate(iscsi_sess_t *s)
    613 {
    614 	iscsi_sess_t	*check;
    615 
    616 	queue_prt(s->s_mgmtq, Q_SESS_NONIO,
    617 	    "SES%x  %s ISID[%02x%02x%02x%02x%02x%02x]\n",
    618 	    s->s_num, s->s_i_alias == NULL ? s->s_i_name : s->s_i_alias,
    619 	    s->s_isid[0], s->s_isid[1], s->s_isid[2],
    620 	    s->s_isid[3], s->s_isid[4], s->s_isid[5]);
    621 
    622 
    623 	/*
    624 	 * SessionType=Discovery which means no target name and therefore
    625 	 * this is okay.
    626 	 */
    627 	if (s->s_t_name == NULL)
    628 		return (True);
    629 
    630 	(void) pthread_mutex_lock(&sess_mutex);
    631 	for (check = sess_head; check; check = check->s_next) {
    632 		/*
    633 		 * Ignore ourselves in this check.
    634 		 */
    635 		if (check == s)
    636 			continue;
    637 		if ((check->s_t_name == NULL) ||
    638 		    (strcmp(check->s_t_name, s->s_t_name) != 0))
    639 			continue;
    640 		if (strcmp(check->s_i_name, s->s_i_name) != 0)
    641 			continue;
    642 		if (check->s_conn_head->c_tpgt != s->s_conn_head->c_tpgt)
    643 			continue;
    644 		/*
    645 		 * Section 5.3.5
    646 		 * Session reinstatement is the process of the initiator
    647 		 * logging in with an ISID that is possible active from
    648 		 * the target's perspective. Thus implicitly logging out
    649 		 * the session that corresponds to the ISID and
    650 		 * reinstating a new iSCSI session in its place (with the
    651 		 * same ISID).
    652 		 */
    653 		if (bcmp(check->s_isid, s->s_isid, 6) == 0) {
    654 			queue_prt(s->s_mgmtq, Q_SESS_NONIO,
    655 			    "SES%x  Implicit shutdown\n", check->s_num);
    656 			if (check->s_conn_head->c_state == S5_LOGGED_IN)
    657 				conn_state(check->s_conn_head, T8);
    658 			else
    659 				conn_state(check->s_conn_head, T7);
    660 			break;
    661 		}
    662 	}
    663 	(void) pthread_mutex_unlock(&sess_mutex);
    664 
    665 	return (True);
    666 }
    667 
    668 /*
    669  * []----
    670  * | static iscsi_sess_set_auth -
    671  * []----
    672  */
    673 static void
    674 sess_set_auth(iscsi_sess_t *isp)
    675 {
    676 	IscsiAuthClient		*auth_client	= NULL;
    677 	tgt_node_t		*node		= NULL;
    678 
    679 	if (isp == (iscsi_sess_t *)NULL)
    680 		return;
    681 
    682 	/* Zero out the session authentication structure */
    683 	bzero(&isp->sess_auth, sizeof (iscsi_auth_t));
    684 	isp->sess_auth.auth_enabled = B_TRUE;
    685 
    686 	/* Load CHAP name */
    687 	node = tgt_node_next_child(main_config, XML_ELEMENT_CHAPNAME, NULL);
    688 	if (node != NULL && node->x_value != NULL) {
    689 		(void) strcpy(isp->sess_auth.username, node->x_value);
    690 	}
    691 
    692 	/* Load CHAP secret */
    693 	node = tgt_node_next_child(main_config, XML_ELEMENT_CHAPSECRET, NULL);
    694 	if (node != NULL && node->x_value != NULL) {
    695 		(void) strcpy((char *)isp->sess_auth.password, node->x_value);
    696 		isp->sess_auth.password_length = strlen(node->x_value);
    697 	}
    698 
    699 	/*
    700 	 * Set up authentication buffers always.   We don't know if
    701 	 * initiator will request CHAP until later.
    702 	 */
    703 	isp->sess_auth.num_auth_buffers = 5;
    704 	isp->sess_auth.auth_buffers[0].address =
    705 	    &(isp->sess_auth.auth_client_block);
    706 	isp->sess_auth.auth_buffers[0].length =
    707 	    sizeof (isp->sess_auth.auth_client_block);
    708 	isp->sess_auth.auth_buffers[1].address =
    709 	    &(isp->sess_auth.auth_recv_string_block);
    710 	isp->sess_auth.auth_buffers[1].length =
    711 	    sizeof (isp->sess_auth.auth_recv_string_block);
    712 	isp->sess_auth.auth_buffers[2].address =
    713 	    &(isp->sess_auth.auth_send_string_block);
    714 	isp->sess_auth.auth_buffers[2].length =
    715 	    sizeof (isp->sess_auth.auth_send_string_block);
    716 	isp->sess_auth.auth_buffers[3].address =
    717 	    &(isp->sess_auth.auth_recv_binary_block);
    718 	isp->sess_auth.auth_buffers[3].length =
    719 	    sizeof (isp->sess_auth.auth_recv_binary_block);
    720 	isp->sess_auth.auth_buffers[4].address =
    721 	    &(isp->sess_auth.auth_send_binary_block);
    722 	isp->sess_auth.auth_buffers[4].length =
    723 	    sizeof (isp->sess_auth.auth_send_binary_block);
    724 
    725 	if (isp->sess_auth.auth_buffers &&
    726 	    isp->sess_auth.num_auth_buffers) {
    727 
    728 		auth_client = (IscsiAuthClient *)isp->
    729 		    sess_auth.auth_buffers[0].address;
    730 
    731 		/*
    732 		 * prepare for authentication
    733 		 */
    734 		if (iscsiAuthClientInit(iscsiAuthNodeTypeTarget,
    735 		    isp->sess_auth.num_auth_buffers,
    736 		    isp->sess_auth.auth_buffers) !=
    737 		    iscsiAuthStatusNoError) {
    738 			syslog(LOG_ERR, "iscsi connection login failed - "
    739 			    "unable to initialize authentication\n");
    740 			return;
    741 		}
    742 
    743 		if (iscsiAuthClientSetVersion(auth_client,
    744 		    iscsiAuthVersionRfc) != iscsiAuthStatusNoError) {
    745 			syslog(LOG_ERR, "iscsi connection login failed - "
    746 			    "unable to set version\n");
    747 			return;
    748 		}
    749 
    750 		if (isp->sess_auth.username &&
    751 		    (iscsiAuthClientSetUsername(auth_client,
    752 		    isp->sess_auth.username) !=
    753 		    iscsiAuthStatusNoError)) {
    754 			syslog(LOG_ERR, "iscsi connection login failed - "
    755 			    "unable to set username\n");
    756 			return;
    757 		}
    758 
    759 		if (isp->sess_auth.password &&
    760 		    (iscsiAuthClientSetPassword(auth_client,
    761 		    isp->sess_auth.password, isp->sess_auth.password_length) !=
    762 		    iscsiAuthStatusNoError)) {
    763 			syslog(LOG_ERR, "iscsi connection login failed - "
    764 			    "unable to set password\n");
    765 			return;
    766 		}
    767 
    768 		/*
    769 		 * FIXME: we disable the minimum size check for now
    770 		 */
    771 		if (iscsiAuthClientSetIpSec(auth_client, 1) !=
    772 		    iscsiAuthStatusNoError) {
    773 			syslog(LOG_ERR, "iscsi connection login failed - "
    774 			    "unable to set ipsec\n");
    775 			return;
    776 		}
    777 
    778 		if (iscsiAuthClientSetAuthRemote(auth_client,
    779 		    isp->sess_auth.auth_enabled) != iscsiAuthStatusNoError) {
    780 			syslog(LOG_ERR, "iscsi connection login failed - "
    781 			    "unable to set remote authentication\n");
    782 			return;
    783 		}
    784 	}
    785 }
    786