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 <sys/types.h>
     28 #include <sys/socket.h>
     29 #include <string.h>
     30 #include <strings.h>
     31 #include <stdlib.h>
     32 #include <stdio.h>
     33 #include <errno.h>
     34 #include <netinet/in.h>
     35 #include <netinet/tcp.h>
     36 #include <assert.h>
     37 #include <syslog.h>
     38 #include <unistd.h>
     39 
     40 #include "queue.h"
     41 #include "port.h"
     42 #include "iscsi_conn.h"
     43 #include "utility.h"
     44 
     45 pthread_mutex_t port_mutex;
     46 int port_conn_num;
     47 iscsi_conn_t	*conn_head = NULL;
     48 iscsi_conn_t	*conn_tail = NULL;
     49 
     50 void
     51 port_init(void)
     52 {
     53 	(void) pthread_mutex_init(&port_mutex, NULL);
     54 	port_conn_num = 0;
     55 }
     56 
     57 void
     58 canonicalize_sockaddr(struct sockaddr_storage *st)
     59 {
     60 	struct in6_addr *addr6 = &((struct sockaddr_in6 *)st)->sin6_addr;
     61 
     62 	/*
     63 	 * If target address is IPv4 mapped IPv6 address convert it to IPv4
     64 	 * address.
     65 	 */
     66 	if (st->ss_family == AF_INET6 &&
     67 	    (IN6_IS_ADDR_V4MAPPED(addr6) || IN6_IS_ADDR_V4COMPAT(addr6))) {
     68 		struct in_addr *addr = &((struct sockaddr_in *)st)->sin_addr;
     69 		IN6_V4MAPPED_TO_INADDR(addr6, addr);
     70 		st->ss_family = AF_INET;
     71 	}
     72 }
     73 
     74 
     75 void *
     76 port_watcher(void *v)
     77 {
     78 	int			s;
     79 	int			fd;
     80 	int			on = 1;
     81 	char			debug[80];
     82 	struct sockaddr_in	sin_ip;
     83 	struct sockaddr_in6	sin6_ip;
     84 	struct sockaddr_storage	st;
     85 	socklen_t		socklen;
     86 	iscsi_conn_t		*conn;
     87 	port_args_t		*p = (port_args_t *)v;
     88 	target_queue_t		*q = p->port_mgmtq;
     89 	int			l;
     90 	const int		just_say_no = 1;
     91 	pthread_t		junk;
     92 
     93 	/*
     94 	 * Try creating an IPv6 socket first
     95 	 * If failed, try creating an IPv4 socket
     96 	 */
     97 	if ((s = socket(PF_INET6, SOCK_STREAM, 0)) == -1) {
     98 
     99 		queue_str(q, Q_GEN_ERRS, msg_log, "Opening IPv4 socket");
    100 		if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
    101 			queue_str(q, Q_GEN_ERRS, msg_log,
    102 			    "Can't open socket");
    103 			return (NULL);
    104 		} else {
    105 			bzero(&sin_ip, sizeof (sin_ip));
    106 			sin_ip.sin_family = AF_INET;
    107 			sin_ip.sin_port = htons(p->port_num);
    108 			sin_ip.sin_addr.s_addr = INADDR_ANY;
    109 
    110 			(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
    111 			    (char *)&on, sizeof (on));
    112 
    113 			if ((bind(s, (struct sockaddr *)&sin_ip,
    114 			    sizeof (sin_ip))) < 0) {
    115 				(void) snprintf(debug, sizeof (debug),
    116 				    "bind on port %d failed, errno %d",
    117 				    p->port_num, errno);
    118 				queue_str(q, Q_GEN_ERRS, msg_status, debug);
    119 				return (NULL);
    120 			}
    121 		}
    122 
    123 	} else {
    124 
    125 		queue_str(q, Q_GEN_DETAILS, msg_log, "Got IPv6 socket");
    126 		bzero(&sin6_ip, sizeof (sin6_ip));
    127 		sin6_ip.sin6_family = AF_INET6;
    128 		sin6_ip.sin6_port = htons(p->port_num);
    129 		sin6_ip.sin6_addr = in6addr_any;
    130 
    131 		(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
    132 		    sizeof (on));
    133 
    134 		if ((bind(s, (struct sockaddr *)&sin6_ip, sizeof (sin6_ip)))
    135 		    < 0) {
    136 			(void) snprintf(debug, sizeof (debug),
    137 			    "bind on port %d failed, errno %d",
    138 			    p->port_num, errno);
    139 			queue_str(q, Q_GEN_ERRS, msg_status, debug);
    140 			return (NULL);
    141 		}
    142 	}
    143 
    144 	if (listen(s, 128) < 0) {
    145 		queue_str(q, Q_GEN_ERRS, msg_status, "listen failed");
    146 		return (NULL);
    147 	}
    148 
    149 	/*CONSTANTCONDITION*/
    150 	while (1) {
    151 
    152 		socklen = sizeof (st);
    153 		if ((fd = accept(s, (struct sockaddr *)&st,
    154 		    &socklen)) < 0) {
    155 			if (errno != EINTR)
    156 				queue_prt(q, Q_GEN_ERRS,
    157 				    "accept failed, %s", strerror(errno));
    158 			(void) sleep(1);
    159 			continue;
    160 		}
    161 
    162 		l = 128 * 1024;
    163 		if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&l,
    164 		    sizeof (l)) < 0)
    165 			queue_str(q, Q_GEN_ERRS, msg_status,
    166 			    "setsockopt failed");
    167 		if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&l,
    168 		    sizeof (l)) < 0)
    169 			queue_str(q, Q_GEN_ERRS, msg_status,
    170 			    "setsockopt failed");
    171 		l = 1;
    172 		if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&l,
    173 		    sizeof (l)) < 0)
    174 			queue_str(q, Q_GEN_ERRS, msg_status,
    175 			    "setsockopt keepalive failed");
    176 
    177 		if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
    178 		    (char *)&just_say_no, sizeof (just_say_no)) != 0)
    179 			queue_str(q, Q_GEN_ERRS, msg_status,
    180 			    "setsockopt NODELAY failed");
    181 
    182 		if ((conn = calloc(1, sizeof (iscsi_conn_t))) == NULL) {
    183 			/*
    184 			 * If we fail to get memory this is all rather
    185 			 * pointless, since it's unlikely that queue_str
    186 			 * could malloc memory to send a message.
    187 			 */
    188 			queue_str(q, Q_GEN_ERRS, msg_status,
    189 			    "connection malloc failed");
    190 			return (NULL);
    191 		}
    192 
    193 		/*
    194 		 * Save initiator address for future use.
    195 		 */
    196 		canonicalize_sockaddr(&st);
    197 		conn->c_initiator_sockaddr = st;
    198 
    199 		/*
    200 		 * Save target address for future use.
    201 		 */
    202 		socklen = sizeof (st);
    203 		if (getsockname(fd, (struct sockaddr *)&st, &socklen) == 0)
    204 			canonicalize_sockaddr(&st);
    205 		else
    206 			st.ss_family = AF_UNSPEC;
    207 		conn->c_target_sockaddr = st;
    208 
    209 		conn->c_fd	= fd;
    210 		conn->c_mgmtq	= q;
    211 		conn->c_up_at	= time(NULL);
    212 		conn->c_state	= S1_FREE;
    213 		(void) pthread_mutex_init(&conn->c_mutex, NULL);
    214 		(void) pthread_mutex_init(&conn->c_state_mutex, NULL);
    215 		(void) pthread_mutex_lock(&port_mutex);
    216 		conn->c_num	= port_conn_num++;
    217 		if (conn_head == NULL) {
    218 			conn_head = conn;
    219 			assert(conn_tail == NULL);
    220 			conn_tail = conn;
    221 		} else {
    222 			conn_tail->c_next = conn;
    223 			conn->c_prev = conn_tail;
    224 			conn_tail = conn;
    225 		}
    226 		(void) pthread_mutex_unlock(&port_mutex);
    227 
    228 		(void) pthread_create(&junk, NULL, conn_process, conn);
    229 	}
    230 	return (NULL);
    231 }
    232 
    233 void
    234 port_conn_remove(iscsi_conn_t *c)
    235 {
    236 	iscsi_conn_t	*n;
    237 
    238 	(void) pthread_mutex_lock(&port_mutex);
    239 	if (conn_head == c) {
    240 		conn_head = c->c_next;
    241 		if (conn_head == NULL)
    242 			conn_tail = NULL;
    243 		else
    244 			conn_head->c_prev = NULL;
    245 	} else {
    246 		n = c->c_prev;
    247 		n->c_next = c->c_next;
    248 		if (c->c_next != NULL)
    249 			c->c_next->c_prev = n;
    250 		else {
    251 			assert(conn_tail == c);
    252 			conn_tail = n;
    253 		}
    254 	}
    255 
    256 	/*
    257 	 * The connection queue is freed here so that it's protected by
    258 	 * locks. The main thread of the deamon when processing incoming
    259 	 * management requests will send them on the connection queues.
    260 	 * The main thread will grab the port_mutex so that we know the
    261 	 * queue is still valid.
    262 	 */
    263 	queue_free(c->c_dataq, conn_queue_data_remove);
    264 	(void) pthread_mutex_unlock(&port_mutex);
    265 }
    266