/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <poll.h>
#ifdef DEBUG
#include <time.h>
#endif
#include "isns_server.h"
#include "isns_cache.h"
#include "isns_pdu.h"
#include "isns_msgq.h"
#include "isns_func.h"
#include "isns_log.h"
#include "isns_provider.h"
/* external functions */
#ifdef DEBUG
extern void dump_pdu1(isns_pdu_t *);
extern int verbose_tc;
#endif
extern boolean_t time_to_exit;
void *
isns_connection(
void *arg
)
{
int status = 0;
conn_arg_t *conn;
isns_pdu_t *pdu, *combined_pdu, *new_combined_pdu;
uint8_t *payload_ptr;
size_t pdu_sz;
conn = (conn_arg_t *)arg;
conn->out_packet.pdu = NULL;
conn->out_packet.sz = 0;
combined_pdu = NULL;
pdu = NULL;
while (status == 0 &&
time_to_exit == B_FALSE &&
isns_rcv_pdu(conn->so, &pdu, &pdu_sz, ISNS_RCV_TIMEOUT) > 0) {
uint16_t flags = pdu->flags;
if (ISNS_MSG_RECEIVED_ENABLED()) {
char buf[INET6_ADDRSTRLEN];
struct sockaddr_storage *ssp = &conn->ss;
struct sockaddr_in *sinp = (struct sockaddr_in *)ssp;
if (ssp->ss_family == AF_INET) {
(void) inet_ntop(AF_INET,
(void *)&(sinp->sin_addr),
buf, sizeof (buf));
} else {
(void) inet_ntop(AF_INET6,
(void *)&(sinp->sin_addr),
buf, sizeof (buf));
}
ISNS_MSG_RECEIVED((uintptr_t)buf);
}
if ((flags & ISNS_FLAG_FIRST_PDU) == ISNS_FLAG_FIRST_PDU) {
if (combined_pdu != NULL || pdu->seq != 0) {
goto conn_done;
}
combined_pdu = pdu;
pdu = NULL;
} else {
if (combined_pdu == NULL ||
combined_pdu->func_id != pdu->func_id ||
combined_pdu->xid != pdu->xid ||
(combined_pdu->seq + 1) != pdu->seq) {
/* expect the first pdu, the same tranx id */
/* and the next sequence id */
goto conn_done;
}
new_combined_pdu = (isns_pdu_t *)malloc(
ISNSP_HEADER_SIZE +
combined_pdu->payload_len +
pdu->payload_len);
if (new_combined_pdu == NULL) {
goto conn_done;
}
(void) memcpy((void *)new_combined_pdu,
(void *)combined_pdu,
ISNSP_HEADER_SIZE + combined_pdu->payload_len);
payload_ptr = new_combined_pdu->payload +
combined_pdu->payload_len;
(void) memcpy((void *)payload_ptr,
(void *)pdu->payload,
pdu->payload_len);
new_combined_pdu->seq = pdu->seq;
free(combined_pdu);
combined_pdu = new_combined_pdu;
free(pdu);
pdu = NULL;
}
if ((flags & ISNS_FLAG_LAST_PDU) == ISNS_FLAG_LAST_PDU) {
#ifdef DEBUG
time_t t;
clock_t c;
dump_pdu1(combined_pdu);
if (verbose_tc != 0) {
t = time(NULL);
c = clock();
}
#endif
conn->in_packet.pdu = combined_pdu;
conn->out_packet.pl = 0;
conn->ec = 0;
if (packet_split_verify(conn) == 0) {
(void) cache_lock(conn->lock);
status = conn->handler(conn);
conn->ec = cache_unlock(conn->lock, conn->ec);
}
switch (status) {
case -1:
/* error */
break;
case 0:
status = isns_response(conn);
isnslog(LOG_DEBUG, "isns_connection",
"Response status: %d.", status);
if (ISNS_MSG_RESPONDED_ENABLED()) {
char buf[INET6_ADDRSTRLEN];
struct sockaddr_storage *ssp =
&conn->ss;
struct sockaddr_in *sinp =
(struct sockaddr_in *)ssp;
if (ssp->ss_family == AF_INET) {
(void) inet_ntop(AF_INET,
(void *)&(sinp->sin_addr),
buf, sizeof (buf));
} else {
(void) inet_ntop(AF_INET6,
(void *)&(sinp->sin_addr),
buf, sizeof (buf));
}
ISNS_MSG_RESPONDED((uintptr_t)buf);
}
break;
default:
/* no need to send response message */
status = 0;
break;
}
#ifdef DEBUG
if (verbose_tc != 0) {
t = time(NULL) - t;
c = clock() - c;
printf("time %d clock %.4lf -msg response\n",
t, c / (double)CLOCKS_PER_SEC);
}
#endif
free(combined_pdu);
combined_pdu = NULL;
}
}
conn_done:
if (pdu != NULL) {
free(pdu);
}
if (combined_pdu != NULL) {
free(combined_pdu);
}
(void) close(conn->so);
(void) free(conn->out_packet.pdu);
(void) free(conn);
/* decrease the thread ref count */
dec_thr_count();
return (NULL);
}
/* the iSNS server port watcher */
void *
isns_port_watcher(
/* LINTED E_FUNC_ARG_UNUSED */
void *arg
)
{
int s, f;
int opt = 1;
struct sockaddr_in sin;
struct sockaddr_in *sinp;
struct sockaddr_storage *ssp;
socklen_t sslen;
char buf[INET6_ADDRSTRLEN];
pthread_t tid;
struct pollfd fds;
int poll_ret;
conn_arg_t *conn;
if ((s = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
/* IPv4 */
isnslog(LOG_DEBUG, "isns_port_watcher", "IPv4 socket created.");
(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&opt,
sizeof (opt));
sin.sin_family = AF_INET;
sin.sin_port = htons(ISNS_DEFAULT_SERVER_PORT);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
isnslog(LOG_DEBUG, "isns_port_watcher",
"binding on server port failed: %%m");
goto watch_failed;
}
isnslog(LOG_DEBUG, "isns_port_watcher",
"successful binding on server port.");
} else {
isnslog(LOG_DEBUG, "isns_port_watcher",
"cannot create socket: %%m.");
goto watch_failed;
}
if (listen(s, 5) < 0) {
isnslog(LOG_DEBUG, "isns_port_watcher",
"listening on server port failed: %%m.");
goto watch_failed;
}
isnslog(LOG_DEBUG, "isns_port_watcher", "listening on server port ok.");
fds.fd = s;
fds.events = (POLLIN | POLLRDNORM);
fds.revents = 0;
/* waiting for connections */
for (;;) {
if (time_to_exit) {
return (NULL);
}
poll_ret = poll(&fds, 1, 1000);
if (poll_ret <= 0) {
continue;
}
/* allocate a connection argument */
conn = (conn_arg_t *)malloc(sizeof (conn_arg_t));
if (conn == NULL) {
isnslog(LOG_DEBUG, "isns_port_watcher",
"malloc() failed.");
goto watch_failed;
}
ssp = &conn->ss;
sslen = sizeof (conn->ss);
f = accept(s, (struct sockaddr *)ssp, &sslen);
if (f < 0) {
isnslog(LOG_DEBUG, "isns_port_watcher",
"accepting connection failed: %%m.");
goto watch_failed;
}
sinp = (struct sockaddr_in *)ssp;
if (ssp->ss_family == AF_INET) {
(void) inet_ntop(AF_INET, (void *)&(sinp->sin_addr),
buf, sizeof (buf));
} else {
(void) inet_ntop(AF_INET6, (void *)&(sinp->sin_addr),
buf, sizeof (buf));
}
isnslog(LOG_DEBUG, "isns_port_watcher",
"connection from %s:%d.", buf,
sinp->sin_port);
if (ISNS_CONNECTION_ACCEPTED_ENABLED()) {
ISNS_CONNECTION_ACCEPTED((uintptr_t)buf);
}
conn->so = f;
/* create an isns connection */
if (pthread_create(&tid, NULL,
isns_connection, (void *)conn) != 0) {
(void) close(f);
(void) free(conn);
isnslog(LOG_DEBUG, "isns_port_watcher",
"pthread_create() failed.");
} else {
/* increase the thread ref count */
inc_thr_count();
}
}
watch_failed:
shutdown_server();
return (NULL);
}
static uint16_t xid = 0;
static pthread_mutex_t xid_mtx = PTHREAD_MUTEX_INITIALIZER;
uint16_t
get_server_xid(
)
{
uint16_t tmp;
(void) pthread_mutex_lock(&xid_mtx);
tmp = ++ xid;
(void) pthread_mutex_unlock(&xid_mtx);
return (tmp);
}