Changed to use the generic stream API.
authorMichael Brown <mcb30@etherboot.org>
Wed, 31 Jan 2007 02:09:13 +0000 (02:09 +0000)
committerMichael Brown <mcb30@etherboot.org>
Wed, 31 Jan 2007 02:09:13 +0000 (02:09 +0000)
src/include/gpxe/ftp.h
src/include/gpxe/http.h
src/include/gpxe/iscsi.h
src/include/gpxe/stream.h
src/include/gpxe/tcp.h
src/net/stream.c
src/net/tcp.c
src/net/tcp/ftp.c
src/net/tcp/http.c
src/net/tcp/iscsi.c

index 64e8d4e..41eca8c 100644 (file)
@@ -9,7 +9,7 @@
 
 #include <stdint.h>
 #include <gpxe/async.h>
-#include <gpxe/tcp.h>
+#include <gpxe/stream.h>
 
 struct buffer;
 
@@ -57,10 +57,10 @@ struct ftp_request {
        char status_text[4];
        /** Passive-mode parameters, as text */
        char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */
-       /** TCP application for the control channel */
-       struct tcp_application tcp;
-       /** TCP application for the data channel */
-       struct tcp_application tcp_data;
+       /** Stream application for the control channel */
+       struct stream_application stream;
+       /** Stream application for the data channel */
+       struct stream_application stream_data;
 };
 
 extern int ftp_get ( struct uri *uri, struct buffer *buffer,
index 3cfc888..911a748 100644 (file)
@@ -8,7 +8,7 @@
  */
 
 #include <stdint.h>
-#include <gpxe/tcp.h>
+#include <gpxe/stream.h>
 #include <gpxe/async.h>
 #include <gpxe/linebuf.h>
 #include <gpxe/uri.h>
@@ -43,8 +43,8 @@ struct http_request {
 
        /** Server address */
        struct sockaddr server;
-       /** TCP application for this request */
-       struct tcp_application tcp;
+       /** Stream application for this request */
+       struct stream_application stream;
        /** Number of bytes already sent */
        size_t tx_offset;
        /** RX state */
index 7456064..62b789c 100644 (file)
@@ -8,7 +8,7 @@
  */
 
 #include <stdint.h>
-#include <gpxe/tcp.h>
+#include <gpxe/stream.h>
 #include <gpxe/async.h>
 #include <gpxe/scsi.h>
 #include <gpxe/chap.h>
@@ -489,7 +489,7 @@ struct iscsi_session {
        /** Initiator IQN */
        const char *initiator_iqn;
        /** Target address */
-       struct sockaddr_tcpip target;
+       struct sockaddr target;
        /** Target IQN */
        const char *target_iqn;
        /** Logical Unit Number (LUN) */
@@ -499,8 +499,8 @@ struct iscsi_session {
        /** Password */
        const char *password;
 
-       /** TCP application for this session */
-       struct tcp_application tcp;
+       /** Stream application for this session */
+       struct stream_application stream;
        /** Session status
         *
         * This is the bitwise-OR of zero or more ISCSI_STATUS_XXX
index 8d1c5f5..545bfae 100644 (file)
@@ -125,7 +125,7 @@ struct stream_connection_operations {
         * application's senddata() method.
         */
        int ( * send ) ( struct stream_connection *conn,
-                        void *data, size_t len );
+                        const void *data, size_t len );
        /**
         * Notify connection that data is available to send
         *
@@ -167,6 +167,9 @@ struct stream_connection {
        struct stream_connection_operations *op;        
 };
 
+extern void stream_associate ( struct stream_application *app,
+                              struct stream_connection *conn );
+
 extern void stream_connected ( struct stream_connection *conn );
 extern void stream_closed ( struct stream_connection *conn, int rc );
 extern void stream_senddata ( struct stream_connection *conn,
@@ -181,7 +184,7 @@ extern int stream_connect ( struct stream_application *app,
                            struct sockaddr *peer );
 extern void stream_close ( struct stream_application *app );
 extern int stream_send ( struct stream_application *app,
-                        void *data, size_t len );
+                        const void *data, size_t len );
 extern int stream_kick ( struct stream_application *app );
 
 #endif /* _GPXE_STREAM_H */
index f618ae3..576898e 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "latch.h"
 #include <gpxe/tcpip.h>
+#include <gpxe/stream.h>
 
 /**
  * A TCP header
@@ -252,105 +253,7 @@ struct tcp_mss_option {
  */
 #define TCP_MSL ( 2 * 60 * TICKS_PER_SEC )
 
-struct tcp_application;
-
-/**
- * TCP operations
- *
- */
-struct tcp_operations {
-       /*
-        * Connection closed
-        *
-        * @v app       TCP application
-        * @v status    Error code, if any
-        *
-        * This is called when the connection is closed for any
-        * reason, including timeouts or aborts.  The status code
-        * contains the negative error number, if the closure is due
-        * to an error.
-        *
-        * When closed() is called, the application no longer has a
-        * valid TCP connection.  Note that connected() may not have
-        * been called before closed(), if the close is due to an
-        * error during connection setup.
-        */
-       void ( * closed ) ( struct tcp_application *app, int status );
-       /**
-        * Connection established
-        *
-        * @v app       TCP application
-        */
-       void ( * connected ) ( struct tcp_application *app );
-       /**
-        * Data acknowledged
-        *
-        * @v app       TCP application
-        * @v len       Length of acknowledged data
-        *
-        * @c len is guaranteed to not exceed the outstanding amount
-        * of unacknowledged data.
-        */
-       void ( * acked ) ( struct tcp_application *app, size_t len );
-       /**
-        * New data received
-        *
-        * @v app       TCP application
-        * @v data      Data
-        * @v len       Length of data
-        */
-       void ( * newdata ) ( struct tcp_application *app,
-                            void *data, size_t len );
-       /**
-        * Transmit data
-        *
-        * @v app       TCP application
-        * @v buf       Temporary data buffer
-        * @v len       Length of temporary data buffer
-        *
-        * The application should transmit whatever it currently wants
-        * to send using tcp_send().  If retransmissions are required,
-        * senddata() will be called again and the application must
-        * regenerate the data.  The easiest way to implement this is
-        * to ensure that senddata() never changes the application's
-        * state.
-        *
-        * The application may use the temporary data buffer to
-        * construct the data to be sent.  Note that merely filling
-        * the buffer will do nothing; the application must call
-        * tcp_send() in order to actually transmit the data.  Use of
-        * the buffer is not compulsory; the application may call
-        * tcp_send() on any block of data.
-        */
-       void ( * senddata ) ( struct tcp_application *app, void *buf,
-                             size_t len );
-};
-
-struct tcp_connection;
-
-/**
- * A TCP application
- *
- * This data structure represents an application with a TCP connection.
- */
-struct tcp_application {
-       /** TCP connection data
-        *
-        * This is filled in by TCP calls that initiate a connection,
-        * and reset to NULL when the connection is closed.
-        */
-       struct tcp_connection *conn;
-       /** TCP connection operations table */
-       struct tcp_operations *tcp_op;
-};
-
-extern int tcp_connect ( struct tcp_application *app,
-                        struct sockaddr_tcpip *peer,
-                        uint16_t local_port );
-extern void tcp_close ( struct tcp_application *app );
-extern int tcp_senddata ( struct tcp_application *app );
-extern int tcp_send ( struct tcp_application *app, const void *data, 
-                     size_t len );
+extern int tcp_open ( struct stream_application *app );
 
 extern struct tcpip_protocol tcp_protocol;
 
index 92e00d7..e255d85 100644 (file)
 #include <stdint.h>
 #include <string.h>
 #include <errno.h>
+#include <assert.h>
 #include <gpxe/stream.h>
 
+/**
+ * Associate application with connection
+ *
+ * @v app              Stream application
+ * @v conn             Stream connection
+ */
+void stream_associate ( struct stream_application *app,
+                       struct stream_connection *conn ) {
+
+       DBGC ( app, "Stream %p associating with connection %p\n", app, conn );
+
+       assert ( conn->app == NULL );
+       assert ( app->conn == NULL );
+       conn->app = app;
+       app->conn = conn;
+}
+
 /**
  * Connection established
  *
@@ -45,7 +63,8 @@ void stream_connected ( struct stream_connection *conn ) {
        }
 
        /* Hand off to application */
-       app->op->connected ( app );
+       if ( app->op->connected )
+               app->op->connected ( app );
 }
 
 /**
@@ -66,8 +85,13 @@ void stream_closed ( struct stream_connection *conn, int rc ) {
                return;
        }
 
+       /* Disassociate application from connection */
+       app->conn = NULL;
+       conn->app = NULL;
+
        /* Hand off to application */
-       app->op->closed ( app, rc );
+       if ( app->op->closed )
+               app->op->closed ( app, rc );
 }
 
 /**
@@ -91,7 +115,8 @@ void stream_senddata ( struct stream_connection *conn,
        }
 
        /* Hand off to application */
-       app->op->senddata ( app, data, len );
+       if ( app->op->senddata )
+               app->op->senddata ( app, data, len );
 }
 
 /**
@@ -116,7 +141,8 @@ void stream_acked ( struct stream_connection *conn, size_t len ) {
        }
 
        /* Hand off to application */
-       app->op->acked ( app, len );
+       if ( app->op->acked )
+               app->op->acked ( app, len );
 }
 
 /**
@@ -140,7 +166,8 @@ void stream_newdata ( struct stream_connection *conn,
        }
 
        /* Hand off to application */
-       app->op->newdata ( app, data, len );
+       if ( app->op->newdata )
+               app->op->newdata ( app, data, len );
 }
 
 /**
@@ -163,6 +190,8 @@ int stream_bind ( struct stream_application *app, struct sockaddr *local ) {
        }
 
        /* Hand off to connection */
+       if ( ! conn->op->bind )
+               return -ENOTSUP;
        if ( ( rc = conn->op->bind ( conn, local ) ) != 0 ) {
                DBGC ( app, "Stream %p failed to bind: %s\n",
                       app, strerror ( rc ) );
@@ -192,6 +221,8 @@ int stream_connect ( struct stream_application *app, struct sockaddr *peer ) {
        }
 
        /* Hand off to connection */
+       if ( ! conn->op->connect )
+               return -ENOTSUP;
        if ( ( rc = conn->op->connect ( conn, peer ) ) != 0 ) {
                DBGC ( app, "Stream %p failed to connect: %s\n",
                       app, strerror ( rc ) );
@@ -217,7 +248,13 @@ void stream_close ( struct stream_application *app ) {
                return;
        }
 
+       /* Disassociate application from connection */
+       app->conn = NULL;
+       conn->app = NULL;
+
        /* Hand off to connection */
+       if ( ! conn->op->close )
+               return;
        conn->op->close ( conn );
 }
 
@@ -232,7 +269,8 @@ void stream_close ( struct stream_application *app ) {
  * This method should be called only in the context of an
  * application's senddata() method.
  */
-int stream_send ( struct stream_application *app, void *data, size_t len ) {
+int stream_send ( struct stream_application *app,
+                 const void *data, size_t len ) {
        struct stream_connection *conn = app->conn;
        int rc;
 
@@ -245,6 +283,8 @@ int stream_send ( struct stream_application *app, void *data, size_t len ) {
        }
 
        /* Hand off to connection */
+       if ( ! conn->op->send )
+               return -ENOTSUP;
        if ( ( rc = conn->op->send ( conn, data, len ) ) != 0 ) {
                DBGC ( app, "Stream %p failed to send %zd bytes: %s\n",
                       app, len, strerror ( rc ) );
@@ -273,6 +313,8 @@ int stream_kick ( struct stream_application *app ) {
        }
 
        /* Hand off to connection */
+       if ( ! conn->op->send )
+               return -ENOTSUP;
        if ( ( rc = conn->op->kick ( conn ) ) != 0 ) {
                DBGC ( app, "Stream %p failed to kick connection: %s\n",
                       app, strerror ( rc ) );
index 2c2f0b3..056c3e3 100644 (file)
@@ -8,6 +8,7 @@
 #include <gpxe/pkbuff.h>
 #include <gpxe/malloc.h>
 #include <gpxe/retry.h>
+#include <gpxe/stream.h>
 #include <gpxe/tcpip.h>
 #include <gpxe/tcp.h>
 
  *
  */
 
+struct tcp_connection;
 static void tcp_expired ( struct retry_timer *timer, int over );
-static int tcp_senddata_conn ( struct tcp_connection *conn, int force_send );
+static int tcp_senddata_conn ( struct tcp_connection *tcp, int force_send );
+static struct stream_connection_operations tcp_op;
 
 /**
  * A TCP connection
  *
  * This data structure represents the internal state of a TCP
- * connection.  It is kept separate from @c struct @c tcp_application
- * because the internal state is still required for some time after
- * the application closes the connection.
+ * connection.
  */
 struct tcp_connection {
+       /** The stream connection */
+       struct stream_connection stream;
        /** List of TCP connections */
        struct list_head list;
-       /** The associated TCP application, if any */
-       struct tcp_application *app;
 
        /** Remote socket address */
        struct sockaddr_tcpip peer;
@@ -108,17 +109,17 @@ tcp_state ( int state ) {
 /**
  * Dump TCP state transition
  *
- * @v conn             TCP connection
+ * @v tcp              TCP connection
  */
 static inline __attribute__ (( always_inline )) void
-tcp_dump_state ( struct tcp_connection *conn ) {
+tcp_dump_state ( struct tcp_connection *tcp ) {
 
-       if ( conn->tcp_state != conn->prev_tcp_state ) {
-               DBGC ( conn, "TCP %p transitioned from %s to %s\n", conn,
-                      tcp_state ( conn->prev_tcp_state ),
-                      tcp_state ( conn->tcp_state ) );
+       if ( tcp->tcp_state != tcp->prev_tcp_state ) {
+               DBGC ( tcp, "TCP %p transitioned from %s to %s\n", tcp,
+                      tcp_state ( tcp->prev_tcp_state ),
+                      tcp_state ( tcp->tcp_state ) );
        }
-       conn->prev_tcp_state = conn->tcp_state;
+       tcp->prev_tcp_state = tcp->tcp_state;
 }
 
 /**
@@ -127,17 +128,17 @@ tcp_dump_state ( struct tcp_connection *conn ) {
  * @v flags            TCP flags
  */
 static inline __attribute__ (( always_inline )) void
-tcp_dump_flags ( struct tcp_connection *conn, unsigned int flags ) {
+tcp_dump_flags ( struct tcp_connection *tcp, unsigned int flags ) {
        if ( flags & TCP_RST )
-               DBGC ( conn, " RST" );
+               DBGC ( tcp, " RST" );
        if ( flags & TCP_SYN )
-               DBGC ( conn, " SYN" );
+               DBGC ( tcp, " SYN" );
        if ( flags & TCP_PSH )
-               DBGC ( conn, " PSH" );
+               DBGC ( tcp, " PSH" );
        if ( flags & TCP_FIN )
-               DBGC ( conn, " FIN" );
+               DBGC ( tcp, " FIN" );
        if ( flags & TCP_ACK )
-               DBGC ( conn, " ACK" );
+               DBGC ( tcp, " ACK" );
 }
 
 /**
@@ -148,105 +149,68 @@ tcp_dump_flags ( struct tcp_connection *conn, unsigned int flags ) {
  * Allocates TCP connection and adds it to the TCP connection list.
  */
 static struct tcp_connection * alloc_tcp ( void ) {
-       struct tcp_connection *conn;
-
-       conn = malloc ( sizeof ( *conn ) );
-       if ( conn ) {
-               DBGC ( conn, "TCP %p allocated\n", conn );
-               memset ( conn, 0, sizeof ( *conn ) );
-               conn->tcp_state = conn->prev_tcp_state = TCP_CLOSED;
-               conn->snd_seq = random();
-               conn->timer.expired = tcp_expired;
-               list_add ( &conn->list, &tcp_conns );
-       }
-       return conn;
+       struct tcp_connection *tcp;
+
+       tcp = malloc ( sizeof ( *tcp ) );
+       if ( tcp ) {
+               DBGC ( tcp, "TCP %p allocated\n", tcp );
+               memset ( tcp, 0, sizeof ( *tcp ) );
+               tcp->tcp_state = tcp->prev_tcp_state = TCP_CLOSED;
+               tcp->snd_seq = random();
+               tcp->timer.expired = tcp_expired;
+               tcp->stream.op = &tcp_op;
+               list_add ( &tcp->list, &tcp_conns );
+       }
+       return tcp;
 }
 
 /**
  * Free TCP connection
  *
- * @v conn             TCP connection
+ * @v tcp              TCP connection
  *
  * Removes connection from TCP connection list and frees the data
  * structure.
  */
-static void free_tcp ( struct tcp_connection *conn ) {
+static void free_tcp ( struct tcp_connection *tcp ) {
 
-       assert ( conn );
-       assert ( conn->tcp_state == TCP_CLOSED );
-       assert ( conn->app == NULL );
+       assert ( tcp );
+       assert ( tcp->tcp_state == TCP_CLOSED );
 
-       stop_timer ( &conn->timer );
-       list_del ( &conn->list );
-       free ( conn );
-       DBGC ( conn, "TCP %p freed\n", conn );
-}
-
-/**
- * Associate TCP connection with application
- *
- * @v conn             TCP connection
- * @v app              TCP application
- */
-static void tcp_associate ( struct tcp_connection *conn,
-                           struct tcp_application *app ) {
-       assert ( conn->app == NULL );
-       assert ( app->conn == NULL );
-       conn->app = app;
-       app->conn = conn;
-       DBGC ( conn, "TCP %p associated with application %p\n", conn, app );
-}
-
-/**
- * Disassociate TCP connection from application
- *
- * @v conn             TCP connection
- */
-static void tcp_disassociate ( struct tcp_connection *conn ) {
-       struct tcp_application *app = conn->app;
-
-       if ( app ) {
-               assert ( app->conn == conn );
-               conn->app = NULL;
-               app->conn = NULL;
-               DBGC ( conn, "TCP %p disassociated from application %p\n",
-                      conn, app );
-       }
+       stop_timer ( &tcp->timer );
+       list_del ( &tcp->list );
+       free ( tcp );
+       DBGC ( tcp, "TCP %p freed\n", tcp );
 }
 
 /**
  * Abort TCP connection
  *
- * @v conn             TCP connection
+ * @v tcp              TCP connection
  * @v send_rst         Send a RST after closing
  * @v rc               Reason code
  */
-static void tcp_abort ( struct tcp_connection *conn, int send_rst, int rc ) {
-       struct tcp_application *app = conn->app;
+static void tcp_abort ( struct tcp_connection *tcp, int send_rst, int rc ) {
 
        /* Transition to CLOSED */
-       conn->tcp_state = TCP_CLOSED;
-       tcp_dump_state ( conn );
+       tcp->tcp_state = TCP_CLOSED;
+       tcp_dump_state ( tcp );
 
        /* Send RST if requested to do so */
        if ( send_rst )
-               tcp_senddata_conn ( conn, 1 );
+               tcp_senddata_conn ( tcp, 1 );
 
-       /* Break association between application and connection */
-       tcp_disassociate ( conn );
+       /* Close stream */
+       stream_closed ( &tcp->stream, rc );
 
        /* Free the connection */
-       free_tcp ( conn );
-
-       /* Notify application */
-       if ( app && app->tcp_op->closed )
-               app->tcp_op->closed ( app, rc );
+       free_tcp ( tcp );
 }
 
 /**
  * Transmit any outstanding data
  *
- * @v conn             TCP connection
+ * @v tcp              TCP connection
  * @v force_send       Force sending of packet
  * 
  * Transmits any outstanding data on the connection.  If the
@@ -257,8 +221,7 @@ static void tcp_abort ( struct tcp_connection *conn, int send_rst, int rc ) {
  * will have been started if necessary, and so the stack will
  * eventually attempt to retransmit the failed packet.
  */
-static int tcp_senddata_conn ( struct tcp_connection *conn, int force_send ) {
-       struct tcp_application *app = conn->app;
+static int tcp_senddata_conn ( struct tcp_connection *tcp, int force_send ) {
        struct pk_buff *pkb;
        struct tcp_header *tcphdr;
        struct tcp_mss_option *mssopt;
@@ -272,7 +235,7 @@ static int tcp_senddata_conn ( struct tcp_connection *conn, int force_send ) {
        /* Allocate space to the TX buffer */
        pkb = alloc_pkb ( MAX_PKB_LEN );
        if ( ! pkb ) {
-               DBGC ( conn, "TCP %p could not allocate data buffer\n", conn );
+               DBGC ( tcp, "TCP %p could not allocate data buffer\n", tcp );
                /* Start the retry timer so that we attempt to
                 * retransmit this packet later.  (Start it
                 * unconditionally, since without a packet buffer we
@@ -280,7 +243,7 @@ static int tcp_senddata_conn ( struct tcp_connection *conn, int force_send ) {
                 * be able to tell whether or not we have something
                 * that actually needs to be retransmitted).
                 */
-               start_timer ( &conn->timer );
+               start_timer ( &tcp->timer );
                return -ENOMEM;
        }
        pkb_reserve ( pkb, MAX_HDR_LEN );
@@ -288,28 +251,28 @@ static int tcp_senddata_conn ( struct tcp_connection *conn, int force_send ) {
        /* If we are connected, call the senddata() method, which may
         * call tcp_send() to queue up a data payload.
         */
-       if ( TCP_CAN_SEND_DATA ( conn->tcp_state ) &&
-            app && app->tcp_op->senddata ) {
-               conn->tx_pkb = pkb;
-               app->tcp_op->senddata ( app, pkb->data, pkb_tailroom ( pkb ) );
-               conn->tx_pkb = NULL;
+       if ( TCP_CAN_SEND_DATA ( tcp->tcp_state ) ) {
+               tcp->tx_pkb = pkb;
+               stream_senddata ( &tcp->stream, pkb->data,
+                                 pkb_tailroom ( pkb ) );
+               tcp->tx_pkb = NULL;
        }
 
        /* Truncate payload length to fit transmit window */
        len = pkb_len ( pkb );
-       if ( len > conn->snd_win )
-               len = conn->snd_win;
+       if ( len > tcp->snd_win )
+               len = tcp->snd_win;
 
        /* Calculate amount of sequence space that this transmission
         * consumes.  (SYN or FIN consume one byte, and we can never
         * send both at once).
         */
        seq_len = len;
-       flags = TCP_FLAGS_SENDING ( conn->tcp_state );
+       flags = TCP_FLAGS_SENDING ( tcp->tcp_state );
        assert ( ! ( ( flags & TCP_SYN ) && ( flags & TCP_FIN ) ) );
        if ( flags & ( TCP_SYN | TCP_FIN ) )
                seq_len++;
-       conn->snd_sent = seq_len;
+       tcp->snd_sent = seq_len;
 
        /* If we have nothing to transmit, drop the packet */
        if ( ( seq_len == 0 ) && ! force_send ) {
@@ -322,7 +285,7 @@ static int tcp_senddata_conn ( struct tcp_connection *conn, int force_send ) {
         * retransmission timer.
         */
        if ( seq_len )
-               start_timer ( &conn->timer );
+               start_timer ( &tcp->timer );
 
        /* Estimate window size */
        window = freemem;
@@ -340,25 +303,25 @@ static int tcp_senddata_conn ( struct tcp_connection *conn, int force_send ) {
        }
        tcphdr = pkb_push ( pkb, sizeof ( *tcphdr ) );
        memset ( tcphdr, 0, sizeof ( *tcphdr ) );
-       tcphdr->src = conn->local_port;
-       tcphdr->dest = conn->peer.st_port;
-       tcphdr->seq = htonl ( conn->snd_seq );
-       tcphdr->ack = htonl ( conn->rcv_ack );
+       tcphdr->src = tcp->local_port;
+       tcphdr->dest = tcp->peer.st_port;
+       tcphdr->seq = htonl ( tcp->snd_seq );
+       tcphdr->ack = htonl ( tcp->rcv_ack );
        tcphdr->hlen = ( ( payload - pkb->data ) << 2 );
        tcphdr->flags = flags;
        tcphdr->win = htons ( window );
        tcphdr->csum = tcpip_chksum ( pkb->data, pkb_len ( pkb ) );
 
        /* Dump header */
-       DBGC ( conn, "TCP %p TX %d->%d %08lx..%08lx           %08lx %4zd",
-              conn, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
+       DBGC ( tcp, "TCP %p TX %d->%d %08lx..%08lx           %08lx %4zd",
+              tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
               ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) + seq_len ),
               ntohl ( tcphdr->ack ), len );
-       tcp_dump_flags ( conn, tcphdr->flags );
-       DBGC ( conn, "\n" );
+       tcp_dump_flags ( tcp, tcphdr->flags );
+       DBGC ( tcp, "\n" );
 
        /* Transmit packet */
-       rc = tcpip_tx ( pkb, &tcp_protocol, &conn->peer, NULL, &tcphdr->csum );
+       rc = tcpip_tx ( pkb, &tcp_protocol, &tcp->peer, NULL, &tcphdr->csum );
 
        /* If we got -ENETUNREACH, kill the connection immediately
         * because there is no point retrying.  This isn't strictly
@@ -367,11 +330,11 @@ static int tcp_senddata_conn ( struct tcp_connection *conn, int force_send ) {
         * RST packets transmitted on connection abort, to avoid a
         * potential infinite loop.
         */
-       if ( ( ! ( conn->tcp_state & TCP_STATE_SENT ( TCP_RST ) ) ) &&
+       if ( ( ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_RST ) ) ) &&
             ( rc == -ENETUNREACH ) ) {
-               DBGC ( conn, "TCP %p aborting after TX failed: %s\n",
-                      conn, strerror ( rc ) );
-               tcp_abort ( conn, 0, rc );
+               DBGC ( tcp, "TCP %p aborting after TX failed: %s\n",
+                      tcp, strerror ( rc ) );
+               tcp_abort ( tcp, 0, rc );
        }
 
        return rc;
@@ -380,28 +343,23 @@ static int tcp_senddata_conn ( struct tcp_connection *conn, int force_send ) {
 /**
  * Transmit any outstanding data
  *
- * @v conn     TCP connection
+ * @v stream           TCP stream
  * 
  * This function allocates space to the transmit buffer and invokes
  * the senddata() callback function, to allow the application to
  * transmit new data.
  */
-int tcp_senddata ( struct tcp_application *app ) {
-       struct tcp_connection *conn = app->conn;
-
-       /* Check connection actually exists */
-       if ( ! conn ) {
-               DBG ( "TCP app %p has no connection\n", app );
-               return -ENOTCONN;
-       }
+static int tcp_kick ( struct stream_connection *stream ) {
+       struct tcp_connection *tcp =
+               container_of ( stream, struct tcp_connection, stream );
 
-       return tcp_senddata_conn ( conn, 0 );
+       return tcp_senddata_conn ( tcp, 0 );
 }
 
 /**
  * Transmit data
  *
- * @v app              TCP application
+ * @v stream           TCP stream
  * @v data             Data to be sent
  * @v len              Length of the data
  * @ret rc             Return status code
@@ -410,21 +368,17 @@ int tcp_senddata ( struct tcp_application *app ) {
  * can be called only in the context of an application's senddata()
  * method.
  */
-int tcp_send ( struct tcp_application *app, const void *data, size_t len ) {
-       struct tcp_connection *conn = app->conn;
+static int tcp_send ( struct stream_connection *stream,
+                     const void *data, size_t len ) {
+       struct tcp_connection *tcp =
+               container_of ( stream, struct tcp_connection, stream );
        struct pk_buff *pkb;
 
-       /* Check connection actually exists */
-       if ( ! conn ) {
-               DBG ( "TCP app %p has no connection\n", app );
-               return -ENOTCONN;
-       }
-
        /* Check that we have a packet buffer to fill */
-       pkb = conn->tx_pkb;
+       pkb = tcp->tx_pkb;
        if ( ! pkb ) {
-               DBG ( "TCP app %p tried to send data outside of the "
-                     "senddata() method\n", app );
+               DBGC ( tcp, "TCP %p tried to send data outside of the "
+                      "senddata() method\n", tcp );
                return -EINVAL;
        }
 
@@ -445,30 +399,30 @@ int tcp_send ( struct tcp_application *app, const void *data, size_t len ) {
  * @v over     Failure indicator
  */
 static void tcp_expired ( struct retry_timer *timer, int over ) {
-       struct tcp_connection *conn =
+       struct tcp_connection *tcp =
                container_of ( timer, struct tcp_connection, timer );
-       int graceful_close = TCP_CLOSED_GRACEFULLY ( conn->tcp_state );
+       int graceful_close = TCP_CLOSED_GRACEFULLY ( tcp->tcp_state );
 
-       DBGC ( conn, "TCP %p timer %s in %s\n", conn,
-              ( over ? "expired" : "fired" ), tcp_state ( conn->tcp_state ) );
+       DBGC ( tcp, "TCP %p timer %s in %s\n", tcp,
+              ( over ? "expired" : "fired" ), tcp_state ( tcp->tcp_state ) );
 
-       assert ( ( conn->tcp_state == TCP_SYN_SENT ) ||
-                ( conn->tcp_state == TCP_SYN_RCVD ) ||
-                ( conn->tcp_state == TCP_ESTABLISHED ) ||
-                ( conn->tcp_state == TCP_FIN_WAIT_1 ) ||
-                ( conn->tcp_state == TCP_TIME_WAIT ) ||
-                ( conn->tcp_state == TCP_CLOSE_WAIT ) ||
-                ( conn->tcp_state == TCP_CLOSING_OR_LAST_ACK ) );
+       assert ( ( tcp->tcp_state == TCP_SYN_SENT ) ||
+                ( tcp->tcp_state == TCP_SYN_RCVD ) ||
+                ( tcp->tcp_state == TCP_ESTABLISHED ) ||
+                ( tcp->tcp_state == TCP_FIN_WAIT_1 ) ||
+                ( tcp->tcp_state == TCP_TIME_WAIT ) ||
+                ( tcp->tcp_state == TCP_CLOSE_WAIT ) ||
+                ( tcp->tcp_state == TCP_CLOSING_OR_LAST_ACK ) );
 
        if ( over || graceful_close ) {
                /* If we have finally timed out and given up, or if
                 * this is the result of a graceful close, terminate
                 * the connection
                 */
-               tcp_abort ( conn, 1, -ETIMEDOUT );
+               tcp_abort ( tcp, 1, -ETIMEDOUT );
        } else {
                /* Otherwise, retransmit the packet */
-               tcp_senddata_conn ( conn, 0 );
+               tcp_senddata_conn ( tcp, 0 );
        }
 }
 
@@ -478,7 +432,7 @@ static void tcp_expired ( struct retry_timer *timer, int over ) {
  * @v in_tcphdr                TCP header of incoming packet
  * @ret rc             Return status code
  */
-static int tcp_send_reset ( struct tcp_connection *conn,
+static int tcp_send_reset ( struct tcp_connection *tcp,
                            struct tcp_header *in_tcphdr ) {
        struct pk_buff *pkb;
        struct tcp_header *tcphdr;
@@ -486,7 +440,7 @@ static int tcp_send_reset ( struct tcp_connection *conn,
        /* Allocate space for dataless TX buffer */
        pkb = alloc_pkb ( MAX_HDR_LEN );
        if ( ! pkb ) {
-               DBGC ( conn, "TCP %p could not allocate data buffer\n", conn );
+               DBGC ( tcp, "TCP %p could not allocate data buffer\n", tcp );
                return -ENOMEM;
        }
        pkb_reserve ( pkb, MAX_HDR_LEN );
@@ -504,15 +458,15 @@ static int tcp_send_reset ( struct tcp_connection *conn,
        tcphdr->csum = tcpip_chksum ( pkb->data, pkb_len ( pkb ) );
 
        /* Dump header */
-       DBGC ( conn, "TCP %p TX %d->%d %08lx..%08lx           %08lx %4zd",
-              conn, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
+       DBGC ( tcp, "TCP %p TX %d->%d %08lx..%08lx           %08lx %4zd",
+              tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
               ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) ),
               ntohl ( tcphdr->ack ), 0 );
-       tcp_dump_flags ( conn, tcphdr->flags );
-       DBGC ( conn, "\n" );
+       tcp_dump_flags ( tcp, tcphdr->flags );
+       DBGC ( tcp, "\n" );
 
        /* Transmit packet */
-       return tcpip_tx ( pkb, &tcp_protocol, &conn->peer,
+       return tcpip_tx ( pkb, &tcp_protocol, &tcp->peer,
                          NULL, &tcphdr->csum );
 }
 
@@ -520,14 +474,14 @@ static int tcp_send_reset ( struct tcp_connection *conn,
  * Identify TCP connection by local port number
  *
  * @v local_port       Local port (in network-endian order)
- * @ret conn           TCP connection, or NULL
+ * @ret tcp            TCP connection, or NULL
  */
 static struct tcp_connection * tcp_demux ( uint16_t local_port ) {
-       struct tcp_connection *conn;
+       struct tcp_connection *tcp;
 
-       list_for_each_entry ( conn, &tcp_conns, list ) {
-               if ( conn->local_port == local_port )
-                       return conn;
+       list_for_each_entry ( tcp, &tcp_conns, list ) {
+               if ( tcp->local_port == local_port )
+                       return tcp;
        }
        return NULL;
 }
@@ -535,32 +489,30 @@ static struct tcp_connection * tcp_demux ( uint16_t local_port ) {
 /**
  * Handle TCP received SYN
  *
- * @v conn             TCP connection
+ * @v tcp              TCP connection
  * @v seq              SEQ value (in host-endian order)
  * @ret rc             Return status code
  */
-static int tcp_rx_syn ( struct tcp_connection *conn, uint32_t seq ) {
-       struct tcp_application *app = conn->app;
+static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq ) {
 
        /* Synchronise sequence numbers on first SYN */
-       if ( ! ( conn->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) )
-               conn->rcv_ack = seq;
+       if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) )
+               tcp->rcv_ack = seq;
 
        /* Ignore duplicate SYN */
-       if ( ( conn->rcv_ack - seq ) > 0 )
+       if ( ( tcp->rcv_ack - seq ) > 0 )
                return 0;
 
        /* Mark SYN as received and start sending ACKs with each packet */
-       conn->tcp_state |= ( TCP_STATE_SENT ( TCP_ACK ) |
+       tcp->tcp_state |= ( TCP_STATE_SENT ( TCP_ACK ) |
                             TCP_STATE_RCVD ( TCP_SYN ) );
 
        /* Acknowledge SYN */
-       conn->rcv_ack++;
+       tcp->rcv_ack++;
 
        /* Notify application of established connection, if applicable */
-       if ( ( conn->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) &&
-            app && app->tcp_op->connected )
-               app->tcp_op->connected ( app );
+       if ( ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
+               stream_connected ( &tcp->stream );
 
        return 0;
 }
@@ -568,24 +520,23 @@ static int tcp_rx_syn ( struct tcp_connection *conn, uint32_t seq ) {
 /**
  * Handle TCP received ACK
  *
- * @v conn             TCP connection
+ * @v tcp              TCP connection
  * @v ack              ACK value (in host-endian order)
  * @v win              WIN value (in host-endian order)
  * @ret rc             Return status code
  */
-static int tcp_rx_ack ( struct tcp_connection *conn, uint32_t ack,
+static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
                        uint32_t win ) {
-       struct tcp_application *app = conn->app;
-       size_t ack_len = ( ack - conn->snd_seq );
+       size_t ack_len = ( ack - tcp->snd_seq );
        size_t len;
        unsigned int acked_flags = 0;
 
        /* Ignore duplicate or out-of-range ACK */
-       if ( ack_len > conn->snd_sent ) {
-               DBGC ( conn, "TCP %p received ACK for [%08lx,%08lx), "
-                      "sent only [%08lx,%08lx)\n", conn, conn->snd_seq,
-                      ( conn->snd_seq + ack_len ), conn->snd_seq,
-                      ( conn->snd_seq + conn->snd_sent ) );
+       if ( ack_len > tcp->snd_sent ) {
+               DBGC ( tcp, "TCP %p received ACK for [%08lx,%08lx), "
+                      "sent only [%08lx,%08lx)\n", tcp, tcp->snd_seq,
+                      ( tcp->snd_seq + ack_len ), tcp->snd_seq,
+                      ( tcp->snd_seq + tcp->snd_sent ) );
                return -EINVAL;
        }
 
@@ -595,34 +546,34 @@ static int tcp_rx_ack ( struct tcp_connection *conn, uint32_t ack,
         * the last outstanding sequence point.)
         */
        len = ack_len;
-       if ( ack_len == conn->snd_sent ) {
-               acked_flags = ( TCP_FLAGS_SENDING ( conn->tcp_state ) &
+       if ( ack_len == tcp->snd_sent ) {
+               acked_flags = ( TCP_FLAGS_SENDING ( tcp->tcp_state ) &
                                ( TCP_SYN | TCP_FIN ) );
                if ( acked_flags )
                        len--;
        }
 
        /* Update SEQ and sent counters, and window size */
-       conn->snd_seq = ack;
-       conn->snd_sent = 0;
-       conn->snd_win = win;
+       tcp->snd_seq = ack;
+       tcp->snd_sent = 0;
+       tcp->snd_win = win;
 
        /* Stop the retransmission timer */
-       stop_timer ( &conn->timer );
+       stop_timer ( &tcp->timer );
 
        /* Notify application of acknowledged data, if any */
-       if ( len && app && app->tcp_op->acked )
-               app->tcp_op->acked ( app, len );
+       if ( len )
+               stream_acked ( &tcp->stream, len );
 
        /* Mark SYN/FIN as acknowledged if applicable. */
        if ( acked_flags )
-               conn->tcp_state |= TCP_STATE_ACKED ( acked_flags );
+               tcp->tcp_state |= TCP_STATE_ACKED ( acked_flags );
 
        /* Notify application of established connection, if applicable */
        if ( ( acked_flags & TCP_SYN ) &&
-            ( conn->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) &&
-            app && app->tcp_op->connected )
-               app->tcp_op->connected ( app );
+            ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) {
+               stream_connected ( &tcp->stream );
+       }
 
        return 0;
 }
@@ -630,30 +581,28 @@ static int tcp_rx_ack ( struct tcp_connection *conn, uint32_t ack,
 /**
  * Handle TCP received data
  *
- * @v conn             TCP connection
+ * @v tcp              TCP connection
  * @v seq              SEQ value (in host-endian order)
  * @v data             Data buffer
  * @v len              Length of data buffer
  * @ret rc             Return status code
  */
-static int tcp_rx_data ( struct tcp_connection *conn, uint32_t seq,
+static int tcp_rx_data ( struct tcp_connection *tcp, uint32_t seq,
                         void *data, size_t len ) {
-       struct tcp_application *app = conn->app;
        size_t already_rcvd;
 
        /* Ignore duplicate data */
-       already_rcvd = ( conn->rcv_ack - seq );
+       already_rcvd = ( tcp->rcv_ack - seq );
        if ( already_rcvd >= len )
                return 0;
        data += already_rcvd;
        len -= already_rcvd;
 
        /* Acknowledge new data */
-       conn->rcv_ack += len;
+       tcp->rcv_ack += len;
 
        /* Notify application */
-       if ( app && app->tcp_op->newdata )
-               app->tcp_op->newdata ( app, data, len );
+       stream_newdata ( &tcp->stream, data, len );
 
        return 0;
 }
@@ -661,28 +610,23 @@ static int tcp_rx_data ( struct tcp_connection *conn, uint32_t seq,
 /**
  * Handle TCP received FIN
  *
- * @v conn             TCP connection
+ * @v tcp              TCP connection
  * @v seq              SEQ value (in host-endian order)
  * @ret rc             Return status code
  */
-static int tcp_rx_fin ( struct tcp_connection *conn, uint32_t seq ) {
-       struct tcp_application *app = conn->app;
+static int tcp_rx_fin ( struct tcp_connection *tcp, uint32_t seq ) {
 
        /* Ignore duplicate FIN */
-       if ( ( conn->rcv_ack - seq ) > 0 )
+       if ( ( tcp->rcv_ack - seq ) > 0 )
                return 0;
 
        /* Mark FIN as received, acknowledge it, and send our own FIN */
-       conn->tcp_state |= ( TCP_STATE_RCVD ( TCP_FIN ) |
+       tcp->tcp_state |= ( TCP_STATE_RCVD ( TCP_FIN ) |
                             TCP_STATE_SENT ( TCP_FIN ) );
-       conn->rcv_ack++;
-
-       /* Break association with application */
-       tcp_disassociate ( conn );
+       tcp->rcv_ack++;
 
-       /* Notify application */
-       if ( app && app->tcp_op->closed )
-               app->tcp_op->closed ( app, 0 );
+       /* Close stream */
+       stream_closed ( &tcp->stream, 0 );
 
        return 0;
 }
@@ -690,27 +634,27 @@ static int tcp_rx_fin ( struct tcp_connection *conn, uint32_t seq ) {
 /**
  * Handle TCP received RST
  *
- * @v conn             TCP connection
+ * @v tcp              TCP connection
  * @v seq              SEQ value (in host-endian order)
  * @ret rc             Return status code
  */
-static int tcp_rx_rst ( struct tcp_connection *conn, uint32_t seq ) {
+static int tcp_rx_rst ( struct tcp_connection *tcp, uint32_t seq ) {
 
        /* Accept RST only if it falls within the window.  If we have
         * not yet received a SYN, then we have no window to test
         * against, so fall back to checking that our SYN has been
         * ACKed.
         */
-       if ( conn->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) {
-               if ( ( conn->rcv_ack - seq ) > 0 )
+       if ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) {
+               if ( ( tcp->rcv_ack - seq ) > 0 )
                        return 0;
        } else {
-               if ( ! ( conn->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
+               if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
                        return 0;
        }
 
        /* Abort connection without sending a RST */
-       tcp_abort ( conn, 0, -ECONNRESET );
+       tcp_abort ( tcp, 0, -ECONNRESET );
 
        return -ECONNRESET;
 }
@@ -729,7 +673,7 @@ static int tcp_rx ( struct pk_buff *pkb,
                    struct sockaddr_tcpip *st_dest __unused,
                    uint16_t pshdr_csum ) {
        struct tcp_header *tcphdr = pkb->data;
-       struct tcp_connection *conn;
+       struct tcp_connection *tcp;
        unsigned int hlen;
        uint16_t csum;
        uint32_t start_seq;
@@ -770,7 +714,7 @@ static int tcp_rx ( struct pk_buff *pkb,
        }
        
        /* Parse parameters from header and strip header */
-       conn = tcp_demux ( tcphdr->dest );
+       tcp = tcp_demux ( tcphdr->dest );
        start_seq = seq = ntohl ( tcphdr->seq );
        ack = ntohl ( tcphdr->ack );
        win = ntohs ( tcphdr->win );
@@ -779,65 +723,65 @@ static int tcp_rx ( struct pk_buff *pkb,
        len = pkb_len ( pkb );
 
        /* Dump header */
-       DBGC ( conn, "TCP %p RX %d<-%d           %08lx %08lx..%08lx %4zd",
-              conn, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ),
+       DBGC ( tcp, "TCP %p RX %d<-%d           %08lx %08lx..%08lx %4zd",
+              tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ),
               ntohl ( tcphdr->ack ), ntohl ( tcphdr->seq ),
               ( ntohl ( tcphdr->seq ) + len +
                 ( ( tcphdr->flags & ( TCP_SYN | TCP_FIN ) ) ? 1 : 0 ) ), len);
-       tcp_dump_flags ( conn, tcphdr->flags );
-       DBGC ( conn, "\n" );
+       tcp_dump_flags ( tcp, tcphdr->flags );
+       DBGC ( tcp, "\n" );
 
        /* If no connection was found, send RST */
-       if ( ! conn ) {
-               tcp_send_reset ( conn, tcphdr );
+       if ( ! tcp ) {
+               tcp_send_reset ( tcp, tcphdr );
                rc = -ENOTCONN;
                goto done;
        }
 
        /* Handle ACK, if present */
        if ( flags & TCP_ACK ) {
-               if ( ( rc = tcp_rx_ack ( conn, ack, win ) ) != 0 ) {
-                       tcp_send_reset ( conn, tcphdr );
+               if ( ( rc = tcp_rx_ack ( tcp, ack, win ) ) != 0 ) {
+                       tcp_send_reset ( tcp, tcphdr );
                        goto done;
                }
        }
 
        /* Handle SYN, if present */
        if ( flags & TCP_SYN ) {
-               tcp_rx_syn ( conn, seq );
+               tcp_rx_syn ( tcp, seq );
                seq++;
        }
 
        /* Handle RST, if present */
        if ( flags & TCP_RST ) {
-               if ( ( rc = tcp_rx_rst ( conn, seq ) ) != 0 )
+               if ( ( rc = tcp_rx_rst ( tcp, seq ) ) != 0 )
                        goto done;
        }
 
        /* Handle new data, if any */
-       tcp_rx_data ( conn, seq, data, len );
+       tcp_rx_data ( tcp, seq, data, len );
        seq += len;
 
        /* Handle FIN, if present */
        if ( flags & TCP_FIN ) {
-               tcp_rx_fin ( conn, seq );
+               tcp_rx_fin ( tcp, seq );
                seq++;
        }
 
        /* Dump out any state change as a result of the received packet */
-       tcp_dump_state ( conn );
+       tcp_dump_state ( tcp );
 
        /* Send out any pending data.  If peer is expecting an ACK for
         * this packet then force sending a reply.
         */
-       tcp_senddata_conn ( conn, ( start_seq != seq ) );
+       tcp_senddata_conn ( tcp, ( start_seq != seq ) );
 
        /* If this packet was the last we expect to receive, set up
         * timer to expire and cause the connection to be freed.
         */
-       if ( TCP_CLOSED_GRACEFULLY ( conn->tcp_state ) ) {
-               conn->timer.timeout = ( 2 * TCP_MSL );
-               start_timer ( &conn->timer );
+       if ( TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) {
+               tcp->timer.timeout = ( 2 * TCP_MSL );
+               start_timer ( &tcp->timer );
        }
 
        rc = 0;
@@ -850,88 +794,88 @@ static int tcp_rx ( struct pk_buff *pkb,
 /**
  * Bind TCP connection to local port
  *
- * @v conn             TCP connection
- * @v local_port       Local port (in network byte order), or 0
+ * @v stream           TCP stream
+ * @v local            Local address
  * @ret rc             Return status code
  *
- * This function adds the connection to the list of registered TCP
- * connections.  If the local port is 0, the connection is assigned an
- * available port between 1024 and 65535.
+ * Only the port portion of the local address is used.  If the local
+ * port is 0, the connection is assigned an available port between
+ * 1024 and 65535.
  */
-static int tcp_bind ( struct tcp_connection *conn, uint16_t local_port ) {
+static int tcp_bind ( struct stream_connection *stream,
+                     struct sockaddr *local ) {
+       struct tcp_connection *tcp =
+               container_of ( stream, struct tcp_connection, stream );
+       struct sockaddr_tcpip *st = ( ( struct sockaddr_tcpip * ) local );
        struct tcp_connection *existing;
+       struct sockaddr_tcpip try;
        static uint16_t try_port = 1024;
+       uint16_t local_port = st->st_port;
 
        /* If no port specified, find the first available port */
        if ( ! local_port ) {
                for ( ; try_port ; try_port++ ) {
                        if ( try_port < 1024 )
                                continue;
-                       if ( tcp_bind ( conn, htons ( try_port ) ) == 0 )
+                       try.st_port = htons ( try_port );
+                       if ( tcp_bind ( stream,
+                                       ( struct sockaddr * ) &try ) == 0 ) {
                                return 0;
+                       }
                }
-               DBGC ( conn, "TCP %p could not bind: no free ports\n", conn );
+               DBGC ( tcp, "TCP %p could not bind: no free ports\n", tcp );
                return -EADDRINUSE;
        }
 
        /* Attempt bind to local port */
        list_for_each_entry ( existing, &tcp_conns, list ) {
                if ( existing->local_port == local_port ) {
-                       DBGC ( conn, "TCP %p could not bind: port %d in use\n",
-                              conn, ntohs ( local_port ) );
+                       DBGC ( tcp, "TCP %p could not bind: port %d in use\n",
+                              tcp, ntohs ( local_port ) );
                        return -EADDRINUSE;
                }
        }
-       conn->local_port = local_port;
+       tcp->local_port = local_port;
 
-       DBGC ( conn, "TCP %p bound to port %d\n", conn, ntohs ( local_port ) );
+       DBGC ( tcp, "TCP %p bound to port %d\n", tcp, ntohs ( local_port ) );
        return 0;
 }
 
 /**
  * Connect to a remote server
  *
- * @v app              TCP application
+ * @v stream           TCP stream
  * @v peer             Remote socket address
- * @v local_port       Local port number (in network byte order), or 0
  * @ret rc             Return status code
  *
  * This function initiates a TCP connection to the socket address specified in
  * peer. It sends a SYN packet to peer. When the connection is established, the
  * TCP stack calls the connected() callback function.
  */
-int tcp_connect ( struct tcp_application *app, struct sockaddr_tcpip *peer,
-                 uint16_t local_port ) {
-       struct tcp_connection *conn;
+static int tcp_connect ( struct stream_connection *stream,
+                        struct sockaddr *peer ) {
+       struct tcp_connection *tcp =
+               container_of ( stream, struct tcp_connection, stream );
+       struct sockaddr_tcpip *st = ( ( struct sockaddr_tcpip * ) peer );
+       struct sockaddr_tcpip local;
        int rc;
 
-       /* Application must not already have an open connection */
-       if ( app->conn ) {
-               DBG ( "TCP app %p already open on %p\n", app, app->conn );
-               return -EISCONN;
-       }
-
-       /* Allocate connection state storage and add to connection list */
-       conn = alloc_tcp();
-       if ( ! conn ) {
-               DBG ( "TCP app %p could not allocate connection\n", app );
-               return -ENOMEM;
-       }
-
-       /* Bind to peer and to local port */
-       memcpy ( &conn->peer, peer, sizeof ( conn->peer ) );
-       if ( ( rc = tcp_bind ( conn, local_port ) ) != 0 ) {
-               free_tcp ( conn );
-               return rc;
+       /* Bind to local port if not already bound */
+       if ( ! tcp->local_port ) {
+               local.st_port = 0;
+               if ( ( rc = tcp_bind ( stream,
+                                      ( struct sockaddr * ) &local ) ) != 0 ){
+                       return rc;
+               }
        }
 
-       /* Associate with application */
-       tcp_associate ( conn, app );
+       /* Bind to peer */
+       memcpy ( &tcp->peer, st, sizeof ( tcp->peer ) );
 
        /* Transition to TCP_SYN_SENT and send the SYN */
-       conn->tcp_state = TCP_SYN_SENT;
-       tcp_dump_state ( conn );
-       tcp_senddata_conn ( conn, 0 );
+       tcp->tcp_state = TCP_SYN_SENT;
+       tcp_dump_state ( tcp );
+       tcp_senddata_conn ( tcp, 0 );
 
        return 0;
 }
@@ -939,30 +883,22 @@ int tcp_connect ( struct tcp_application *app, struct sockaddr_tcpip *peer,
 /**
  * Close the connection
  *
- * @v app              TCP application
+ * @v stream           TCP stream
  *
- * The association between the application and the TCP connection is
- * immediately severed, and the TCP application data structure can be
- * reused or freed immediately.  The TCP connection will persist until
- * the state machine has returned to the TCP_CLOSED state.
+ * The TCP connection will persist until the state machine has
+ * returned to the TCP_CLOSED state.
  */
-void tcp_close ( struct tcp_application *app ) {
-       struct tcp_connection *conn = app->conn;
-
-       /* If no connection exists, do nothing */
-       if ( ! conn )
-               return;
-
-       /* Break association between application and connection */
-       tcp_disassociate ( conn );
+static void tcp_close ( struct stream_connection *stream ) {
+       struct tcp_connection *tcp =
+               container_of ( stream, struct tcp_connection, stream );
 
        /* If we have not yet received a SYN (i.e. we are in CLOSED,
         * LISTEN or SYN_SENT), just delete the connection
         */
-       if ( ! ( conn->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) {
-               conn->tcp_state = TCP_CLOSED;
-               tcp_dump_state ( conn );
-               free_tcp ( conn );
+       if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) {
+               tcp->tcp_state = TCP_CLOSED;
+               tcp_dump_state ( tcp );
+               free_tcp ( tcp );
                return;
        }
 
@@ -970,15 +906,52 @@ void tcp_close ( struct tcp_application *app ) {
         * SYN_RCVD), pretend that it has been acknowledged so that we
         * can send a FIN without breaking things.
         */
-       if ( ! ( conn->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
-               tcp_rx_ack ( conn, ( conn->snd_seq + 1 ), 0 );
+       if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
+               tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 );
 
        /* Send a FIN to initiate the close */
-       conn->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
-       tcp_dump_state ( conn );
-       tcp_senddata_conn ( conn, 0 );
+       tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
+       tcp_dump_state ( tcp );
+       tcp_senddata_conn ( tcp, 0 );
 }
 
+/**
+ * Open TCP connection
+ *
+ * @v app              Stream application
+ * @ret rc             Return status code
+ */
+int tcp_open ( struct stream_application *app ) {
+       struct tcp_connection *tcp;
+
+       /* Application must not already have an open connection */
+       if ( app->conn ) {
+               DBG ( "TCP app %p already open on %p\n", app, app->conn );
+               return -EISCONN;
+       }
+
+       /* Allocate connection state storage and add to connection list */
+       tcp = alloc_tcp();
+       if ( ! tcp ) {
+               DBG ( "TCP app %p could not allocate connection\n", app );
+               return -ENOMEM;
+       }
+
+       /* Associate application with connection */
+       stream_associate ( app, &tcp->stream );
+
+       return 0;
+}
+
+/** TCP stream operations */
+static struct stream_connection_operations tcp_op = {
+       .bind           = tcp_bind,
+       .connect        = tcp_connect,
+       .close          = tcp_close,
+       .send           = tcp_send,
+       .kick           = tcp_kick,
+};
+
 /** TCP protocol */
 struct tcpip_protocol tcp_protocol __tcpip_protocol = {
        .name = "TCP",
index 7856788..061240c 100644 (file)
@@ -4,10 +4,12 @@
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
+#include <byteswap.h>
 #include <gpxe/async.h>
 #include <gpxe/buffer.h>
 #include <gpxe/uri.h>
 #include <gpxe/download.h>
+#include <gpxe/tcp.h>
 #include <gpxe/ftp.h>
 
 /** @file
@@ -40,13 +42,14 @@ static const char * ftp_strings[] = {
 };
 
 /**
- * Get FTP request from control TCP application
+ * Get FTP request from control stream application
  *
- * @v app              TCP application
+ * @v app              Stream application
  * @ret ftp            FTP request
  */
-static inline struct ftp_request * tcp_to_ftp ( struct tcp_application *app ) {
-       return container_of ( app, struct ftp_request, tcp );
+static inline struct ftp_request *
+stream_to_ftp ( struct stream_application *app ) {
+       return container_of ( app, struct ftp_request, stream );
 }
 
 /**
@@ -59,9 +62,9 @@ static void ftp_done ( struct ftp_request *ftp, int rc ) {
 
        DBGC ( ftp, "FTP %p completed with status %d\n", ftp, rc );
 
-       /* Close both TCP connections */
-       tcp_close ( &ftp->tcp );
-       tcp_close ( &ftp->tcp_data );
+       /* Close both stream connections */
+       stream_close ( &ftp->stream );
+       stream_close ( &ftp->stream_data );
 
        /* Mark asynchronous operation as complete */
        async_done ( &ftp->async, rc );
@@ -70,9 +73,9 @@ static void ftp_done ( struct ftp_request *ftp, int rc ) {
 /**
  * Parse FTP byte sequence value
  *
- * @v text     Text string
- * @v value    Value buffer
- * @v len      Length of value buffer
+ * @v text             Text string
+ * @v value            Value buffer
+ * @v len              Length of value buffer
  *
  * This parses an FTP byte sequence value (e.g. the "aaa,bbb,ccc,ddd"
  * form for IP addresses in PORT commands) into a byte sequence.  @c
@@ -93,7 +96,7 @@ static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) {
 /**
  * Handle an FTP control channel response
  *
- * @v ftp      FTP request
+ * @v ftp              FTP request
  *
  * This is called once we have received a complete response line.
  */
@@ -121,7 +124,7 @@ static void ftp_reply ( struct ftp_request *ftp ) {
                char *ptr = ftp->passive_text;
                union {
                        struct sockaddr_in sin;
-                       struct sockaddr_tcpip st;
+                       struct sockaddr sa;
                } sa;
                int rc;
 
@@ -130,7 +133,14 @@ static void ftp_reply ( struct ftp_request *ftp ) {
                                  sizeof ( sa.sin.sin_addr ) );
                ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_port,
                                  sizeof ( sa.sin.sin_port ) );
-               if ( ( rc = tcp_connect ( &ftp->tcp_data, &sa.st, 0 ) ) != 0 ){
+               if ( ( rc = tcp_open ( &ftp->stream_data ) ) != 0 ) {
+                       DBGC ( ftp, "FTP %p could not open data connection\n",
+                              ftp );
+                       ftp_done ( ftp, rc );
+                       return;
+               }
+               if ( ( rc = stream_connect ( &ftp->stream_data,
+                                            &sa.sa ) ) != 0 ){
                        DBGC ( ftp, "FTP %p could not make data connection\n",
                               ftp );
                        ftp_done ( ftp, rc );
@@ -154,16 +164,16 @@ static void ftp_reply ( struct ftp_request *ftp ) {
 /**
  * Handle new data arriving on FTP control channel
  *
- * @v app      TCP application
- * @v data     New data
- * @v len      Length of new data
+ * @v app              Stream application
+ * @v data             New data
+ * @v len              Length of new data
  *
  * Data is collected until a complete line is received, at which point
  * its information is passed to ftp_reply().
  */
-static void ftp_newdata ( struct tcp_application *app,
+static void ftp_newdata ( struct stream_application *app,
                          void *data, size_t len ) {
-       struct ftp_request *ftp = tcp_to_ftp ( app );
+       struct ftp_request *ftp = stream_to_ftp ( app );
        char *recvbuf = ftp->recvbuf;
        size_t recvsize = ftp->recvsize;
        char c;
@@ -210,10 +220,10 @@ static void ftp_newdata ( struct tcp_application *app,
 /**
  * Handle acknowledgement of data sent on FTP control channel
  *
- * @v app      TCP application
+ * @v app              Stream application
  */
-static void ftp_acked ( struct tcp_application *app, size_t len ) {
-       struct ftp_request *ftp = tcp_to_ftp ( app );
+static void ftp_acked ( struct stream_application *app, size_t len ) {
+       struct ftp_request *ftp = stream_to_ftp ( app );
        
        /* Mark off ACKed portion of the currently-transmitted data */
        ftp->already_sent += len;
@@ -222,31 +232,31 @@ static void ftp_acked ( struct tcp_application *app, size_t len ) {
 /**
  * Construct data to send on FTP control channel
  *
- * @v app      TCP application
- * @v buf      Temporary data buffer
- * @v len      Length of temporary data buffer
+ * @v app              Stream application
+ * @v buf              Temporary data buffer
+ * @v len              Length of temporary data buffer
  */
-static void ftp_senddata ( struct tcp_application *app,
+static void ftp_senddata ( struct stream_application *app,
                           void *buf, size_t len ) {
-       struct ftp_request *ftp = tcp_to_ftp ( app );
+       struct ftp_request *ftp = stream_to_ftp ( app );
 
        /* Send the as-yet-unACKed portion of the string for the
         * current state.
         */
        len = snprintf ( buf, len, ftp_strings[ftp->state], ftp->uri->path );
-       tcp_send ( app, buf + ftp->already_sent, len - ftp->already_sent );
+       stream_send ( app, buf + ftp->already_sent, len - ftp->already_sent );
 }
 
 /**
  * Handle control channel being closed
  *
- * @v app              TCP application
+ * @v app              Stream application
  *
  * When the control channel is closed, the data channel must also be
  * closed, if it is currently open.
  */
-static void ftp_closed ( struct tcp_application *app, int rc ) {
-       struct ftp_request *ftp = tcp_to_ftp ( app );
+static void ftp_closed ( struct stream_application *app, int rc ) {
+       struct ftp_request *ftp = stream_to_ftp ( app );
 
        DBGC ( ftp, "FTP %p control connection closed: %s\n",
               ftp, strerror ( rc ) );
@@ -256,7 +266,7 @@ static void ftp_closed ( struct tcp_application *app, int rc ) {
 }
 
 /** FTP control channel operations */
-static struct tcp_operations ftp_tcp_operations = {
+static struct stream_application_operations ftp_stream_operations = {
        .closed         = ftp_closed,
        .acked          = ftp_acked,
        .newdata        = ftp_newdata,
@@ -270,20 +280,20 @@ static struct tcp_operations ftp_tcp_operations = {
  */
 
 /**
- * Get FTP request from data TCP application
+ * Get FTP request from data stream application
  *
- * @v app              TCP application
+ * @v app              Stream application
  * @ret ftp            FTP request
  */
 static inline struct ftp_request *
-tcp_to_ftp_data ( struct tcp_application *app ) {
-       return container_of ( app, struct ftp_request, tcp_data );
+stream_to_ftp_data ( struct stream_application *app ) {
+       return container_of ( app, struct ftp_request, stream_data );
 }
 
 /**
  * Handle data channel being closed
  *
- * @v app              TCP application
+ * @v app              Stream application
  *
  * When the data channel is closed, the control channel should be left
  * alone; the server will send a completion message via the control
@@ -291,8 +301,8 @@ tcp_to_ftp_data ( struct tcp_application *app ) {
  *
  * If the data channel is closed due to an error, we abort the request.
  */
-static void ftp_data_closed ( struct tcp_application *app, int rc ) {
-       struct ftp_request *ftp = tcp_to_ftp_data ( app );
+static void ftp_data_closed ( struct stream_application *app, int rc ) {
+       struct ftp_request *ftp = stream_to_ftp_data ( app );
 
        DBGC ( ftp, "FTP %p data connection closed: %s\n",
               ftp, strerror ( rc ) );
@@ -305,13 +315,13 @@ static void ftp_data_closed ( struct tcp_application *app, int rc ) {
 /**
  * Handle new data arriving on the FTP data channel
  *
- * @v app      TCP application
- * @v data     New data
- * @v len      Length of new data
+ * @v app              Stream application
+ * @v data             New data
+ * @v len              Length of new data
  */
-static void ftp_data_newdata ( struct tcp_application *app,
+static void ftp_data_newdata ( struct stream_application *app,
                               void *data, size_t len ) {
-       struct ftp_request *ftp = tcp_to_ftp_data ( app );
+       struct ftp_request *ftp = stream_to_ftp_data ( app );
        int rc;
 
        /* Fill data buffer */
@@ -325,7 +335,7 @@ static void ftp_data_newdata ( struct tcp_application *app,
 }
 
 /** FTP data channel operations */
-static struct tcp_operations ftp_data_tcp_operations = {
+static struct stream_application_operations ftp_data_stream_operations = {
        .closed         = ftp_data_closed,
        .newdata        = ftp_data_newdata,
 };
@@ -353,9 +363,6 @@ static struct async_operations ftp_async_operations = {
        .reap = ftp_reap,
 };
 
-#warning "Quick name resolution hack"
-#include <byteswap.h>
-
 /**
  * Initiate an FTP connection
  *
@@ -387,12 +394,12 @@ int ftp_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
        ftp->already_sent = 0;
        ftp->recvbuf = ftp->status_text;
        ftp->recvsize = sizeof ( ftp->status_text ) - 1;
-       ftp->tcp.tcp_op = &ftp_tcp_operations;
-       ftp->tcp_data.tcp_op = &ftp_data_tcp_operations;
+       ftp->stream.op = &ftp_stream_operations;
+       ftp->stream_data.op = &ftp_data_stream_operations;
 
 #warning "Quick name resolution hack"
        union {
-               struct sockaddr_tcpip st;
+               struct sockaddr sa;
                struct sockaddr_in sin;
        } server;
        server.sin.sin_port = htons ( FTP_PORT );
@@ -404,7 +411,9 @@ int ftp_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
 
        DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path );
 
-       if ( ( rc = tcp_connect ( &ftp->tcp, &server.st, 0 ) ) != 0 )
+       if ( ( rc = tcp_open ( &ftp->stream ) ) != 0 )
+               goto err;
+       if ( ( rc = stream_connect ( &ftp->stream, &server.sa ) ) != 0 )
                goto err;
 
        async_init ( &ftp->async, &ftp_async_operations, parent );
index 18030fe..381ab1f 100644 (file)
 #include <gpxe/buffer.h>
 #include <gpxe/download.h>
 #include <gpxe/resolv.h>
+#include <gpxe/tcp.h>
 #include <gpxe/http.h>
 
 static struct async_operations http_async_operations;
 
 static inline struct http_request *
-tcp_to_http ( struct tcp_application *app ) {
-       return container_of ( app, struct http_request, tcp );
+stream_to_http ( struct stream_application *app ) {
+       return container_of ( app, struct http_request, stream );
 }
 
 /**
@@ -54,8 +55,8 @@ tcp_to_http ( struct tcp_application *app ) {
  */
 static void http_done ( struct http_request *http, int rc ) {
 
-       /* Close TCP connection */
-       tcp_close ( &http->tcp );
+       /* Close stream connection */
+       stream_close ( &http->stream );
 
        /* Prevent further processing of any current packet */
        http->rx_state = HTTP_RX_DEAD;
@@ -271,9 +272,9 @@ static void http_rx_data ( struct http_request *http,
  * @v data             New data
  * @v len              Length of new data
  */
-static void http_newdata ( struct tcp_application *app,
+static void http_newdata ( struct stream_application *app,
                           void *data, size_t len ) {
-       struct http_request *http = tcp_to_http ( app );
+       struct http_request *http = stream_to_http ( app );
        const char *buf = data;
        char *line;
        int rc;
@@ -319,13 +320,13 @@ static void http_newdata ( struct tcp_application *app,
 /**
  * Send HTTP data
  *
- * @v app              TCP application
+ * @v app              Stream application
  * @v buf              Temporary data buffer
  * @v len              Length of temporary data buffer
  */
-static void http_senddata ( struct tcp_application *app,
+static void http_senddata ( struct stream_application *app,
                            void *buf, size_t len ) {
-       struct http_request *http = tcp_to_http ( app );
+       struct http_request *http = stream_to_http ( app );
        const char *path = http->uri->path;
        const char *host = http->uri->host;
 
@@ -338,17 +339,18 @@ static void http_senddata ( struct tcp_application *app,
                         "Host: %s\r\n"
                         "\r\n", path, host );
 
-       tcp_send ( app, ( buf + http->tx_offset ), ( len - http->tx_offset ) );
+       stream_send ( app, ( buf + http->tx_offset ),
+                     ( len - http->tx_offset ) );
 }
 
 /**
  * HTTP data acknowledged
  *
- * @v app              TCP application
+ * @v app              Stream application
  * @v len              Length of acknowledged data
  */
-static void http_acked ( struct tcp_application *app, size_t len ) {
-       struct http_request *http = tcp_to_http ( app );
+static void http_acked ( struct stream_application *app, size_t len ) {
+       struct http_request *http = stream_to_http ( app );
 
        http->tx_offset += len;
 }
@@ -356,10 +358,10 @@ static void http_acked ( struct tcp_application *app, size_t len ) {
 /**
  * HTTP connection closed by network stack
  *
- * @v app              TCP application
+ * @v app              Stream application
  */
-static void http_closed ( struct tcp_application *app, int rc ) {
-       struct http_request *http = tcp_to_http ( app );
+static void http_closed ( struct stream_application *app, int rc ) {
+       struct http_request *http = stream_to_http ( app );
 
        DBGC ( http, "HTTP %p connection closed: %s\n",
               http, strerror ( rc ) );
@@ -367,8 +369,8 @@ static void http_closed ( struct tcp_application *app, int rc ) {
        http_done ( http, rc );
 }
 
-/** HTTP TCP operations */
-static struct tcp_operations http_tcp_operations = {
+/** HTTP stream operations */
+static struct stream_application_operations http_stream_operations = {
        .closed         = http_closed,
        .acked          = http_acked,
        .newdata        = http_newdata,
@@ -395,6 +397,7 @@ int http_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
        http->uri = uri;
        http->buffer = buffer;
        async_init ( &http->async, &http_async_operations, parent );
+       http->stream.op = &http_stream_operations;
 
        /* Start name resolution.  The download proper will start when
         * name resolution completes.
@@ -432,10 +435,15 @@ static void http_sigchld ( struct async *async, enum signal signal __unused ) {
        }
 
        /* Otherwise, start the HTTP connection */
-       http->tcp.tcp_op = &http_tcp_operations;
+       if ( ( rc = tcp_open ( &http->stream ) ) != 0 ) {
+               DBGC ( http, "HTTP %p could not open stream connection: %s\n",
+                      http, strerror ( rc ) );
+               http_done ( http, rc );
+               return;
+       }
        st->st_port = htons ( uri_port ( http->uri, HTTP_PORT ) );
-       if ( ( rc = tcp_connect ( &http->tcp, st, 0 ) ) != 0 ) {
-               DBGC ( http, "HTTP %p could not open TCP connection: %s\n",
+       if ( ( rc = stream_connect ( &http->stream, &http->server ) ) != 0 ) {
+               DBGC ( http, "HTTP %p could not connect stream: %s\n",
                       http, strerror ( rc ) );
                http_done ( http, rc );
                return;
index 4d95c0b..5656797 100644 (file)
@@ -26,6 +26,7 @@
 #include <gpxe/scsi.h>
 #include <gpxe/process.h>
 #include <gpxe/uaccess.h>
+#include <gpxe/tcp.h>
 #include <gpxe/iscsi.h>
 
 /** @file
@@ -85,8 +86,8 @@ static void iscsi_rx_buffered_data_done ( struct iscsi_session *iscsi ) {
  */
 static void iscsi_close ( struct iscsi_session *iscsi ) {
 
-       /* Close TCP connection */
-       tcp_close ( &iscsi->tcp );
+       /* Close stream connection */
+       stream_close ( &iscsi->stream );
 
        /* Clear connection status */
        iscsi->status = 0;
@@ -338,7 +339,7 @@ static void iscsi_tx_data_out ( struct iscsi_session *iscsi,
                len = remaining;
        copy_from_user ( buf, iscsi->command->data_out, offset, len );
 
-       tcp_send ( &iscsi->tcp, buf, len );
+       stream_send ( &iscsi->stream, buf, len );
 }
 
 /****************************************************************************
@@ -507,7 +508,7 @@ static void iscsi_login_request_done ( struct iscsi_session *iscsi ) {
 static void iscsi_tx_login_request ( struct iscsi_session *iscsi,
                                     void *buf, size_t len ) {
        len = iscsi_build_login_request_strings ( iscsi, buf, len );
-       tcp_send ( &iscsi->tcp, buf + iscsi->tx_offset,
+       stream_send ( &iscsi->stream, buf + iscsi->tx_offset,
                   len - iscsi->tx_offset );
 }
 
@@ -750,11 +751,18 @@ static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *data,
        if ( response->status_class == ISCSI_STATUS_REDIRECT ) {
                DBGC ( iscsi, "iSCSI %p redirecting to new server\n", iscsi );
                iscsi_close ( iscsi );
-               if ( ( rc = tcp_connect ( &iscsi->tcp, &iscsi->target,
-                                         0 ) ) != 0 ) {
-                       DBGC ( iscsi, "iSCSI %p could not open TCP "
-                              "connection: %s\n", iscsi, strerror ( rc ) );
+               if ( ( rc = tcp_open ( &iscsi->stream ) ) != 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not open stream: %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;
        }
@@ -810,13 +818,13 @@ static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *data,
 
 /****************************************************************************
  *
- * iSCSI to TCP interface
+ * iSCSI to stream interface
  *
  */
 
 static inline struct iscsi_session *
-tcp_to_iscsi ( struct tcp_application *app ) {
-       return container_of ( app, struct iscsi_session, tcp );
+stream_to_iscsi ( struct stream_application *app ) {
+       return container_of ( app, struct iscsi_session, stream );
 }
 
 /**
@@ -889,15 +897,15 @@ static void iscsi_tx_done ( struct iscsi_session *iscsi ) {
 }
 
 /**
- * Handle TCP ACKs
+ * 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 tcp_application *app, size_t len ) {
-       struct iscsi_session *iscsi = tcp_to_iscsi ( app );
+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;
        
@@ -953,9 +961,9 @@ static void iscsi_acked ( struct tcp_application *app, size_t len ) {
  * 
  * Constructs data to be sent for the current TX state
  */
-static void iscsi_senddata ( struct tcp_application *app,
+static void iscsi_senddata ( struct stream_application *app,
                             void *buf, size_t len ) {
-       struct iscsi_session *iscsi = tcp_to_iscsi ( app );
+       struct iscsi_session *iscsi = stream_to_iscsi ( app );
        struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
        static const char pad[] = { '\0', '\0', '\0' };
 
@@ -964,7 +972,7 @@ static void iscsi_senddata ( struct tcp_application *app,
                /* Nothing to send */
                break;
        case ISCSI_TX_BHS:
-               tcp_send ( app, &iscsi->tx_bhs.bytes[iscsi->tx_offset],
+               stream_send ( app, &iscsi->tx_bhs.bytes[iscsi->tx_offset],
                           ( sizeof ( iscsi->tx_bhs ) - iscsi->tx_offset ) );
                break;
        case ISCSI_TX_AHS:
@@ -975,8 +983,8 @@ static void iscsi_senddata ( struct tcp_application *app,
                iscsi_tx_data ( iscsi, buf, len );
                break;
        case ISCSI_TX_DATA_PADDING:
-               tcp_send ( app, pad, ( ISCSI_DATA_PAD_LEN ( common->lengths )
-                                       - iscsi->tx_offset ) );
+               stream_send ( app, pad, ( ISCSI_DATA_PAD_LEN( common->lengths )
+                                         - iscsi->tx_offset ) );
                break;
        default:
                assert ( 0 );
@@ -1068,7 +1076,7 @@ static void iscsi_rx_bhs ( struct iscsi_session *iscsi, void *data,
 /**
  * Receive new data
  *
- * @v tcp              TCP application
+ * @v stream           Stream application
  * @v data             Received data
  * @v len              Length of received data
  *
@@ -1079,9 +1087,9 @@ 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 tcp_application *app, void *data,
+static void iscsi_newdata ( struct stream_application *app, void *data,
                            size_t len ) {
-       struct iscsi_session *iscsi = tcp_to_iscsi ( app );
+       struct iscsi_session *iscsi = stream_to_iscsi ( app );
        struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
        void ( *process ) ( struct iscsi_session *iscsi, void *data,
                            size_t len, size_t remaining );
@@ -1138,14 +1146,14 @@ static void iscsi_newdata ( struct tcp_application *app, void *data,
 }
 
 /**
- * Handle TCP connection closure
+ * Handle stream connection closure
  *
- * @v app              TCP application
+ * @v app              Stream application
  * @v status           Error code, if any
  *
  */
-static void iscsi_closed ( struct tcp_application *app, int status ) {
-       struct iscsi_session *iscsi = tcp_to_iscsi ( app );
+static void iscsi_closed ( struct stream_application *app, int status ) {
+       struct iscsi_session *iscsi = stream_to_iscsi ( app );
        int rc;
 
        /* Even a graceful close counts as an error for iSCSI */
@@ -1159,26 +1167,34 @@ static void iscsi_closed ( struct tcp_application *app, int status ) {
        if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
                DBGC ( iscsi, "iSCSI %p retrying connection (retry #%d)\n",
                       iscsi, iscsi->retry_count );
-               if ( ( rc = tcp_connect ( app, &iscsi->target, 0 ) ) != 0 ) {
-                       DBGC ( iscsi, "iSCSI %p could not open TCP "
-                              "connection: %s\n", iscsi, strerror ( rc ) );
+               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",
+                              iscsi, strerror ( rc ) );
+                       iscsi_done ( iscsi, rc );
+                       return;
                }
        } else {
                DBGC ( iscsi, "iSCSI %p retry count exceeded\n", iscsi );
                iscsi->instant_rc = status;
                iscsi_done ( iscsi, status );
+               return;
        }
 }
 
 /**
- * Handle TCP connection opening
+ * Handle stream connection opening
  *
- * @v app              TCP application
+ * @v app              Stream application
  *
  */
-static void iscsi_connected ( struct tcp_application *app ) {
-       struct iscsi_session *iscsi = tcp_to_iscsi ( app );
+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 );
@@ -1194,8 +1210,8 @@ static void iscsi_connected ( struct tcp_application *app ) {
        iscsi_start_login ( iscsi );
 }
 
-/** iSCSI TCP operations */
-static struct tcp_operations iscsi_tcp_operations = {
+/** iSCSI stream operations */
+static struct stream_application_operations iscsi_stream_operations = {
        .closed         = iscsi_closed,
        .connected      = iscsi_connected,
        .acked          = iscsi_acked,
@@ -1224,14 +1240,19 @@ int iscsi_issue ( struct iscsi_session *iscsi, struct scsi_command *command,
        } else if ( iscsi->status ) {
                /* Session already open: issue command */
                iscsi_start_command ( iscsi );
-               tcp_senddata ( &iscsi->tcp );
+               stream_kick ( &iscsi->stream );
        } else {
                /* Session not open: initiate login */
-               iscsi->tcp.tcp_op = &iscsi_tcp_operations;
-               if ( ( rc = tcp_connect ( &iscsi->tcp, &iscsi->target,
-                                         0 ) ) != 0 ) {
-                       DBGC ( iscsi, "iSCSI %p could not open TCP "
-                              "connection: %s\n", iscsi, strerror ( rc ) );
+               iscsi->stream.op = &iscsi_stream_operations;
+               if ( ( rc = tcp_open ( &iscsi->stream ) ) != 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not open stream: %s\n ",
+                              iscsi, strerror ( rc ) );
+                       return rc;
+               }
+               if ( ( rc = stream_connect ( &iscsi->stream,
+                                            &iscsi->target ) ) != 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not connect: %s\n",
+                              iscsi, strerror ( rc ) );
                        return rc;
                }
        }