|
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 |
}
|