Blame usr/io.c

Packit eace71
/*
Packit eace71
 * iSCSI I/O Library
Packit eace71
 *
Packit eace71
 * Copyright (C) 2002 Cisco Systems, Inc.
Packit eace71
 * maintained by linux-iscsi-devel@lists.sourceforge.net
Packit eace71
 *
Packit eace71
 * This program is free software; you can redistribute it and/or modify
Packit eace71
 * it under the terms of the GNU General Public License as published
Packit eace71
 * by the Free Software Foundation; either version 2 of the License, or
Packit eace71
 * (at your option) any later version.
Packit eace71
 *
Packit eace71
 * This program is distributed in the hope that it will be useful, but
Packit eace71
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit eace71
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit eace71
 * General Public License for more details.
Packit eace71
 *
Packit eace71
 * See the file COPYING included with this distribution for more details.
Packit eace71
 */
Packit eace71
#include <string.h>
Packit eace71
#include <stdint.h>
Packit eace71
#include <unistd.h>
Packit eace71
#include <errno.h>
Packit eace71
#include <stdio.h>
Packit eace71
#include <signal.h>
Packit eace71
#include <unistd.h>
Packit eace71
#include <fcntl.h>
Packit eace71
#include <sys/poll.h>
Packit eace71
#include <sys/ioctl.h>
Packit eace71
#include <netinet/tcp.h>
Packit eace71
#include <arpa/inet.h>
Packit eace71
#include <sys/uio.h>
Packit eace71
Packit eace71
#include "types.h"
Packit eace71
#include "iscsi_proto.h"
Packit eace71
#include "iscsi_settings.h"
Packit eace71
#include "initiator.h"
Packit eace71
#include "iscsi_ipc.h"
Packit eace71
#include "log.h"
Packit eace71
#include "transport.h"
Packit eace71
#include "idbm.h"
Packit eace71
#include "iface.h"
Packit eace71
#include "sysdeps.h"
Packit eace71
Packit eace71
#define LOG_CONN_CLOSED(conn) \
Packit eace71
do { \
Packit eace71
	getnameinfo((struct sockaddr *) &conn->saddr, sizeof(conn->saddr), \
Packit eace71
		    conn->host, sizeof(conn->host), NULL, 0, NI_NUMERICHOST); \
Packit eace71
	log_error("Connection to Discovery Address %s closed", conn->host); \
Packit eace71
} while (0)
Packit eace71
Packit eace71
#define LOG_CONN_FAIL(conn) \
Packit eace71
do { \
Packit eace71
	getnameinfo((struct sockaddr *) &conn->saddr, sizeof(conn->saddr), \
Packit eace71
		    conn->host, sizeof(conn->host), NULL, 0, NI_NUMERICHOST); \
Packit eace71
	log_error("Connection to Discovery Address %s failed", conn->host); \
Packit eace71
} while (0)
Packit eace71
Packit eace71
static int timedout;
Packit eace71
Packit eace71
static void
Packit eace71
sigalarm_handler(int unused)
Packit eace71
{
Packit eace71
	timedout = 1;
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
set_non_blocking(int fd)
Packit eace71
{
Packit eace71
	int res = fcntl(fd, F_GETFL);
Packit eace71
Packit eace71
	if (res != -1) {
Packit eace71
		res = fcntl(fd, F_SETFL, res | O_NONBLOCK);
Packit eace71
		if (res)
Packit eace71
			log_warning("unable to set fd flags (%s)!",
Packit eace71
				    strerror(errno));
Packit eace71
	} else
Packit eace71
		log_warning("unable to get fd flags (%s)!", strerror(errno));
Packit eace71
Packit eace71
}
Packit eace71
Packit eace71
#if 0
Packit eace71
/* not used by anyone */
Packit eace71
static int get_hwaddress_from_netdev(char *netdev, char *hwaddress)
Packit eace71
{
Packit eace71
	struct ifaddrs *ifap, *ifa;
Packit eace71
	struct sockaddr_in *s4;
Packit eace71
	struct sockaddr_in6 *s6;
Packit eace71
	struct ifreq if_hwaddr;
Packit eace71
	int found = 0, sockfd;
Packit eace71
	unsigned char *hwaddr;
Packit eace71
	char buf[INET6_ADDRSTRLEN];
Packit eace71
Packit eace71
	if (getifaddrs(&ifap)) {
Packit eace71
		log_error("Could not match hwaddress %s to netdev. "
Packit eace71
			  "getifaddrs failed %d", hwaddress, errno);
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	/* Open a basic socket. */
Packit eace71
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
Packit eace71
	if (sockfd < 0) {
Packit eace71
		log_error("Could not open socket for ioctl.");
Packit eace71
		goto free_ifap;
Packit eace71
	}
Packit eace71
Packit eace71
	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
Packit eace71
		if (!ifa->ifa_addr)
Packit eace71
			continue;
Packit eace71
Packit eace71
		switch (ifa->ifa_addr->sa_family) {
Packit eace71
		case AF_INET:
Packit eace71
			s4 = (struct sockaddr_in *)(ifa->ifa_addr);
Packit eace71
			if (!inet_ntop(ifa->ifa_addr->sa_family,
Packit eace71
				      (void *)&(s4->sin_addr), buf,
Packit eace71
				      INET_ADDRSTRLEN))
Packit eace71
				continue;
Packit eace71
			log_debug(4, "name %s addr %s", ifa->ifa_name, buf);
Packit eace71
			break;
Packit eace71
		case AF_INET6:
Packit eace71
			s6 = (struct sockaddr_in6 *)(ifa->ifa_addr);
Packit eace71
			if (!inet_ntop(ifa->ifa_addr->sa_family,
Packit eace71
			    (void *)&(s6->sin6_addr), buf, INET6_ADDRSTRLEN))
Packit eace71
				continue;
Packit eace71
			log_debug(4, "name %s addr %s", ifa->ifa_name, buf);
Packit eace71
			break;
Packit eace71
		default:
Packit eace71
			continue;
Packit eace71
		}
Packit eace71
Packit eace71
		if (strcmp(ifa->ifa_name, netdev))
Packit eace71
			continue;
Packit eace71
Packit eace71
		strncpy(if_hwaddr.ifr_name, ifa->ifa_name, IFNAMSIZ);
Packit eace71
		if (ioctl(sockfd, SIOCGIFHWADDR, &if_hwaddr) < 0) {
Packit eace71
			log_error("Could not match %s to netdevice.",
Packit eace71
				  hwaddress);
Packit eace71
			continue;
Packit eace71
		}
Packit eace71
Packit eace71
		/* check for ARPHRD_ETHER (ethernet) */
Packit eace71
		if (if_hwaddr.ifr_hwaddr.sa_family != 1)
Packit eace71
			continue;
Packit eace71
		hwaddr = (unsigned char *)if_hwaddr.ifr_hwaddr.sa_data;
Packit eace71
Packit eace71
		memset(hwaddress, 0, ISCSI_MAX_IFACE_LEN);
Packit eace71
		/* TODO should look and covert so we do not need tmp buf */
Packit eace71
		sprintf(hwaddress, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
Packit eace71
			hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3],
Packit eace71
			hwaddr[4], hwaddr[5]);
Packit eace71
		log_debug(4, "Found hardware address %s", hwaddress);
Packit eace71
		found = 1;
Packit eace71
		break;
Packit eace71
	}
Packit eace71
Packit eace71
	close(sockfd);
Packit eace71
free_ifap:
Packit eace71
	freeifaddrs(ifap);
Packit eace71
	return found;
Packit eace71
}
Packit eace71
#endif
Packit eace71
Packit eace71
Packit eace71
#if 0
Packit eace71
Packit eace71
This is not supported for now, because it is not exactly what we want.
Packit eace71
It also turns out that targets will send packets to other interfaces
Packit eace71
causing all types of weird things to happen.
Packit eace71
Packit eace71
Packit eace71
static int bind_src_by_address(int sockfd, char *address)
Packit eace71
{
Packit eace71
	int rc = 0;
Packit eace71
	char port[NI_MAXSERV];
Packit eace71
	struct sockaddr_storage saddr;
Packit eace71
Packit eace71
	memset(&saddr, 0, sizeof(struct sockaddr_storage));
Packit eace71
	if (resolve_address(address, port, &saddr)) {
Packit eace71
		log_error("Could not bind %s to conn.", address);
Packit eace71
		return -1;
Packit eace71
	}
Packit eace71
Packit eace71
	switch (saddr.ss_family) {
Packit eace71
	case AF_INET:
Packit eace71
		rc = bind(sockfd, (struct sockaddr *)&saddr,
Packit eace71
			  sizeof(struct sockaddr_in));
Packit eace71
		break;
Packit eace71
	case AF_INET6:
Packit eace71
		rc = bind(sockfd, (struct sockaddr *)&saddr,
Packit eace71
			  sizeof(struct sockaddr_in6));
Packit eace71
		break;
Packit eace71
	default:
Packit eace71
		rc = -1;
Packit eace71
	}
Packit eace71
	if (rc)
Packit eace71
		log_error("Could not bind %s to %d.", address, sockfd);
Packit eace71
	else
Packit eace71
		log_debug(4, "Bound %s to socket fd %d", address, sockfd);
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
#endif
Packit eace71
Packit eace71
static int bind_conn_to_iface(iscsi_conn_t *conn, struct iface_rec *iface)
Packit eace71
{
Packit eace71
	struct iscsi_session *session = conn->session;
Packit eace71
Packit eace71
	if (strcmp(iface->transport_name, DEFAULT_TRANSPORT))
Packit eace71
		return 0;
Packit eace71
Packit eace71
	memset(session->netdev, 0, IFNAMSIZ);
Packit eace71
	if (iface_is_bound_by_hwaddr(iface)) {
Packit eace71
		if (net_get_netdev_from_hwaddress(iface->hwaddress,
Packit eace71
						  session->netdev)) {
Packit eace71
			log_error("Cannot match %s to net/scsi interface.",
Packit eace71
				  iface->hwaddress);
Packit eace71
			return -1;
Packit eace71
		}
Packit eace71
	} else if (iface_is_bound_by_netdev(iface)) {
Packit eace71
		strcpy(session->netdev, iface->netdev);
Packit eace71
	} else if (iface_is_bound_by_ipaddr(iface)) {
Packit eace71
		/*
Packit eace71
		 * we never supported this but now with offload having to
Packit eace71
		 * set the ip address in the iface, useris may forget to
Packit eace71
		 * set the offload's transport type and we end up here by
Packit eace71
		 * accident.
Packit eace71
		 */
Packit eace71
		log_error("Cannot bind %s to net/scsi interface. This is not "
Packit eace71
			  "supported with software iSCSI (iscsi_tcp).",
Packit eace71
			   iface->ipaddress);
Packit eace71
		return -1;
Packit eace71
	}
Packit eace71
Packit eace71
	if (strlen(session->netdev)) {
Packit eace71
		struct ifreq ifr;
Packit eace71
Packit eace71
		log_debug(4, "Binding session %d to %s", session->id,
Packit eace71
			  session->netdev);
Packit eace71
		memset(&ifr, 0, sizeof(ifr));
Packit eace71
		strlcpy(ifr.ifr_name, session->netdev, IFNAMSIZ);
Packit eace71
Packit eace71
		if (setsockopt(conn->socket_fd, SOL_SOCKET, SO_BINDTODEVICE,
Packit eace71
			       session->netdev,
Packit eace71
			       strlen(session->netdev) + 1) < 0) {
Packit eace71
			log_error("Could not bind connection %d to %s",
Packit eace71
				  conn->id, session->netdev);
Packit eace71
			return -1;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
int
Packit eace71
iscsi_io_tcp_connect(iscsi_conn_t *conn, int non_blocking)
Packit eace71
{
Packit eace71
	int rc, onearg;
Packit eace71
	struct sockaddr_storage *ss = &conn->saddr;
Packit eace71
	char serv[NI_MAXSERV];
Packit eace71
Packit eace71
	/* create a socket */
Packit eace71
	conn->socket_fd = socket(ss->ss_family, SOCK_STREAM, IPPROTO_TCP);
Packit eace71
Packit eace71
	/* the trasport ep handle is used to bind with */
Packit eace71
	conn->transport_ep_handle = conn->socket_fd;
Packit eace71
Packit eace71
	if (conn->socket_fd < 0) {
Packit eace71
		log_error("cannot create TCP socket");
Packit eace71
		return -1;
Packit eace71
	}
Packit eace71
Packit eace71
	if (bind_conn_to_iface(conn, &conn->session->nrec.iface))
Packit eace71
		return -1;
Packit eace71
Packit eace71
	onearg = 1;
Packit eace71
	rc = setsockopt(conn->socket_fd, IPPROTO_TCP, TCP_NODELAY, &onearg,
Packit eace71
			sizeof (onearg));
Packit eace71
	if (rc < 0) {
Packit eace71
		log_error("cannot set TCP_NODELAY option on socket");
Packit eace71
		close(conn->socket_fd);
Packit eace71
		conn->socket_fd = -1;
Packit eace71
		return rc;
Packit eace71
	}
Packit eace71
Packit eace71
	/* optionally set the window sizes */
Packit eace71
	if (conn->tcp_window_size) {
Packit eace71
		int window_size = conn->tcp_window_size;
Packit eace71
		socklen_t arglen = sizeof (window_size);
Packit eace71
Packit eace71
		if (setsockopt(conn->socket_fd, SOL_SOCKET, SO_RCVBUF,
Packit eace71
		       (char *) &window_size, sizeof (window_size)) < 0) {
Packit eace71
			log_warning("failed to set TCP recv window size "
Packit eace71
				    "to %u", window_size);
Packit eace71
		} else {
Packit eace71
			if (getsockopt(conn->socket_fd, SOL_SOCKET, SO_RCVBUF,
Packit eace71
				       (char *) &window_size, &arglen) >= 0) {
Packit eace71
				log_debug(4, "set TCP recv window size to %u, "
Packit eace71
					  "actually got %u",
Packit eace71
					  conn->tcp_window_size, window_size);
Packit eace71
			}
Packit eace71
		}
Packit eace71
Packit eace71
		window_size = conn->tcp_window_size;
Packit eace71
		arglen = sizeof (window_size);
Packit eace71
Packit eace71
		if (setsockopt(conn->socket_fd, SOL_SOCKET, SO_SNDBUF,
Packit eace71
		       (char *) &window_size, sizeof (window_size)) < 0) {
Packit eace71
			log_warning("failed to set TCP send window size "
Packit eace71
				    "to %u", window_size);
Packit eace71
		} else {
Packit eace71
			if (getsockopt(conn->socket_fd, SOL_SOCKET, SO_SNDBUF,
Packit eace71
				       (char *) &window_size, &arglen) >= 0) {
Packit eace71
				log_debug(4, "set TCP send window size to %u, "
Packit eace71
					  "actually got %u",
Packit eace71
					  conn->tcp_window_size, window_size);
Packit eace71
			}
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	/*
Packit eace71
	 * Build a TCP connection to the target
Packit eace71
	 */
Packit eace71
	getnameinfo((struct sockaddr *) ss, sizeof(*ss),
Packit eace71
		    conn->host, sizeof(conn->host), serv, sizeof(serv),
Packit eace71
		    NI_NUMERICHOST|NI_NUMERICSERV);
Packit eace71
Packit eace71
	log_debug(1, "connecting to %s:%s", conn->host, serv);
Packit eace71
	if (non_blocking)
Packit eace71
		set_non_blocking(conn->socket_fd);
Packit eace71
	rc = connect(conn->socket_fd, (struct sockaddr *) ss, sizeof (*ss));
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
int
Packit eace71
iscsi_io_tcp_poll(iscsi_conn_t *conn, int timeout_ms)
Packit eace71
{
Packit eace71
	int rc;
Packit eace71
	struct pollfd pdesc;
Packit eace71
	char serv[NI_MAXSERV], lserv[NI_MAXSERV];
Packit eace71
	struct sockaddr_storage ss;
Packit eace71
	socklen_t len;
Packit eace71
Packit eace71
	pdesc.fd = conn->socket_fd;
Packit eace71
	pdesc.events = POLLOUT;
Packit eace71
	rc = poll(&pdesc, 1, timeout_ms);
Packit eace71
	if (rc == 0)
Packit eace71
		return 0;
Packit eace71
Packit eace71
	if (rc < 0) {
Packit eace71
		getnameinfo((struct sockaddr *) &conn->saddr,
Packit eace71
			    sizeof(conn->saddr),
Packit eace71
			    conn->host, sizeof(conn->host), serv, sizeof(serv),
Packit eace71
			    NI_NUMERICHOST|NI_NUMERICSERV);
Packit eace71
Packit eace71
		log_error("cannot make connection to %s:%s (%s)",
Packit eace71
			  conn->host, serv, strerror(errno));
Packit eace71
		return rc;
Packit eace71
	}
Packit eace71
Packit eace71
	len = sizeof(int);
Packit eace71
	if (getsockopt(conn->socket_fd, SOL_SOCKET, SO_ERROR,
Packit eace71
			(char *) &rc, &len) < 0) {
Packit eace71
		log_error("getsockopt for connect poll failed");
Packit eace71
		return -1;
Packit eace71
	}
Packit eace71
	if (rc) {
Packit eace71
		getnameinfo((struct sockaddr *) &conn->saddr,
Packit eace71
			    sizeof(conn->saddr),
Packit eace71
			    conn->host, sizeof(conn->host), serv, sizeof(serv),
Packit eace71
			    NI_NUMERICHOST|NI_NUMERICSERV);
Packit eace71
Packit eace71
		log_error("connect to %s:%s failed (%s)",
Packit eace71
			  conn->host, serv, strerror(rc));
Packit eace71
		return -rc;
Packit eace71
	}
Packit eace71
Packit eace71
	len = sizeof(ss);
Packit eace71
	if (log_level > 0 &&
Packit eace71
	    getsockname(conn->socket_fd, (struct sockaddr *) &ss, &len) >= 0) {
Packit eace71
		getnameinfo((struct sockaddr *) &conn->saddr,
Packit eace71
			    sizeof(conn->saddr), conn->host,
Packit eace71
			    sizeof(conn->host), serv, sizeof(serv),
Packit eace71
			    NI_NUMERICHOST|NI_NUMERICSERV);
Packit eace71
Packit eace71
		getnameinfo((struct sockaddr *) &ss, sizeof(ss),
Packit eace71
			     NULL, 0, lserv, sizeof(lserv), NI_NUMERICSERV);
Packit eace71
Packit eace71
		log_debug(1, "connected local port %s to %s:%s",
Packit eace71
			  lserv, conn->host, serv);
Packit eace71
	}
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
void
Packit eace71
iscsi_io_tcp_disconnect(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	struct linger so_linger = { .l_onoff = 1, .l_linger = 0 };
Packit eace71
Packit eace71
	if (conn->socket_fd >= 0) {
Packit eace71
		log_debug(1, "disconnecting conn %p, fd %d", conn,
Packit eace71
			 conn->socket_fd);
Packit eace71
Packit eace71
		/* If the state is not IN_LOGOUT, this isn't a clean shutdown
Packit eace71
		 * and there's some sort of error handling going on. In that
Packit eace71
		 * case, set a 0 SO_LINGER to force an abortive close (RST) and
Packit eace71
		 * free whatever is sitting in the TCP transmit queue. This is
Packit eace71
		 * done to prevent stale data from being sent should the
Packit eace71
		 * network connection be restored before TCP times out.
Packit eace71
		 */
Packit eace71
		if (conn->state != ISCSI_CONN_STATE_IN_LOGOUT) {
Packit eace71
			setsockopt(conn->socket_fd, SOL_SOCKET, SO_LINGER,
Packit eace71
				   &so_linger, sizeof(so_linger));
Packit eace71
		}
Packit eace71
Packit eace71
		close(conn->socket_fd);
Packit eace71
		conn->socket_fd = -1;
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
int
Packit eace71
iscsi_io_connect(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	int rc, ret;
Packit eace71
	struct sigaction action;
Packit eace71
	struct sigaction old;
Packit eace71
Packit eace71
	/* set a timeout, since the socket calls may take a long time to
Packit eace71
	 * timeout on their own
Packit eace71
	 */
Packit eace71
	memset(&action, 0, sizeof (struct sigaction));
Packit eace71
	memset(&old, 0, sizeof (struct sigaction));
Packit eace71
	action.sa_sigaction = NULL;
Packit eace71
	action.sa_flags = 0;
Packit eace71
	action.sa_handler = sigalarm_handler;
Packit eace71
	sigaction(SIGALRM, &action, &old;;
Packit eace71
	timedout = 0;
Packit eace71
	alarm(conn->login_timeout);
Packit eace71
Packit eace71
	/* perform blocking TCP connect operation when no async request
Packit eace71
	 * associated. SendTargets Discovery know to work in such a mode.
Packit eace71
	 */
Packit eace71
	rc = iscsi_io_tcp_connect(conn, 0);
Packit eace71
	if (timedout) {
Packit eace71
		log_error("connect to %s timed out", conn->host);
Packit eace71
			  
Packit eace71
		log_debug(1, "socket %d connect timed out", conn->socket_fd);
Packit eace71
		ret = 0;
Packit eace71
		goto done;
Packit eace71
	} else if (rc < 0) {
Packit eace71
		log_error("cannot make connection to %s: %s",
Packit eace71
			  conn->host, strerror(errno));
Packit eace71
		close(conn->socket_fd);
Packit eace71
		ret = 0;
Packit eace71
		goto done;
Packit eace71
	} else if (log_level > 0) {
Packit eace71
		struct sockaddr_storage ss;
Packit eace71
		char lserv[NI_MAXSERV];
Packit eace71
		char serv[NI_MAXSERV];
Packit eace71
		socklen_t salen = sizeof(ss);
Packit eace71
Packit eace71
		if (getsockname(conn->socket_fd, (struct sockaddr *) &ss,
Packit eace71
				&salen) >= 0) {
Packit eace71
			getnameinfo((struct sockaddr *) &conn->saddr,
Packit eace71
				    sizeof(conn->saddr),
Packit eace71
				    conn->host, sizeof(conn->host), serv,
Packit eace71
				    sizeof(serv), NI_NUMERICHOST|NI_NUMERICSERV);
Packit eace71
Packit eace71
			getnameinfo((struct sockaddr *) &ss,
Packit eace71
				    sizeof(ss),
Packit eace71
				    NULL, 0, lserv, sizeof(lserv),
Packit eace71
				    NI_NUMERICSERV);
Packit eace71
Packit eace71
			log_debug(1, "connected local port %s to %s:%s",
Packit eace71
				  lserv, conn->host, serv);
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	ret = 1;
Packit eace71
Packit eace71
done:
Packit eace71
	alarm(0);
Packit eace71
	sigaction(SIGALRM, &old, NULL);
Packit eace71
	return ret;
Packit eace71
}
Packit eace71
Packit eace71
void
Packit eace71
iscsi_io_disconnect(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	iscsi_io_tcp_disconnect(conn);
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
iscsi_log_text(struct iscsi_hdr *pdu, char *data)
Packit eace71
{
Packit eace71
	int dlength = ntoh24(pdu->dlength);
Packit eace71
	char *text = data;
Packit eace71
	char *end = text + dlength;
Packit eace71
Packit eace71
	while (text && (text < end)) {
Packit eace71
		log_debug(4, ">    %s", text);
Packit eace71
		text += strlen(text);
Packit eace71
		while ((text < end) && (*text == '\0'))
Packit eace71
			text++;
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
int
Packit eace71
iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
Packit eace71
	       int hdr_digest, char *data, int data_digest, int timeout)
Packit eace71
{
Packit eace71
	int rc, ret = 0;
Packit eace71
	char *header = (char *) hdr;
Packit eace71
	char *end;
Packit eace71
	char pad[4];
Packit eace71
	struct iovec vec[3];
Packit eace71
	int pad_bytes;
Packit eace71
	int pdu_length = sizeof (*hdr) + hdr->hlength + ntoh24(hdr->dlength);
Packit eace71
	int remaining;
Packit eace71
	struct sigaction action;
Packit eace71
	struct sigaction old;
Packit eace71
	iscsi_session_t *session = conn->session;
Packit eace71
Packit eace71
	/* set a timeout, since the socket calls may take a long time
Packit eace71
	 * to timeout on their own
Packit eace71
	 */
Packit eace71
	if (!session->use_ipc) {
Packit eace71
		memset(&action, 0, sizeof (struct sigaction));
Packit eace71
		memset(&old, 0, sizeof (struct sigaction));
Packit eace71
		action.sa_sigaction = NULL;
Packit eace71
		action.sa_flags = 0;
Packit eace71
		action.sa_handler = sigalarm_handler;
Packit eace71
		sigaction(SIGALRM, &action, &old;;
Packit eace71
		timedout = 0;
Packit eace71
		alarm(timeout);
Packit eace71
	}
Packit eace71
Packit eace71
	memset(&pad, 0, sizeof (pad));
Packit eace71
	memset(&vec, 0, sizeof (vec));
Packit eace71
Packit eace71
	switch (hdr->opcode & ISCSI_OPCODE_MASK) {
Packit eace71
	case ISCSI_OP_LOGIN:{
Packit eace71
		struct iscsi_login *login_hdr = (struct iscsi_login *) hdr;
Packit eace71
Packit eace71
		log_debug(4, "sending login PDU with current stage "
Packit eace71
			 "%d, next stage %d, transit 0x%x, isid"
Packit eace71
			 " 0x%02x%02x%02x%02x%02x%02x exp_statsn %u",
Packit eace71
			 ISCSI_LOGIN_CURRENT_STAGE(login_hdr->flags),
Packit eace71
			 ISCSI_LOGIN_NEXT_STAGE(login_hdr->flags),
Packit eace71
			 login_hdr->flags & ISCSI_FLAG_LOGIN_TRANSIT,
Packit eace71
			 login_hdr->isid[0], login_hdr->isid[1],
Packit eace71
			 login_hdr->isid[2], login_hdr->isid[3],
Packit eace71
			 login_hdr->isid[4], login_hdr->isid[5],
Packit eace71
			 ntohl(login_hdr->exp_statsn));
Packit eace71
Packit eace71
			iscsi_log_text(hdr, data);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
	case ISCSI_OP_TEXT:{
Packit eace71
		struct iscsi_text *text_hdr = (struct iscsi_text *) hdr;
Packit eace71
Packit eace71
		log_debug(4, "sending text pdu with CmdSN %x, exp_statsn %u",
Packit eace71
			 ntohl(text_hdr->cmdsn), ntohl(text_hdr->cmdsn));
Packit eace71
		iscsi_log_text(hdr, data);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
	case ISCSI_OP_NOOP_OUT:{
Packit eace71
		struct iscsi_nopout *nopout_hdr = (struct iscsi_nopout *) hdr;
Packit eace71
Packit eace71
		log_debug(4, "sending Nop-out pdu with ttt %x, CmdSN %x:",
Packit eace71
			 ntohl(nopout_hdr->ttt), ntohl(nopout_hdr->cmdsn));
Packit eace71
		iscsi_log_text(hdr, data);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
	default:
Packit eace71
		log_debug(4, "sending pdu opcode 0x%x:", hdr->opcode);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
Packit eace71
	/* send the PDU header */
Packit eace71
	header = (char *) hdr;
Packit eace71
	end = header + sizeof (*hdr) + hdr->hlength;
Packit eace71
Packit eace71
	/* send all the data and any padding */
Packit eace71
	if (pdu_length % ISCSI_PAD_LEN)
Packit eace71
		pad_bytes = ISCSI_PAD_LEN - (pdu_length % ISCSI_PAD_LEN);
Packit eace71
	else
Packit eace71
		pad_bytes = 0;
Packit eace71
Packit eace71
	if (session->use_ipc)
Packit eace71
		ipc->send_pdu_begin(session->t->handle, session->id,
Packit eace71
				    conn->id, end - header,
Packit eace71
				    ntoh24(hdr->dlength) + pad_bytes);
Packit eace71
Packit eace71
	while (header < end) {
Packit eace71
		vec[0].iov_base = header;
Packit eace71
		vec[0].iov_len = end - header;
Packit eace71
Packit eace71
		if (!session->use_ipc)
Packit eace71
			rc = writev(conn->socket_fd, vec, 1);
Packit eace71
		else
Packit eace71
			rc = ipc->writev(0, vec, 1);
Packit eace71
		if (timedout) {
Packit eace71
			log_error("socket %d write timed out",
Packit eace71
			       conn->socket_fd);
Packit eace71
			ret = 0;
Packit eace71
			goto done;
Packit eace71
		} else if ((rc <= 0) && (errno != EAGAIN)) {
Packit eace71
			LOG_CONN_FAIL(conn);
Packit eace71
			ret = 0;
Packit eace71
			goto done;
Packit eace71
		} else if (rc > 0) {
Packit eace71
			log_debug(4, "wrote %d bytes of PDU header", rc);
Packit eace71
			header += rc;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	end = data + ntoh24(hdr->dlength);
Packit eace71
	remaining = ntoh24(hdr->dlength) + pad_bytes;
Packit eace71
Packit eace71
	while (remaining > 0) {
Packit eace71
		vec[0].iov_base = data;
Packit eace71
		vec[0].iov_len = end - data;
Packit eace71
		vec[1].iov_base = (void *) &pad;
Packit eace71
		vec[1].iov_len = pad_bytes;
Packit eace71
Packit eace71
		if (!session->use_ipc)
Packit eace71
			rc = writev(conn->socket_fd, vec, 2);
Packit eace71
		else
Packit eace71
			rc = ipc->writev(0, vec, 2);
Packit eace71
		if (timedout) {
Packit eace71
			log_error("socket %d write timed out",
Packit eace71
				  conn->socket_fd);
Packit eace71
			ret = 0;
Packit eace71
			goto done;
Packit eace71
		} else if ((rc <= 0) && (errno != EAGAIN)) {
Packit eace71
			LOG_CONN_FAIL(conn);
Packit eace71
			ret = 0;
Packit eace71
			goto done;
Packit eace71
		} else if (rc > 0) {
Packit eace71
			log_debug(4, "wrote %d bytes of PDU data", rc);
Packit eace71
			remaining -= rc;
Packit eace71
			if (data < end) {
Packit eace71
				data += rc;
Packit eace71
				if (data > end)
Packit eace71
					data = end;
Packit eace71
			}
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	if (session->use_ipc) {
Packit eace71
		if (ipc->send_pdu_end(session->t->handle, session->id,
Packit eace71
				      conn->id, &rc)) {
Packit eace71
			ret = 0;
Packit eace71
			goto done;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	ret = 1;
Packit eace71
Packit eace71
      done:
Packit eace71
	if (!session->use_ipc) {
Packit eace71
		alarm(0);
Packit eace71
		sigaction(SIGALRM, &old, NULL);
Packit eace71
		timedout = 0;
Packit eace71
	}
Packit eace71
	return ret;
Packit eace71
}
Packit eace71
Packit eace71
int
Packit eace71
iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
Packit eace71
	       int hdr_digest, char *data, int max_data_length, int data_digest,
Packit eace71
	       int timeout)
Packit eace71
{
Packit eace71
	uint32_t h_bytes = 0;
Packit eace71
	uint32_t ahs_bytes = 0;
Packit eace71
	uint32_t d_bytes = 0;
Packit eace71
	uint32_t ahslength = 0;
Packit eace71
	uint32_t dlength = 0;
Packit eace71
	uint32_t pad = 0;
Packit eace71
	int rlen = 0;
Packit eace71
	int failed = 0;
Packit eace71
	char *header = (char *) hdr;
Packit eace71
	char *end = data + max_data_length;
Packit eace71
	struct sigaction action;
Packit eace71
	struct sigaction old;
Packit eace71
	iscsi_session_t *session = conn->session;
Packit eace71
Packit eace71
	memset(data, 0, max_data_length);
Packit eace71
Packit eace71
	/* set a timeout, since the socket calls may take a long
Packit eace71
	 * time to timeout on their own
Packit eace71
	 */
Packit eace71
	if (!session->use_ipc) {
Packit eace71
		memset(&action, 0, sizeof (struct sigaction));
Packit eace71
		memset(&old, 0, sizeof (struct sigaction));
Packit eace71
		action.sa_sigaction = NULL;
Packit eace71
		action.sa_flags = 0;
Packit eace71
		action.sa_handler = sigalarm_handler;
Packit eace71
		sigaction(SIGALRM, &action, &old;;
Packit eace71
		timedout = 0;
Packit eace71
		alarm(timeout);
Packit eace71
	} else {
Packit eace71
		failed = ipc->recv_pdu_begin(conn);
Packit eace71
		if (failed == -EAGAIN)
Packit eace71
			return -EAGAIN;
Packit eace71
		else if (failed < 0) {
Packit eace71
			failed = 1;
Packit eace71
			goto done;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	/* read a response header */
Packit eace71
	do {
Packit eace71
		if (!session->use_ipc)
Packit eace71
			rlen = read(conn->socket_fd, header,
Packit eace71
					sizeof (*hdr) - h_bytes);
Packit eace71
		else
Packit eace71
			rlen = ipc->read(header, sizeof (*hdr) - h_bytes);
Packit eace71
		if (timedout) {
Packit eace71
			log_error("socket %d header read timed out",
Packit eace71
				  conn->socket_fd);
Packit eace71
			failed = 1;
Packit eace71
			goto done;
Packit eace71
		} else if (rlen == 0) {
Packit eace71
			LOG_CONN_CLOSED(conn);
Packit eace71
			failed = 1;
Packit eace71
			goto done;
Packit eace71
		} else if ((rlen < 0) && (errno != EAGAIN)) {
Packit eace71
			LOG_CONN_FAIL(conn);
Packit eace71
			failed = 1;
Packit eace71
			goto done;
Packit eace71
		} else if (rlen > 0) {
Packit eace71
			log_debug(4, "read %d bytes of PDU header", rlen);
Packit eace71
			header += rlen;
Packit eace71
			h_bytes += rlen;
Packit eace71
		}
Packit eace71
	} while (h_bytes < sizeof (*hdr));
Packit eace71
Packit eace71
	log_debug(4, "read %d PDU header bytes, opcode 0x%x, dlength %u, "
Packit eace71
		 "data %p, max %u", h_bytes, hdr->opcode & ISCSI_OPCODE_MASK,
Packit eace71
		 ntoh24(hdr->dlength), data, max_data_length);
Packit eace71
Packit eace71
	/* check for additional headers */
Packit eace71
	ahslength = hdr->hlength;	/* already includes padding */
Packit eace71
	if (ahslength) {
Packit eace71
		log_warning("additional header segment length %u not supported",
Packit eace71
		       ahslength);
Packit eace71
		failed = 1;
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	/* read exactly what we expect, plus padding */
Packit eace71
	dlength = hdr->dlength[0] << 16;
Packit eace71
	dlength |= hdr->dlength[1] << 8;
Packit eace71
	dlength |= hdr->dlength[2];
Packit eace71
Packit eace71
	/* if we only expected to receive a header, exit */
Packit eace71
	if (dlength == 0)
Packit eace71
		goto done;
Packit eace71
Packit eace71
	if (data + dlength > end) {
Packit eace71
		log_warning("buffer size %u too small for data length %u",
Packit eace71
		       max_data_length, dlength);
Packit eace71
		failed = 1;
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	/* read the rest into our buffer */
Packit eace71
	d_bytes = 0;
Packit eace71
	while (d_bytes < dlength) {
Packit eace71
		if (!session->use_ipc)
Packit eace71
			rlen = read(conn->socket_fd, data + d_bytes,
Packit eace71
					dlength - d_bytes);
Packit eace71
		else
Packit eace71
			rlen = ipc->read(data + d_bytes, dlength - d_bytes);
Packit eace71
		if (timedout) {
Packit eace71
			log_error("socket %d data read timed out",
Packit eace71
				  conn->socket_fd);
Packit eace71
			failed = 1;
Packit eace71
			goto done;
Packit eace71
		} else if (rlen == 0) {
Packit eace71
			LOG_CONN_CLOSED(conn);
Packit eace71
			failed = 1;
Packit eace71
			goto done;
Packit eace71
		} else if ((rlen < 0 && errno != EAGAIN)) {
Packit eace71
			LOG_CONN_FAIL(conn);
Packit eace71
			failed = 1;
Packit eace71
			goto done;
Packit eace71
		} else if (rlen > 0) {
Packit eace71
			log_debug(4, "read %d bytes of PDU data", rlen);
Packit eace71
			d_bytes += rlen;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	/* handle PDU data padding.
Packit eace71
	 * data is padded in case of kernel_io */
Packit eace71
	pad = dlength % ISCSI_PAD_LEN;
Packit eace71
	if (pad && !session->use_ipc) {
Packit eace71
		int pad_bytes = pad = ISCSI_PAD_LEN - pad;
Packit eace71
		char bytes[ISCSI_PAD_LEN];
Packit eace71
Packit eace71
		while (pad_bytes > 0) {
Packit eace71
			rlen = read(conn->socket_fd, &bytes, pad_bytes);
Packit eace71
			if (timedout) {
Packit eace71
				log_error("socket %d pad read timed out",
Packit eace71
					  conn->socket_fd);
Packit eace71
				failed = 1;
Packit eace71
				goto done;
Packit eace71
			} else if (rlen == 0) {
Packit eace71
				LOG_CONN_CLOSED(conn);
Packit eace71
				failed = 1;
Packit eace71
				goto done;
Packit eace71
			} else if ((rlen < 0 && errno != EAGAIN)) {
Packit eace71
				LOG_CONN_FAIL(conn);
Packit eace71
				failed = 1;
Packit eace71
				goto done;
Packit eace71
			} else if (rlen > 0) {
Packit eace71
				log_debug(4, "read %d pad bytes", rlen);
Packit eace71
				pad_bytes -= rlen;
Packit eace71
			}
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	switch (hdr->opcode) {
Packit eace71
	case ISCSI_OP_TEXT_RSP:
Packit eace71
		log_debug(4, "finished reading text PDU, %u hdr, %u "
Packit eace71
			 "ah, %u data, %u pad",
Packit eace71
			 h_bytes, ahs_bytes, d_bytes, pad);
Packit eace71
		iscsi_log_text(hdr, data);
Packit eace71
		break;
Packit eace71
	case ISCSI_OP_LOGIN_RSP:{
Packit eace71
		struct iscsi_login_rsp *login_rsp =
Packit eace71
			    (struct iscsi_login_rsp *) hdr;
Packit eace71
Packit eace71
		log_debug(4, "finished reading login PDU, %u hdr, "
Packit eace71
			 "%u ah, %u data, %u pad",
Packit eace71
			  h_bytes, ahs_bytes, d_bytes, pad);
Packit eace71
		log_debug(4, "login current stage %d, next stage "
Packit eace71
			 "%d, transit 0x%x",
Packit eace71
			 ISCSI_LOGIN_CURRENT_STAGE(login_rsp->flags),
Packit eace71
			 ISCSI_LOGIN_NEXT_STAGE(login_rsp->flags),
Packit eace71
			 login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT);
Packit eace71
		iscsi_log_text(hdr, data);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
	case ISCSI_OP_ASYNC_EVENT:
Packit eace71
		/* FIXME: log the event info */
Packit eace71
		break;
Packit eace71
	default:
Packit eace71
		break;
Packit eace71
	}
Packit eace71
Packit eace71
done:
Packit eace71
	if (!session->use_ipc) {
Packit eace71
		alarm(0);
Packit eace71
		sigaction(SIGALRM, &old, NULL);
Packit eace71
	} else {
Packit eace71
		/* finalyze receive transaction */
Packit eace71
		if (ipc->recv_pdu_end(conn)) {
Packit eace71
			failed = 1;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	if (timedout || failed) {
Packit eace71
		timedout = 0;
Packit eace71
		return -EIO;
Packit eace71
	}
Packit eace71
Packit eace71
	return h_bytes + ahs_bytes + d_bytes;
Packit eace71
}