1/*	$NetBSD: iscsid_driverif.c,v 1.3 2011/11/20 01:23:57 agc Exp $	*/
2
3/*-
4 * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Wasabi Systems, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31#include "iscsid_globals.h"
32
33#include <sys/socket.h>
34#include <netinet/in.h>
35#include <netinet/tcp.h>
36#include <netdb.h>
37
38
39/* Global node name (Initiator Name and Alias) */
40iscsid_set_node_name_req_t node_name;
41
42/* -------------------------------------------------------------------------- */
43
44/*
45 * set_node_name:
46 *    Handle set_node_name request. Copy names into our own buffers and
47 *    set the driver's info as well.
48 *
49 *    Parameter:
50 *          par         The request parameter
51 *
52 *    Returns: Status.
53 */
54
55uint32_t
56set_node_name(iscsid_set_node_name_req_t * par)
57{
58	iscsi_set_node_name_parameters_t snp;
59
60	(void) memset(&snp, 0x0, sizeof(snp));
61	if (!par->InitiatorName[0])
62		return ISCSID_STATUS_NO_INITIATOR_NAME;
63
64	if (strlen((char *)par->InitiatorName) > ISCSI_STRING_LENGTH
65		|| strlen((char *)par->InitiatorAlias) > ISCSI_STRING_LENGTH)
66		return ISCSID_STATUS_PARAMETER_INVALID;
67
68	if (!par->InitiatorAlias[0])
69		gethostname((char *)node_name.InitiatorAlias, sizeof(node_name.InitiatorAlias));
70
71	node_name = *par;
72
73#ifdef ISCSI_DEBUG				/* DEBUG ONLY: Allow op without driver present */
74	if (driver < 0)
75		return ISCSID_STATUS_SUCCESS;
76#endif
77
78	strlcpy((char *)snp.InitiatorName, (char *)par->InitiatorName,
79		sizeof(snp.InitiatorName));
80	strlcpy((char *)snp.InitiatorAlias, (char *)par->InitiatorAlias,
81		sizeof(snp.InitiatorAlias));
82	memcpy(snp.ISID, par->ISID, 6);
83
84	DEB(10, ("Setting Node Name: %s (%s)\n",
85			 snp.InitiatorName, snp.InitiatorAlias));
86	(void)ioctl(driver, ISCSI_SET_NODE_NAME, &snp);
87	return snp.status;
88}
89
90
91/*
92 * bind_socket:
93 *    Bind socket to initiator portal.
94 *
95 *    Parameter:
96 *       sock     The socket
97 *       addr     The initiator portal address
98 *
99 *    Returns:
100 *       TRUE on success, FALSE on error.
101 */
102
103static int
104bind_socket(int sock, uint8_t * addr)
105{
106	struct sockaddr_in serverAddress;
107	struct hostent *host;
108
109	DEB(8, ("Binding to <%s>\n", addr));
110	(void) memset(&serverAddress, 0x0, sizeof(serverAddress));
111	host = gethostbyname((char *)addr);
112	if (host == NULL)
113		return FALSE;
114	if (host->h_length > (int)sizeof(serverAddress.sin_addr))
115		return FALSE;
116	serverAddress.sin_family = host->h_addrtype;
117	serverAddress.sin_port = 0;
118	serverAddress.sin_len = host->h_length;
119	memcpy(&serverAddress.sin_addr, host->h_addr_list[0], host->h_length);
120
121	return bind(sock, (struct sockaddr *)(void *)&serverAddress,
122				(socklen_t)sizeof(serverAddress)) >= 0;
123}
124
125
126/* -------------------------------------------------------------------------- */
127
128/*
129 * find_free_portal:
130 *    Find the Portal with the least number of connections.
131 *
132 *    Parameter:  the portal group
133 *
134 *    Returns:    The pointer to the first free portal (or NULL if none found)
135 */
136
137static portal_t *
138find_free_portal(portal_group_t * group)
139{
140	portal_t *curr, *m;
141	uint32_t n;
142
143	if ((curr = TAILQ_FIRST(&group->portals)) == NULL)
144		return NULL;
145
146	m = curr;
147	n = curr->active_connections;
148
149	while ((curr = TAILQ_NEXT(curr, group_list)) != NULL)
150		if (curr->active_connections < n) {
151			m = curr;
152			n = curr->active_connections;
153		}
154
155	return m;
156}
157
158
159/*
160 * make_connection:
161 *    Common routine for login and add_connection. Creates the connection
162 *    structure, connects the socket, and executes the login.
163 *
164 *    Parameter:
165 *          sess        The associated session. NULL for a send_targets request.
166 *          req         The request parameters. NULL for send_targets.
167 *          res         The response buffer. For SendTargets, only the status
168 *                      is set. For a "real" login, the login response
169 *                      is filled in.
170 *          stid        Send target request only, else NULL. Pointer to uint32:
171 *                         On Input, contains send target ID
172 *                         On Output, receives session ID
173 *
174 *    Returns:    The connection structure on successful login, else NULL.
175 *
176 *    NOTE: Session list must be locked on entry.
177 */
178
179static connection_t *
180make_connection(session_t * sess, iscsid_login_req_t * req,
181				iscsid_response_t * res, uint32_t * stid)
182{
183	connection_t *conn;
184	iscsi_login_parameters_t loginp;
185	int sock;
186	int ret;
187	int yes = 1;
188	target_t *target;
189	portal_t *portal = NULL;
190	iscsi_portal_address_t *addr;
191	struct sockaddr_in serverAddress;
192	struct hostent *host;
193	initiator_t *init;
194
195	DEB(9, ("Make Connection sess=%p, req=%p, res=%p, stid=%p\n",
196			 sess, req, res, stid));
197	(void) memset(&loginp, 0x0, sizeof(loginp));
198	(void) memset(&serverAddress, 0x0, sizeof(serverAddress));
199
200	/* find the target portal */
201	if (stid != NULL) {
202		send_target_t *starget;
203
204		if ((starget = find_send_target_id(*stid)) == NULL) {
205			res->status = ISCSID_STATUS_INVALID_TARGET_ID;
206			return NULL;
207		}
208		addr = &starget->addr;
209		target = (target_t *)(void *)starget;
210	} else {
211		if (NO_ID(&req->portal_id)
212			|| (portal = find_portal(&req->portal_id)) == NULL) {
213			portal_group_t *group;
214
215			/* if no ID was specified, use target from existing session */
216			if (NO_ID(&req->portal_id)) {
217				if (!sess->num_connections ||
218					((target = find_target_id(TARGET_LIST,
219					sess->target.sid.id)) == NULL)) {
220					res->status = ISCSID_STATUS_INVALID_PORTAL_ID;
221					return NULL;
222				}
223			}
224			/* if a target was given instead, use it */
225			else if ((target =
226							  find_target(TARGET_LIST, &req->portal_id)) == NULL) {
227				res->status = ISCSID_STATUS_INVALID_PORTAL_ID;
228				return NULL;
229			}
230			/* now get from target to portal - if this is the first connection, */
231			/* just use the first portal group. */
232			if (!sess->num_connections) {
233				group = TAILQ_FIRST(&target->group_list);
234			}
235			/* if it's a second connection, use an available portal in the same */
236			/* portal group */
237			else {
238				conn = (connection_t *)(void *)
239				    TAILQ_FIRST(&sess->connections);
240
241				if (conn == NULL ||
242					(portal = find_portal_id(conn->portal.sid.id)) == NULL) {
243					res->status = ISCSID_STATUS_INVALID_PORTAL_ID;
244					return NULL;
245				}
246				group = portal->group;
247			}
248
249			if ((portal = find_free_portal(group)) == NULL) {
250				res->status = ISCSID_STATUS_INVALID_PORTAL_ID;
251				return NULL;
252			}
253			DEB(1, ("find_free_portal returns pid=%d\n", portal->entry.sid.id));
254		} else
255			target = portal->target;
256
257		addr = &portal->addr;
258
259		/* symbolic name for connection? check for duplicates */
260		if (req->sym_name[0]) {
261			void *p;
262
263			if (sess->num_connections)
264				p = find_connection_name(sess, req->sym_name);
265			else
266				p = find_session_name(req->sym_name);
267			if (p) {
268				res->status = ISCSID_STATUS_DUPLICATE_NAME;
269				return NULL;
270			}
271		}
272	}
273
274	if (req != NULL && !NO_ID(&req->initiator_id)) {
275		if ((init = find_initiator(&req->initiator_id)) == NULL) {
276			res->status = ISCSID_STATUS_INVALID_INITIATOR_ID;
277			return NULL;
278		}
279	} else
280		init = select_initiator();
281
282	/* translate target address */
283	DEB(8, ("Connecting to <%s>, port %d\n", addr->address, addr->port));
284
285	host = gethostbyname((char *)addr->address);
286	if (host == NULL) {
287		switch (h_errno) {
288		case HOST_NOT_FOUND:
289			res->status = ISCSID_STATUS_HOST_NOT_FOUND;
290			break;
291		case TRY_AGAIN:
292			res->status = ISCSID_STATUS_HOST_TRY_AGAIN;
293			break;
294		default:
295			res->status = ISCSID_STATUS_HOST_ERROR;
296			break;
297		}
298		return NULL;
299	}
300	if (host->h_length > (int)sizeof(serverAddress.sin_addr)) {
301		res->status = ISCSID_STATUS_HOST_ERROR;
302		return NULL;
303	}
304	DEB(8, ("Gethostbyname OK, addrtype %d, len %d, addr %x\n",
305			host->h_addrtype, host->h_length, *((int *) host->h_addr_list[0])));
306	serverAddress.sin_family = host->h_addrtype;
307	serverAddress.sin_port = htons((addr->port)
308		? addr->port : ISCSI_DEFAULT_PORT);
309	serverAddress.sin_len = host->h_length;
310	memcpy(&serverAddress.sin_addr, host->h_addr_list[0], host->h_length);
311
312	/* alloc the connection structure */
313	conn = calloc(1, sizeof(*conn));
314	if (conn == NULL) {
315		res->status = ISCSID_STATUS_NO_RESOURCES;
316		return NULL;
317	}
318	/* create and connect the socket */
319	sock = socket(AF_INET, SOCK_STREAM, 0);
320	if (sock < 0) {
321		free(conn);
322		res->status = ISCSID_STATUS_SOCKET_ERROR;
323		return NULL;
324	}
325
326	if (init) {
327		if (!bind_socket(sock, init->address)) {
328			close(sock);
329			free(conn);
330			res->status = ISCSID_STATUS_INITIATOR_BIND_ERROR;
331			return NULL;
332		}
333	}
334
335	DEB(8, ("Connecting socket\n"));
336	if (connect(sock, (struct sockaddr *)(void *)&serverAddress,
337		(socklen_t)sizeof(serverAddress)) < 0) {
338		close(sock);
339		free(conn);
340		res->status = ISCSID_STATUS_CONNECT_ERROR;
341		DEB(1, ("Connecting to socket failed (error %d), returning %d\n",
342				errno, res->status));
343		return NULL;
344	}
345	/* speed up socket processing */
346	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, (socklen_t)sizeof(yes));
347
348	/* setup login parameter structure */
349	loginp.socket = sock;
350	if (target->TargetName[0]) {
351		loginp.is_present.TargetName = 1;
352		loginp.TargetName = target->TargetName;
353	}
354	if (target->options.is_present.MaxConnections) {
355		loginp.is_present.MaxConnections = 1;
356		loginp.MaxConnections = target->options.MaxConnections;
357	}
358	if (target->options.is_present.DataDigest) {
359		loginp.is_present.DataDigest = 1;
360		loginp.DataDigest = target->options.DataDigest;
361	}
362	if (target->options.is_present.HeaderDigest) {
363		loginp.is_present.HeaderDigest = 1;
364		loginp.HeaderDigest = target->options.HeaderDigest;
365	}
366	if (target->options.is_present.DefaultTime2Retain) {
367		loginp.is_present.DefaultTime2Retain = 1;
368		loginp.DefaultTime2Retain = target->options.DefaultTime2Retain;
369	}
370	if (target->options.is_present.DefaultTime2Wait) {
371		loginp.is_present.DefaultTime2Wait = 1;
372		loginp.DefaultTime2Wait = target->options.DefaultTime2Wait;
373	}
374	if (target->options.is_present.ErrorRecoveryLevel) {
375		loginp.is_present.ErrorRecoveryLevel = 1;
376		loginp.ErrorRecoveryLevel = target->options.ErrorRecoveryLevel;
377	}
378	if (target->options.is_present.MaxRecvDataSegmentLength) {
379		loginp.is_present.MaxRecvDataSegmentLength = 1;
380		loginp.MaxRecvDataSegmentLength =
381			target->options.MaxRecvDataSegmentLength;
382	}
383	if (target->auth.auth_info.auth_number) {
384		loginp.is_present.auth_info = 1;
385		loginp.auth_info = target->auth.auth_info;
386		if (target->auth.password[0]) {
387			loginp.is_present.password = 1;
388			loginp.password = target->auth.password;
389		}
390		if (target->auth.target_password[0]) {
391			loginp.is_present.target_password = 1;
392			loginp.target_password = target->auth.target_password;
393		}
394		if (target->auth.user_name[0]) {
395			loginp.is_present.user_name = 1;
396			loginp.user_name = target->auth.user_name;
397		}
398	}
399	loginp.is_present.TargetAlias = 1;
400	loginp.TargetAlias = target->TargetAlias;
401
402	if (portal != NULL) {
403		/* override general target options with portal options (if specified) */
404		if (portal->options.is_present.DataDigest) {
405			loginp.is_present.DataDigest = 1;
406			loginp.DataDigest = portal->options.DataDigest;
407		}
408		if (portal->options.is_present.HeaderDigest) {
409			loginp.is_present.HeaderDigest = 1;
410			loginp.HeaderDigest = portal->options.HeaderDigest;
411		}
412		if (portal->options.is_present.MaxRecvDataSegmentLength) {
413			loginp.is_present.MaxRecvDataSegmentLength = 1;
414			loginp.MaxRecvDataSegmentLength =
415				portal->options.MaxRecvDataSegmentLength;
416		}
417	}
418
419	if (req != NULL) {
420		loginp.session_id = get_id(&list[SESSION_LIST].list, &req->session_id);
421		loginp.login_type = req->login_type;
422	} else
423		loginp.login_type = ISCSI_LOGINTYPE_DISCOVERY;
424
425	DEB(5, ("Calling Login...\n"));
426
427	ret = ioctl(driver, (sess != NULL && sess->num_connections)
428				? ISCSI_ADD_CONNECTION : ISCSI_LOGIN, &loginp);
429
430	res->status = loginp.status;
431
432	if (ret)
433		close(sock);
434
435	if (ret || loginp.status) {
436		free(conn);
437		if (!res->status)
438			res->status = ISCSID_STATUS_GENERAL_ERROR;
439		return NULL;
440	}
441	/* connection established! link connection into session and return IDs */
442
443	conn->loginp = loginp;
444	conn->entry.sid.id = loginp.connection_id;
445	if (req != NULL) {
446		strlcpy((char *)conn->entry.sid.name, (char *)req->sym_name,
447			sizeof(conn->entry.sid.name));
448	}
449
450	/*
451	   Copy important target information
452	 */
453	conn->target.sid = target->entry.sid;
454	strlcpy((char *)conn->target.TargetName, (char *)target->TargetName,
455		sizeof(conn->target.TargetName));
456	strlcpy((char *)conn->target.TargetAlias, (char *)target->TargetAlias,
457		sizeof(conn->target.TargetAlias));
458	conn->target.options = target->options;
459	conn->target.auth = target->auth;
460	conn->portal.addr = *addr;
461
462	conn->session = sess;
463
464	if (stid == NULL) {
465		iscsid_login_rsp_t *rsp = (iscsid_login_rsp_t *)(void *)
466		    res->parameter;
467
468		sess->entry.sid.id = loginp.session_id;
469		TAILQ_INSERT_TAIL(&sess->connections, &conn->entry, link);
470		sess->num_connections++;
471
472		res->parameter_length = sizeof(*rsp);
473		rsp->connection_id = conn->entry.sid;
474		rsp->session_id = sess->entry.sid;
475
476		if (init != NULL) {
477			conn->initiator_id = init->entry.sid.id;
478			init->active_connections++;
479		}
480	} else
481		*stid = loginp.session_id;
482
483	/*
484	   Copy important portal information
485	 */
486	if (portal != NULL) {
487		conn->portal.sid = portal->entry.sid;
488		portal->active_connections++;
489	}
490
491	return conn;
492}
493
494
495/*
496 * event_recover_connection:
497 *    Handle RECOVER_CONNECTION event: Attempt to re-establish connection.
498 *
499 *    Parameter:
500 *          sid         Session ID
501 *          cid         Connection ID
502 */
503
504static void
505event_recover_connection(uint32_t sid, uint32_t cid)
506{
507	int sock, ret;
508	int yes = 1;
509	session_t *sess;
510	connection_t *conn;
511	portal_t *portal;
512	initiator_t *init;
513	iscsi_portal_address_t *addr;
514	struct sockaddr_in serverAddress;
515	struct hostent *host;
516
517	DEB(1, ("Event_Recover_Connection sid=%d, cid=%d\n", sid, cid));
518	(void) memset(&serverAddress, 0x0, sizeof(serverAddress));
519
520	LOCK_SESSIONS;
521
522	sess = find_session_id(sid);
523	if (sess == NULL) {
524		UNLOCK_SESSIONS;
525		return;
526	}
527
528	conn = find_connection_id(sess, cid);
529	if (conn == NULL) {
530		UNLOCK_SESSIONS;
531		return;
532	}
533
534	UNLOCK_SESSIONS;
535
536	conn->loginp.status = ISCSI_STATUS_CONNECTION_FAILED;
537
538	/* If we can't find the portal to connect to, abort. */
539
540	if ((portal = find_portal_id(conn->portal.sid.id)) == NULL)
541		return;
542
543	init = find_initiator_id(conn->initiator_id);
544	addr = &portal->addr;
545	conn->portal.addr = *addr;
546
547	/* translate target address */
548	DEB(1, ("Event_Recover_Connection Connecting to <%s>, port %d\n",
549			addr->address, addr->port));
550
551	if ((host = gethostbyname((char *)addr->address)) == NULL) {
552		DEB(1, ("GetHostByName failed (error %d)\n", h_errno));
553		return;
554	}
555	if (host->h_length > (int)sizeof(serverAddress.sin_addr)) {
556		DEB(1, ("Host address length invalid (%d)\n", host->h_length));
557		return;
558	}
559
560	serverAddress.sin_family = host->h_addrtype;
561	serverAddress.sin_port = htons((addr->port)
562									? addr->port : ISCSI_DEFAULT_PORT);
563	serverAddress.sin_len = host->h_length;
564	memcpy(&serverAddress.sin_addr, host->h_addr_list[0], host->h_length);
565
566	/* create and connect the socket */
567	sock = socket(AF_INET, SOCK_STREAM, 0);
568	if (sock < 0) {
569		DEB(1, ("Creating socket failed (error %d)\n", errno));
570		return;
571	}
572
573	DEB(1, ("recover_connection: Socket = %d\n", sock));
574
575	if (init) {
576		if (!bind_socket(sock, init->address)) {
577			DEB(1, ("Binding to interface failed (error %d)\n", errno));
578			close(sock);
579			return;
580		}
581	}
582
583	if (connect(sock, (struct sockaddr *)(void *)&serverAddress,
584		(socklen_t)sizeof(serverAddress)) < 0) {
585		DEB(1, ("Connecting to socket failed (error %d)\n", errno));
586		close(sock);
587		return;
588	}
589	/* speed up socket processing */
590	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, (socklen_t)sizeof(yes));
591
592	conn->loginp.socket = sock;
593	conn->loginp.status = 0;
594	ret = ioctl(driver, ISCSI_RESTORE_CONNECTION, &conn->loginp);
595
596	if (ret)
597		close(sock);
598}
599
600
601/*
602 * login:
603 *    Handle LOGIN request: Log into given portal. Create session, then
604 *    let make_connection do the rest.
605 *
606 *    Parameter:
607 *          req         The request parameters
608 *          res         The response buffer
609 */
610
611void
612login(iscsid_login_req_t * req, iscsid_response_t * res)
613{
614	session_t *sess;
615	connection_t *conn;
616
617	sess = calloc(1, sizeof(*sess));
618	if (sess == NULL) {
619		res->status = ISCSID_STATUS_NO_RESOURCES;
620		return;
621	}
622	TAILQ_INIT(&sess->connections);
623	strlcpy((char *)sess->entry.sid.name, (char *)req->sym_name,
624			sizeof(sess->entry.sid.name));
625
626	LOCK_SESSIONS;
627	conn = make_connection(sess, req, res, 0);
628
629	if (conn == NULL)
630		free(sess);
631	else {
632		sess->target = conn->target;
633		TAILQ_INSERT_TAIL(&list[SESSION_LIST].list, &sess->entry, link);
634		list[SESSION_LIST].num_entries++;
635	}
636	UNLOCK_SESSIONS;
637}
638
639
640/*
641 * add_connection:
642 *    Handle ADD_CONNECTION request: Log secondary connection into given portal.
643 *    Find the session, then let make_connection do the rest.
644 *
645 *    Parameter:
646 *          req         The request parameters
647 *          res         The response buffer
648 */
649
650void
651add_connection(iscsid_login_req_t * req, iscsid_response_t * res)
652{
653	session_t *sess;
654
655	LOCK_SESSIONS;
656	sess = find_session(&req->session_id);
657	if (sess == NULL) {
658		UNLOCK_SESSIONS;
659		res->status = ISCSID_STATUS_INVALID_SESSION_ID;
660		return;
661	}
662
663	make_connection(sess, req, res, 0);
664	UNLOCK_SESSIONS;
665}
666
667
668/*
669 * logout:
670 *    Handle LOGOUT request: Log out the given session.
671 *
672 *    Parameter:
673 *          req         The request parameters
674 *
675 *    Returns: Response status
676 */
677
678uint32_t
679logout(iscsid_sym_id_t * req)
680{
681	iscsi_logout_parameters_t logoutp;
682	session_t *sess;
683	int ret;
684
685	(void) memset(&logoutp, 0x0, sizeof(logoutp));
686	LOCK_SESSIONS;
687	sess = find_session(req);
688	if (sess == NULL) {
689		UNLOCK_SESSIONS;
690		return ISCSID_STATUS_INVALID_SESSION_ID;
691	}
692
693	logoutp.session_id = sess->entry.sid.id;
694	UNLOCK_SESSIONS;
695
696	ret = ioctl(driver, ISCSI_LOGOUT, &logoutp);
697	DEB(9, ("Logout returns %d, status = %d\n", ret, logoutp.status));
698
699	return logoutp.status;
700}
701
702
703/*
704 * remove_connection:
705 *    Handle REMOVE_CONNECTION request: Log out the given connection.
706 *
707 *    Parameter:
708 *          req         The request parameters
709 *
710 *    Returns: Response status
711 */
712
713uint32_t
714remove_connection(iscsid_remove_connection_req_t * req)
715{
716	iscsi_remove_parameters_t removep;
717	session_t *sess;
718	connection_t *conn;
719	int ret;
720
721	LOCK_SESSIONS;
722	sess = find_session(&req->session_id);
723	if (sess == NULL) {
724		UNLOCK_SESSIONS;
725		return ISCSID_STATUS_INVALID_SESSION_ID;
726	}
727	conn = find_connection(sess, &req->connection_id);
728	if (conn == NULL) {
729		UNLOCK_SESSIONS;
730		return ISCSID_STATUS_INVALID_CONNECTION_ID;
731	}
732
733	removep.session_id = sess->entry.sid.id;
734	removep.connection_id = conn->entry.sid.id;
735	UNLOCK_SESSIONS;
736
737	ret = ioctl(driver, ISCSI_REMOVE_CONNECTION, &removep);
738	DEB(9, ("Remove Connection returns %d, status=%d\n", ret, removep.status));
739
740	return removep.status;
741}
742
743/*
744 * send_targets:
745 *    Handle SEND_TARGETS request:
746 *       First login with type = discovery.
747 *       Then send the SendTargets iSCSI request to the target, which will
748 *       return a list of target portals.
749 *       Then logout.
750 *
751 *    Parameter:
752 *          stid              The send target ID
753 *          response_buffer   Pointer to pointer to buffer containing response
754 *                            The response contains the list of the target
755 *							  portals. The caller frees the buffer after it
756 *							  is done with it.
757 *          response_size     Pointer to variable which upon return will hold
758 *							  the size of the response buffer.
759 *
760 *    Returns: Response status
761 */
762
763uint32_t
764send_targets(uint32_t stid, uint8_t **response_buffer, uint32_t *response_size)
765{
766	iscsi_send_targets_parameters_t sendt;
767	iscsi_logout_parameters_t logoutp;
768	int ret;
769	connection_t *conn;
770	iscsid_response_t res;
771	uint32_t rc = ISCSID_STATUS_SUCCESS;
772
773	(void) memset(&sendt, 0x0, sizeof(sendt));
774	(void) memset(&logoutp, 0x0, sizeof(logoutp));
775	(void) memset(&res, 0x0, sizeof(res));
776	conn = make_connection(NULL, NULL, &res, &stid);
777	DEB(9, ("Make connection returns, status = %d\n", res.status));
778
779	if (conn == NULL)
780		return res.status;
781
782	sendt.session_id = stid;
783	sendt.response_buffer = NULL;
784	sendt.response_size = 0;
785	sendt.response_used = sendt.response_total = 0;
786	strlcpy((char *)sendt.key, "All", sizeof(sendt.key));
787
788	/*Call once to get the size of the buffer necessary */
789	ret = ioctl(driver, ISCSI_SEND_TARGETS, &sendt);
790
791	if (!ret && !sendt.status) {
792		/* Allocate buffer required and call again to retrieve data */
793		/* We allocate one extra byte so we can place a terminating 0 */
794		/* at the end of the buffer. */
795
796		sendt.response_size = sendt.response_total;
797		sendt.response_buffer = calloc(1, sendt.response_size + 1);
798		if (sendt.response_buffer == NULL)
799			rc = ISCSID_STATUS_NO_RESOURCES;
800		else {
801			ret = ioctl(driver, ISCSI_SEND_TARGETS, &sendt);
802			((uint8_t *)sendt.response_buffer)[sendt.response_size] = 0;
803
804			if (ret || sendt.status) {
805				free(sendt.response_buffer);
806				sendt.response_buffer = NULL;
807				sendt.response_used = 0;
808				if ((rc = sendt.status) == 0)
809					rc = ISCSID_STATUS_GENERAL_ERROR;
810			}
811		}
812	} else if ((rc = sendt.status) == 0)
813		rc = ISCSID_STATUS_GENERAL_ERROR;
814
815	*response_buffer = sendt.response_buffer;
816	*response_size = sendt.response_used;
817
818	logoutp.session_id = stid;
819	ret = ioctl(driver, ISCSI_LOGOUT, &logoutp);
820	/* ignore logout status */
821
822	free(conn);
823
824	return rc;
825}
826
827
828
829/*
830 * get_version:
831 *    Handle GET_VERSION request.
832 *
833 *    Returns: Filled get_version_rsp structure.
834 */
835
836void
837get_version(iscsid_response_t ** prsp, int *prsp_temp)
838{
839	iscsid_response_t *rsp = *prsp;
840	iscsid_get_version_rsp_t *ver;
841	iscsi_get_version_parameters_t drv_ver;
842
843	rsp = make_rsp(sizeof(iscsid_get_version_rsp_t), prsp, prsp_temp);
844	if (rsp == NULL)
845		return;
846	ver = (iscsid_get_version_rsp_t *)(void *)rsp->parameter;
847
848	ver->interface_version = INTERFACE_VERSION;
849	ver->major = VERSION_MAJOR;
850	ver->minor = VERSION_MINOR;
851	strlcpy ((char *)ver->version_string, VERSION_STRING, sizeof(ver->version_string));
852
853#ifdef ISCSI_DEBUG				/* DEBUG ONLY: Allow op without driver present */
854	if (driver < 0)
855		return;
856#endif
857	ioctl(driver, ISCSI_GET_VERSION, &drv_ver);
858	ver->driver_interface_version = drv_ver.interface_version;
859	ver->driver_major = drv_ver.major;
860	ver->driver_minor = drv_ver.minor;
861	strlcpy ((char *)ver->driver_version_string, (char *)drv_ver.version_string,
862			 sizeof (ver->driver_version_string));
863}
864
865
866/* -------------------------------------------------------------------------- */
867
868iscsi_register_event_parameters_t event_reg;	/* registered event ID */
869
870
871/*
872 * register_event_handler:
873 *    Call driver to register the event handler.
874 *
875 *    Returns:
876 *       TRUE on success.
877 */
878
879boolean_t
880register_event_handler(void)
881{
882	ioctl(driver, ISCSI_REGISTER_EVENT, &event_reg);
883	return event_reg.event_id != 0;
884}
885
886
887/*
888 * deregister_event_handler:
889 *    Call driver to deregister the event handler. If the event handler thread
890 *    is waiting for an event, this will wake it up and cause it to exit.
891 */
892
893void
894deregister_event_handler(void)
895{
896	if (event_reg.event_id) {
897		ioctl(driver, ISCSI_DEREGISTER_EVENT, &event_reg);
898		event_reg.event_id = 0;
899	}
900}
901
902
903/*
904 * event_handler:
905 *    Event handler thread. Wait for the driver to generate an event and
906 *    process it appropriately. Exits when the driver terminates or the
907 *    handler is deregistered because the daemon is terminating.
908 *
909 *    Parameter:
910 *          par         Not used.
911 */
912
913void *
914/*ARGSUSED*/
915event_handler(void *par)
916{
917	iscsi_wait_event_parameters_t evtp;
918	int rc;
919
920	DEB(99, ("Event handler starts\n"));
921	(void) memset(&evtp, 0x0, sizeof(evtp));
922
923	evtp.event_id = event_reg.event_id;
924
925	do {
926		if (nothreads)
927			rc = ioctl(driver, ISCSI_POLL_EVENT, &evtp);
928		else
929			rc = ioctl(driver, ISCSI_WAIT_EVENT, &evtp);
930		if (rc || evtp.status)
931			break;
932
933		DEB(1, ("Got Event: kind %d, status %d, sid %d, cid %d, reason %d\n",
934				evtp.event_kind, evtp.status, evtp.session_id,
935				evtp.connection_id, evtp.reason));
936
937		switch (evtp.event_kind) {
938		case ISCSI_SESSION_TERMINATED:
939			event_kill_session(evtp.session_id);
940			break;
941
942		case ISCSI_CONNECTION_TERMINATED:
943			event_kill_connection(evtp.session_id, evtp.connection_id);
944			break;
945
946		case ISCSI_RECOVER_CONNECTION:
947			event_recover_connection(evtp.session_id, evtp.connection_id);
948			break;
949
950		default:
951			break;
952		}
953	} while (evtp.event_kind != ISCSI_DRIVER_TERMINATING);
954
955	if (nothreads && evtp.event_kind == ISCSI_DRIVER_TERMINATING)
956		exit_daemon();
957
958	return NULL;
959}
960
961#if 0
962/*
963 * verify_connection:
964 *    Verify that a specific connection still exists, delete it if not.
965 *
966 * Parameter:  The connection pointer.
967 *
968 * Returns:    The status returned by the driver.
969 */
970
971uint32_t
972verify_connection(connection_t * conn)
973{
974	iscsi_conn_status_parameters_t req;
975	session_t *sess = conn->session;
976
977	req.connection_id = conn->entry.sid.id;
978	req.session_id = sess->entry.sid.id;
979
980	ioctl(driver, ISCSI_CONNECTION_STATUS, &req);
981
982	if (req.status) {
983		TAILQ_REMOVE(&sess->connections, &conn->entry, link);
984		sess->num_connections--;
985		free(conn);
986	}
987	DEB(9, ("Verify connection returns status %d\n", req.status));
988	return req.status;
989}
990
991#endif
992