iPXE
Data Structures | Defines | Enumerations | Functions | Variables
iscsi.c File Reference

iSCSI protocol More...

#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <byteswap.h>
#include <ipxe/vsprintf.h>
#include <ipxe/socket.h>
#include <ipxe/iobuf.h>
#include <ipxe/uri.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
#include <ipxe/scsi.h>
#include <ipxe/process.h>
#include <ipxe/uaccess.h>
#include <ipxe/tcpip.h>
#include <ipxe/settings.h>
#include <ipxe/features.h>
#include <ipxe/base16.h>
#include <ipxe/base64.h>
#include <ipxe/ibft.h>
#include <ipxe/iscsi.h>

Go to the source code of this file.

Data Structures

struct  iscsi_string_type
 An iSCSI text string that we want to handle. More...

Defines

#define EACCES_INCORRECT_TARGET_USERNAME   __einfo_error ( EINFO_EACCES_INCORRECT_TARGET_USERNAME )
#define EINFO_EACCES_INCORRECT_TARGET_USERNAME   __einfo_uniqify ( EINFO_EACCES, 0x01, "Incorrect target username" )
#define EACCES_INCORRECT_TARGET_PASSWORD   __einfo_error ( EINFO_EACCES_INCORRECT_TARGET_PASSWORD )
#define EINFO_EACCES_INCORRECT_TARGET_PASSWORD   __einfo_uniqify ( EINFO_EACCES, 0x02, "Incorrect target password" )
#define EINVAL_ROOT_PATH_TOO_SHORT   __einfo_error ( EINFO_EINVAL_ROOT_PATH_TOO_SHORT )
#define EINFO_EINVAL_ROOT_PATH_TOO_SHORT   __einfo_uniqify ( EINFO_EINVAL, 0x01, "Root path too short" )
#define EINVAL_BAD_CREDENTIAL_MIX   __einfo_error ( EINFO_EINVAL_BAD_CREDENTIAL_MIX )
#define EINFO_EINVAL_BAD_CREDENTIAL_MIX   __einfo_uniqify ( EINFO_EINVAL, 0x02, "Bad credential mix" )
#define EINVAL_NO_ROOT_PATH   __einfo_error ( EINFO_EINVAL_NO_ROOT_PATH )
#define EINFO_EINVAL_NO_ROOT_PATH   __einfo_uniqify ( EINFO_EINVAL, 0x03, "No root path" )
#define EINVAL_NO_TARGET_IQN   __einfo_error ( EINFO_EINVAL_NO_TARGET_IQN )
#define EINFO_EINVAL_NO_TARGET_IQN   __einfo_uniqify ( EINFO_EINVAL, 0x04, "No target IQN" )
#define EINVAL_NO_INITIATOR_IQN   __einfo_error ( EINFO_EINVAL_NO_INITIATOR_IQN )
#define EINFO_EINVAL_NO_INITIATOR_IQN   __einfo_uniqify ( EINFO_EINVAL, 0x05, "No initiator IQN" )
#define EIO_TARGET_UNAVAILABLE   __einfo_error ( EINFO_EIO_TARGET_UNAVAILABLE )
#define EINFO_EIO_TARGET_UNAVAILABLE   __einfo_uniqify ( EINFO_EIO, 0x01, "Target not currently operational" )
#define EIO_TARGET_NO_RESOURCES   __einfo_error ( EINFO_EIO_TARGET_NO_RESOURCES )
#define EINFO_EIO_TARGET_NO_RESOURCES   __einfo_uniqify ( EINFO_EIO, 0x02, "Target out of resources" )
#define ENOTSUP_INITIATOR_STATUS   __einfo_error ( EINFO_ENOTSUP_INITIATOR_STATUS )
#define EINFO_ENOTSUP_INITIATOR_STATUS   __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported initiator status" )
#define ENOTSUP_OPCODE   __einfo_error ( EINFO_ENOTSUP_OPCODE )
#define EINFO_ENOTSUP_OPCODE   __einfo_uniqify ( EINFO_ENOTSUP, 0x02, "Unsupported opcode" )
#define ENOTSUP_DISCOVERY   __einfo_error ( EINFO_ENOTSUP_DISCOVERY )
#define EINFO_ENOTSUP_DISCOVERY   __einfo_uniqify ( EINFO_ENOTSUP, 0x03, "Discovery not supported" )
#define ENOTSUP_TARGET_STATUS   __einfo_error ( EINFO_ENOTSUP_TARGET_STATUS )
#define EINFO_ENOTSUP_TARGET_STATUS   __einfo_uniqify ( EINFO_ENOTSUP, 0x04, "Unsupported target status" )
#define EPERM_INITIATOR_AUTHENTICATION   __einfo_error ( EINFO_EPERM_INITIATOR_AUTHENTICATION )
#define EINFO_EPERM_INITIATOR_AUTHENTICATION   __einfo_uniqify ( EINFO_EPERM, 0x01, "Initiator authentication failed" )
#define EPERM_INITIATOR_AUTHORISATION   __einfo_error ( EINFO_EPERM_INITIATOR_AUTHORISATION )
#define EINFO_EPERM_INITIATOR_AUTHORISATION   __einfo_uniqify ( EINFO_EPERM, 0x02, "Initiator not authorised" )
#define EPROTO_INVALID_CHAP_ALGORITHM   __einfo_error ( EINFO_EPROTO_INVALID_CHAP_ALGORITHM )
#define EINFO_EPROTO_INVALID_CHAP_ALGORITHM   __einfo_uniqify ( EINFO_EPROTO, 0x01, "Invalid CHAP algorithm" )
#define EPROTO_INVALID_CHAP_IDENTIFIER   __einfo_error ( EINFO_EPROTO_INVALID_CHAP_IDENTIFIER )
#define EINFO_EPROTO_INVALID_CHAP_IDENTIFIER   __einfo_uniqify ( EINFO_EPROTO, 0x02, "Invalid CHAP identifier" )
#define EPROTO_INVALID_LARGE_BINARY   __einfo_error ( EINFO_EPROTO_INVALID_LARGE_BINARY )
#define EINFO_EPROTO_INVALID_LARGE_BINARY   __einfo_uniqify ( EINFO_EPROTO, 0x03, "Invalid large binary value" )
#define EPROTO_INVALID_CHAP_RESPONSE   __einfo_error ( EINFO_EPROTO_INVALID_CHAP_RESPONSE )
#define EINFO_EPROTO_INVALID_CHAP_RESPONSE   __einfo_uniqify ( EINFO_EPROTO, 0x04, "Invalid CHAP response" )
#define EPROTO_INVALID_KEY_VALUE_PAIR   __einfo_error ( EINFO_EPROTO_INVALID_KEY_VALUE_PAIR )
#define EINFO_EPROTO_INVALID_KEY_VALUE_PAIR   __einfo_uniqify ( EINFO_EPROTO, 0x05, "Invalid key/value pair" )
#define EPROTO_VALUE_REJECTED   __einfo_error ( EINFO_EPROTO_VALUE_REJECTED )
#define EINFO_EPROTO_VALUE_REJECTED   __einfo_uniqify ( EINFO_EPROTO, 0x06, "Parameter rejected" )

Enumerations

enum  iscsi_root_path_component {
  RP_SERVERNAME = 0, RP_PROTOCOL, RP_PORT, RP_LUN,
  RP_TARGETNAME, NUM_RP_COMPONENTS
}
 iSCSI root path components (as per RFC4173) More...

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
 FEATURE (FEATURE_PROTOCOL,"iSCSI", DHCP_EB_FEATURE_ISCSI, 1)
static void iscsi_start_tx (struct iscsi_session *iscsi)
 Start up a new TX PDU.
static void iscsi_start_login (struct iscsi_session *iscsi)
 Build iSCSI login request BHS.
static void iscsi_start_data_out (struct iscsi_session *iscsi, unsigned int datasn)
 Build iSCSI data-out BHS.
static void iscsi_rx_buffered_data_done (struct iscsi_session *iscsi)
 Finish receiving PDU data into buffer.
static int iscsi_rx_buffered_data (struct iscsi_session *iscsi, const void *data, size_t len)
 Receive PDU data into buffer.
static void iscsi_free (struct refcnt *refcnt)
 Free iSCSI session.
static void iscsi_close (struct iscsi_session *iscsi, int rc)
 Shut down iSCSI interface.
static void iscsi_new_itt (struct iscsi_session *iscsi)
 Assign new iSCSI initiator task tag.
static int iscsi_open_connection (struct iscsi_session *iscsi)
 Open iSCSI transport-layer connection.
static void iscsi_close_connection (struct iscsi_session *iscsi, int rc)
 Close iSCSI transport-layer connection.
static void iscsi_scsi_done (struct iscsi_session *iscsi, int rc, struct scsi_rsp *rsp)
 Mark iSCSI SCSI operation as complete.
static void iscsi_start_command (struct iscsi_session *iscsi)
 Build iSCSI SCSI command BHS.
static int iscsi_rx_scsi_response (struct iscsi_session *iscsi, const void *data, size_t len, size_t remaining)
 Receive data segment of an iSCSI SCSI response PDU.
static int iscsi_rx_data_in (struct iscsi_session *iscsi, const void *data, size_t len, size_t remaining)
 Receive data segment of an iSCSI data-in PDU.
static int iscsi_rx_r2t (struct iscsi_session *iscsi, const void *data __unused, size_t len __unused, size_t remaining __unused)
 Receive data segment of an iSCSI R2T PDU.
static void iscsi_data_out_done (struct iscsi_session *iscsi)
 Complete iSCSI data-out PDU transmission.
static int iscsi_tx_data_out (struct iscsi_session *iscsi)
 Send iSCSI data-out data segment.
static int iscsi_rx_nop_in (struct iscsi_session *iscsi, const void *data __unused, size_t len __unused, size_t remaining __unused)
 Receive data segment of an iSCSI NOP-In.
static int iscsi_build_login_request_strings (struct iscsi_session *iscsi, void *data, size_t len)
 Build iSCSI login request strings.
static void iscsi_login_request_done (struct iscsi_session *iscsi)
 Complete iSCSI login request PDU transmission.
static int iscsi_tx_login_request (struct iscsi_session *iscsi)
 Transmit data segment of an iSCSI login request PDU.
static int iscsi_large_binary_decode (const char *encoded, uint8_t *raw, size_t len)
 Decode large binary value.
static int iscsi_handle_targetaddress_value (struct iscsi_session *iscsi, const char *value)
 Handle iSCSI TargetAddress text value.
static int iscsi_handle_authmethod_value (struct iscsi_session *iscsi, const char *value)
 Handle iSCSI AuthMethod text value.
static int iscsi_handle_chap_a_value (struct iscsi_session *iscsi, const char *value)
 Handle iSCSI CHAP_A text value.
static int iscsi_handle_chap_i_value (struct iscsi_session *iscsi, const char *value)
 Handle iSCSI CHAP_I text value.
static int iscsi_handle_chap_c_value (struct iscsi_session *iscsi, const char *value)
 Handle iSCSI CHAP_C text value.
static int iscsi_handle_chap_n_value (struct iscsi_session *iscsi, const char *value)
 Handle iSCSI CHAP_N text value.
static int iscsi_handle_chap_r_value (struct iscsi_session *iscsi, const char *value)
 Handle iSCSI CHAP_R text value.
static int iscsi_handle_string (struct iscsi_session *iscsi, const char *string)
 Handle iSCSI string.
static int iscsi_handle_strings (struct iscsi_session *iscsi, const char *strings, size_t len)
 Handle iSCSI strings.
static int iscsi_status_to_rc (unsigned int status_class, unsigned int status_detail)
 Convert iSCSI response status to return status code.
static int iscsi_rx_login_response (struct iscsi_session *iscsi, const void *data, size_t len, size_t remaining)
 Receive data segment of an iSCSI login response PDU.
static void iscsi_tx_pause (struct iscsi_session *iscsi)
 Pause TX engine.
static void iscsi_tx_resume (struct iscsi_session *iscsi)
 Resume TX engine.
static int iscsi_tx_nothing (struct iscsi_session *iscsi __unused)
 Transmit nothing.
static int iscsi_tx_bhs (struct iscsi_session *iscsi)
 Transmit basic header segment of an iSCSI PDU.
static int iscsi_tx_data (struct iscsi_session *iscsi)
 Transmit data segment of an iSCSI PDU.
static void iscsi_tx_done (struct iscsi_session *iscsi)
 Complete iSCSI PDU transmission.
static void iscsi_tx_step (struct iscsi_session *iscsi)
 Transmit iSCSI PDU.
static int iscsi_rx_bhs (struct iscsi_session *iscsi, const void *data, size_t len, size_t remaining __unused)
 Receive basic header segment of an iSCSI PDU.
static int iscsi_rx_discard (struct iscsi_session *iscsi __unused, const void *data __unused, size_t len __unused, size_t remaining __unused)
 Discard portion of an iSCSI PDU.
static int iscsi_rx_data (struct iscsi_session *iscsi, const void *data, size_t len, size_t remaining)
 Receive data segment of an iSCSI PDU.
static int iscsi_socket_deliver (struct iscsi_session *iscsi, struct io_buffer *iobuf, struct xfer_metadata *meta __unused)
 Receive new data.
static int iscsi_vredirect (struct iscsi_session *iscsi, int type, va_list args)
 Handle redirection event.
static size_t iscsi_scsi_window (struct iscsi_session *iscsi)
 Check iSCSI flow-control window.
static int iscsi_scsi_command (struct iscsi_session *iscsi, struct interface *parent, struct scsi_cmd *command)
 Issue iSCSI SCSI command.
static struct acpi_descriptoriscsi_describe (struct iscsi_session *iscsi)
 Get iSCSI ACPI descriptor.
static void iscsi_command_close (struct iscsi_session *iscsi, int rc)
 Close iSCSI command.
struct setting
initiator_iqn_setting 
__setting (SETTING_SANBOOT_EXTRA, initiator-iqn)
 iSCSI initiator IQN setting
struct setting
reverse_username_setting 
__setting (SETTING_AUTH_EXTRA, reverse-username)
 iSCSI reverse username setting
static int iscsi_parse_root_path (struct iscsi_session *iscsi, const char *root_path)
 Parse iSCSI root path.
static int iscsi_fetch_settings (struct iscsi_session *iscsi)
 Fetch iSCSI settings.
static int iscsi_check_auth (struct iscsi_session *iscsi)
 Check iSCSI authentication details.
static int iscsi_open (struct interface *parent, struct uri *uri)
 Open iSCSI URI.

Variables

static struct iscsi_string_type iscsi_string_types []
 iSCSI text strings that we want to handle
static struct process_descriptor iscsi_process_desc
 iSCSI TX process descriptor
static struct interface_operation iscsi_socket_operations []
 iSCSI socket interface operations
static struct interface_descriptor iscsi_socket_desc
 iSCSI socket interface descriptor
static struct interface_operation iscsi_control_op []
 iSCSI SCSI command-issuing interface operations
static struct interface_descriptor iscsi_control_desc
 iSCSI SCSI command-issuing interface descriptor
static struct interface_operation iscsi_data_op []
 iSCSI SCSI command interface operations
static struct interface_descriptor iscsi_data_desc
 iSCSI SCSI command interface descriptor
struct uri_opener iscsi_uri_opener __uri_opener
 iSCSI URI opener

Detailed Description

iSCSI protocol

Definition in file iscsi.c.


Define Documentation

Definition at line 60 of file iscsi.c.

Referenced by iscsi_handle_chap_n_value().

#define EINFO_EACCES_INCORRECT_TARGET_USERNAME   __einfo_uniqify ( EINFO_EACCES, 0x01, "Incorrect target username" )

Definition at line 62 of file iscsi.c.

Definition at line 64 of file iscsi.c.

Referenced by iscsi_handle_chap_r_value().

#define EINFO_EACCES_INCORRECT_TARGET_PASSWORD   __einfo_uniqify ( EINFO_EACCES, 0x02, "Incorrect target password" )

Definition at line 66 of file iscsi.c.

Definition at line 68 of file iscsi.c.

Referenced by iscsi_parse_root_path().

#define EINFO_EINVAL_ROOT_PATH_TOO_SHORT   __einfo_uniqify ( EINFO_EINVAL, 0x01, "Root path too short" )

Definition at line 70 of file iscsi.c.

Definition at line 72 of file iscsi.c.

Referenced by iscsi_check_auth().

#define EINFO_EINVAL_BAD_CREDENTIAL_MIX   __einfo_uniqify ( EINFO_EINVAL, 0x02, "Bad credential mix" )

Definition at line 74 of file iscsi.c.

Definition at line 76 of file iscsi.c.

Referenced by iscsi_open().

#define EINFO_EINVAL_NO_ROOT_PATH   __einfo_uniqify ( EINFO_EINVAL, 0x03, "No root path" )

Definition at line 78 of file iscsi.c.

Definition at line 80 of file iscsi.c.

Referenced by iscsi_open().

#define EINFO_EINVAL_NO_TARGET_IQN   __einfo_uniqify ( EINFO_EINVAL, 0x04, "No target IQN" )

Definition at line 82 of file iscsi.c.

Definition at line 84 of file iscsi.c.

Referenced by iscsi_fetch_settings().

#define EINFO_EINVAL_NO_INITIATOR_IQN   __einfo_uniqify ( EINFO_EINVAL, 0x05, "No initiator IQN" )

Definition at line 86 of file iscsi.c.

Definition at line 88 of file iscsi.c.

Referenced by iscsi_status_to_rc().

#define EINFO_EIO_TARGET_UNAVAILABLE   __einfo_uniqify ( EINFO_EIO, 0x01, "Target not currently operational" )

Definition at line 90 of file iscsi.c.

Definition at line 92 of file iscsi.c.

Referenced by iscsi_status_to_rc().

#define EINFO_EIO_TARGET_NO_RESOURCES   __einfo_uniqify ( EINFO_EIO, 0x02, "Target out of resources" )

Definition at line 94 of file iscsi.c.

Definition at line 96 of file iscsi.c.

Referenced by iscsi_status_to_rc().

#define EINFO_ENOTSUP_INITIATOR_STATUS   __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported initiator status" )

Definition at line 98 of file iscsi.c.

Definition at line 100 of file iscsi.c.

Referenced by iscsi_rx_data().

#define EINFO_ENOTSUP_OPCODE   __einfo_uniqify ( EINFO_ENOTSUP, 0x02, "Unsupported opcode" )

Definition at line 102 of file iscsi.c.

Definition at line 104 of file iscsi.c.

Referenced by iscsi_open().

#define EINFO_ENOTSUP_DISCOVERY   __einfo_uniqify ( EINFO_ENOTSUP, 0x03, "Discovery not supported" )

Definition at line 106 of file iscsi.c.

Definition at line 108 of file iscsi.c.

Referenced by iscsi_status_to_rc().

#define EINFO_ENOTSUP_TARGET_STATUS   __einfo_uniqify ( EINFO_ENOTSUP, 0x04, "Unsupported target status" )

Definition at line 110 of file iscsi.c.

Definition at line 112 of file iscsi.c.

Referenced by iscsi_status_to_rc().

#define EINFO_EPERM_INITIATOR_AUTHENTICATION   __einfo_uniqify ( EINFO_EPERM, 0x01, "Initiator authentication failed" )

Definition at line 114 of file iscsi.c.

Definition at line 116 of file iscsi.c.

Referenced by iscsi_status_to_rc().

#define EINFO_EPERM_INITIATOR_AUTHORISATION   __einfo_uniqify ( EINFO_EPERM, 0x02, "Initiator not authorised" )

Definition at line 118 of file iscsi.c.

Definition at line 120 of file iscsi.c.

Referenced by iscsi_handle_chap_a_value().

#define EINFO_EPROTO_INVALID_CHAP_ALGORITHM   __einfo_uniqify ( EINFO_EPROTO, 0x01, "Invalid CHAP algorithm" )

Definition at line 122 of file iscsi.c.

Definition at line 124 of file iscsi.c.

Referenced by iscsi_handle_chap_i_value().

#define EINFO_EPROTO_INVALID_CHAP_IDENTIFIER   __einfo_uniqify ( EINFO_EPROTO, 0x02, "Invalid CHAP identifier" )

Definition at line 126 of file iscsi.c.

Definition at line 128 of file iscsi.c.

Referenced by iscsi_large_binary_decode().

#define EINFO_EPROTO_INVALID_LARGE_BINARY   __einfo_uniqify ( EINFO_EPROTO, 0x03, "Invalid large binary value" )

Definition at line 130 of file iscsi.c.

Definition at line 132 of file iscsi.c.

Referenced by iscsi_handle_chap_r_value().

#define EINFO_EPROTO_INVALID_CHAP_RESPONSE   __einfo_uniqify ( EINFO_EPROTO, 0x04, "Invalid CHAP response" )

Definition at line 134 of file iscsi.c.

Definition at line 136 of file iscsi.c.

Referenced by iscsi_handle_string().

#define EINFO_EPROTO_INVALID_KEY_VALUE_PAIR   __einfo_uniqify ( EINFO_EPROTO, 0x05, "Invalid key/value pair" )

Definition at line 138 of file iscsi.c.

Definition at line 140 of file iscsi.c.

Referenced by iscsi_handle_string().

#define EINFO_EPROTO_VALUE_REJECTED   __einfo_uniqify ( EINFO_EPROTO, 0x06, "Parameter rejected" )

Definition at line 142 of file iscsi.c.


Enumeration Type Documentation

iSCSI root path components (as per RFC4173)

Enumerator:
RP_SERVERNAME 
RP_PROTOCOL 
RP_PORT 
RP_LUN 
RP_TARGETNAME 
NUM_RP_COMPONENTS 

Definition at line 1876 of file iscsi.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
FEATURE ( FEATURE_PROTOCOL  ,
"iSCSI"  ,
DHCP_EB_FEATURE_ISCSI  ,
 
)
static void iscsi_start_tx ( struct iscsi_session iscsi) [static]

Start up a new TX PDU.

Parameters:
iscsiiSCSI session

This initiates the process of sending a new PDU. Only one PDU may be in transit at any one time.

Definition at line 1370 of file iscsi.c.

References assert, ISCSI_TX_BHS, ISCSI_TX_IDLE, iscsi_tx_resume(), memset(), iscsi_session::tx_bhs, and iscsi_session::tx_state.

Referenced by iscsi_start_command(), iscsi_start_data_out(), and iscsi_start_login().

                                                           {

        assert ( iscsi->tx_state == ISCSI_TX_IDLE );

        /* Initialise TX BHS */
        memset ( &iscsi->tx_bhs, 0, sizeof ( iscsi->tx_bhs ) );

        /* Flag TX engine to start transmitting */
        iscsi->tx_state = ISCSI_TX_BHS;

        /* Start transmission process */
        iscsi_tx_resume ( iscsi );
}
static void iscsi_start_login ( struct iscsi_session iscsi) [static]

Build iSCSI login request BHS.

Parameters:
iscsiiSCSI session

Definition at line 758 of file iscsi.c.

References assert, iscsi_bhs_login_request::cmdsn, iscsi_session::cmdsn, DBGC, iscsi_bhs_login_request::expstatsn, iscsi_bhs_login_request::flags, htonl, htons, IANA_EN_FEN_SYSTEMS, iscsi_build_login_request_strings(), ISCSI_FLAG_IMMEDIATE, ISCSI_ISID_IANA, ISCSI_LOGIN_CSG_MASK, ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION, ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION, ISCSI_LOGIN_FLAG_TRANSITION, ISCSI_OPCODE_LOGIN_REQUEST, ISCSI_SET_LENGTHS, iscsi_start_tx(), ISCSI_STATUS_PHASE_MASK, iscsi_bhs_login_request::isid_iana_en, iscsi_bhs_login_request::isid_iana_qual, iscsi_session::isid_iana_qual, iscsi_bhs_login_request::itt, iscsi_session::itt, len, iscsi_bhs_login_request::lengths, iscsi_bhs::login_request, NULL, iscsi_bhs_login_request::opcode, request, iscsi_session::statsn, iscsi_session::status, and iscsi_session::tx_bhs.

Referenced by iscsi_open_connection(), and iscsi_rx_login_response().

                                                              {
        struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
        int len;

        switch ( iscsi->status & ISCSI_LOGIN_CSG_MASK ) {
        case ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION:
                DBGC ( iscsi, "iSCSI %p entering security negotiation\n",
                       iscsi );
                break;
        case ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION:
                DBGC ( iscsi, "iSCSI %p entering operational negotiation\n",
                       iscsi );
                break;
        default:
                assert ( 0 );
        }

        /* Construct BHS and initiate transmission */
        iscsi_start_tx ( iscsi );
        request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
                            ISCSI_FLAG_IMMEDIATE );
        request->flags = ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) |
                           ISCSI_LOGIN_FLAG_TRANSITION );
        /* version_max and version_min left as zero */
        len = iscsi_build_login_request_strings ( iscsi, NULL, 0 );
        ISCSI_SET_LENGTHS ( request->lengths, 0, len );
        request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
                                        IANA_EN_FEN_SYSTEMS );
        request->isid_iana_qual = htons ( iscsi->isid_iana_qual );
        /* tsih left as zero */
        request->itt = htonl ( iscsi->itt );
        /* cid left as zero */
        request->cmdsn = htonl ( iscsi->cmdsn );
        request->expstatsn = htonl ( iscsi->statsn + 1 );
}
static void iscsi_start_data_out ( struct iscsi_session iscsi,
unsigned int  datasn 
) [static]

Build iSCSI data-out BHS.

Parameters:
iscsiiSCSI session
datasnData sequence number within the transfer

Definition at line 519 of file iscsi.c.

References iscsi_session::command, iscsi_bhs::data_out, iscsi_bhs_data_out::datasn, DBGC, iscsi_bhs_data_out::expstatsn, iscsi_bhs_data_out::flags, htonl, ISCSI_FLAG_FINAL, ISCSI_OPCODE_DATA_OUT, ISCSI_SET_LENGTHS, iscsi_start_tx(), iscsi_bhs_data_out::itt, iscsi_session::itt, len, iscsi_bhs_data_out::lengths, scsi_cmd::lun, iscsi_bhs_data_out::lun, offset, iscsi_bhs_data_out::offset, iscsi_bhs_data_out::opcode, iscsi_session::statsn, iscsi_session::transfer_len, iscsi_session::transfer_offset, iscsi_bhs_data_out::ttt, iscsi_session::ttt, and iscsi_session::tx_bhs.

Referenced by iscsi_data_out_done(), and iscsi_rx_r2t().

                                                         {
        struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
        unsigned long offset;
        unsigned long remaining;
        unsigned long len;

        /* We always send 512-byte Data-Out PDUs; this removes the
         * need to worry about the target's MaxRecvDataSegmentLength.
         */
        offset = datasn * 512;
        remaining = iscsi->transfer_len - offset;
        len = remaining;
        if ( len > 512 )
                len = 512;

        /* Construct BHS and initiate transmission */
        iscsi_start_tx ( iscsi );
        data_out->opcode = ISCSI_OPCODE_DATA_OUT;
        if ( len == remaining )
                data_out->flags = ( ISCSI_FLAG_FINAL );
        ISCSI_SET_LENGTHS ( data_out->lengths, 0, len );
        data_out->lun = iscsi->command->lun;
        data_out->itt = htonl ( iscsi->itt );
        data_out->ttt = htonl ( iscsi->ttt );
        data_out->expstatsn = htonl ( iscsi->statsn + 1 );
        data_out->datasn = htonl ( datasn );
        data_out->offset = htonl ( iscsi->transfer_offset + offset );
        DBGC ( iscsi, "iSCSI %p start data out DataSN %#x len %#lx\n",
               iscsi, datasn, len );
}
static void iscsi_rx_buffered_data_done ( struct iscsi_session iscsi) [static]

Finish receiving PDU data into buffer.

Parameters:
iscsiiSCSI session

Definition at line 155 of file iscsi.c.

References free, NULL, and iscsi_session::rx_buffer.

Referenced by iscsi_close_connection(), iscsi_free(), iscsi_rx_login_response(), and iscsi_rx_scsi_response().

                                                                        {
        free ( iscsi->rx_buffer );
        iscsi->rx_buffer = NULL;
}
static int iscsi_rx_buffered_data ( struct iscsi_session iscsi,
const void *  data,
size_t  len 
) [static]

Receive PDU data into buffer.

Parameters:
iscsiiSCSI session
dataData to receive
lenLength of data
Return values:
rcReturn status code

This can be used when the RX PDU type handler wishes to buffer up all received data and process the PDU as a single unit. The caller is repsonsible for calling iscsi_rx_buffered_data_done() after processing the data.

Definition at line 173 of file iscsi.c.

References assert, ENOMEM, malloc(), memcpy(), iscsi_session::rx_buffer, iscsi_session::rx_len, and iscsi_session::rx_offset.

Referenced by iscsi_rx_login_response(), and iscsi_rx_scsi_response().

                                                                   {

        /* Allocate buffer on first call */
        if ( ! iscsi->rx_buffer ) {
                iscsi->rx_buffer = malloc ( iscsi->rx_len );
                if ( ! iscsi->rx_buffer )
                        return -ENOMEM;
        }

        /* Copy data to buffer */
        assert ( ( iscsi->rx_offset + len ) <= iscsi->rx_len );
        memcpy ( ( iscsi->rx_buffer + iscsi->rx_offset ), data, len );

        return 0;
}
static void iscsi_free ( struct refcnt refcnt) [static]
static void iscsi_close ( struct iscsi_session iscsi,
int  rc 
) [static]

Shut down iSCSI interface.

Parameters:
iscsiiSCSI session
rcReason for close

Definition at line 218 of file iscsi.c.

References iscsi_session::control, iscsi_session::data, DBGC, ECONNRESET, intfs_shutdown(), NULL, iscsi_session::process, process_del(), iscsi_session::socket, and strerror().

Referenced by iscsi_command_close(), iscsi_open(), iscsi_socket_deliver(), iscsi_tx_step(), and iscsi_vredirect().

                                                                {

        /* A TCP graceful close is still an error from our point of view */
        if ( rc == 0 )
                rc = -ECONNRESET;

        DBGC ( iscsi, "iSCSI %p closed: %s\n", iscsi, strerror ( rc ) );

        /* Stop transmission process */
        process_del ( &iscsi->process );

        /* Shut down interfaces */
        intfs_shutdown ( rc, &iscsi->socket, &iscsi->control, &iscsi->data,
                         NULL );
}
static void iscsi_new_itt ( struct iscsi_session iscsi) [static]

Assign new iSCSI initiator task tag.

Parameters:
iscsiiSCSI session

Definition at line 239 of file iscsi.c.

References ISCSI_TAG_MAGIC, and iscsi_session::itt.

Referenced by iscsi_open_connection(), and iscsi_scsi_command().

                                                          {
        static uint16_t itt_idx;

        iscsi->itt = ( ISCSI_TAG_MAGIC | (++itt_idx) );
}
static int iscsi_open_connection ( struct iscsi_session iscsi) [static]

Open iSCSI transport-layer connection.

Parameters:
iscsiiSCSI session
Return values:
rcReturn status code

Definition at line 251 of file iscsi.c.

References assert, DBGC, htons, iscsi_new_itt(), ISCSI_RX_BHS, iscsi_start_login(), ISCSI_STATUS_AUTH_REVERSE_REQUIRED, ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE, ISCSI_STATUS_STRINGS_SECURITY, ISCSI_TX_IDLE, iscsi_session::isid_iana_qual, memset(), NULL, random(), rc, iscsi_session::rx_offset, iscsi_session::rx_state, SOCK_STREAM, iscsi_session::socket, sockaddr_tcpip::st_port, iscsi_session::status, strerror(), iscsi_session::target_address, iscsi_session::target_port, iscsi_session::target_username, iscsi_session::tx_state, and xfer_open_named_socket().

Referenced by iscsi_open(), and iscsi_rx_login_response().

                                                                 {
        struct sockaddr_tcpip target;
        int rc;

        assert ( iscsi->tx_state == ISCSI_TX_IDLE );
        assert ( iscsi->rx_state == ISCSI_RX_BHS );
        assert ( iscsi->rx_offset == 0 );

        /* Open socket */
        memset ( &target, 0, sizeof ( target ) );
        target.st_port = htons ( iscsi->target_port );
        if ( ( rc = xfer_open_named_socket ( &iscsi->socket, SOCK_STREAM,
                                             ( struct sockaddr * ) &target,
                                             iscsi->target_address,
                                             NULL ) ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p could not open socket: %s\n",
                       iscsi, strerror ( rc ) );
                return rc;
        }

        /* Enter security negotiation phase */
        iscsi->status = ( ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE |
                          ISCSI_STATUS_STRINGS_SECURITY );
        if ( iscsi->target_username )
                iscsi->status |= ISCSI_STATUS_AUTH_REVERSE_REQUIRED;

        /* Assign new ISID */
        iscsi->isid_iana_qual = ( random() & 0xffff );

        /* Assign fresh initiator task tag */
        iscsi_new_itt ( iscsi );

        /* Initiate login */
        iscsi_start_login ( iscsi );

        return 0;
}
static void iscsi_close_connection ( struct iscsi_session iscsi,
int  rc 
) [static]

Close iSCSI transport-layer connection.

Parameters:
iscsiiSCSI session
rcReason for close

Closes the transport-layer connection and resets the session state ready to attempt a fresh login.

Definition at line 298 of file iscsi.c.

References iscsi_session::chap, chap_finish(), intf_restart(), ISCSI_RX_BHS, iscsi_rx_buffered_data_done(), ISCSI_TX_IDLE, iscsi_session::rx_offset, iscsi_session::rx_state, iscsi_session::socket, iscsi_session::status, and iscsi_session::tx_state.

Referenced by iscsi_rx_login_response().

                                                                           {

        /* Close all data transfer interfaces */
        intf_restart ( &iscsi->socket, rc );

        /* Clear connection status */
        iscsi->status = 0;

        /* Reset TX and RX state machines */
        iscsi->tx_state = ISCSI_TX_IDLE;
        iscsi->rx_state = ISCSI_RX_BHS;
        iscsi->rx_offset = 0;

        /* Free any temporary dynamically allocated memory */
        chap_finish ( &iscsi->chap );
        iscsi_rx_buffered_data_done ( iscsi );
}
static void iscsi_scsi_done ( struct iscsi_session iscsi,
int  rc,
struct scsi_rsp rsp 
) [static]

Mark iSCSI SCSI operation as complete.

Parameters:
iscsiiSCSI session
rcReturn status code
rspSCSI response, if any

Note that iscsi_scsi_done() will not close the connection, and must therefore be called only when the internal state machines are in an appropriate state, otherwise bad things may happen on the next call to iscsi_scsi_command(). The general rule is to call iscsi_scsi_done() only at the end of receiving a PDU; at this point the TX and RX engines should both be idle.

Definition at line 330 of file iscsi.c.

References assert, iscsi_session::command, iscsi_session::data, free, intf_restart(), ISCSI_TX_IDLE, iscsi_session::itt, NULL, scsi_response(), and iscsi_session::tx_state.

Referenced by iscsi_rx_data_in(), and iscsi_rx_scsi_response().

                                                     {
        uint32_t itt = iscsi->itt;

        assert ( iscsi->tx_state == ISCSI_TX_IDLE );

        /* Clear command */
        free ( iscsi->command );
        iscsi->command = NULL;

        /* Send SCSI response, if any */
        if ( rsp )
                scsi_response ( &iscsi->data, rsp );

        /* Close SCSI command, if this is still the same command.  (It
         * is possible that the command interface has already been
         * closed as a result of the SCSI response we sent.)
         */
        if ( iscsi->itt == itt )
                intf_restart ( &iscsi->data, rc );
}
static void iscsi_start_command ( struct iscsi_session iscsi) [static]

Build iSCSI SCSI command BHS.

Parameters:
iscsiiSCSI session

We don't currently support bidirectional commands (i.e. with both Data-In and Data-Out segments); these would require providing code to generate an AHS, and there doesn't seem to be any need for it at the moment.

Definition at line 368 of file iscsi.c.

References assert, scsi_cmd::cdb, iscsi_bhs_scsi_command::cdb, iscsi_bhs_scsi_command::cmdsn, iscsi_session::cmdsn, iscsi_session::command, scsi_cmd::data_in, scsi_cmd::data_in_len, scsi_cmd::data_out, scsi_cmd::data_out_len, DBGC2, iscsi_bhs_scsi_command::exp_len, iscsi_bhs_scsi_command::expstatsn, iscsi_bhs_scsi_command::flags, htonl, ISCSI_COMMAND_ATTR_SIMPLE, ISCSI_COMMAND_FLAG_READ, ISCSI_COMMAND_FLAG_WRITE, ISCSI_FLAG_FINAL, ISCSI_OPCODE_SCSI_COMMAND, iscsi_start_tx(), iscsi_bhs_scsi_command::itt, iscsi_session::itt, scsi_cmd::lun, iscsi_bhs_scsi_command::lun, memcpy(), iscsi_bhs_scsi_command::opcode, SCSI_CDB_DATA, SCSI_CDB_FORMAT, iscsi_bhs::scsi_command, iscsi_session::statsn, and iscsi_session::tx_bhs.

Referenced by iscsi_scsi_command().

                                                                {
        struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command;

        assert ( ! ( iscsi->command->data_in && iscsi->command->data_out ) );

        /* Construct BHS and initiate transmission */
        iscsi_start_tx ( iscsi );
        command->opcode = ISCSI_OPCODE_SCSI_COMMAND;
        command->flags = ( ISCSI_FLAG_FINAL |
                           ISCSI_COMMAND_ATTR_SIMPLE );
        if ( iscsi->command->data_in )
                command->flags |= ISCSI_COMMAND_FLAG_READ;
        if ( iscsi->command->data_out )
                command->flags |= ISCSI_COMMAND_FLAG_WRITE;
        /* lengths left as zero */
        memcpy ( &command->lun, &iscsi->command->lun,
                 sizeof ( command->lun ) );
        command->itt = htonl ( iscsi->itt );
        command->exp_len = htonl ( iscsi->command->data_in_len |
                                   iscsi->command->data_out_len );
        command->cmdsn = htonl ( iscsi->cmdsn );
        command->expstatsn = htonl ( iscsi->statsn + 1 );
        memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
        DBGC2 ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n",
                iscsi, SCSI_CDB_DATA ( command->cdb ),
                ( iscsi->command->data_in ? "in" : "out" ),
                ( iscsi->command->data_in ?
                  iscsi->command->data_in_len :
                  iscsi->command->data_out_len ) );
}
static int iscsi_rx_scsi_response ( struct iscsi_session iscsi,
const void *  data,
size_t  len,
size_t  remaining 
) [static]

Receive data segment of an iSCSI SCSI response PDU.

Parameters:
iscsiiSCSI session
dataReceived data
lenLength of received data
remainingData remaining after this data
Return values:
rcReturn status code

Definition at line 408 of file iscsi.c.

References DBGC, EIO, iscsi_bhs_scsi_response::flags, ISCSI_DATA_FLAG_OVERFLOW, ISCSI_DATA_FLAG_UNDERFLOW, ISCSI_DATA_LEN, ISCSI_RESPONSE_COMMAND_COMPLETE, iscsi_rx_buffered_data(), iscsi_rx_buffered_data_done(), iscsi_scsi_done(), iscsi_bhs_scsi_response::lengths, memset(), ntohl, scsi_rsp::overrun, rc, iscsi_bhs_scsi_response::residual_count, iscsi_bhs_scsi_response::response, iscsi_session::rx_bhs, iscsi_session::rx_buffer, scsi_parse_sense(), iscsi_bhs::scsi_response, scsi_rsp::sense, iscsi_bhs_scsi_response::status, scsi_rsp::status, and strerror().

Referenced by iscsi_rx_data().

                                                       {
        struct iscsi_bhs_scsi_response *response
                = &iscsi->rx_bhs.scsi_response;
        struct scsi_rsp rsp;
        uint32_t residual_count;
        size_t data_len;
        int rc;

        /* Buffer up the PDU data */
        if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p could not buffer SCSI response: %s\n",
                       iscsi, strerror ( rc ) );
                return rc;
        }
        if ( remaining )
                return 0;

        /* Parse SCSI response and discard buffer */
        memset ( &rsp, 0, sizeof ( rsp ) );
        rsp.status = response->status;
        residual_count = ntohl ( response->residual_count );
        if ( response->flags & ISCSI_DATA_FLAG_OVERFLOW ) {
                rsp.overrun = residual_count;
        } else if ( response->flags & ISCSI_DATA_FLAG_UNDERFLOW ) {
                rsp.overrun = -(residual_count);
        }
        data_len = ISCSI_DATA_LEN ( response->lengths );
        if ( data_len ) {
                scsi_parse_sense ( ( iscsi->rx_buffer + 2 ), ( data_len - 2 ),
                                   &rsp.sense );
        }
        iscsi_rx_buffered_data_done ( iscsi );

        /* Check for errors */
        if ( response->response != ISCSI_RESPONSE_COMMAND_COMPLETE )
                return -EIO;

        /* Mark as completed */
        iscsi_scsi_done ( iscsi, 0, &rsp );
        return 0;
}
static int iscsi_rx_data_in ( struct iscsi_session iscsi,
const void *  data,
size_t  len,
size_t  remaining 
) [static]

Receive data segment of an iSCSI data-in PDU.

Parameters:
iscsiiSCSI session
dataReceived data
lenLength of received data
remainingData remaining after this data
Return values:
rcReturn status code

Definition at line 461 of file iscsi.c.

References assert, iscsi_session::command, copy_to_user(), scsi_cmd::data_in, iscsi_bhs::data_in, scsi_cmd::data_in_len, iscsi_bhs_data_in::flags, ISCSI_DATA_FLAG_STATUS, ISCSI_FLAG_FINAL, iscsi_scsi_done(), ntohl, NULL, offset, iscsi_bhs_data_in::offset, iscsi_session::rx_bhs, and iscsi_session::rx_offset.

Referenced by iscsi_rx_data().

                                                 {
        struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in;
        unsigned long offset;

        /* Copy data to data-in buffer */
        offset = ntohl ( data_in->offset ) + iscsi->rx_offset;
        assert ( iscsi->command != NULL );
        assert ( iscsi->command->data_in );
        assert ( ( offset + len ) <= iscsi->command->data_in_len );
        copy_to_user ( iscsi->command->data_in, offset, data, len );

        /* Wait for whole SCSI response to arrive */
        if ( remaining )
                return 0;

        /* Mark as completed if status is present */
        if ( data_in->flags & ISCSI_DATA_FLAG_STATUS ) {
                assert ( ( offset + len ) == iscsi->command->data_in_len );
                assert ( data_in->flags & ISCSI_FLAG_FINAL );
                /* iSCSI cannot return an error status via a data-in */
                iscsi_scsi_done ( iscsi, 0, NULL );
        }

        return 0;
}
static int iscsi_rx_r2t ( struct iscsi_session iscsi,
const void *data  __unused,
size_t len  __unused,
size_t remaining  __unused 
) [static]

Receive data segment of an iSCSI R2T PDU.

Parameters:
iscsiiSCSI session
dataReceived data
lenLength of received data
remainingData remaining after this data
Return values:
rcReturn status code

Definition at line 498 of file iscsi.c.

References iscsi_start_data_out(), iscsi_bhs_r2t::len, ntohl, iscsi_bhs_r2t::offset, iscsi_bhs::r2t, iscsi_session::rx_bhs, iscsi_session::transfer_len, iscsi_session::transfer_offset, iscsi_bhs_r2t::ttt, and iscsi_session::ttt.

Referenced by iscsi_rx_data().

                                                      {
        struct iscsi_bhs_r2t *r2t = &iscsi->rx_bhs.r2t;

        /* Record transfer parameters and trigger first data-out */
        iscsi->ttt = ntohl ( r2t->ttt );
        iscsi->transfer_offset = ntohl ( r2t->offset );
        iscsi->transfer_len = ntohl ( r2t->len );
        iscsi_start_data_out ( iscsi, 0 );

        return 0;
}
static void iscsi_data_out_done ( struct iscsi_session iscsi) [static]

Complete iSCSI data-out PDU transmission.

Parameters:
iscsiiSCSI session

Definition at line 557 of file iscsi.c.

References iscsi_bhs::data_out, iscsi_bhs_data_out::datasn, iscsi_bhs_data_out::flags, ISCSI_FLAG_FINAL, iscsi_start_data_out(), ntohl, and iscsi_session::tx_bhs.

Referenced by iscsi_tx_done().

                                                                {
        struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;

        /* If we haven't reached the end of the sequence, start
         * sending the next data-out PDU.
         */
        if ( ! ( data_out->flags & ISCSI_FLAG_FINAL ) )
                iscsi_start_data_out ( iscsi, ntohl ( data_out->datasn ) + 1 );
}
static int iscsi_tx_data_out ( struct iscsi_session iscsi) [static]

Send iSCSI data-out data segment.

Parameters:
iscsiiSCSI session
Return values:
rcReturn status code

Definition at line 573 of file iscsi.c.

References assert, iscsi_session::command, copy_from_user(), scsi_cmd::data_out, iscsi_bhs::data_out, scsi_cmd::data_out_len, ENOMEM, iob_put, ISCSI_DATA_LEN, ISCSI_DATA_PAD_LEN, len, iscsi_bhs_data_out::lengths, memset(), ntohl, NULL, offset, iscsi_bhs_data_out::offset, iscsi_session::socket, iscsi_session::tx_bhs, xfer_alloc_iob(), and xfer_deliver_iob().

Referenced by iscsi_tx_data().

                                                             {
        struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
        struct io_buffer *iobuf;
        unsigned long offset;
        size_t len;
        size_t pad_len;

        offset = ntohl ( data_out->offset );
        len = ISCSI_DATA_LEN ( data_out->lengths );
        pad_len = ISCSI_DATA_PAD_LEN ( data_out->lengths );

        assert ( iscsi->command != NULL );
        assert ( iscsi->command->data_out );
        assert ( ( offset + len ) <= iscsi->command->data_out_len );

        iobuf = xfer_alloc_iob ( &iscsi->socket, ( len + pad_len ) );
        if ( ! iobuf )
                return -ENOMEM;
        
        copy_from_user ( iob_put ( iobuf, len ),
                         iscsi->command->data_out, offset, len );
        memset ( iob_put ( iobuf, pad_len ), 0, pad_len );

        return xfer_deliver_iob ( &iscsi->socket, iobuf );
}
static int iscsi_rx_nop_in ( struct iscsi_session iscsi,
const void *data  __unused,
size_t len  __unused,
size_t remaining  __unused 
) [static]

Receive data segment of an iSCSI NOP-In.

Parameters:
iscsiiSCSI session
dataReceived data
lenLength of received data
remainingData remaining after this data
Return values:
rcReturn status code

Definition at line 608 of file iscsi.c.

References DBGC, DBGC2, htonl, ISCSI_TAG_RESERVED, iscsi_bhs::nop_in, ntohl, iscsi_session::rx_bhs, and iscsi_nop_in::ttt.

Referenced by iscsi_rx_data().

                                                         {
        struct iscsi_nop_in *nop_in = &iscsi->rx_bhs.nop_in;

        DBGC2 ( iscsi, "iSCSI %p received NOP-In\n", iscsi );

        /* We don't currently have the ability to respond to NOP-Ins
         * sent as ping requests, but we can happily accept NOP-Ins
         * sent merely to update CmdSN.
         */
        if ( nop_in->ttt == htonl ( ISCSI_TAG_RESERVED ) )
                return 0;

        /* Ignore any other NOP-Ins.  The target may eventually
         * disconnect us for failing to respond, but this minimises
         * unnecessary connection closures.
         */
        DBGC ( iscsi, "iSCSI %p received unsupported NOP-In with TTT %08x\n",
               iscsi, ntohl ( nop_in->ttt ) );
        return 0;
}
static int iscsi_build_login_request_strings ( struct iscsi_session iscsi,
void *  data,
size_t  len 
) [static]

Build iSCSI login request strings.

Parameters:
iscsiiSCSI session

These are the initial set of strings sent in the first login request PDU. We want the following settings:

HeaderDigest=None DataDigest=None MaxConnections=1 (irrelevant; we make only one connection anyway) [4] InitialR2T=Yes [1] ImmediateData=No (irrelevant; we never send immediate data) [4] MaxRecvDataSegmentLength=8192 (default; we don't care) [3] MaxBurstLength=262144 (default; we don't care) [3] FirstBurstLength=65536 (irrelevant due to other settings) [5] DefaultTime2Wait=0 [2] DefaultTime2Retain=0 [2] MaxOutstandingR2T=1 DataPDUInOrder=Yes DataSequenceInOrder=Yes ErrorRecoveryLevel=0

[1] InitialR2T has an OR resolution function, so the target may force us to use it. We therefore simplify our logic by always using it.

[2] These ensure that we can safely start a new task once we have reconnected after a failure, without having to manually tidy up after the old one.

[3] We are quite happy to use the RFC-defined default values for these parameters, but some targets (notably OpenSolaris) incorrectly assume a default value of zero, so we explicitly specify the default values.

[4] We are quite happy to use the RFC-defined default values for these parameters, but some targets (notably a QNAP TS-639Pro) fail unless they are supplied, so we explicitly specify the default values.

[5] FirstBurstLength is defined to be irrelevant since we already force InitialR2T=Yes and ImmediateData=No, but some targets (notably LIO as of kernel 4.11) fail unless it is specified, so we explicitly specify the default value.

Definition at line 683 of file iscsi.c.

References assert, base16_encoded_len(), iscsi_session::chap, iscsi_session::chap_challenge, iscsi_session::initiator_iqn, iscsi_session::initiator_username, ISCSI_STATUS_STRINGS_CHAP_ALGORITHM, ISCSI_STATUS_STRINGS_CHAP_CHALLENGE, ISCSI_STATUS_STRINGS_CHAP_RESPONSE, ISCSI_STATUS_STRINGS_OPERATIONAL, ISCSI_STATUS_STRINGS_SECURITY, NULL, chap_response::response, chap_response::response_len, ssnprintf(), iscsi_session::status, iscsi_session::target_iqn, and iscsi_session::target_username.

Referenced by iscsi_start_login(), and iscsi_tx_login_request().

                                                                        {
        unsigned int used = 0;
        const char *auth_method;

        if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
                /* Default to allowing no authentication */
                auth_method = "None";
                /* If we have a credential to supply, permit CHAP */
                if ( iscsi->initiator_username )
                        auth_method = "CHAP,None";
                /* If we have a credential to check, force CHAP */
                if ( iscsi->target_username )
                        auth_method = "CHAP";
                used += ssnprintf ( data + used, len - used,
                                    "InitiatorName=%s%c"
                                    "TargetName=%s%c"
                                    "SessionType=Normal%c"
                                    "AuthMethod=%s%c",
                                    iscsi->initiator_iqn, 0,
                                    iscsi->target_iqn, 0, 0,
                                    auth_method, 0 );
        }

        if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) {
                used += ssnprintf ( data + used, len - used, "CHAP_A=5%c", 0 );
        }
        
        if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) {
                char buf[ base16_encoded_len ( iscsi->chap.response_len ) + 1 ];
                assert ( iscsi->initiator_username != NULL );
                base16_encode ( iscsi->chap.response, iscsi->chap.response_len,
                                buf, sizeof ( buf ) );
                used += ssnprintf ( data + used, len - used,
                                    "CHAP_N=%s%cCHAP_R=0x%s%c",
                                    iscsi->initiator_username, 0, buf, 0 );
        }

        if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_CHALLENGE ) ) {
                size_t challenge_len = ( sizeof ( iscsi->chap_challenge ) - 1 );
                char buf[ base16_encoded_len ( challenge_len ) + 1 ];
                base16_encode ( ( iscsi->chap_challenge + 1 ), challenge_len,
                                buf, sizeof ( buf ) );
                used += ssnprintf ( data + used, len - used,
                                    "CHAP_I=%d%cCHAP_C=0x%s%c",
                                    iscsi->chap_challenge[0], 0, buf, 0 );
        }

        if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
                used += ssnprintf ( data + used, len - used,
                                    "HeaderDigest=None%c"
                                    "DataDigest=None%c"
                                    "MaxConnections=1%c"
                                    "InitialR2T=Yes%c"
                                    "ImmediateData=No%c"
                                    "MaxRecvDataSegmentLength=8192%c"
                                    "MaxBurstLength=262144%c"
                                    "FirstBurstLength=65536%c"
                                    "DefaultTime2Wait=0%c"
                                    "DefaultTime2Retain=0%c"
                                    "MaxOutstandingR2T=1%c"
                                    "DataPDUInOrder=Yes%c"
                                    "DataSequenceInOrder=Yes%c"
                                    "ErrorRecoveryLevel=0%c",
                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
        }

        return used;
}
static void iscsi_login_request_done ( struct iscsi_session iscsi) [static]

Complete iSCSI login request PDU transmission.

Parameters:
iscsiiSCSI session

Definition at line 800 of file iscsi.c.

References iscsi_session::chap, chap_finish(), ISCSI_STATUS_STRINGS_MASK, and iscsi_session::status.

Referenced by iscsi_tx_done().

                                                                     {

        /* Clear any "strings to send" flags */
        iscsi->status &= ~ISCSI_STATUS_STRINGS_MASK;

        /* Free any dynamically allocated storage used for login */
        chap_finish ( &iscsi->chap );
}
static int iscsi_tx_login_request ( struct iscsi_session iscsi) [static]

Transmit data segment of an iSCSI login request PDU.

Parameters:
iscsiiSCSI session
Return values:
rcReturn status code

For login requests, the data segment consists of the login strings.

Definition at line 817 of file iscsi.c.

References io_buffer::data, ENOMEM, iob_put, iscsi_build_login_request_strings(), ISCSI_DATA_LEN, ISCSI_DATA_PAD_LEN, len, iscsi_bhs_login_request::lengths, iscsi_bhs::login_request, memset(), request, iscsi_session::socket, iscsi_session::tx_bhs, xfer_alloc_iob(), and xfer_deliver_iob().

Referenced by iscsi_tx_data().

                                                                  {
        struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
        struct io_buffer *iobuf;
        size_t len;
        size_t pad_len;

        len = ISCSI_DATA_LEN ( request->lengths );
        pad_len = ISCSI_DATA_PAD_LEN ( request->lengths );
        iobuf = xfer_alloc_iob ( &iscsi->socket, ( len + pad_len ) );
        if ( ! iobuf )
                return -ENOMEM;
        iob_put ( iobuf, len );
        iscsi_build_login_request_strings ( iscsi, iobuf->data, len );
        memset ( iob_put ( iobuf, pad_len ), 0, pad_len );

        return xfer_deliver_iob ( &iscsi->socket, iobuf );
}
static int iscsi_large_binary_decode ( const char *  encoded,
uint8_t raw,
size_t  len 
) [static]

Decode large binary value.

Parameters:
encodedEncoded large binary value
rawRaw data
lenLength of data buffer
Return values:
lenLength of raw data, or negative error

Definition at line 843 of file iscsi.c.

References base64_decode(), EPROTO_INVALID_LARGE_BINARY, and tolower().

Referenced by iscsi_handle_chap_c_value(), and iscsi_handle_chap_r_value().

                                                    {

        /* Check for initial '0x' or '0b' and decode as appropriate */
        if ( *(encoded++) == '0' ) {
                switch ( tolower ( *(encoded++) ) ) {
                case 'x' :
                        return base16_decode ( encoded, raw, len );
                case 'b' :
                        return base64_decode ( encoded, raw, len );
                }
        }

        return -EPROTO_INVALID_LARGE_BINARY;
}
static int iscsi_handle_targetaddress_value ( struct iscsi_session iscsi,
const char *  value 
) [static]

Handle iSCSI TargetAddress text value.

Parameters:
iscsiiSCSI session
valueTargetAddress value
Return values:
rcReturn status code

Definition at line 866 of file iscsi.c.

References DBGC, ENOMEM, free, htons, ISCSI_PORT, NULL, strchr(), strdup(), strtoul(), iscsi_session::target_address, and iscsi_session::target_port.

                                                                  {
        char *separator;

        DBGC ( iscsi, "iSCSI %p will redirect to %s\n", iscsi, value );

        /* Replace target address */
        free ( iscsi->target_address );
        iscsi->target_address = strdup ( value );
        if ( ! iscsi->target_address )
                return -ENOMEM;

        /* Replace target port */
        iscsi->target_port = htons ( ISCSI_PORT );
        separator = strchr ( iscsi->target_address, ':' );
        if ( separator ) {
                *separator = '\0';
                iscsi->target_port = strtoul ( ( separator + 1 ), NULL, 0 );
        }

        return 0;
}
static int iscsi_handle_authmethod_value ( struct iscsi_session iscsi,
const char *  value 
) [static]

Handle iSCSI AuthMethod text value.

Parameters:
iscsiiSCSI session
valueAuthMethod value
Return values:
rcReturn status code

Definition at line 896 of file iscsi.c.

References DBGC, ISCSI_STATUS_AUTH_FORWARD_REQUIRED, ISCSI_STATUS_STRINGS_CHAP_ALGORITHM, iscsi_session::status, and strcmp().

                                                               {

        /* If server requests CHAP, send the CHAP_A string */
        if ( strcmp ( value, "CHAP" ) == 0 ) {
                DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n",
                       iscsi );
                iscsi->status |= ( ISCSI_STATUS_STRINGS_CHAP_ALGORITHM |
                                   ISCSI_STATUS_AUTH_FORWARD_REQUIRED );
        }

        return 0;
}
static int iscsi_handle_chap_a_value ( struct iscsi_session iscsi,
const char *  value 
) [static]

Handle iSCSI CHAP_A text value.

Parameters:
iscsiiSCSI session
valueCHAP_A value
Return values:
rcReturn status code

Definition at line 917 of file iscsi.c.

References DBGC, EPROTO_INVALID_CHAP_ALGORITHM, and strcmp().

                                                           {

        /* We only ever offer "5" (i.e. MD5) as an algorithm, so if
         * the server responds with anything else it is a protocol
         * violation.
         */
        if ( strcmp ( value, "5" ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p got invalid CHAP algorithm \"%s\"\n",
                       iscsi, value );
                return -EPROTO_INVALID_CHAP_ALGORITHM;
        }

        return 0;
}
static int iscsi_handle_chap_i_value ( struct iscsi_session iscsi,
const char *  value 
) [static]

Handle iSCSI CHAP_I text value.

Parameters:
iscsiiSCSI session
valueCHAP_I value
Return values:
rcReturn status code

Definition at line 940 of file iscsi.c.

References iscsi_session::chap, chap_finish(), chap_init(), chap_set_identifier(), chap_update(), DBGC, EPROTO_INVALID_CHAP_IDENTIFIER, iscsi_session::initiator_password, md5_algorithm, rc, strerror(), strlen(), and strtoul().

                                                           {
        unsigned int identifier;
        char *endp;
        int rc;

        /* The CHAP identifier is an integer value */
        identifier = strtoul ( value, &endp, 0 );
        if ( *endp != '\0' ) {
                DBGC ( iscsi, "iSCSI %p saw invalid CHAP identifier \"%s\"\n",
                       iscsi, value );
                return -EPROTO_INVALID_CHAP_IDENTIFIER;
        }

        /* Prepare for CHAP with MD5 */
        chap_finish ( &iscsi->chap );
        if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
                       iscsi, strerror ( rc ) );
                return rc;
        }

        /* Identifier and secret are the first two components of the
         * challenge.
         */
        chap_set_identifier ( &iscsi->chap, identifier );
        if ( iscsi->initiator_password ) {
                chap_update ( &iscsi->chap, iscsi->initiator_password,
                              strlen ( iscsi->initiator_password ) );
        }

        return 0;
}
static int iscsi_handle_chap_c_value ( struct iscsi_session iscsi,
const char *  value 
) [static]

Handle iSCSI CHAP_C text value.

Parameters:
iscsiiSCSI session
valueCHAP_C value
Return values:
rcReturn status code

Definition at line 981 of file iscsi.c.

References iscsi_session::chap, iscsi_session::chap_challenge, chap_respond(), chap_update(), DBGC, iscsi_large_binary_decode(), ISCSI_STATUS_STRINGS_CHAP_CHALLENGE, ISCSI_STATUS_STRINGS_CHAP_RESPONSE, len, random(), rc, iscsi_session::status, strerror(), strlen(), and iscsi_session::target_username.

                                                           {
        uint8_t buf[ strlen ( value ) ]; /* Decoding never expands data */
        unsigned int i;
        int len;
        int rc;

        /* Process challenge */
        len = iscsi_large_binary_decode ( value, buf, sizeof ( buf ) );
        if ( len < 0 ) {
                rc = len;
                DBGC ( iscsi, "iSCSI %p invalid CHAP challenge \"%s\": %s\n",
                       iscsi, value, strerror ( rc ) );
                return rc;
        }
        chap_update ( &iscsi->chap, buf, len );

        /* Build CHAP response */
        DBGC ( iscsi, "iSCSI %p sending CHAP response\n", iscsi );
        chap_respond ( &iscsi->chap );
        iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;

        /* Send CHAP challenge, if applicable */
        if ( iscsi->target_username ) {
                iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_CHALLENGE;
                /* Generate CHAP challenge data */
                for ( i = 0 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) {
                        iscsi->chap_challenge[i] = random();
                }
        }

        return 0;
}
static int iscsi_handle_chap_n_value ( struct iscsi_session iscsi,
const char *  value 
) [static]

Handle iSCSI CHAP_N text value.

Parameters:
iscsiiSCSI session
valueCHAP_N value
Return values:
rcReturn status code

Definition at line 1022 of file iscsi.c.

References DBGC, EACCES_INCORRECT_TARGET_USERNAME, strcmp(), and iscsi_session::target_username.

                                                           {

        /* The target username isn't actually involved at any point in
         * the authentication process; it merely serves to identify
         * which password the target is using to generate the CHAP
         * response.  We unnecessarily verify that the username is as
         * expected, in order to provide mildly helpful diagnostics if
         * the target is supplying the wrong username/password
         * combination.
         */
        if ( iscsi->target_username &&
             ( strcmp ( iscsi->target_username, value ) != 0 ) ) {
                DBGC ( iscsi, "iSCSI %p target username \"%s\" incorrect "
                       "(wanted \"%s\")\n",
                       iscsi, value, iscsi->target_username );
                return -EACCES_INCORRECT_TARGET_USERNAME;
        }

        return 0;
}
static int iscsi_handle_chap_r_value ( struct iscsi_session iscsi,
const char *  value 
) [static]

Handle iSCSI CHAP_R text value.

Parameters:
iscsiiSCSI session
valueCHAP_R value
Return values:
rcReturn status code

Definition at line 1051 of file iscsi.c.

References iscsi_session::chap, iscsi_session::chap_challenge, chap_finish(), chap_init(), chap_respond(), chap_set_identifier(), chap_update(), DBGC, EACCES_INCORRECT_TARGET_PASSWORD, EPROTO_INVALID_CHAP_RESPONSE, iscsi_large_binary_decode(), ISCSI_STATUS_AUTH_REVERSE_OK, len, md5_algorithm, memcmp(), rc, chap_response::response, chap_response::response_len, iscsi_session::status, strerror(), strlen(), and iscsi_session::target_password.

                                                           {
        uint8_t buf[ strlen ( value ) ]; /* Decoding never expands data */
        int len;
        int rc;

        /* Generate CHAP response for verification */
        chap_finish ( &iscsi->chap );
        if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
                       iscsi, strerror ( rc ) );
                return rc;
        }
        chap_set_identifier ( &iscsi->chap, iscsi->chap_challenge[0] );
        if ( iscsi->target_password ) {
                chap_update ( &iscsi->chap, iscsi->target_password,
                              strlen ( iscsi->target_password ) );
        }
        chap_update ( &iscsi->chap, &iscsi->chap_challenge[1],
                      ( sizeof ( iscsi->chap_challenge ) - 1 ) );
        chap_respond ( &iscsi->chap );

        /* Process response */
        len = iscsi_large_binary_decode ( value, buf, sizeof ( buf ) );
        if ( len < 0 ) {
                rc = len;
                DBGC ( iscsi, "iSCSI %p invalid CHAP response \"%s\": %s\n",
                       iscsi, value, strerror ( rc ) );
                return rc;
        }

        /* Check CHAP response */
        if ( len != ( int ) iscsi->chap.response_len ) {
                DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n",
                       iscsi );
                return -EPROTO_INVALID_CHAP_RESPONSE;
        }
        if ( memcmp ( buf, iscsi->chap.response, len ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p incorrect CHAP response \"%s\"\n",
                       iscsi, value );
                return -EACCES_INCORRECT_TARGET_PASSWORD;
        }

        /* Mark session as authenticated */
        iscsi->status |= ISCSI_STATUS_AUTH_REVERSE_OK;

        return 0;
}
static int iscsi_handle_string ( struct iscsi_session iscsi,
const char *  string 
) [static]

Handle iSCSI string.

Parameters:
iscsiiSCSI session
stringiSCSI string (in "key=value" format)
Return values:
rcReturn status code

Definition at line 1136 of file iscsi.c.

References DBGC, EPROTO_INVALID_KEY_VALUE_PAIR, EPROTO_VALUE_REJECTED, iscsi_string_type::handle, iscsi_string_type::key, rc, strchr(), strcmp(), strerror(), string, strncmp(), type, and value.

Referenced by iscsi_handle_strings().

                                                      {
        struct iscsi_string_type *type;
        const char *separator;
        const char *value;
        size_t key_len;
        int rc;

        /* Find separator */
        separator = strchr ( string, '=' );
        if ( ! separator ) {
                DBGC ( iscsi, "iSCSI %p malformed string %s\n",
                       iscsi, string );
                return -EPROTO_INVALID_KEY_VALUE_PAIR;
        }
        key_len = ( separator - string );
        value = ( separator + 1 );

        /* Check for rejections.  Since we send only non-rejectable
         * values, any rejection is a fatal protocol error.
         */
        if ( strcmp ( value, "Reject" ) == 0 ) {
                DBGC ( iscsi, "iSCSI %p rejection: %s\n", iscsi, string );
                return -EPROTO_VALUE_REJECTED;
        }

        /* Handle key/value pair */
        for ( type = iscsi_string_types ; type->key ; type++ ) {
                if ( strncmp ( string, type->key, key_len ) != 0 )
                        continue;
                DBGC ( iscsi, "iSCSI %p handling %s\n", iscsi, string );
                if ( ( rc = type->handle ( iscsi, value ) ) != 0 ) {
                        DBGC ( iscsi, "iSCSI %p could not handle %s: %s\n",
                               iscsi, string, strerror ( rc ) );
                        return rc;
                }
                return 0;
        }
        DBGC ( iscsi, "iSCSI %p ignoring %s\n", iscsi, string );
        return 0;
}
static int iscsi_handle_strings ( struct iscsi_session iscsi,
const char *  strings,
size_t  len 
) [static]

Handle iSCSI strings.

Parameters:
iscsiiSCSI session
stringiSCSI string buffer
lenLength of string buffer
Return values:
rcReturn status code

Definition at line 1186 of file iscsi.c.

References iscsi_handle_string(), rc, and strnlen().

Referenced by iscsi_rx_login_response().

                                                                    {
        size_t string_len;
        int rc;

        /* Handle each string in turn, taking care not to overrun the
         * data buffer in case of badly-terminated data.
         */
        while ( 1 ) {
                string_len = ( strnlen ( strings, len ) + 1 );
                if ( string_len > len )
                        break;
                if ( ( rc = iscsi_handle_string ( iscsi, strings ) ) != 0 )
                        return rc;
                strings += string_len;
                len -= string_len;
        }
        return 0;
}
static int iscsi_status_to_rc ( unsigned int  status_class,
unsigned int  status_detail 
) [static]
static int iscsi_rx_login_response ( struct iscsi_session iscsi,
const void *  data,
size_t  len,
size_t  remaining 
) [static]

Receive data segment of an iSCSI login response PDU.

Parameters:
iscsiiSCSI session
dataReceived data
lenLength of received data
remainingData remaining after this data
Return values:
rcReturn status code

Definition at line 1251 of file iscsi.c.

References iscsi_session::control, DBGC, EIO, EPROTO, iscsi_bhs_login_response::flags, iscsi_close_connection(), iscsi_handle_strings(), ISCSI_LOGIN_FLAG_TRANSITION, ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE, ISCSI_LOGIN_NSG_MASK, ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION, iscsi_open_connection(), iscsi_rx_buffered_data(), iscsi_rx_buffered_data_done(), iscsi_start_login(), ISCSI_STATUS_AUTH_REVERSE_OK, ISCSI_STATUS_AUTH_REVERSE_REQUIRED, ISCSI_STATUS_FULL_FEATURE_PHASE, ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE, ISCSI_STATUS_PHASE_MASK, ISCSI_STATUS_REDIRECT, ISCSI_STATUS_STRINGS_MASK, ISCSI_STATUS_STRINGS_OPERATIONAL, iscsi_status_to_rc(), iscsi_bhs::login_response, rc, iscsi_session::rx_bhs, iscsi_session::rx_buffer, iscsi_session::rx_len, iscsi_session::status, iscsi_bhs_login_response::status_class, iscsi_bhs_login_response::status_detail, strerror(), and xfer_window_changed().

Referenced by iscsi_rx_data().

                                                        {
        struct iscsi_bhs_login_response *response
                = &iscsi->rx_bhs.login_response;
        int rc;

        /* Buffer up the PDU data */
        if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p could not buffer login response: %s\n",
                       iscsi, strerror ( rc ) );
                return rc;
        }
        if ( remaining )
                return 0;

        /* Process string data and discard string buffer */
        if ( ( rc = iscsi_handle_strings ( iscsi, iscsi->rx_buffer,
                                           iscsi->rx_len ) ) != 0 )
                return rc;
        iscsi_rx_buffered_data_done ( iscsi );

        /* Check for login redirection */
        if ( response->status_class == ISCSI_STATUS_REDIRECT ) {
                DBGC ( iscsi, "iSCSI %p redirecting to new server\n", iscsi );
                iscsi_close_connection ( iscsi, 0 );
                if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
                        DBGC ( iscsi, "iSCSI %p could not redirect: %s\n ",
                               iscsi, strerror ( rc ) );
                        return rc;
                }
                return 0;
        }

        /* Check for fatal errors */
        if ( response->status_class != 0 ) {
                DBGC ( iscsi, "iSCSI login failure: class %02x detail %02x\n",
                       response->status_class, response->status_detail );
                rc = iscsi_status_to_rc ( response->status_class,
                                          response->status_detail );
                return rc;
        }

        /* Handle login transitions */
        if ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) {
                iscsi->status &= ~( ISCSI_STATUS_PHASE_MASK |
                                    ISCSI_STATUS_STRINGS_MASK );
                switch ( response->flags & ISCSI_LOGIN_NSG_MASK ) {
                case ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION:
                        iscsi->status |=
                                ( ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE |
                                  ISCSI_STATUS_STRINGS_OPERATIONAL );
                        break;
                case ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE:
                        iscsi->status |= ISCSI_STATUS_FULL_FEATURE_PHASE;
                        break;
                default:
                        DBGC ( iscsi, "iSCSI %p got invalid response flags "
                               "%02x\n", iscsi, response->flags );
                        return -EIO;
                }
        }

        /* Send next login request PDU if we haven't reached the full
         * feature phase yet.
         */
        if ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) !=
             ISCSI_STATUS_FULL_FEATURE_PHASE ) {
                iscsi_start_login ( iscsi );
                return 0;
        }

        /* Check that target authentication was successful (if required) */
        if ( ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_REQUIRED ) &&
             ! ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_OK ) ) {
                DBGC ( iscsi, "iSCSI %p nefarious target tried to bypass "
                       "authentication\n", iscsi );
                return -EPROTO;
        }

        /* Notify SCSI layer of window change */
        DBGC ( iscsi, "iSCSI %p entering full feature phase\n", iscsi );
        xfer_window_changed ( &iscsi->control );

        return 0;
}
static void iscsi_tx_pause ( struct iscsi_session iscsi) [static]

Pause TX engine.

Parameters:
iscsiiSCSI session

Definition at line 1349 of file iscsi.c.

References iscsi_session::process, and process_del().

Referenced by iscsi_tx_done(), and iscsi_tx_step().

                                                           {
        process_del ( &iscsi->process );
}
static void iscsi_tx_resume ( struct iscsi_session iscsi) [static]

Resume TX engine.

Parameters:
iscsiiSCSI session

Definition at line 1358 of file iscsi.c.

References iscsi_session::process, and process_add().

Referenced by iscsi_start_tx().

                                                            {
        process_add ( &iscsi->process );
}
static int iscsi_tx_nothing ( struct iscsi_session *iscsi  __unused) [static]

Transmit nothing.

Parameters:
iscsiiSCSI session
Return values:
rcReturn status code

Definition at line 1390 of file iscsi.c.

Referenced by iscsi_tx_step().

                                                                     {
        return 0;
}
static int iscsi_tx_bhs ( struct iscsi_session iscsi) [static]

Transmit basic header segment of an iSCSI PDU.

Parameters:
iscsiiSCSI session
Return values:
rcReturn status code

Definition at line 1400 of file iscsi.c.

References iscsi_session::socket, iscsi_session::tx_bhs, and xfer_deliver_raw().

Referenced by iscsi_tx_step().

                                                        {
        return xfer_deliver_raw ( &iscsi->socket,  &iscsi->tx_bhs,
                                  sizeof ( iscsi->tx_bhs ) );
}
static int iscsi_tx_data ( struct iscsi_session iscsi) [static]

Transmit data segment of an iSCSI PDU.

Parameters:
iscsiiSCSI session
Return values:
rcReturn status code

Handle transmission of part of a PDU data segment. iscsi::tx_bhs will be valid when this is called.

Definition at line 1414 of file iscsi.c.

References iscsi_bhs::common, common, ISCSI_OPCODE_DATA_OUT, ISCSI_OPCODE_LOGIN_REQUEST, ISCSI_OPCODE_MASK, iscsi_tx_data_out(), iscsi_tx_login_request(), iscsi_bhs_common::opcode, and iscsi_session::tx_bhs.

Referenced by iscsi_tx_step().

                                                         {
        struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;

        switch ( common->opcode & ISCSI_OPCODE_MASK ) {
        case ISCSI_OPCODE_DATA_OUT:
                return iscsi_tx_data_out ( iscsi );
        case ISCSI_OPCODE_LOGIN_REQUEST:
                return iscsi_tx_login_request ( iscsi );
        default:
                /* Nothing to send in other states */
                return 0;
        }
}
static void iscsi_tx_done ( struct iscsi_session iscsi) [static]

Complete iSCSI PDU transmission.

Parameters:
iscsiiSCSI session

Called when a PDU has been completely transmitted and the TX state machine is about to enter the idle state. iscsi::tx_bhs will be valid for the just-completed PDU when this is called.

Definition at line 1437 of file iscsi.c.

References iscsi_bhs::common, common, iscsi_data_out_done(), iscsi_login_request_done(), ISCSI_OPCODE_DATA_OUT, ISCSI_OPCODE_LOGIN_REQUEST, ISCSI_OPCODE_MASK, iscsi_tx_pause(), iscsi_bhs_common::opcode, and iscsi_session::tx_bhs.

Referenced by iscsi_tx_step().

                                                          {
        struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;

        /* Stop transmission process */
        iscsi_tx_pause ( iscsi );

        switch ( common->opcode & ISCSI_OPCODE_MASK ) {
        case ISCSI_OPCODE_DATA_OUT:
                iscsi_data_out_done ( iscsi );
                break;
        case ISCSI_OPCODE_LOGIN_REQUEST:
                iscsi_login_request_done ( iscsi );
                break;
        default:
                /* No action */
                break;
        }
}
static void iscsi_tx_step ( struct iscsi_session iscsi) [static]

Transmit iSCSI PDU.

Parameters:
iscsiiSCSI session
bufTemporary data buffer
lenLength of temporary data buffer

Constructs data to be sent for the current TX state

Definition at line 1465 of file iscsi.c.

References assert, iscsi_bhs::common, common, DBGC, iscsi_close(), ISCSI_DATA_LEN, ISCSI_TX_AHS, ISCSI_TX_BHS, iscsi_tx_bhs(), ISCSI_TX_DATA, iscsi_tx_data(), iscsi_tx_done(), ISCSI_TX_IDLE, iscsi_tx_nothing(), iscsi_tx_pause(), iscsi_bhs_common::lengths, rc, iscsi_session::socket, strerror(), tx, iscsi_session::tx_bhs, iscsi_session::tx_state, and xfer_window().

                                                          {
        struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
        int ( * tx ) ( struct iscsi_session *iscsi );
        enum iscsi_tx_state next_state;
        size_t tx_len;
        int rc;

        /* Select fragment to transmit */
        while ( 1 ) {
                switch ( iscsi->tx_state ) {
                case ISCSI_TX_BHS:
                        tx = iscsi_tx_bhs;
                        tx_len = sizeof ( iscsi->tx_bhs );
                        next_state = ISCSI_TX_AHS;
                        break;
                case ISCSI_TX_AHS:
                        tx = iscsi_tx_nothing;
                        tx_len = 0;
                        next_state = ISCSI_TX_DATA;
                        break;
                case ISCSI_TX_DATA:
                        tx = iscsi_tx_data;
                        tx_len = ISCSI_DATA_LEN ( common->lengths );
                        next_state = ISCSI_TX_IDLE;
                        break;
                case ISCSI_TX_IDLE:
                        /* Nothing to do; pause processing */
                        iscsi_tx_pause ( iscsi );
                        return;
                default:
                        assert ( 0 );
                        return;
                }

                /* Check for window availability, if needed */
                if ( tx_len && ( xfer_window ( &iscsi->socket ) == 0 ) ) {
                        /* Cannot transmit at this point; pause
                         * processing and wait for window to reopen
                         */
                        iscsi_tx_pause ( iscsi );
                        return;
                }

                /* Transmit data */
                if ( ( rc = tx ( iscsi ) ) != 0 ) {
                        DBGC ( iscsi, "iSCSI %p could not transmit: %s\n",
                               iscsi, strerror ( rc ) );
                        /* Transmission errors are fatal */
                        iscsi_close ( iscsi, rc );
                        return;
                }

                /* Move to next state */
                iscsi->tx_state = next_state;

                /* If we have moved to the idle state, mark
                 * transmission as complete
                 */
                if ( iscsi->tx_state == ISCSI_TX_IDLE )
                        iscsi_tx_done ( iscsi );
        }
}
static int iscsi_rx_bhs ( struct iscsi_session iscsi,
const void *  data,
size_t  len,
size_t remaining  __unused 
) [static]

Receive basic header segment of an iSCSI PDU.

Parameters:
iscsiiSCSI session
dataReceived data
lenLength of received data
remainingData remaining after this data
Return values:
rcReturn status code

This fills in iscsi::rx_bhs with the data from the BHS portion of the received PDU.

Definition at line 1544 of file iscsi.c.

References iscsi_bhs::bytes, iscsi_bhs::common, DBGC2, ISCSI_DATA_LEN, iscsi_bhs_common::lengths, memcpy(), iscsi_bhs_common::opcode, iscsi_session::rx_bhs, and iscsi_session::rx_offset.

Referenced by iscsi_socket_deliver().

                                                                  {
        memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
        if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
                DBGC2 ( iscsi, "iSCSI %p received PDU opcode %#x len %#x\n",
                        iscsi, iscsi->rx_bhs.common.opcode,
                        ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
        }
        return 0;
}
static int iscsi_rx_discard ( struct iscsi_session *iscsi  __unused,
const void *data  __unused,
size_t len  __unused,
size_t remaining  __unused 
) [static]

Discard portion of an iSCSI PDU.

Parameters:
iscsiiSCSI session
dataReceived data
lenLength of received data
remainingData remaining after this data
Return values:
rcReturn status code

This discards data from a portion of a received PDU.

Definition at line 1566 of file iscsi.c.

Referenced by iscsi_socket_deliver().

                                                          {
        /* Do nothing */
        return 0;
}
static int iscsi_rx_data ( struct iscsi_session iscsi,
const void *  data,
size_t  len,
size_t  remaining 
) [static]

Receive data segment of an iSCSI PDU.

Parameters:
iscsiiSCSI session
dataReceived data
lenLength of received data
remainingData remaining after this data
Return values:
rcReturn status code

Handle processing of part of a PDU data segment. iscsi::rx_bhs will be valid when this is called.

Definition at line 1585 of file iscsi.c.

References iscsi_session::cmdsn, iscsi_bhs::common_response, DBGC, ENOTSUP_OPCODE, iscsi_bhs_common_response::expcmdsn, ISCSI_OPCODE_DATA_IN, ISCSI_OPCODE_LOGIN_RESPONSE, ISCSI_OPCODE_MASK, ISCSI_OPCODE_NOP_IN, ISCSI_OPCODE_R2T, ISCSI_OPCODE_SCSI_RESPONSE, iscsi_rx_data_in(), iscsi_rx_login_response(), iscsi_rx_nop_in(), iscsi_rx_r2t(), iscsi_rx_scsi_response(), ntohl, iscsi_bhs_common_response::opcode, iscsi_session::rx_bhs, iscsi_bhs_common_response::statsn, and iscsi_session::statsn.

Referenced by iscsi_socket_deliver().

                                                          {
        struct iscsi_bhs_common_response *response
                = &iscsi->rx_bhs.common_response;

        /* Update cmdsn and statsn */
        iscsi->cmdsn = ntohl ( response->expcmdsn );
        iscsi->statsn = ntohl ( response->statsn );

        switch ( response->opcode & ISCSI_OPCODE_MASK ) {
        case ISCSI_OPCODE_LOGIN_RESPONSE:
                return iscsi_rx_login_response ( iscsi, data, len, remaining );
        case ISCSI_OPCODE_SCSI_RESPONSE:
                return iscsi_rx_scsi_response ( iscsi, data, len, remaining );
        case ISCSI_OPCODE_DATA_IN:
                return iscsi_rx_data_in ( iscsi, data, len, remaining );
        case ISCSI_OPCODE_R2T:
                return iscsi_rx_r2t ( iscsi, data, len, remaining );
        case ISCSI_OPCODE_NOP_IN:
                return iscsi_rx_nop_in ( iscsi, data, len, remaining );
        default:
                if ( remaining )
                        return 0;
                DBGC ( iscsi, "iSCSI %p unknown opcode %02x\n", iscsi,
                       response->opcode );
                return -ENOTSUP_OPCODE;
        }
}
static int iscsi_socket_deliver ( struct iscsi_session iscsi,
struct io_buffer iobuf,
struct xfer_metadata *meta  __unused 
) [static]

Receive new data.

Parameters:
iscsiiSCSI session
iobufI/O buffer
metaData transfer metadata
Return values:
rcReturn status code

This handles received PDUs. The receive strategy is to fill in iscsi::rx_bhs with the contents of the BHS portion of the PDU, throw away any AHS portion, and then process each part of the data portion as it arrives. The data processing routine therefore always has a full copy of the BHS available, even for portions of the data in different packets to the BHS.

Definition at line 1629 of file iscsi.c.

References assert, iscsi_bhs::common, common, io_buffer::data, data, DBGC, done, EINVAL, free_iob(), iob_len(), iob_pull, ISCSI_AHS_LEN, iscsi_close(), ISCSI_DATA_LEN, ISCSI_DATA_PAD_LEN, ISCSI_RX_AHS, ISCSI_RX_BHS, iscsi_rx_bhs(), ISCSI_RX_DATA, iscsi_rx_data(), ISCSI_RX_DATA_PADDING, iscsi_rx_discard(), len, iscsi_bhs_common::lengths, rc, rx, iscsi_session::rx_bhs, iscsi_session::rx_len, iscsi_session::rx_offset, iscsi_session::rx_state, and strerror().

                                                                        {
        struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
        int ( * rx ) ( struct iscsi_session *iscsi, const void *data,
                       size_t len, size_t remaining );
        enum iscsi_rx_state next_state;
        size_t frag_len;
        size_t remaining;
        int rc;

        while ( 1 ) {
                switch ( iscsi->rx_state ) {
                case ISCSI_RX_BHS:
                        rx = iscsi_rx_bhs;
                        iscsi->rx_len = sizeof ( iscsi->rx_bhs );
                        next_state = ISCSI_RX_AHS;                      
                        break;
                case ISCSI_RX_AHS:
                        rx = iscsi_rx_discard;
                        iscsi->rx_len = 4 * ISCSI_AHS_LEN ( common->lengths );
                        next_state = ISCSI_RX_DATA;
                        break;
                case ISCSI_RX_DATA:
                        rx = iscsi_rx_data;
                        iscsi->rx_len = ISCSI_DATA_LEN ( common->lengths );
                        next_state = ISCSI_RX_DATA_PADDING;
                        break;
                case ISCSI_RX_DATA_PADDING:
                        rx = iscsi_rx_discard;
                        iscsi->rx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
                        next_state = ISCSI_RX_BHS;
                        break;
                default:
                        assert ( 0 );
                        rc = -EINVAL;
                        goto done;
                }

                frag_len = iscsi->rx_len - iscsi->rx_offset;
                if ( frag_len > iob_len ( iobuf ) )
                        frag_len = iob_len ( iobuf );
                remaining = iscsi->rx_len - iscsi->rx_offset - frag_len;
                if ( ( rc = rx ( iscsi, iobuf->data, frag_len,
                                 remaining ) ) != 0 ) {
                        DBGC ( iscsi, "iSCSI %p could not process received "
                               "data: %s\n", iscsi, strerror ( rc ) );
                        goto done;
                }

                iscsi->rx_offset += frag_len;
                iob_pull ( iobuf, frag_len );

                /* If all the data for this state has not yet been
                 * received, stay in this state for now.
                 */
                if ( iscsi->rx_offset != iscsi->rx_len ) {
                        rc = 0;
                        goto done;
                }

                iscsi->rx_state = next_state;
                iscsi->rx_offset = 0;
        }

 done:
        /* Free I/O buffer */
        free_iob ( iobuf );

        /* Destroy session on error */
        if ( rc != 0 )
                iscsi_close ( iscsi, rc );

        return rc;
}
static int iscsi_vredirect ( struct iscsi_session iscsi,
int  type,
va_list  args 
) [static]

Handle redirection event.

Parameters:
iscsiiSCSI session
typeLocation type
argsRemaining arguments depend upon location type
Return values:
rcReturn status code

Definition at line 1713 of file iscsi.c.

References iscsi_close(), LOCATION_SOCKET, memcpy(), rc, iscsi_session::socket, iscsi_session::target_sockaddr, va_arg, va_copy, va_end, and xfer_vreopen().

                                            {
        va_list tmp;
        struct sockaddr *peer;
        int rc;

        /* Intercept redirects to a LOCATION_SOCKET and record the IP
         * address for the iBFT.  This is a bit of a hack, but avoids
         * inventing an ioctl()-style call to retrieve the socket
         * address from a data-xfer interface.
         */
        if ( type == LOCATION_SOCKET ) {
                va_copy ( tmp, args );
                ( void ) va_arg ( tmp, int ); /* Discard "semantics" */
                peer = va_arg ( tmp, struct sockaddr * );
                memcpy ( &iscsi->target_sockaddr, peer,
                         sizeof ( iscsi->target_sockaddr ) );
                va_end ( tmp );
        }

        /* Redirect to new location */
        if ( ( rc = xfer_vreopen ( &iscsi->socket, type, args ) ) != 0 )
                goto err;

        return 0;

 err:
        iscsi_close ( iscsi, rc );
        return rc;
}
static size_t iscsi_scsi_window ( struct iscsi_session iscsi) [static]

Check iSCSI flow-control window.

Parameters:
iscsiiSCSI session
Return values:
lenLength of window

Definition at line 1769 of file iscsi.c.

References iscsi_session::command, ISCSI_STATUS_FULL_FEATURE_PHASE, ISCSI_STATUS_PHASE_MASK, NULL, and iscsi_session::status.

Referenced by iscsi_scsi_command().

                                                                {

        if ( ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) ==
               ISCSI_STATUS_FULL_FEATURE_PHASE ) &&
             ( iscsi->command == NULL ) ) {
                /* We cannot handle concurrent commands */
                return 1;
        } else {
                return 0;
        }
}
static int iscsi_scsi_command ( struct iscsi_session iscsi,
struct interface parent,
struct scsi_cmd command 
) [static]

Issue iSCSI SCSI command.

Parameters:
iscsiiSCSI session
parentParent interface
commandSCSI command
Return values:
tagCommand tag, or negative error

Definition at line 1789 of file iscsi.c.

References iscsi_session::command, iscsi_session::data, DBGC, ENOMEM, EOPNOTSUPP, intf_plug_plug(), iscsi_new_itt(), iscsi_scsi_window(), iscsi_start_command(), iscsi_session::itt, malloc(), and memcpy().

                                                           {

        /* This iSCSI implementation cannot handle multiple concurrent
         * commands or commands arriving before login is complete.
         */
        if ( iscsi_scsi_window ( iscsi ) == 0 ) {
                DBGC ( iscsi, "iSCSI %p cannot handle concurrent commands\n",
                       iscsi );
                return -EOPNOTSUPP;
        }

        /* Store command */
        iscsi->command = malloc ( sizeof ( *command ) );
        if ( ! iscsi->command )
                return -ENOMEM;
        memcpy ( iscsi->command, command, sizeof ( *command ) );

        /* Assign new ITT */
        iscsi_new_itt ( iscsi );

        /* Start sending command */
        iscsi_start_command ( iscsi );

        /* Attach to parent interface and return */
        intf_plug_plug ( &iscsi->data, parent );
        return iscsi->itt;
}
static struct acpi_descriptor* iscsi_describe ( struct iscsi_session iscsi) [static, read]

Get iSCSI ACPI descriptor.

Parameters:
iscsiiSCSI session
Return values:
descACPI descriptor

Definition at line 1825 of file iscsi.c.

References iscsi_session::desc.

                                                                               {

        return &iscsi->desc;
}
static void iscsi_command_close ( struct iscsi_session iscsi,
int  rc 
) [static]

Close iSCSI command.

Parameters:
iscsiiSCSI session
rcReason for close

Definition at line 1848 of file iscsi.c.

References iscsi_session::command, iscsi_session::data, ECANCELED, intf_restart(), iscsi_close(), and NULL.

                                                                        {

        /* Restart interface */
        intf_restart ( &iscsi->data, rc );

        /* Treat unsolicited command closures mid-command as fatal,
         * because we have no code to handle partially-completed PDUs.
         */
        if ( iscsi->command != NULL )
                iscsi_close ( iscsi, ( ( rc == 0 ) ? -ECANCELED : rc ) );
}
struct setting initiator_iqn_setting __setting ( SETTING_SANBOOT_EXTRA  ,
initiator-  iqn 
) [read]

iSCSI initiator IQN setting

struct setting reverse_password_setting __setting ( SETTING_AUTH_EXTRA  ,
reverse-  username 
) [read]
Initial value:
 {
        .name = "reverse-password",
        .description = "Reverse password",
        .tag = DHCP_EB_REVERSE_PASSWORD,
        .type = &setting_type_string,
}

iSCSI reverse username setting

iSCSI reverse password setting

static int iscsi_parse_root_path ( struct iscsi_session iscsi,
const char *  root_path 
) [static]

Parse iSCSI root path.

Parameters:
iscsiiSCSI session
root_pathiSCSI root path (as per RFC4173)
Return values:
rcReturn status code

Definition at line 1919 of file iscsi.c.

References DBGC, EINVAL_ROOT_PATH_TOO_SHORT, ENOMEM, ISCSI_PORT, iscsi_session::lun, NULL, NUM_RP_COMPONENTS, rc, RP_LUN, RP_PORT, RP_SERVERNAME, RP_TARGETNAME, scsi_parse_lun(), strcpy(), strdup(), strlen(), strtoul(), iscsi_session::target_address, iscsi_session::target_iqn, and iscsi_session::target_port.

Referenced by iscsi_open().

                                                           {
        char rp_copy[ strlen ( root_path ) + 1 ];
        char *rp_comp[NUM_RP_COMPONENTS];
        char *rp = rp_copy;
        int skip = 0;
        int i = 0;
        int rc;

        /* Split root path into component parts */
        strcpy ( rp_copy, root_path );
        while ( 1 ) {
                rp_comp[i++] = rp;
                if ( i == NUM_RP_COMPONENTS )
                        break;
                for ( ; ( ( *rp != ':' ) || skip ) ; rp++ ) {
                        if ( ! *rp ) {
                                DBGC ( iscsi, "iSCSI %p root path \"%s\" "
                                       "too short\n", iscsi, root_path );
                                return -EINVAL_ROOT_PATH_TOO_SHORT;
                        } else if ( *rp == '[' ) {
                                skip = 1;
                        } else if ( *rp == ']' ) {
                                skip = 0;
                        }
                }
                *(rp++) = '\0';
        }

        /* Use root path components to configure iSCSI session */
        iscsi->target_address = strdup ( rp_comp[RP_SERVERNAME] );
        if ( ! iscsi->target_address )
                return -ENOMEM;
        iscsi->target_port = strtoul ( rp_comp[RP_PORT], NULL, 10 );
        if ( ! iscsi->target_port )
                iscsi->target_port = ISCSI_PORT;
        if ( ( rc = scsi_parse_lun ( rp_comp[RP_LUN], &iscsi->lun ) ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p invalid LUN \"%s\"\n",
                       iscsi, rp_comp[RP_LUN] );
                return rc;
        }
        iscsi->target_iqn = strdup ( rp_comp[RP_TARGETNAME] );
        if ( ! iscsi->target_iqn )
                return -ENOMEM;

        return 0;
}
static int iscsi_fetch_settings ( struct iscsi_session iscsi) [static]

Fetch iSCSI settings.

Parameters:
iscsiiSCSI session
Return values:
rcReturn status code

Definition at line 1973 of file iscsi.c.

References asprintf(), assert, DBGC, EINVAL_NO_INITIATOR_IQN, ENOMEM, fetch_string_setting_copy(), fetch_uuid_setting(), free, iscsi_session::initiator_iqn, iscsi_session::initiator_password, iscsi_session::initiator_username, ISCSI_DEFAULT_IQN_PREFIX, len, NULL, iscsi_session::target_password, iscsi_session::target_username, and uuid_ntoa().

Referenced by iscsi_open().

                                                                {
        char *hostname;
        union uuid uuid;
        int len;

        /* Fetch relevant settings.  Don't worry about freeing on
         * error, since iscsi_free() will take care of that anyway.
         */
        fetch_string_setting_copy ( NULL, &username_setting,
                                    &iscsi->initiator_username );
        fetch_string_setting_copy ( NULL, &password_setting,
                                    &iscsi->initiator_password );
        fetch_string_setting_copy ( NULL, &reverse_username_setting,
                                    &iscsi->target_username );
        fetch_string_setting_copy ( NULL, &reverse_password_setting,
                                    &iscsi->target_password );

        /* Use explicit initiator IQN if provided */
        fetch_string_setting_copy ( NULL, &initiator_iqn_setting,
                                    &iscsi->initiator_iqn );
        if ( iscsi->initiator_iqn )
                return 0;

        /* Otherwise, try to construct an initiator IQN from the hostname */
        fetch_string_setting_copy ( NULL, &hostname_setting, &hostname );
        if ( hostname ) {
                len = asprintf ( &iscsi->initiator_iqn,
                                 ISCSI_DEFAULT_IQN_PREFIX ":%s", hostname );
                free ( hostname );
                if ( len < 0 ) {
                        DBGC ( iscsi, "iSCSI %p could not allocate initiator "
                               "IQN\n", iscsi );
                        return -ENOMEM;
                }
                assert ( iscsi->initiator_iqn );
                return 0;
        }

        /* Otherwise, try to construct an initiator IQN from the UUID */
        if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting, &uuid ) ) < 0 ) {
                DBGC ( iscsi, "iSCSI %p has no suitable initiator IQN\n",
                       iscsi );
                return -EINVAL_NO_INITIATOR_IQN;
        }
        if ( ( len = asprintf ( &iscsi->initiator_iqn,
                                ISCSI_DEFAULT_IQN_PREFIX ":%s",
                                uuid_ntoa ( &uuid ) ) ) < 0 ) {
                DBGC ( iscsi, "iSCSI %p could not allocate initiator IQN\n",
                       iscsi );
                return -ENOMEM;
        }
        assert ( iscsi->initiator_iqn );

        return 0;
}
static int iscsi_check_auth ( struct iscsi_session iscsi) [static]

Check iSCSI authentication details.

Parameters:
iscsiiSCSI session
Return values:
rcReturn status code

Definition at line 2036 of file iscsi.c.

References DBGC, EINVAL_BAD_CREDENTIAL_MIX, iscsi_session::initiator_password, iscsi_session::initiator_username, iscsi_session::target_password, and iscsi_session::target_username.

Referenced by iscsi_open().

                                                            {

        /* Check for invalid authentication combinations */
        if ( ( /* Initiator username without password (or vice-versa) */
                ( !! iscsi->initiator_username ) ^
                ( !! iscsi->initiator_password ) ) ||
             ( /* Target username without password (or vice-versa) */
                ( !! iscsi->target_username ) ^
                ( !! iscsi->target_password ) ) ||
             ( /* Target (reverse) without initiator (forward) */
                ( iscsi->target_username &&
                  ( ! iscsi->initiator_username ) ) ) ) {
                DBGC ( iscsi, "iSCSI %p invalid credentials: initiator "
                       "%sname,%spw, target %sname,%spw\n", iscsi,
                       ( iscsi->initiator_username ? "" : "no " ),
                       ( iscsi->initiator_password ? "" : "no " ),
                       ( iscsi->target_username ? "" : "no " ),
                       ( iscsi->target_password ? "" : "no " ) );
                return -EINVAL_BAD_CREDENTIAL_MIX;
        }

        return 0;
}
static int iscsi_open ( struct interface parent,
struct uri uri 
) [static]

Open iSCSI URI.

Parameters:
parentParent interface
uriURI
Return values:
rcReturn status code

Definition at line 2067 of file iscsi.c.

References acpi_init(), iscsi_session::control, iscsi_session::data, DBGC, iscsi_session::desc, EINVAL_NO_ROOT_PATH, EINVAL_NO_TARGET_IQN, ENOMEM, ENOTSUP_DISCOVERY, iscsi_session::initiator_iqn, intf_init(), iscsi_check_auth(), iscsi_close(), iscsi_fetch_settings(), iscsi_free(), iscsi_open_connection(), iscsi_parse_root_path(), iscsi_session::lun, uri::opaque, iscsi_session::process, process_init_stopped(), rc, ref_init, ref_put, iscsi_session::refcnt, scsi_open(), iscsi_session::socket, strerror(), iscsi_session::target_address, iscsi_session::target_iqn, and zalloc().

                                                                    {
        struct iscsi_session *iscsi;
        int rc;

        /* Sanity check */
        if ( ! uri->opaque ) {
                rc = -EINVAL_NO_ROOT_PATH;
                goto err_sanity_uri;
        }

        /* Allocate and initialise structure */
        iscsi = zalloc ( sizeof ( *iscsi ) );
        if ( ! iscsi ) {
                rc = -ENOMEM;
                goto err_zalloc;
        }
        ref_init ( &iscsi->refcnt, iscsi_free );
        intf_init ( &iscsi->control, &iscsi_control_desc, &iscsi->refcnt );
        intf_init ( &iscsi->data, &iscsi_data_desc, &iscsi->refcnt );
        intf_init ( &iscsi->socket, &iscsi_socket_desc, &iscsi->refcnt );
        process_init_stopped ( &iscsi->process, &iscsi_process_desc,
                               &iscsi->refcnt );
        acpi_init ( &iscsi->desc, &ibft_model, &iscsi->refcnt );

        /* Parse root path */
        if ( ( rc = iscsi_parse_root_path ( iscsi, uri->opaque ) ) != 0 )
                goto err_parse_root_path;
        /* Set fields not specified by root path */
        if ( ( rc = iscsi_fetch_settings ( iscsi ) ) != 0 )
                goto err_fetch_settings;
        /* Validate authentication */
        if ( ( rc = iscsi_check_auth ( iscsi ) ) != 0 )
                goto err_check_auth;

        /* Sanity checks */
        if ( ! iscsi->target_address ) {
                DBGC ( iscsi, "iSCSI %p does not yet support discovery\n",
                       iscsi );
                rc = -ENOTSUP_DISCOVERY;
                goto err_sanity_address;
        }
        if ( ! iscsi->target_iqn ) {
                DBGC ( iscsi, "iSCSI %p no target address supplied in %s\n",
                       iscsi, uri->opaque );
                rc = -EINVAL_NO_TARGET_IQN;
                goto err_sanity_iqn;
        }
        DBGC ( iscsi, "iSCSI %p initiator %s\n",iscsi, iscsi->initiator_iqn );
        DBGC ( iscsi, "iSCSI %p target %s %s\n",
               iscsi, iscsi->target_address, iscsi->target_iqn );

        /* Open socket */
        if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 )
                goto err_open_connection;

        /* Attach SCSI device to parent interface */
        if ( ( rc = scsi_open ( parent, &iscsi->control,
                                &iscsi->lun ) ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p could not create SCSI device: %s\n",
                       iscsi, strerror ( rc ) );
                goto err_scsi_open;
        }

        /* Mortalise self, and return */
        ref_put ( &iscsi->refcnt );
        return 0;
        
 err_scsi_open:
 err_open_connection:
 err_sanity_iqn:
 err_sanity_address:
 err_check_auth:
 err_fetch_settings:
 err_parse_root_path:
        iscsi_close ( iscsi, rc );
        ref_put ( &iscsi->refcnt );
 err_zalloc:
 err_sanity_uri:
        return rc;
}

Variable Documentation

Initial value:
 {
        { "TargetAddress", iscsi_handle_targetaddress_value },
        { "AuthMethod", iscsi_handle_authmethod_value },
        { "CHAP_A", iscsi_handle_chap_a_value },
        { "CHAP_I", iscsi_handle_chap_i_value },
        { "CHAP_C", iscsi_handle_chap_c_value },
        { "CHAP_N", iscsi_handle_chap_n_value },
        { "CHAP_R", iscsi_handle_chap_r_value },
        { NULL, NULL }
}

iSCSI text strings that we want to handle

Definition at line 1118 of file iscsi.c.

Initial value:

iSCSI TX process descriptor

Definition at line 1529 of file iscsi.c.

Initial value:

iSCSI socket interface operations

Definition at line 1745 of file iscsi.c.

Initial value:

iSCSI socket interface descriptor

Definition at line 1754 of file iscsi.c.

Initial value:

iSCSI SCSI command-issuing interface operations

Definition at line 1831 of file iscsi.c.

Initial value:

iSCSI SCSI command-issuing interface descriptor

Definition at line 1839 of file iscsi.c.

Initial value:

iSCSI SCSI command interface operations

Definition at line 1861 of file iscsi.c.

Initial value:

iSCSI SCSI command interface descriptor

Definition at line 1866 of file iscsi.c.

struct uri_opener iscsi_uri_opener __uri_opener
Initial value:
 {
        .scheme = "iscsi",
        .open = iscsi_open,
}

iSCSI URI opener

Definition at line 2149 of file iscsi.c.