/*
* 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
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* iSCSI Software Initiator
*/
#include <sys/bootconf.h>
#include <sys/bootprops.h>
#include "iscsi.h"
/*
* This is a high level description of the default
* iscsi_net transport interfaces. These are used
* messages. In addition there are extensions to send
* and recv iSCSI PDU data.
*
* NOTE: It would be very easy for an iSCSI HBA vendor
* to register their own functions over the top of
* the default interfaces. This would allow an iSCSI
* HBA to use the same iscsiadm management interfaces
* and the Solaris iSCSI session / connection management.
* The current problem with this approach is we only
* allow one one registered transport table. This
* would be pretty easy to correct although will require
* additional CLI changes to manage multiple interfaces.
* If a vendor can present compelling performance data,
* then Sun will be willing to enhance this support for
* multiple interface tables and better CLI management.
*
* The following listing describes the iscsi_net
* entry points:
*
* default implementation creates a sonode
* via the sockfs kernel layer.
* the default implementation this only act
* as a soft binding based on the IP and routing
* tables. It would be preferred if this was
* a hard binding but that is currently not
* possible with Solaris's networking stack.
* establishes the TCP SYN to the peer IP address.
* listens for incoming peer connections.
* accepts incoming peer connections.
* maintaining the resources.
* releases the resources.
*
* getsockopt - Gets socket option for specified socket.
* setsockopt - Sets socket option for specified socket.
*
* The current socket options that are used by the initiator
* are listed below.
*
* TCP_CONN_NOTIFY_THRESHOLD
* TCP_CONN_ABORT_THRESHOLD
* TCP_ABORT_THRESHOLD
* TCP_NODELAY
* SO_RCVBUF
* SO_SNDBUF
*
* iscsi_net_poll - Poll socket interface for a specified amount
* of data. If data not received in timeout
* period fail request.
* iscsi_net_sendmsg - Send message on socket connection
* iscsi_net_recvmsg - Receive message on socket connection
*
* iscsi_net_sendpdu - Send iSCSI PDU on socket connection
* iscsi_net_recvhdr - Receive iSCSI header on socket connection
* iscsi_net_recvdata - Receive iSCSI data on socket connection
*
* The iSCSI interfaces have the below optional flags.
*
* ISCSI_NET_HEADER_DIGEST - The interface should either
* generate or validate the iSCSI
* header digest CRC.
* ISCSI_NET_DATA_DIGESt - The interface should either
* generate or validate the iSCSI
* data digest CRC.
*/
/* global */
/* consts */
/*
* This table is used for quick validation of incoming
* iSCSI PDU opcodes. A value of '0' in the table below
* indicated that the opcode is invalid for an iSCSI
* initiator to receive.
*/
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0x0X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x1X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x2X */ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x3X */ 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
/* 0x4X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x5X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x6X */ 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x7X */ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
/* 0x8X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x9X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xAX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xBX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xCX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xDX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xEX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xFX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
extern int modrootloaded;
extern ib_boot_prop_t *iscsiboot_prop;
/* prototypes */
int *addr_len);
static void iscsi_net_close(void *socket);
static void iscsi_net_set_connect_options(void *socket);
/*
* +--------------------------------------------------------------------+
* | network interface registration functions |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_net_init - initialize network interface
*/
void
{
}
/*
* iscsi_net_fini - release network interface
*/
void
{
}
/*
* iscsi_net_set_connect_options -
*/
static void
{
int ret = 0;
conn_notify_threshold, sizeof (int));
conn_abort_threshold, sizeof (int));
if (ret != 0) {
"TCP_CONN_NOTIFY_THRESHOLD, TCP_CONN_ABORT_THRESHOLD,"
"TCP_ABORT_THRESHOLD, TCP_NODELAY, SO_RCVBUF or SO_SNDBUF");
}
}
/*
* +--------------------------------------------------------------------+
* | register network interfaces |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_net_socket - create socket
*/
static void *
{
int err = 0;
CRED());
if (!err)
return ((void *)socket);
else
return (NULL);
}
/*
* iscsi_net_bind - bind socket to a specific sockaddr
*/
/* ARGSUSED */
static int
{
int error;
return (error);
}
/*
* iscsi_net_connect - connect socket to peer sockaddr
*/
/* ARGSUSED */
static int
{
int rval;
return (rval);
}
/*
* iscsi_net_listen - listen to socket for peer connections
*/
static int
{
}
/*
* iscsi_net_accept - accept peer socket connections
*/
static void *
{
CRED());
return ((void *)listen_ks);
}
/*
* iscsi_net_getsockname -
*/
static int
{
}
/*
* iscsi_net_getsockopt - get value of option on socket
*/
/* ARGSUSED */
static int
{
option_len, CRED()));
}
/*
* iscsi_net_setsockopt - set value for option on socket
*/
static int
void *option_val, int option_len)
{
option_len, CRED()));
}
/*
* iscsi_net_shutdown - shutdown socket connection
*/
static int
{
}
/*
* iscsi_net_close - shutdown socket connection and release resources
*/
static void
{
}
/*
* iscsi_net_poll - poll socket for data
*/
/* ARGSUSED */
static size_t
{
int pflag;
if (get_udatamodel() == DATAMODEL_NONE ||
get_udatamodel() == DATAMODEL_NATIVE) {
/* timeout is millisecond */
return (0);
} else {
/* timeout is millisecond */
return (0);
}
}
/*
* iscsi_net_sendmsg - send message on socket
*/
/* ARGSUSED */
static size_t
{
return (sent);
}
/*
* iscsi_net_recvmsg - receive message on socket
*/
/* ARGSUSED */
static size_t
{
/* Set recv timeout */
if (get_udatamodel() == DATAMODEL_NONE ||
get_udatamodel() == DATAMODEL_NATIVE) {
return (0);
} else {
return (0);
}
/*
* Receive the requested data. Block until all
* data is received or timeout.
*/
return (recv);
}
/*
* iscsi_net_sendpdu - send iscsi pdu on socket
*/
static iscsi_status_t
{
int iovlen = 0;
/*
* Let's send the header first. 'hlength' is in 32-bit
* quantities, so we need to multiply by four to get bytes
*/
iovlen++;
/* Let's transmit the header digest if we have to. */
if ((flags & ISCSI_NET_HEADER_DIGEST) != 0) {
/*
* Converting the calculated CRC via htonl is not
* necessary because iscsi_crc32c calculates
* the value as it expects to be written
*/
iovlen++;
}
/* Let's transmit the data if any. */
if (data_len) {
iovlen++;
pad_len = ((ISCSI_PAD_WORD_LEN -
(ISCSI_PAD_WORD_LEN - 1));
/* Let's transmit the data pad if any. */
if (pad_len) {
pad = 0;
iovlen++;
}
/* Let's transmit the data digest if we have to. */
if ((flags & ISCSI_NET_DATA_DIGEST) != 0) {
/*
* Converting the calculated CRC via htonl is not
* necessary because iscsi_crc32c calculates the
* value as it expects to be written
*/
iovlen++;
}
}
/* Initialization of the message header. */
return (ISCSI_STATUS_TCP_TX_ERROR);
}
return (ISCSI_STATUS_SUCCESS);
}
/*
* iscsi_net_recvhdr - receive iscsi hdr on socket
*/
static iscsi_status_t
{
int total_len = 0;
int adhdr_length = 0;
if (header_length < sizeof (iscsi_hdr_t)) {
return (ISCSI_STATUS_INTERNAL_ERROR);
}
/*
* Receive primary header
*/
if (recv_len != sizeof (iscsi_hdr_t)) {
return (ISCSI_STATUS_TCP_RX_ERROR);
}
/* verify incoming opcode is a valid operation */
"received an unsupported opcode:0x%02x",
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
/*
* Setup receipt of additional header
*/
/* make sure enough space is available for adhdr */
return (ISCSI_STATUS_INTERNAL_ERROR);
}
iovlen++;
}
/*
* Setup receipt of header digest if enabled and connection
* is in full feature mode.
*/
if ((flags & ISCSI_NET_HEADER_DIGEST) != 0) {
iovlen++;
}
/*
* are available
*/
if (iovlen > 1) {
return (ISCSI_STATUS_TCP_RX_ERROR);
}
/*
* Verify header digest if enabled and connection
* is in full feature mode
*/
if ((flags & ISCSI_NET_HEADER_DIGEST) != 0) {
/*
* Converting actual CRC read via ntohl is not
* necessary because iscsi_crc32c calculates the
* value as it expect to be read
*/
if (crc_calculated != crc_actual) {
/* Invalid Header Digest */
"protocol error - encountered a header "
"digest error expected:0x%08x "
"received:0x%08x", socket,
return (ISCSI_STATUS_HEADER_DIGEST_ERROR);
}
}
}
return (ISCSI_STATUS_SUCCESS);
}
/*
* iscsi_net_recvdata - receive iscsi data payload from socket
*/
static iscsi_status_t
{
int total_len = 0;
int dlength = 0;
int pad_len = 0;
/* short hand dlength */
/* verify dlength is valid */
if (dlength > max_data_length) {
"invalid data lengths itt:0x%x received:0x%x "
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
if (dlength) {
/* calculate pad */
pad_len = ((ISCSI_PAD_WORD_LEN -
(ISCSI_PAD_WORD_LEN - 1));
/* setup data iovec */
/* if pad setup pad iovec */
if (pad_len) {
iovlen++;
}
/* setup data digest */
if ((flags & ISCSI_NET_DATA_DIGEST) != 0) {
total_len += sizeof (crc_actual);
iovlen++;
}
return (ISCSI_STATUS_TCP_RX_ERROR);
}
/* verify data digest is present */
if ((flags & ISCSI_NET_DATA_DIGEST) != 0) {
/*
* Converting actual CRC read via ntohl is not
* necessary because iscsi_crc32c calculates the
* value as it expects to be read
*/
if (crc_calculated != crc_actual) {
"protocol error - encountered a data "
"digest error itt:0x%x expected:0x%08x "
"received:0x%08x", socket,
return (ISCSI_STATUS_DATA_DIGEST_ERROR);
}
}
}
return (ISCSI_STATUS_SUCCESS);
}
/*
* Convert a prefix length to a mask.
*/
static iscsi_status_t
{
return (ISCSI_STATUS_INTERNAL_ERROR);
}
while (prefixlen > 0) {
if (prefixlen >= 8) {
*mask = 0xff;
mask++;
continue;
}
prefixlen--;
}
return (ISCSI_STATUS_SUCCESS);
}
{
0, };
0, };
/*
* Assumes only one linkage array element.
*/
if (status != ISCSI_STATUS_SUCCESS) {
return (status);
}
/*
* Set the last mask bits of the ip address with 1, then
* we can get the broadcast address.
*/
/* initialize interface */
int ret = 0;
} else if (defgateway.s_addr == 0) {
/* No default gate way specified */
} else {
}
if (ret != 0) {
" iSCSI boot nic");
return (ISCSI_STATUS_INTERNAL_ERROR);
}
} else {
" iSCSI boot nic");
return (ISCSI_STATUS_INTERNAL_ERROR);
}
return (ISCSI_STATUS_SUCCESS);
} else {
if (status != ISCSI_STATUS_SUCCESS) {
return (status);
}
" iSCSI boot nic");
return (ISCSI_STATUS_INTERNAL_ERROR);
}
} else {
" iSCSI boot nic");
return (ISCSI_STATUS_INTERNAL_ERROR);
}
return (ISCSI_STATUS_SUCCESS);
}
}