util_port.c revision 36c5fee33fa8b822175d410202aebcf592c8d342
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <netinet/in.h>
#include <assert.h>
#include <syslog.h>
#include <unistd.h>
#include "queue.h"
#include "port.h"
#include "iscsi_conn.h"
#include "utility.h"
pthread_mutex_t port_mutex;
int port_conn_num;
iscsi_conn_t *conn_head,
*conn_tail;
void
port_init()
{
(void) pthread_mutex_init(&port_mutex, NULL);
port_conn_num = 0;
}
void *
port_watcher(void *v)
{
int s,
fd,
on = 1;
char debug[80];
struct sockaddr_in sin_ip;
struct sockaddr_in6 sin6_ip;
struct sockaddr_storage st;
socklen_t socklen;
iscsi_conn_t *conn;
port_args_t *p = (port_args_t *)v;
target_queue_t *q = p->port_mgmtq;
int l,
accept_err_sleep = 1;
pthread_t junk;
struct in_addr addr;
struct in6_addr addr6;
/*
* Try creating an IPv6 socket first
* If failed, try creating an IPv4 socket
*/
if ((s = socket(PF_INET6, SOCK_STREAM, 0)) == -1) {
queue_str(q, Q_GEN_ERRS, msg_log, "Opening IPv4 socket");
if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
queue_str(q, Q_GEN_ERRS, msg_log,
"Can't open socket");
return (NULL);
} else {
bzero(&sin_ip, sizeof (sin_ip));
sin_ip.sin_family = AF_INET;
sin_ip.sin_port = htons(p->port_num);
sin_ip.sin_addr.s_addr = INADDR_ANY;
(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof (on));
if ((bind(s, (struct sockaddr *)&sin_ip,
sizeof (sin_ip))) < 0) {
(void) snprintf(debug, sizeof (debug),
"bind on port %d failed, errno %d",
p->port_num, errno);
queue_str(q, Q_GEN_ERRS, msg_status, debug);
return (NULL);
}
}
} else {
queue_str(q, Q_GEN_DETAILS, msg_log, "Got IPv6 socket");
bzero(&sin6_ip, sizeof (sin6_ip));
sin6_ip.sin6_family = AF_INET6;
sin6_ip.sin6_port = htons(p->port_num);
sin6_ip.sin6_addr = in6addr_any;
(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
sizeof (on));
if ((bind(s, (struct sockaddr *)&sin6_ip, sizeof (sin6_ip)))
< 0) {
(void) snprintf(debug, sizeof (debug),
"bind on port %d failed, errno %d",
p->port_num, errno);
queue_str(q, Q_GEN_ERRS, msg_status, debug);
return (NULL);
}
}
if (listen(s, 5) < 0) {
queue_str(q, Q_GEN_ERRS, msg_status, "listen failed");
return (NULL);
}
/*CONSTANTCONDITION*/
while (1) {
socklen = sizeof (st);
if ((fd = accept(s, (struct sockaddr *)&st,
&socklen)) < 0) {
accept_err_sleep *= 2;
(void) sleep(accept_err_sleep);
if (accept_err_sleep > 60) {
accept_err_sleep = 1;
queue_prt(q, Q_GEN_ERRS,
"accept failed, errno %d", errno);
}
continue;
}
l = 128 * 1024;
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&l,
sizeof (l)) < 0)
queue_str(q, Q_GEN_ERRS, msg_status,
"setsockopt failed");
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&l,
sizeof (l)) < 0)
queue_str(q, Q_GEN_ERRS, msg_status,
"setsockopt failed");
l = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&l,
sizeof (l)) < 0)
queue_str(q, Q_GEN_ERRS, msg_status,
"setsockopt keepalive failed");
if ((conn = (iscsi_conn_t *)calloc(sizeof (iscsi_conn_t),
1)) == NULL) {
/*
* If we fail to get memory this is all rather
* pointless, since it's unlikely that queue_str
* could malloc memory to send a message.
*/
queue_str(q, Q_GEN_ERRS, msg_status,
"connection malloc failed");
return (NULL);
}
/*
* Save initiator sockaddr for future use
*/
conn->c_initiator_sockaddr = st;
socklen = sizeof (st);
if (getsockname(fd, (struct sockaddr *)&st,
&socklen) == 0) {
/*
* Save target sockaddr for future use
*/
addr6 = ((struct sockaddr_in6 *)&st)->sin6_addr;
if (st.ss_family == AF_INET6 &&
IN6_IS_ADDR_V4MAPPED(&addr6)) {
/*
* If target address is IPv4 mapped IPv6 address
* convert it to IPv4 address
*/
IN6_V4MAPPED_TO_INADDR(&addr6, &addr);
((struct sockaddr_in *)&st)->sin_addr = addr;
st.ss_family = AF_INET;
}
conn->c_target_sockaddr = st;
}
conn->c_fd = fd;
conn->c_mgmtq = q;
conn->c_up_at = time(NULL);
conn->c_state = S1_FREE;
(void) pthread_mutex_init(&conn->c_mutex, NULL);
(void) pthread_mutex_init(&conn->c_state_mutex, NULL);
(void) pthread_mutex_lock(&port_mutex);
conn->c_num = port_conn_num++;
if (conn_head == NULL) {
conn_head = conn;
assert(conn_tail == NULL);
conn_tail = conn;
} else {
conn_tail->c_next = conn;
conn->c_prev = conn_tail;
conn_tail = conn;
}
(void) pthread_mutex_unlock(&port_mutex);
(void) pthread_create(&junk, NULL, conn_process, conn);
}
return (NULL);
}
void
port_conn_remove(iscsi_conn_t *c)
{
iscsi_conn_t *n;
(void) pthread_mutex_lock(&port_mutex);
if (conn_head == c) {
conn_head = c->c_next;
if (conn_head == NULL)
conn_tail = NULL;
else
conn_head->c_prev = NULL;
} else {
n = c->c_prev;
n->c_next = c->c_next;
if (c->c_next != NULL)
c->c_next->c_prev = n;
else {
assert(conn_tail == c);
conn_tail = n;
}
}
/*
* The connection queue is freed here so that it's protected by
* locks. The main thread of the deamon when processing incoming
* management requests will send them on the connection queues.
* The main thread will grab the port_mutex so that we know the
* queue is still valid.
*/
queue_free(c->c_dataq, conn_queue_data_remove);
(void) pthread_mutex_unlock(&port_mutex);
}