iSCSI updated to use data-xfer interface on the socket side (TCP).
authorMichael Brown <mcb30@etherboot.org>
Sun, 8 Jul 2007 15:04:13 +0000 (16:04 +0100)
committerMichael Brown <mcb30@etherboot.org>
Sun, 8 Jul 2007 15:04:13 +0000 (16:04 +0100)
SCSI interface not yet implemented.

src/include/gpxe/iscsi.h
src/net/tcp/iscsi.c

index 62b789c..5e60c4c 100644 (file)
@@ -8,10 +8,11 @@
  */
 
 #include <stdint.h>
-#include <gpxe/stream.h>
-#include <gpxe/async.h>
 #include <gpxe/scsi.h>
 #include <gpxe/chap.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/xfer.h>
+#include <gpxe/process.h>
 
 /** Default iSCSI port */
 #define ISCSI_PORT 3260
@@ -486,21 +487,25 @@ enum iscsi_rx_state {
 
 /** An iSCSI session */
 struct iscsi_session {
+       /** Reference counter */
+       struct refcnt refcnt;
+
+       /** Transport-layer socket */
+       struct xfer_interface socket;
+
        /** Initiator IQN */
-       const char *initiator_iqn;
+       char *initiator_iqn;
        /** Target address */
-       struct sockaddr target;
+       char *target_address;
        /** Target IQN */
-       const char *target_iqn;
+       char *target_iqn;
        /** Logical Unit Number (LUN) */
        uint64_t lun;
        /** Username */
-       const char *username;
+       char *username;
        /** Password */
-       const char *password;
+       char *password;
 
-       /** Stream application for this session */
-       struct stream_application stream;
        /** Session status
         *
         * This is the bitwise-OR of zero or more ISCSI_STATUS_XXX
@@ -569,10 +574,8 @@ struct iscsi_session {
        union iscsi_bhs tx_bhs;
        /** State of the TX engine */
        enum iscsi_tx_state tx_state;
-       /** Byte offset within the current TX state */
-       size_t tx_offset;
-       /** Length of the current TX state */
-       size_t tx_len;
+       /** TX process */
+       struct process process;
 
        /** Basic header segment for current RX PDU */
        union iscsi_bhs rx_bhs;
@@ -590,8 +593,6 @@ struct iscsi_session {
         * Set to NULL when command is complete.
         */
        struct scsi_command *command;
-       /** Asynchronous operation for the current iSCSI operation */
-       struct async async;
        /** Instant return code
         *
         * Set to a non-zero value if all requests should return
@@ -637,20 +638,4 @@ struct iscsi_session {
 /** Maximum number of retries at connecting */
 #define ISCSI_MAX_RETRIES 2
 
-extern int iscsi_issue ( struct iscsi_session *iscsi,
-                        struct scsi_command *command,
-                        struct async *parent );
-extern void iscsi_shutdown ( struct iscsi_session *iscsi );
-
-/** An iSCSI device */
-struct iscsi_device {
-       /** SCSI device interface */
-       struct scsi_device scsi;
-       /** iSCSI protocol instance */
-       struct iscsi_session iscsi;
-};
-
-extern int init_iscsidev ( struct iscsi_device *iscsidev );
-extern void fini_iscsidev ( struct iscsi_device *iscsidev );
-
 #endif /* _GPXE_ISCSI_H */
index c986d71..500f189 100644 (file)
 #include <assert.h>
 #include <byteswap.h>
 #include <gpxe/vsprintf.h>
+#include <gpxe/socket.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
 #include <gpxe/scsi.h>
 #include <gpxe/process.h>
 #include <gpxe/uaccess.h>
-#include <gpxe/tcp.h>
+#include <gpxe/tcpip.h>
 #include <gpxe/iscsi.h>
 
 /** @file
  *
  */
 
-#warning "Update to use data-xfer interface"
-extern int tcp_open ( struct stream_application *stream );
-
 static void iscsi_start_tx ( struct iscsi_session *iscsi );
+static void iscsi_start_login ( struct iscsi_session *iscsi );
 static void iscsi_start_data_out ( struct iscsi_session *iscsi,
                                   unsigned int datasn );
 
 /**
- * Receive PDU data into buffer
+ * Finish receiving PDU data into buffer
  *
  * @v iscsi            iSCSI session
- * @v data             Data to receive
- * @v len              Length of data
- * @ret rc             Return 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.
  */
-static int iscsi_rx_buffered_data ( struct iscsi_session *iscsi,
-                                   const void *data, size_t len ) {
-
-       /* 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 );
+static void iscsi_rx_buffered_data_done ( struct iscsi_session *iscsi ) {
+       free ( iscsi->rx_buffer );
+       iscsi->rx_buffer = NULL;
+}
 
-       return 0;
+/**
+ * Free iSCSI session
+ *
+ * @v refcnt           Reference counter
+ */
+static void iscsi_free ( struct refcnt *refcnt ) {
+       struct iscsi_session *iscsi =
+               container_of ( refcnt, struct iscsi_session, refcnt );
+
+       free ( iscsi->initiator_iqn );
+       free ( iscsi->target_address );
+       free ( iscsi->target_iqn );
+       free ( iscsi->username );
+       free ( iscsi->password );
+       chap_finish ( &iscsi->chap );
+       iscsi_rx_buffered_data_done ( iscsi );
+       free ( iscsi );
 }
 
 /**
- * Finish receiving PDU data into buffer
+ * Open iSCSI transport-layer connection
  *
  * @v iscsi            iSCSI session
+ * @ret rc             Return status code
  */
-static void iscsi_rx_buffered_data_done ( struct iscsi_session *iscsi ) {
-       free ( iscsi->rx_buffer );
-       iscsi->rx_buffer = NULL;
+static int iscsi_open_connection ( struct iscsi_session *iscsi ) {
+       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_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 );
+
+       /* Assign fresh initiator task tag */
+       iscsi->itt++;
+
+       /* Initiate login */
+       iscsi_start_login ( iscsi );
+
+       return 0;
 }
 
 /**
- * Close iSCSI connection
+ * Close iSCSI transport-layer connection
  *
  * @v iscsi            iSCSI session
+ * @v rc               Reason for close
+ *
+ * Closes the transport-layer connection and resets the session state
+ * ready to attempt a fresh login.
  */
-static void iscsi_close ( struct iscsi_session *iscsi ) {
+static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) {
 
-       /* Close stream connection */
-       stream_close ( &iscsi->stream );
+       /* Close all data transfer interfaces */
+       xfer_close ( &iscsi->socket, rc );
 
        /* Clear connection status */
        iscsi->status = 0;
@@ -100,25 +133,25 @@ static void iscsi_close ( struct iscsi_session *iscsi ) {
        iscsi->tx_state = ISCSI_TX_IDLE;
        iscsi->rx_state = ISCSI_RX_BHS;
 
-       /* Free any dynamically allocated memory */
+       /* Free any temporary dynamically allocated memory */
        chap_finish ( &iscsi->chap );
        iscsi_rx_buffered_data_done ( iscsi );
 }
 
 /**
- * Mark iSCSI operation as complete
+ * Mark iSCSI SCSI operation as complete
  *
  * @v iscsi            iSCSI session
  * @v rc               Return status code
  *
- * Note that iscsi_done() will not close the connection, and must
+ * 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_issue().  The general rule is to call iscsi_done() only at
- * the end of receiving a PDU; at this point the TX and RX engines
- * should both be idle.
+ * to iscsi_issue().  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.
  */
-static void iscsi_done ( struct iscsi_session *iscsi, int rc ) {
+static void iscsi_scsi_done ( struct iscsi_session *iscsi, int rc ) {
 
        assert ( iscsi->tx_state == ISCSI_TX_IDLE );
 
@@ -181,10 +214,11 @@ static void iscsi_start_command ( struct iscsi_session *iscsi ) {
  * @v data             Received data
  * @v len              Length of received data
  * @v remaining                Data remaining after this data
- * 
+ * @ret rc             Return status code
  */
-static void iscsi_rx_scsi_response ( struct iscsi_session *iscsi, void *data,
-                                    size_t len, size_t remaining ) {
+static int iscsi_rx_scsi_response ( struct iscsi_session *iscsi,
+                                   const void *data, size_t len,
+                                   size_t remaining ) {
        struct iscsi_bhs_scsi_response *response
                = &iscsi->rx_bhs.scsi_response;
        int sense_offset;
@@ -198,17 +232,18 @@ static void iscsi_rx_scsi_response ( struct iscsi_session *iscsi, void *data,
 
        /* Wait for whole SCSI response to arrive */
        if ( remaining )
-               return;
+               return 0;
        
        /* Record SCSI status code */
        iscsi->command->status = response->status;
 
-       /* Mark as completed, with error if applicable */
-       if ( response->response == ISCSI_RESPONSE_COMMAND_COMPLETE ) {
-               iscsi_done ( iscsi, 0 );
-       } else {
-               iscsi_done ( iscsi, -EIO );
-       }
+       /* Check for errors */
+       if ( response->response != ISCSI_RESPONSE_COMMAND_COMPLETE )
+               return -EIO;
+
+       /* Mark as completed */
+       iscsi_scsi_done ( iscsi, 0 );
+       return 0;
 }
 
 /**
@@ -218,10 +253,11 @@ static void iscsi_rx_scsi_response ( struct iscsi_session *iscsi, void *data,
  * @v data             Received data
  * @v len              Length of received data
  * @v remaining                Data remaining after this data
- * 
+ * @ret rc             Return status code
  */
-static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data,
-                              size_t len, size_t remaining __unused ) {
+static int iscsi_rx_data_in ( struct iscsi_session *iscsi,
+                             const void *data, size_t len,
+                             size_t remaining __unused ) {
        struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in;
        unsigned long offset;
 
@@ -240,8 +276,10 @@ static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data,
        if ( ( offset + len ) == iscsi->command->data_in_len ) {
                assert ( data_in->flags & ISCSI_FLAG_FINAL );
                assert ( remaining == 0 );
-               iscsi_done ( iscsi, 0 );
+               iscsi_scsi_done ( iscsi, 0 );
        }
+
+       return 0;
 }
 
 /**
@@ -251,10 +289,11 @@ static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data,
  * @v data             Received data
  * @v len              Length of received data
  * @v remaining                Data remaining after this data
- * 
+ * @ret rc             Return status code
  */
-static void iscsi_rx_r2t ( struct iscsi_session *iscsi, void *data __unused,
-                          size_t len __unused, size_t remaining __unused ) {
+static int iscsi_rx_r2t ( struct iscsi_session *iscsi,
+                         const void *data __unused, size_t len __unused,
+                         size_t remaining __unused ) {
        struct iscsi_bhs_r2t *r2t = &iscsi->rx_bhs.r2t;
 
        /* Record transfer parameters and trigger first data-out */
@@ -262,6 +301,8 @@ static void iscsi_rx_r2t ( struct iscsi_session *iscsi, void *data __unused,
        iscsi->transfer_offset = ntohl ( r2t->offset );
        iscsi->transfer_len = ntohl ( r2t->len );
        iscsi_start_data_out ( iscsi, 0 );
+
+       return 0;
 }
 
 /**
@@ -323,27 +364,29 @@ static void iscsi_data_out_done ( struct iscsi_session *iscsi ) {
  * Send iSCSI data-out data segment
  *
  * @v iscsi            iSCSI session
- * @v buf              Temporary data buffer
- * @v len              Length of temporary data buffer
+ * @ret rc             Return status code
  */
-static void iscsi_tx_data_out ( struct iscsi_session *iscsi,
-                               void *buf, size_t len ) {
+static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) {
        struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
+       struct io_buffer *iobuf;
        unsigned long offset;
-       unsigned long remaining;
+       size_t len;
+
+       offset = ntohl ( data_out->offset );
+       len = ISCSI_DATA_LEN ( data_out->lengths );
 
-       offset = ( iscsi->transfer_offset + ntohl ( data_out->offset ) +
-                  iscsi->tx_offset );
-       remaining = ( iscsi->tx_len - iscsi->tx_offset );
        assert ( iscsi->command != NULL );
        assert ( iscsi->command->data_out );
-       assert ( ( offset + remaining ) <= iscsi->command->data_out_len );
+       assert ( ( offset + len ) <= iscsi->command->data_out_len );
+
+       iobuf = xfer_alloc_iob ( &iscsi->socket, len );
+       if ( ! iobuf )
+               return -ENOMEM;
        
-       if ( remaining < len )
-               len = remaining;
-       copy_from_user ( buf, iscsi->command->data_out, offset, len );
+       copy_from_user ( iob_put ( iobuf, len ),
+                        iscsi->command->data_out, offset, len );
 
-       stream_send ( &iscsi->stream, buf, len );
+       return xfer_deliver_iob ( &iscsi->socket, iobuf );
 }
 
 /****************************************************************************
@@ -478,16 +521,22 @@ static void iscsi_login_request_done ( struct iscsi_session *iscsi ) {
  * Transmit data segment of an iSCSI login request PDU
  *
  * @v iscsi            iSCSI session
- * @v buf              Temporary data buffer
- * @v len              Length of temporary data buffer
+ * @ret rc             Return status code
  *
  * For login requests, the data segment consists of the login strings.
  */
-static void iscsi_tx_login_request ( struct iscsi_session *iscsi,
-                                    void *buf, size_t len ) {
-       len = iscsi_build_login_request_strings ( iscsi, buf, len );
-       stream_send ( &iscsi->stream, buf + iscsi->tx_offset,
-                  len - iscsi->tx_offset );
+static int iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
+       struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
+       struct io_buffer *iobuf;
+       size_t len;
+
+       len = ISCSI_DATA_LEN ( request->lengths );
+       iobuf = xfer_alloc_iob ( &iscsi->socket, len );
+       if ( ! iobuf )
+               return -ENOMEM;
+       iob_put ( iobuf, len );
+       iscsi_build_login_request_strings ( iscsi, iobuf->data, len );
+       return xfer_deliver_iob ( &iscsi->socket, iobuf );
 }
 
 /**
@@ -495,20 +544,19 @@ static void iscsi_tx_login_request ( struct iscsi_session *iscsi,
  *
  * @v iscsi            iSCSI session
  * @v value            TargetAddress value
+ * @ret rc             Return status code
  */
-static void iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi,
-                                              const char *value ) {
-       struct in_addr address;
-       struct sockaddr_in *sin = ( struct sockaddr_in * ) &iscsi->target;
-
-       if ( inet_aton ( value, &address ) == 0 ) {
-               DBGC ( iscsi, "iSCSI %p received invalid TargetAddress "
-                      "\"%s\"\n", iscsi, value );
-               return;
-       }
+static int iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi,
+                                             const char *value ) {
 
        DBGC ( iscsi, "iSCSI %p will redirect to %s\n", iscsi, value );
-       sin->sin_addr = address;
+
+       /* Replace target address */
+       free ( iscsi->target_address );
+       iscsi->target_address = strdup ( value );
+       if ( ! iscsi->target_address )
+               return -ENOMEM;
+       return 0;
 }
 
 /**
@@ -516,9 +564,10 @@ static void iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi,
  *
  * @v iscsi            iSCSI session
  * @v value            AuthMethod value
+ * @ret rc             Return status code
  */
-static void iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
-                                           const char *value ) {
+static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
+                                          const char *value ) {
 
        /* If server requests CHAP, send the CHAP_A string */
        if ( strcmp ( value, "CHAP" ) == 0 ) {
@@ -526,6 +575,7 @@ static void iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
                       iscsi );
                iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
        }
+       return 0;
 }
 
 /**
@@ -533,9 +583,10 @@ static void iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
  *
  * @v iscsi            iSCSI session
  * @v value            CHAP_A value
+ * @ret rc             Return status code
  */
-static void iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
-                                       const char *value ) {
+static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
+                                      const char *value ) {
        int rc;
 
        /* We only ever offer "5" (i.e. MD5) as an algorithm, so if
@@ -545,15 +596,17 @@ static void iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
        if ( strcmp ( value, "5" ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p got invalid CHAP algorithm \"%s\"\n",
                       iscsi, value );
+               return -EPROTO;
        }
 
        /* Prepare for CHAP with MD5 */
        if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
                DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
                       iscsi, strerror ( rc ) );
-               iscsi_close ( iscsi );
-               iscsi_done ( iscsi, rc );
+               return rc;
        }
+
+       return 0;
 }
 
 /**
@@ -561,9 +614,10 @@ static void iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
  *
  * @v iscsi            iSCSI session
  * @v value            CHAP_I value
+ * @ret rc             Return status code
  */
-static void iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
-                                       const char *value ) {
+static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
+                                      const char *value ) {
        unsigned int identifier;
        char *endp;
 
@@ -572,6 +626,7 @@ static void iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
        if ( *endp != '\0' ) {
                DBGC ( iscsi, "iSCSI %p saw invalid CHAP identifier \"%s\"\n",
                       iscsi, value );
+               return -EPROTO;
        }
 
        /* Identifier and secret are the first two components of the
@@ -582,6 +637,8 @@ static void iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
                chap_update ( &iscsi->chap, iscsi->password,
                              strlen ( iscsi->password ) );
        }
+
+       return 0;
 }
 
 /**
@@ -589,9 +646,10 @@ static void iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
  *
  * @v iscsi            iSCSI session
  * @v value            CHAP_C value
+ * @ret rc             Return status code
  */
-static void iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
-                                       const char *value ) {
+static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
+                                      const char *value ) {
        char buf[3];
        char *endp;
        uint8_t byte;
@@ -611,6 +669,7 @@ static void iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
                if ( *endp != '\0' ) {
                        DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge "
                               "byte \"%s\"\n", iscsi, buf );
+                       return -EPROTO;
                }
                chap_update ( &iscsi->chap, &byte, sizeof ( byte ) );
        }
@@ -619,6 +678,8 @@ static void iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
        DBGC ( iscsi, "iSCSI %p sending CHAP response\n", iscsi );
        chap_respond ( &iscsi->chap );
        iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
+
+       return 0;
 }
 
 /** An iSCSI text string that we want to handle */
@@ -633,9 +694,9 @@ struct iscsi_string_type {
         *
         * @v iscsi             iSCSI session
         * @v value             iSCSI string value
+        * @ret rc              Return status code
         */
-       void ( * handle_value ) ( struct iscsi_session *iscsi,
-                                 const char *value );
+       int ( * handle ) ( struct iscsi_session *iscsi, const char *value );
 };
 
 /** iSCSI text strings that we want to handle */
@@ -653,22 +714,29 @@ struct iscsi_string_type iscsi_string_types[] = {
  *
  * @v iscsi            iSCSI session
  * @v string           iSCSI string (in "key=value" format)
+ * @ret rc             Return status code
  */
-static void iscsi_handle_string ( struct iscsi_session *iscsi,
-                                 const char *string ) {
+static int iscsi_handle_string ( struct iscsi_session *iscsi,
+                                const char *string ) {
        struct iscsi_string_type *type;
        size_t key_len;
+       int rc;
 
        for ( type = iscsi_string_types ; type->key ; type++ ) {
                key_len = strlen ( type->key );
-               if ( strncmp ( string, type->key, key_len ) == 0 ) {
-                       DBGC ( iscsi, "iSCSI %p handling %s\n",
-                              iscsi, string );
-                       type->handle_value ( iscsi, ( string + key_len ) );
-                       return;
+               if ( strncmp ( string, type->key, key_len ) != 0 )
+                       continue;
+               DBGC ( iscsi, "iSCSI %p handling %s\n", iscsi, string );
+               if ( ( rc = type->handle ( iscsi,
+                                          ( string + key_len ) ) ) != 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;
 }
 
 /**
@@ -677,10 +745,12 @@ static void iscsi_handle_string ( struct iscsi_session *iscsi,
  * @v iscsi            iSCSI session
  * @v string           iSCSI string buffer
  * @v len              Length of string buffer
+ * @ret rc             Return status code
  */
-static void iscsi_handle_strings ( struct iscsi_session *iscsi,
-                                  const char *strings, size_t len ) {
+static int iscsi_handle_strings ( struct iscsi_session *iscsi,
+                                 const char *strings, size_t len ) {
        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.
@@ -689,10 +759,42 @@ static void iscsi_handle_strings ( struct iscsi_session *iscsi,
                string_len = ( strnlen ( strings, len ) + 1 );
                if ( string_len > len )
                        break;
-               iscsi_handle_string ( iscsi, strings );
+               if ( ( rc = iscsi_handle_string ( iscsi, strings ) ) != 0 )
+                       return rc;
                strings += string_len;
                len -= string_len;
        }
+       return 0;
+}
+
+/**
+ * Receive PDU data into buffer
+ *
+ * @v iscsi            iSCSI session
+ * @v data             Data to receive
+ * @v len              Length of data
+ * @ret rc             Return 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.
+ */
+static int iscsi_rx_buffered_data ( struct iscsi_session *iscsi,
+                                   const void *data, size_t len ) {
+
+       /* 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;
 }
 
 /**
@@ -702,10 +804,11 @@ static void iscsi_handle_strings ( struct iscsi_session *iscsi,
  * @v data             Received data
  * @v len              Length of received data
  * @v remaining                Data remaining after this data
- * 
+ * @ret rc             Return status code
  */
-static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *data,
-                                     size_t len, size_t remaining ) {
+static int iscsi_rx_login_response ( struct iscsi_session *iscsi,
+                                    const void *data, size_t len,
+                                    size_t remaining ) {
        struct iscsi_bhs_login_response *response
                = &iscsi->rx_bhs.login_response;
        int rc;
@@ -714,35 +817,27 @@ static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *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 ) );
-               iscsi_close ( iscsi );
-               iscsi_done ( iscsi, rc );
-               return;
+               return rc;
        }
        if ( remaining )
-               return;
+               return 0;
 
        /* Process string data and discard string buffer */
-       iscsi_handle_strings ( iscsi, iscsi->rx_buffer, iscsi->rx_len );
+       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 ( iscsi );
-               if ( ( rc = tcp_open ( &iscsi->stream ) ) != 0 ) {
-                       DBGC ( iscsi, "iSCSI %p could not open stream: %s\n ",
+               iscsi_close_connection ( iscsi, 0 );
+               if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not redirect: %s\n ",
                               iscsi, strerror ( rc ) );
-                       iscsi_done ( iscsi, rc );
-                       return;
-               }
-               if ( ( rc = stream_connect ( &iscsi->stream,
-                                            &iscsi->target ) != 0 ) != 0 ) {
-                       DBGC ( iscsi, "iSCSI %p could not connect: %s\n ",
-                              iscsi, strerror ( rc ) );
-                       iscsi_done ( iscsi, rc );
-                       return;
+                       return rc;
                }
-               return;
+               return 0;
        }
 
        /* Check for fatal errors */
@@ -750,9 +845,7 @@ static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *data,
                DBGC ( iscsi, "iSCSI login failure: class %02x detail %02x\n",
                       response->status_class, response->status_detail );
                iscsi->instant_rc = -EPERM;
-               iscsi_close ( iscsi );
-               iscsi_done ( iscsi, -EPERM );
-               return;
+               return -EPERM;
        }
 
        /* Handle login transitions */
@@ -769,9 +862,7 @@ static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *data,
                default:
                        DBGC ( iscsi, "iSCSI %p got invalid response flags "
                               "%02x\n", iscsi, response->flags );
-                       iscsi_close ( iscsi );
-                       iscsi_done ( iscsi, -EIO );
-                       return;
+                       return -EIO;
                }
        }
 
@@ -781,7 +872,7 @@ static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *data,
        if ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) !=
             ISCSI_STATUS_FULL_FEATURE_PHASE ) {
                iscsi_start_login ( iscsi );
-               return;
+               return 0;
        }
 
        /* Reset retry count */
@@ -792,19 +883,16 @@ static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *data,
        
        /* Send the actual SCSI command */
        iscsi_start_command ( iscsi );
+
+       return 0;
 }
 
 /****************************************************************************
  *
- * iSCSI to stream interface
+ * iSCSI to socket interface
  *
  */
 
-static inline struct iscsi_session *
-stream_to_iscsi ( struct stream_application *app ) {
-       return container_of ( app, struct iscsi_session, stream );
-}
-
 /**
  * Start up a new TX PDU
  *
@@ -821,36 +909,63 @@ static void iscsi_start_tx ( struct iscsi_session *iscsi ) {
 
        /* Flag TX engine to start transmitting */
        iscsi->tx_state = ISCSI_TX_BHS;
-       iscsi->tx_offset = 0;
+}
+
+/**
+ * Transmit basic header segment of an iSCSI PDU
+ *
+ * @v iscsi            iSCSI session
+ * @ret rc             Return status code
+ */
+static int iscsi_tx_bhs ( struct iscsi_session *iscsi ) {
+       return xfer_deliver_raw ( &iscsi->socket,  &iscsi->tx_bhs,
+                                 sizeof ( iscsi->tx_bhs ) );
 }
 
 /**
  * Transmit data segment of an iSCSI PDU
  *
  * @v iscsi            iSCSI session
- * @v buf              Temporary data buffer
- * @v len              Length of temporary data buffer
+ * @ret rc             Return status code
  * 
  * Handle transmission of part of a PDU data segment.  iscsi::tx_bhs
  * will be valid when this is called.
  */
-static void iscsi_tx_data ( struct iscsi_session *iscsi,
-                           void *buf, size_t len ) {
+static int iscsi_tx_data ( struct iscsi_session *iscsi ) {
        struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
 
        switch ( common->opcode & ISCSI_OPCODE_MASK ) {
        case ISCSI_OPCODE_DATA_OUT:
-               iscsi_tx_data_out ( iscsi, buf, len );
-               break;
+               return iscsi_tx_data_out ( iscsi );
        case ISCSI_OPCODE_LOGIN_REQUEST:
-               iscsi_tx_login_request ( iscsi, buf, len );
-               break;
+               return iscsi_tx_login_request ( iscsi );
        default:
                assert ( 0 );
-               break;
+               return -EINVAL;
        }
 }
 
+/**
+ * Transmit data padding of an iSCSI PDU
+ *
+ * @v iscsi            iSCSI session
+ * @ret rc             Return status code
+ * 
+ * Handle transmission of any data padding in a PDU data segment.
+ * iscsi::tx_bhs will be valid when this is called.
+ */
+static int iscsi_tx_data_padding ( struct iscsi_session *iscsi ) {
+       static const char pad[] = { '\0', '\0', '\0' };
+       struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
+       size_t pad_len;
+       
+       pad_len = ISCSI_DATA_PAD_LEN ( common->lengths );
+       if ( ! pad_len )
+               return 0;
+
+       return xfer_deliver_raw ( &iscsi->socket, pad, pad_len );
+}
+
 /**
  * Complete iSCSI PDU transmission
  *
@@ -874,62 +989,6 @@ static void iscsi_tx_done ( struct iscsi_session *iscsi ) {
        }
 }
 
-/**
- * Handle stream ACKs
- *
- * @v iscsi            iSCSI session
- * 
- * Updates iscsi->tx_offset and, if applicable, transitions to the
- * next TX state.
- */
-static void iscsi_acked ( struct stream_application *app, size_t len ) {
-       struct iscsi_session *iscsi = stream_to_iscsi ( app );
-       struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
-       enum iscsi_tx_state next_state;
-       
-       iscsi->tx_offset += len;
-       while ( 1 ) {
-               switch ( iscsi->tx_state ) {
-               case ISCSI_TX_BHS:
-                       iscsi->tx_len = sizeof ( iscsi->tx_bhs );
-                       next_state = ISCSI_TX_AHS;
-                       break;
-               case ISCSI_TX_AHS:
-                       iscsi->tx_len = 4 * ISCSI_AHS_LEN ( common->lengths );
-                       next_state = ISCSI_TX_DATA;
-                       break;
-               case ISCSI_TX_DATA:
-                       iscsi->tx_len = ISCSI_DATA_LEN ( common->lengths );
-                       next_state = ISCSI_TX_DATA_PADDING;
-                       break;
-               case ISCSI_TX_DATA_PADDING:
-                       iscsi->tx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
-                       next_state = ISCSI_TX_IDLE;
-                       break;
-               case ISCSI_TX_IDLE:
-                       return;
-               default:
-                       assert ( 0 );
-                       return;
-               }
-               assert ( iscsi->tx_offset <= iscsi->tx_len );
-
-               /* If the whole of the current portion has not yet
-                * been acked, stay in this state for now.
-                */
-               if ( iscsi->tx_offset != iscsi->tx_len )
-                       return;
-
-               /* Move to next state.  Call iscsi_tx_done() when PDU
-                * transmission is complete.
-                */
-               iscsi->tx_state = next_state;
-               iscsi->tx_offset = 0;
-               if ( next_state == ISCSI_TX_IDLE )
-                       iscsi_tx_done ( iscsi );
-       }
-}
-
 /**
  * Transmit iSCSI PDU
  *
@@ -939,30 +998,37 @@ static void iscsi_acked ( struct stream_application *app, size_t len ) {
  * 
  * Constructs data to be sent for the current TX state
  */
-static void iscsi_senddata ( struct stream_application *app,
-                            void *buf, size_t len ) {
-       struct iscsi_session *iscsi = stream_to_iscsi ( app );
-       struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
-       static const char pad[] = { '\0', '\0', '\0' };
+static void iscsi_tx_step ( struct process *process ) {
+       struct iscsi_session *iscsi =
+               container_of ( process, struct iscsi_session, process );
+       int rc = 0;
+
+       if ( xfer_window ( &iscsi->socket ) == 0 )
+               return;
 
        switch ( iscsi->tx_state ) {
        case ISCSI_TX_IDLE:
                /* Nothing to send */
                break;
        case ISCSI_TX_BHS:
-               stream_send ( app, &iscsi->tx_bhs.bytes[iscsi->tx_offset],
-                          ( sizeof ( iscsi->tx_bhs ) - iscsi->tx_offset ) );
+               if ( ( rc = iscsi_tx_bhs ( iscsi ) ) != 0 )
+                       break;
+               iscsi->tx_state = ISCSI_TX_AHS;
                break;
        case ISCSI_TX_AHS:
                /* We don't yet have an AHS transmission mechanism */
-               assert ( 0 );
+               iscsi->tx_state = ISCSI_TX_DATA;
                break;
        case ISCSI_TX_DATA:
-               iscsi_tx_data ( iscsi, buf, len );
+               if ( ( rc = iscsi_tx_data ( iscsi ) ) != 0 )
+                       break;
+               iscsi->tx_state = ISCSI_TX_DATA_PADDING;
                break;
        case ISCSI_TX_DATA_PADDING:
-               stream_send ( app, pad, ( ISCSI_DATA_PAD_LEN( common->lengths )
-                                         - iscsi->tx_offset ) );
+               if ( ( rc = iscsi_tx_data_padding ( iscsi ) ) != 0 )
+                       break;
+               iscsi->tx_state = ISCSI_TX_IDLE;
+               iscsi_tx_done ( iscsi );
                break;
        default:
                assert ( 0 );
@@ -971,47 +1037,26 @@ static void iscsi_senddata ( struct stream_application *app,
 }
 
 /**
- * Receive data segment of an iSCSI PDU
+ * Receive basic header segment of an iSCSI PDU
  *
  * @v iscsi            iSCSI session
  * @v data             Received data
  * @v len              Length of received data
  * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
  *
- * Handle processing of part of a PDU data segment.  iscsi::rx_bhs
- * will be valid when this is called.
+ * This fills in iscsi::rx_bhs with the data from the BHS portion of
+ * the received PDU.
  */
-static void iscsi_rx_data ( struct iscsi_session *iscsi, void *data,
-                           size_t len, size_t remaining ) {
-       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:
-               iscsi_rx_login_response ( iscsi, data, len, remaining );
-               break;
-       case ISCSI_OPCODE_SCSI_RESPONSE:
-               iscsi_rx_scsi_response ( iscsi, data, len, remaining );
-               break;
-       case ISCSI_OPCODE_DATA_IN:
-               iscsi_rx_data_in ( iscsi, data, len, remaining );
-               break;
-       case ISCSI_OPCODE_R2T:
-               iscsi_rx_r2t ( iscsi, data, len, remaining );
-               break;
-       default:
-               if ( remaining )
-                       return;
-               DBGC ( iscsi, "iSCSI %p unknown opcode %02x\n", iscsi,
-                      response->opcode );
-               iscsi_close ( iscsi );
-               iscsi_done ( iscsi, -EOPNOTSUPP );
-               break;
+static int iscsi_rx_bhs ( struct iscsi_session *iscsi, const void *data,
+                         size_t len, size_t remaining __unused ) {
+       memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
+       if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
+               DBGC ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n",
+                      iscsi, iscsi->rx_bhs.common.opcode,
+                      ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
        }
+       return 0;
 }
 
 /**
@@ -1021,42 +1066,63 @@ static void iscsi_rx_data ( struct iscsi_session *iscsi, void *data,
  * @v data             Received data
  * @v len              Length of received data
  * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
  *
  * This discards data from a portion of a received PDU.
  */
-static void iscsi_rx_discard ( struct iscsi_session *iscsi __unused,
-                              void *data __unused, size_t len __unused,
-                              size_t remaining __unused ) {
+static int iscsi_rx_discard ( struct iscsi_session *iscsi __unused,
+                             const void *data __unused, size_t len __unused,
+                             size_t remaining __unused ) {
        /* Do nothing */
+       return 0;
 }
 
 /**
- * Receive basic header segment of an iSCSI PDU
+ * Receive data segment of an iSCSI PDU
  *
  * @v iscsi            iSCSI session
  * @v data             Received data
  * @v len              Length of received data
  * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
  *
- * This fills in iscsi::rx_bhs with the data from the BHS portion of
- * the received PDU.
+ * Handle processing of part of a PDU data segment.  iscsi::rx_bhs
+ * will be valid when this is called.
  */
-static void iscsi_rx_bhs ( struct iscsi_session *iscsi, void *data,
-                          size_t len, size_t remaining __unused ) {
-       memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
-       if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
-               DBGC ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n",
-                      iscsi, iscsi->rx_bhs.common.opcode,
-                      ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
+static int iscsi_rx_data ( struct iscsi_session *iscsi, const void *data,
+                          size_t len, size_t remaining ) {
+       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 );
+       default:
+               if ( remaining )
+                       return 0;
+               DBGC ( iscsi, "iSCSI %p unknown opcode %02x\n", iscsi,
+                      response->opcode );
+               return -EOPNOTSUPP;
        }
 }
 
 /**
  * Receive new data
  *
- * @v stream           Stream application
+ * @v socket           Transport layer interface
  * @v data             Received data
  * @v len              Length of received data
+ * @ret rc             Return 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,
@@ -1065,15 +1131,17 @@ static void iscsi_rx_bhs ( struct iscsi_session *iscsi, void *data,
  * always has a full copy of the BHS available, even for portions of
  * the data in different packets to the BHS.
  */
-static void iscsi_newdata ( struct stream_application *app, void *data,
-                           size_t len ) {
-       struct iscsi_session *iscsi = stream_to_iscsi ( app );
+static int iscsi_socket_deliver_raw ( struct xfer_interface *socket,
+                                     const void *data, size_t len ) {
+       struct iscsi_session *iscsi =
+               container_of ( socket, struct iscsi_session, socket );
        struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
-       void ( *process ) ( struct iscsi_session *iscsi, void *data,
-                           size_t len, size_t remaining );
+       int ( *process ) ( 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 ) {
@@ -1099,14 +1167,21 @@ static void iscsi_newdata ( struct stream_application *app, void *data,
                        break;
                default:
                        assert ( 0 );
-                       return;
+                       return -EINVAL;
                }
 
                frag_len = iscsi->rx_len - iscsi->rx_offset;
                if ( frag_len > len )
                        frag_len = len;
                remaining = iscsi->rx_len - iscsi->rx_offset - frag_len;
-               process ( iscsi, data, frag_len, remaining );
+               if ( ( rc = process ( iscsi, data, frag_len,
+                                     remaining ) ) != 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not process received "
+                              "data: %s\n", iscsi, strerror ( rc ) );
+                       iscsi_close_connection ( iscsi, rc );
+                       iscsi_scsi_done ( iscsi, rc );
+                       return rc;
+               }
 
                iscsi->rx_offset += frag_len;
                data += frag_len;
@@ -1116,85 +1191,58 @@ static void iscsi_newdata ( struct stream_application *app, void *data,
                 * received, stay in this state for now.
                 */
                if ( iscsi->rx_offset != iscsi->rx_len )
-                       return;
+                       return 0;
 
                iscsi->rx_state = next_state;
                iscsi->rx_offset = 0;
        }
+
+       return 0;
 }
 
 /**
  * Handle stream connection closure
  *
- * @v app              Stream application
- * @v status           Error code, if any
+ * @v socket           Transport layer interface
+ * @v rc               Reason for close
  *
  */
-static void iscsi_closed ( struct stream_application *app, int status ) {
-       struct iscsi_session *iscsi = stream_to_iscsi ( app );
-       int rc;
+static void iscsi_socket_close ( struct xfer_interface *socket, int rc ) {
+       struct iscsi_session *iscsi =
+               container_of ( socket, struct iscsi_session, socket );
 
        /* Even a graceful close counts as an error for iSCSI */
-       if ( ! status )
-               status = -ECONNRESET;
+       if ( ! rc )
+               rc = -ECONNRESET;
 
        /* Close session cleanly */
-       iscsi_close ( iscsi );
+       iscsi_close_connection ( iscsi, rc );
 
        /* Retry connection if within the retry limit, otherwise fail */
        if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
                DBGC ( iscsi, "iSCSI %p retrying connection (retry #%d)\n",
                       iscsi, iscsi->retry_count );
-               if ( ( rc = tcp_open ( app ) ) != 0 ) {
-                       DBGC ( iscsi, "iSCSI %p could not open stream: %s\n ",
-                              iscsi, strerror ( rc ) );
-                       iscsi_done ( iscsi, rc );
-                       return;
-               }
-               if ( ( rc = stream_connect ( app, &iscsi->target ) ) != 0 ){
-                       DBGC ( iscsi, "iSCSI %p could not connect: %s\n",
+               if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not reconnect: %s\n",
                               iscsi, strerror ( rc ) );
-                       iscsi_done ( iscsi, rc );
-                       return;
+                       iscsi_scsi_done ( iscsi, rc );
                }
        } else {
                DBGC ( iscsi, "iSCSI %p retry count exceeded\n", iscsi );
-               iscsi->instant_rc = status;
-               iscsi_done ( iscsi, status );
-               return;
+               iscsi->instant_rc = rc;
+               iscsi_scsi_done ( iscsi, rc );
        }
 }
 
-/**
- * Handle stream connection opening
- *
- * @v app              Stream application
- *
- */
-static void iscsi_connected ( struct stream_application *app ) {
-       struct iscsi_session *iscsi = stream_to_iscsi ( app );
-
-       assert ( iscsi->rx_state == ISCSI_RX_BHS );
-       assert ( iscsi->rx_offset == 0 );
-
-       /* Enter security negotiation phase */
-       iscsi->status = ( ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE |
-                         ISCSI_STATUS_STRINGS_SECURITY );
-
-       /* Assign fresh initiator task tag */
-       iscsi->itt++;
-
-       /* Start logging in */
-       iscsi_start_login ( iscsi );
-}
-
-/** iSCSI stream operations */
-static struct stream_application_operations iscsi_stream_operations = {
-       .closed         = iscsi_closed,
-       .connected      = iscsi_connected,
-       .acked          = iscsi_acked,
-       .newdata        = iscsi_newdata,
-       .senddata       = iscsi_senddata,
+/** iSCSI socket operations */
+static struct xfer_interface_operations iscsi_socket_operations = {
+       .close          = iscsi_socket_close,
+       .vredirect      = xfer_vopen,
+       .seek           = ignore_xfer_seek,
+       .window         = unlimited_xfer_window,
+       .alloc_iob      = default_xfer_alloc_iob,
+       .deliver_iob    = xfer_deliver_as_raw,
+       .deliver_raw    = iscsi_socket_deliver_raw,
 };
 
 /**
@@ -1246,5 +1294,6 @@ int iscsi_issue ( struct iscsi_session *iscsi, struct scsi_command *command,
  * @ret aop            Asynchronous operation
  */
 void iscsi_shutdown ( struct iscsi_session *iscsi ) {
-       iscsi_close ( iscsi );
+       iscsi_close_connection ( iscsi, 0 );
+       ref_put ( &iscsi->refcnt );
 }