iPXE
Data Structures | Enumerations | Functions | Variables
tcp.c File Reference

TCP protocol. More...

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/timer.h>
#include <ipxe/iobuf.h>
#include <ipxe/malloc.h>
#include <ipxe/init.h>
#include <ipxe/retry.h>
#include <ipxe/refcnt.h>
#include <ipxe/pending.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
#include <ipxe/uri.h>
#include <ipxe/netdevice.h>
#include <ipxe/profile.h>
#include <ipxe/process.h>
#include <ipxe/tcpip.h>
#include <ipxe/tcp.h>

Go to the source code of this file.

Data Structures

struct  tcp_connection
 A TCP connection. More...
struct  tcp_rx_queued_header
 TCP internal header. More...

Enumerations

enum  tcp_flags { TCP_XFER_CLOSED = 0x0001, TCP_TS_ENABLED = 0x0002, TCP_ACK_PENDING = 0x0004, TCP_SACK_ENABLED = 0x0008 }
 TCP flags. More...

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static LIST_HEAD (tcp_conns)
 List of registered TCP connections.
static void tcp_expired (struct retry_timer *timer, int over)
 Retransmission timer expired.
static void tcp_keepalive_expired (struct retry_timer *timer, int over __unused)
 Keepalive timer expired.
static void tcp_wait_expired (struct retry_timer *timer, int over __unused)
 Shutdown timer expired.
static struct tcp_connectiontcp_demux (unsigned int local_port)
 Identify TCP connection by local port number.
static int tcp_rx_ack (struct tcp_connection *tcp, uint32_t ack, uint32_t win)
 Handle TCP received ACK.
static const char * tcp_state (int state)
 Name TCP state.
static void tcp_dump_state (struct tcp_connection *tcp)
 Dump TCP state transition.
static void tcp_dump_flags (struct tcp_connection *tcp, unsigned int flags)
 Dump TCP flags.
static int tcp_port_available (int port)
 Check if local TCP port is available.
static int tcp_open (struct interface *xfer, struct sockaddr *peer, struct sockaddr *local)
 Open a TCP connection.
static void tcp_close (struct tcp_connection *tcp, int rc)
 Close TCP connection.
static size_t tcp_xmit_win (struct tcp_connection *tcp)
 Calculate transmission window.
static size_t tcp_xfer_window (struct tcp_connection *tcp)
 Check data-transfer flow control window.
static uint32_t tcp_sack_block (struct tcp_connection *tcp, uint32_t seq, struct tcp_sack_block *sack)
 Find selective acknowledgement block.
static unsigned int tcp_sack (struct tcp_connection *tcp, uint32_t seq)
 Update TCP selective acknowledgement list.
static size_t tcp_process_tx_queue (struct tcp_connection *tcp, size_t max_len, struct io_buffer *dest, int remove)
 Process TCP transmit queue.
static void tcp_xmit_sack (struct tcp_connection *tcp, uint32_t sack_seq)
 Transmit any outstanding data (with selective acknowledgement)
static void tcp_xmit (struct tcp_connection *tcp)
 Transmit any outstanding data.
static int tcp_xmit_reset (struct tcp_connection *tcp, struct sockaddr_tcpip *st_dest, struct tcp_header *in_tcphdr)
 Send RST response to incoming packet.
static int tcp_rx_opts (struct tcp_connection *tcp, const struct tcp_header *tcphdr, size_t hlen, struct tcp_options *options)
 Parse TCP received options.
static void tcp_rx_seq (struct tcp_connection *tcp, uint32_t seq_len)
 Consume received sequence space.
static int tcp_rx_syn (struct tcp_connection *tcp, uint32_t seq, struct tcp_options *options)
 Handle TCP received SYN.
static int tcp_rx_data (struct tcp_connection *tcp, uint32_t seq, struct io_buffer *iobuf)
 Handle TCP received data.
static int tcp_rx_fin (struct tcp_connection *tcp, uint32_t seq)
 Handle TCP received FIN.
static int tcp_rx_rst (struct tcp_connection *tcp, uint32_t seq)
 Handle TCP received RST.
static void tcp_rx_enqueue (struct tcp_connection *tcp, uint32_t seq, uint8_t flags, struct io_buffer *iobuf)
 Enqueue received TCP packet.
static void tcp_process_rx_queue (struct tcp_connection *tcp)
 Process receive queue.
static int tcp_rx (struct io_buffer *iobuf, struct net_device *netdev __unused, struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest __unused, uint16_t pshdr_csum)
 Process received packet.
static unsigned int tcp_discard (void)
 Discard some cached TCP data.
struct cache_discarder
tcp_discarder 
__cache_discarder (CACHE_NORMAL)
 TCP cache discarder.
static struct tcp_connectiontcp_first_unclosed (void)
 Find first TCP connection that has not yet been closed.
static struct tcp_connectiontcp_first_unfinished (void)
 Find first TCP connection that has not yet finished all operations.
static void tcp_shutdown (int booting __unused)
 Shut down all TCP connections.
struct startup_fn tcp_startup_fn __startup_fn (STARTUP_LATE)
 TCP shutdown function.
static void tcp_xfer_close (struct tcp_connection *tcp, int rc)
 Close interface.
static int tcp_xfer_deliver (struct tcp_connection *tcp, struct io_buffer *iobuf, struct xfer_metadata *meta __unused)
 Deliver datagram as I/O buffer.
static int tcp_open_uri (struct interface *xfer, struct uri *uri)
 Open TCP URI.

Variables

static struct profiler
tcp_tx_profiler 
__profiler = { .name = "tcp.tx" }
 Transmit profiler.
static struct process_descriptor tcp_process_desc
 TCP process descriptor.
static struct interface_descriptor tcp_xfer_desc
 TCP data transfer interface descriptor.
struct tcpip_protocol tcp_protocol __tcpip_protocol
 TCP protocol.
static struct interface_operation tcp_xfer_operations []
 TCP data transfer interface operations.
struct socket_opener
tcp_ipv4_socket_opener 
__socket_opener
 TCP IPv4 socket opener.
int tcp_sock_stream = TCP_SOCK_STREAM
 Linkage hack.
struct uri_opener tcp_uri_opener __uri_opener
 TCP URI opener.

Detailed Description

TCP protocol.

Definition in file tcp.c.


Enumeration Type Documentation

enum tcp_flags

TCP flags.

Enumerator:
TCP_XFER_CLOSED 

TCP data transfer interface has been closed.

TCP_TS_ENABLED 

TCP timestamps are enabled.

TCP_ACK_PENDING 

TCP acknowledgement is pending.

TCP_SACK_ENABLED 

TCP selective acknowledgement is enabled.

Definition at line 128 of file tcp.c.

               {
        /** TCP data transfer interface has been closed */
        TCP_XFER_CLOSED = 0x0001,
        /** TCP timestamps are enabled */
        TCP_TS_ENABLED = 0x0002,
        /** TCP acknowledgement is pending */
        TCP_ACK_PENDING = 0x0004,
        /** TCP selective acknowledgement is enabled */
        TCP_SACK_ENABLED = 0x0008,
};

Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static LIST_HEAD ( tcp_conns  ) [static]

List of registered TCP connections.

static void tcp_expired ( struct retry_timer timer,
int  over 
) [static]

Retransmission timer expired.

Parameters:
timerRetransmission timer
overFailure indicator

Definition at line 784 of file tcp.c.

References assert, container_of, DBGC, ETIMEDOUT, tcp_connection::rcv_ack, tcp_connection::snd_sent, tcp_connection::snd_seq, tcp_close(), TCP_CLOSE_WAIT, TCP_CLOSED, TCP_CLOSING_OR_LAST_ACK, tcp_dump_state(), TCP_ESTABLISHED, TCP_FIN_WAIT_1, tcp_connection::tcp_state, tcp_state(), TCP_SYN_RCVD, TCP_SYN_SENT, and tcp_xmit().

Referenced by tcp_open().

                                                                {
        struct tcp_connection *tcp =
                container_of ( timer, struct tcp_connection, timer );

        DBGC ( tcp, "TCP %p timer %s in %s for %08x..%08x %08x\n", tcp,
               ( over ? "expired" : "fired" ), tcp_state ( tcp->tcp_state ),
               tcp->snd_seq, ( tcp->snd_seq + tcp->snd_sent ), tcp->rcv_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_CLOSE_WAIT ) ||
                 ( tcp->tcp_state == TCP_CLOSING_OR_LAST_ACK ) );

        if ( over ) {
                /* If we have finally timed out and given up,
                 * terminate the connection
                 */
                tcp->tcp_state = TCP_CLOSED;
                tcp_dump_state ( tcp );
                tcp_close ( tcp, -ETIMEDOUT );
        } else {
                /* Otherwise, retransmit the packet */
                tcp_xmit ( tcp );
        }
}
static void tcp_keepalive_expired ( struct retry_timer timer,
int over  __unused 
) [static]

Keepalive timer expired.

Parameters:
timerKeepalive timer
overFailure indicator

Definition at line 818 of file tcp.c.

References container_of, DBGC, tcp_connection::flags, tcp_connection::keepalive, start_timer_fixed(), TCP_ACK_PENDING, TCP_KEEPALIVE_DELAY, and tcp_xmit().

Referenced by tcp_open().

                                                        {
        struct tcp_connection *tcp =
                container_of ( timer, struct tcp_connection, keepalive );

        DBGC ( tcp, "TCP %p sending keepalive\n", tcp );

        /* Reset keepalive timer */
        start_timer_fixed ( &tcp->keepalive, TCP_KEEPALIVE_DELAY );

        /* Send keepalive.  We do this only to preserve or restore
         * state in intermediate devices (e.g. firewall NAT tables);
         * we don't actually care about eliciting a response to verify
         * that the peer is still alive.  We therefore send just a
         * pure ACK, to keep our transmit path simple.
         */
        tcp->flags |= TCP_ACK_PENDING;
        tcp_xmit ( tcp );
}
static void tcp_wait_expired ( struct retry_timer timer,
int over  __unused 
) [static]

Shutdown timer expired.

Parameters:
timerShutdown timer
overFailure indicator

Definition at line 844 of file tcp.c.

References assert, container_of, DBGC, tcp_connection::rcv_ack, tcp_connection::snd_sent, tcp_connection::snd_seq, tcp_close(), TCP_CLOSED, tcp_dump_state(), tcp_connection::tcp_state, tcp_state(), TCP_TIME_WAIT, and tcp_connection::wait.

Referenced by tcp_open().

                                                                              {
        struct tcp_connection *tcp =
                container_of ( timer, struct tcp_connection, wait );

        assert ( tcp->tcp_state == TCP_TIME_WAIT );

        DBGC ( tcp, "TCP %p wait complete in %s for %08x..%08x %08x\n", tcp,
               tcp_state ( tcp->tcp_state ), tcp->snd_seq,
               ( tcp->snd_seq + tcp->snd_sent ), tcp->rcv_ack );

        tcp->tcp_state = TCP_CLOSED;
        tcp_dump_state ( tcp );
        tcp_close ( tcp, 0 );
}
static struct tcp_connection * tcp_demux ( unsigned int  local_port) [static, read]

Identify TCP connection by local port number.

Parameters:
local_portLocal port
Return values:
tcpTCP connection, or NULL

Definition at line 928 of file tcp.c.

References tcp_connection::list, list_for_each_entry, tcp_connection::local_port, and NULL.

Referenced by tcp_port_available(), and tcp_rx().

                                                                     {
        struct tcp_connection *tcp;

        list_for_each_entry ( tcp, &tcp_conns, list ) {
                if ( tcp->local_port == local_port )
                        return tcp;
        }
        return NULL;
}
static int tcp_rx_ack ( struct tcp_connection tcp,
uint32_t  ack,
uint32_t  win 
) [static]

Handle TCP received ACK.

Parameters:
tcpTCP connection
ackACK value (in host-endian order)
winWIN value (in host-endian order)
Return values:
rcReturn status code

Definition at line 1114 of file tcp.c.

References DBGC, EINVAL, tcp_connection::flags, tcp_connection::keepalive, len, list_empty, NULL, tcp_connection::pending_flags, pending_get(), pending_put(), tcp_connection::snd_sent, tcp_connection::snd_seq, tcp_connection::snd_win, start_timer_fixed(), stop_timer(), TCP_FIN, TCP_FLAGS_SENDING, TCP_HAS_BEEN_ESTABLISHED, TCP_KEEPALIVE_DELAY, tcp_process_tx_queue(), tcp_connection::tcp_state, TCP_STATE_ACKED, TCP_STATE_SENT, TCP_SYN, TCP_XFER_CLOSED, tcp_connection::timer, and tcp_connection::tx_queue.

Referenced by tcp_close(), and tcp_rx().

                                       {
        uint32_t ack_len = ( ack - tcp->snd_seq );
        size_t len;
        unsigned int acked_flags;

        /* Check for out-of-range or old duplicate ACKs */
        if ( ack_len > tcp->snd_sent ) {
                DBGC ( tcp, "TCP %p received ACK for %08x..%08x, "
                       "sent only %08x..%08x\n", tcp, tcp->snd_seq,
                       ( tcp->snd_seq + ack_len ), tcp->snd_seq,
                       ( tcp->snd_seq + tcp->snd_sent ) );

                if ( TCP_HAS_BEEN_ESTABLISHED ( tcp->tcp_state ) ) {
                        /* Just ignore what might be old duplicate ACKs */
                        return 0;
                } else {
                        /* Send RST if an out-of-range ACK is received
                         * on a not-yet-established connection, as per
                         * RFC 793.
                         */
                        return -EINVAL;
                }
        }

        /* Update window size */
        tcp->snd_win = win;

        /* Hold off (or start) the keepalive timer, if applicable */
        if ( ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) )
                start_timer_fixed ( &tcp->keepalive, TCP_KEEPALIVE_DELAY );

        /* Ignore ACKs that don't actually acknowledge any new data.
         * (In particular, do not stop the retransmission timer; this
         * avoids creating a sorceror's apprentice syndrome when a
         * duplicate ACK is received and we still have data in our
         * transmit queue.)
         */
        if ( ack_len == 0 )
                return 0;

        /* Stop the retransmission timer */
        stop_timer ( &tcp->timer );

        /* Determine acknowledged flags and data length */
        len = ack_len;
        acked_flags = ( TCP_FLAGS_SENDING ( tcp->tcp_state ) &
                        ( TCP_SYN | TCP_FIN ) );
        if ( acked_flags ) {
                len--;
                pending_put ( &tcp->pending_flags );
        }

        /* Update SEQ and sent counters */
        tcp->snd_seq = ack;
        tcp->snd_sent = 0;

        /* Remove any acknowledged data from transmit queue */
        tcp_process_tx_queue ( tcp, len, NULL, 1 );
                
        /* Mark SYN/FIN as acknowledged if applicable. */
        if ( acked_flags )
                tcp->tcp_state |= TCP_STATE_ACKED ( acked_flags );

        /* Start sending FIN if we've had all possible data ACKed */
        if ( list_empty ( &tcp->tx_queue ) &&
             ( tcp->flags & TCP_XFER_CLOSED ) &&
             ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) {
                tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
                pending_get ( &tcp->pending_flags );
        }

        return 0;
}
static const char* tcp_state ( int  state) [inline, static]

Name TCP state.

Parameters:
stateTCP state
Return values:
nameName of TCP state

Definition at line 195 of file tcp.c.

References TCP_CLOSE_WAIT, TCP_CLOSED, TCP_CLOSING_OR_LAST_ACK, TCP_ESTABLISHED, TCP_FIN_WAIT_1, TCP_FIN_WAIT_2, TCP_LISTEN, TCP_SYN_RCVD, TCP_SYN_SENT, and TCP_TIME_WAIT.

Referenced by tcp_dump_state(), tcp_expired(), and tcp_wait_expired().

                        {
        switch ( state ) {
        case TCP_CLOSED:                return "CLOSED";
        case TCP_LISTEN:                return "LISTEN";
        case TCP_SYN_SENT:              return "SYN_SENT";
        case TCP_SYN_RCVD:              return "SYN_RCVD";
        case TCP_ESTABLISHED:           return "ESTABLISHED";
        case TCP_FIN_WAIT_1:            return "FIN_WAIT_1";
        case TCP_FIN_WAIT_2:            return "FIN_WAIT_2";
        case TCP_CLOSING_OR_LAST_ACK:   return "CLOSING/LAST_ACK";
        case TCP_TIME_WAIT:             return "TIME_WAIT";
        case TCP_CLOSE_WAIT:            return "CLOSE_WAIT";
        default:                        return "INVALID";
        }
}
static void tcp_dump_state ( struct tcp_connection tcp) [inline, static]

Dump TCP state transition.

Parameters:
tcpTCP connection

Definition at line 217 of file tcp.c.

References DBGC, and tcp_state().

Referenced by tcp_close(), tcp_expired(), tcp_open(), tcp_rx(), tcp_rx_rst(), tcp_shutdown(), and tcp_wait_expired().

                                              {

        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 ) );
        }
        tcp->prev_tcp_state = tcp->tcp_state;
}
static void tcp_dump_flags ( struct tcp_connection tcp,
unsigned int  flags 
) [inline, static]

Dump TCP flags.

Parameters:
flagsTCP flags

Definition at line 233 of file tcp.c.

References DBGC2, TCP_ACK, TCP_FIN, TCP_PSH, TCP_RST, and TCP_SYN.

Referenced by tcp_rx(), tcp_xmit_reset(), and tcp_xmit_sack().

                                                                  {
        if ( flags & TCP_RST )
                DBGC2 ( tcp, " RST" );
        if ( flags & TCP_SYN )
                DBGC2 ( tcp, " SYN" );
        if ( flags & TCP_PSH )
                DBGC2 ( tcp, " PSH" );
        if ( flags & TCP_FIN )
                DBGC2 ( tcp, " FIN" );
        if ( flags & TCP_ACK )
                DBGC2 ( tcp, " ACK" );
}
static int tcp_port_available ( int  port) [static]

Check if local TCP port is available.

Parameters:
portLocal port number
Return values:
portLocal port number, or negative error

Definition at line 259 of file tcp.c.

References EADDRINUSE, and tcp_demux().

Referenced by tcp_open().

                                           {

        return ( tcp_demux ( port ) ? -EADDRINUSE : port );
}
static int tcp_open ( struct interface xfer,
struct sockaddr peer,
struct sockaddr local 
) [static]

Open a TCP connection.

Parameters:
xferData transfer interface
peerPeer socket address
localLocal socket address, or NULL
Return values:
rcReturn status code

Definition at line 272 of file tcp.c.

References DBGC, ENETUNREACH, ENOMEM, INIT_LIST_HEAD, intf_init(), intf_plug_plug(), tcp_connection::keepalive, tcp_connection::list, list_add, tcp_connection::local_port, memcpy(), tcp_connection::mss, mtu, NULL, tcp_connection::peer, tcp_connection::pending_flags, pending_get(), port, tcp_connection::prev_tcp_state, tcp_connection::process, process_init_stopped(), random(), rc, ref_init, ref_put, tcp_connection::refcnt, tcp_connection::rx_queue, tcp_connection::snd_seq, sock_ntoa(), start_timer_nodelay(), strerror(), TCP_CLOSED, tcp_dump_state(), tcp_expired(), tcp_keepalive_expired(), tcp_port_available(), tcp_process_desc, tcp_connection::tcp_state, TCP_STATE_SENT, TCP_SYN, tcp_wait_expired(), tcp_xfer_desc, tcpip_bind(), tcpip_mtu(), tcp_connection::timer, tcp_connection::tx_queue, tcp_connection::wait, tcp_connection::xfer, and zalloc().

                                               {
        struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
        struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
        struct tcp_connection *tcp;
        size_t mtu;
        int port;
        int rc;

        /* Allocate and initialise structure */
        tcp = zalloc ( sizeof ( *tcp ) );
        if ( ! tcp )
                return -ENOMEM;
        DBGC ( tcp, "TCP %p allocated\n", tcp );
        ref_init ( &tcp->refcnt, NULL );
        intf_init ( &tcp->xfer, &tcp_xfer_desc, &tcp->refcnt );
        process_init_stopped ( &tcp->process, &tcp_process_desc, &tcp->refcnt );
        timer_init ( &tcp->timer, tcp_expired, &tcp->refcnt );
        timer_init ( &tcp->keepalive, tcp_keepalive_expired, &tcp->refcnt );
        timer_init ( &tcp->wait, tcp_wait_expired, &tcp->refcnt );
        tcp->prev_tcp_state = TCP_CLOSED;
        tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN );
        tcp_dump_state ( tcp );
        tcp->snd_seq = random();
        INIT_LIST_HEAD ( &tcp->tx_queue );
        INIT_LIST_HEAD ( &tcp->rx_queue );
        memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) );

        /* Calculate MSS */
        mtu = tcpip_mtu ( &tcp->peer );
        if ( ! mtu ) {
                DBGC ( tcp, "TCP %p has no route to %s\n",
                       tcp, sock_ntoa ( peer ) );
                rc = -ENETUNREACH;
                goto err;
        }
        tcp->mss = ( mtu - sizeof ( struct tcp_header ) );

        /* Bind to local port */
        port = tcpip_bind ( st_local, tcp_port_available );
        if ( port < 0 ) {
                rc = port;
                DBGC ( tcp, "TCP %p could not bind: %s\n",
                       tcp, strerror ( rc ) );
                goto err;
        }
        tcp->local_port = port;
        DBGC ( tcp, "TCP %p bound to port %d\n", tcp, tcp->local_port );

        /* Start timer to initiate SYN */
        start_timer_nodelay ( &tcp->timer );

        /* Add a pending operation for the SYN */
        pending_get ( &tcp->pending_flags );

        /* Attach parent interface, transfer reference to connection
         * list and return
         */
        intf_plug_plug ( &tcp->xfer, xfer );
        list_add ( &tcp->list, &tcp_conns );
        return 0;

 err:
        ref_put ( &tcp->refcnt );
        return rc;
}
static void tcp_close ( struct tcp_connection tcp,
int  rc 
) [static]

Close TCP connection.

Parameters:
tcpTCP connection
rcReason for close

Closes the data transfer interface. If the TCP state machine is in a suitable state, the connection will be deleted.

Definition at line 348 of file tcp.c.

References assert, DBGC, tcp_connection::flags, free_iob(), intf_shutdown(), is_pending(), tcp_connection::keepalive, tcp_connection::list, io_buffer::list, list_del, list_empty, list_for_each_entry_safe, tcp_connection::pending_data, tcp_connection::pending_flags, pending_get(), pending_put(), tcp_connection::process, process_add(), process_del(), ref_put, tcp_connection::refcnt, tcp_connection::rx_queue, tcp_connection::snd_seq, stop_timer(), TCP_CLOSED, tcp_dump_state(), TCP_FIN, tcp_rx_ack(), tcp_connection::tcp_state, TCP_STATE_ACKED, TCP_STATE_RCVD, TCP_STATE_SENT, TCP_SYN, TCP_XFER_CLOSED, tcp_connection::timer, tcp_connection::tx_queue, tcp_connection::wait, and tcp_connection::xfer.

Referenced by tcp_expired(), tcp_rx_fin(), tcp_rx_rst(), tcp_shutdown(), tcp_wait_expired(), and tcp_xfer_close().

                                                             {
        struct io_buffer *iobuf;
        struct io_buffer *tmp;

        /* Close data transfer interface */
        intf_shutdown ( &tcp->xfer, rc );
        tcp->flags |= TCP_XFER_CLOSED;

        /* If we are in CLOSED, or have otherwise not yet received a
         * SYN (i.e. we are in LISTEN or SYN_SENT), just delete the
         * connection.
         */
        if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) {

                /* Transition to CLOSED for the sake of debugging messages */
                tcp->tcp_state = TCP_CLOSED;
                tcp_dump_state ( tcp );

                /* Free any unprocessed I/O buffers */
                list_for_each_entry_safe ( iobuf, tmp, &tcp->rx_queue, list ) {
                        list_del ( &iobuf->list );
                        free_iob ( iobuf );
                }

                /* Free any unsent I/O buffers */
                list_for_each_entry_safe ( iobuf, tmp, &tcp->tx_queue, list ) {
                        list_del ( &iobuf->list );
                        free_iob ( iobuf );
                        pending_put ( &tcp->pending_data );
                }
                assert ( ! is_pending ( &tcp->pending_data ) );

                /* Remove pending operations for SYN and FIN, if applicable */
                pending_put ( &tcp->pending_flags );
                pending_put ( &tcp->pending_flags );

                /* Remove from list and drop reference */
                process_del ( &tcp->process );
                stop_timer ( &tcp->timer );
                stop_timer ( &tcp->keepalive );
                stop_timer ( &tcp->wait );
                list_del ( &tcp->list );
                ref_put ( &tcp->refcnt );
                DBGC ( tcp, "TCP %p connection deleted\n", tcp );
                return;
        }

        /* If we have not had our SYN acknowledged (i.e. we are in
         * SYN_RCVD), pretend that it has been acknowledged so that we
         * can send a FIN without breaking things.
         */
        if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
                tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 );

        /* Stop keepalive timer */
        stop_timer ( &tcp->keepalive );

        /* If we have no data remaining to send, start sending FIN */
        if ( list_empty ( &tcp->tx_queue ) &&
             ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) {

                tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
                tcp_dump_state ( tcp );
                process_add ( &tcp->process );

                /* Add a pending operation for the FIN */
                pending_get ( &tcp->pending_flags );
        }
}
static size_t tcp_xmit_win ( struct tcp_connection tcp) [static]

Calculate transmission window.

Parameters:
tcpTCP connection
Return values:
lenMaximum length that can be sent in a single packet

Definition at line 431 of file tcp.c.

References len, tcp_connection::snd_win, TCP_CAN_SEND_DATA, TCP_PATH_MTU, and tcp_connection::tcp_state.

Referenced by tcp_xfer_window(), and tcp_xmit_sack().

                                                          {
        size_t len;

        /* Not ready if we're not in a suitable connection state */
        if ( ! TCP_CAN_SEND_DATA ( tcp->tcp_state ) )
                return 0;

        /* Length is the minimum of the receiver's window and the path MTU */
        len = tcp->snd_win;
        if ( len > TCP_PATH_MTU )
                len = TCP_PATH_MTU;

        return len;
}
static size_t tcp_xfer_window ( struct tcp_connection tcp) [static]

Check data-transfer flow control window.

Parameters:
tcpTCP connection
Return values:
lenLength of window

Definition at line 452 of file tcp.c.

References list_empty, tcp_xmit_win(), and tcp_connection::tx_queue.

Referenced by tcp_rx().

                                                             {

        /* Not ready if data queue is non-empty.  This imposes a limit
         * of only one unACKed packet in the TX queue at any time; we
         * do this to conserve memory usage.
         */
        if ( ! list_empty ( &tcp->tx_queue ) )
                return 0;

        /* Return TCP window length */
        return tcp_xmit_win ( tcp );
}
static uint32_t tcp_sack_block ( struct tcp_connection tcp,
uint32_t  seq,
struct tcp_sack_block sack 
) [static]

Find selective acknowledgement block.

Parameters:
tcpTCP connection
seqSEQ value in SACK block (in host-endian order)
sackSACK block to fill in (in host-endian order)
Return values:
lenLength of SACK block

Definition at line 473 of file tcp.c.

References io_buffer::data, tcp_sack_block::left, list_for_each_entry, tcp_rx_queued_header::nxt, tcp_connection::rcv_ack, tcp_sack_block::right, tcp_connection::rx_queue, tcp_rx_queued_header::seq, and tcp_cmp().

Referenced by tcp_sack().

                                                               {
        struct io_buffer *iobuf;
        struct tcp_rx_queued_header *tcpqhdr;
        uint32_t left = tcp->rcv_ack;
        uint32_t right = left;

        /* Find highest block which does not start after SEQ */
        list_for_each_entry ( iobuf, &tcp->rx_queue, list ) {
                tcpqhdr = iobuf->data;
                if ( tcp_cmp ( tcpqhdr->seq, right ) > 0 ) {
                        if ( tcp_cmp ( tcpqhdr->seq, seq ) > 0 )
                                break;
                        left = tcpqhdr->seq;
                }
                if ( tcp_cmp ( tcpqhdr->nxt, right ) > 0 )
                        right = tcpqhdr->nxt;
        }

        /* Fail if this block does not contain SEQ */
        if ( tcp_cmp ( right, seq ) < 0 )
                return 0;

        /* Populate SACK block */
        sack->left = left;
        sack->right = right;
        return ( right - left );
}
static unsigned int tcp_sack ( struct tcp_connection tcp,
uint32_t  seq 
) [static]

Update TCP selective acknowledgement list.

Parameters:
tcpTCP connection
seqSEQ value in first SACK block (in host-endian order)
Return values:
countNumber of SACK blocks

Definition at line 509 of file tcp.c.

References tcp_sack_block::left, len, memcpy(), memset(), tcp_sack_block::right, tcp_connection::sack, tcp_sack_block(), and TCP_SACK_MAX.

Referenced by tcp_xmit_sack().

                                                                          {
        struct tcp_sack_block sack[TCP_SACK_MAX];
        unsigned int old = 0;
        unsigned int new = 0;
        unsigned int i;
        uint32_t len;

        /* Populate first new SACK block */
        len = tcp_sack_block ( tcp, seq, &sack[0] );
        if ( len )
                new++;

        /* Populate remaining new SACK blocks based on old SACK blocks */
        for ( old = 0 ; old < TCP_SACK_MAX ; old++ ) {

                /* Stop if we run out of space in the new list */
                if ( new == TCP_SACK_MAX )
                        break;

                /* Skip empty old SACK blocks */
                if ( tcp->sack[old].left == tcp->sack[old].right )
                        continue;

                /* Populate new SACK block */
                len = tcp_sack_block ( tcp, tcp->sack[old].left, &sack[new] );
                if ( len == 0 )
                        continue;

                /* Eliminate duplicates */
                for ( i = 0 ; i < new ; i++ ) {
                        if ( sack[i].left == sack[new].left ) {
                                new--;
                                break;
                        }
                }
                new++;
        }

        /* Update SACK list */
        memset ( tcp->sack, 0, sizeof ( tcp->sack ) );
        memcpy ( tcp->sack, sack, ( new * sizeof ( tcp->sack[0] ) ) );
        return new;
}
static size_t tcp_process_tx_queue ( struct tcp_connection tcp,
size_t  max_len,
struct io_buffer dest,
int  remove 
) [static]

Process TCP transmit queue.

Parameters:
tcpTCP connection
max_lenMaximum length to process
destI/O buffer to fill with data, or NULL
removeRemove data from queue
Return values:
lenLength of data processed

This processes at most max_len bytes from the TCP connection's transmit queue. Data will be copied into the dest I/O buffer (if provided) and, if remove is true, removed from the transmit queue.

Definition at line 567 of file tcp.c.

References io_buffer::data, free_iob(), iob_len(), iob_pull, iob_put, len, io_buffer::list, list_del, list_for_each_entry_safe, max_len, memcpy(), tcp_connection::pending_data, pending_put(), and tcp_connection::tx_queue.

Referenced by tcp_rx_ack(), and tcp_xmit_sack().

                                                                          {
        struct io_buffer *iobuf;
        struct io_buffer *tmp;
        size_t frag_len;
        size_t len = 0;

        list_for_each_entry_safe ( iobuf, tmp, &tcp->tx_queue, list ) {
                frag_len = iob_len ( iobuf );
                if ( frag_len > max_len )
                        frag_len = max_len;
                if ( dest ) {
                        memcpy ( iob_put ( dest, frag_len ), iobuf->data,
                                 frag_len );
                }
                if ( remove ) {
                        iob_pull ( iobuf, frag_len );
                        if ( ! iob_len ( iobuf ) ) {
                                list_del ( &iobuf->list );
                                free_iob ( iobuf );
                                pending_put ( &tcp->pending_data );
                        }
                }
                len += frag_len;
                max_len -= frag_len;
        }
        return len;
}
static void tcp_xmit_sack ( struct tcp_connection tcp,
uint32_t  sack_seq 
) [static]

Transmit any outstanding data (with selective acknowledgement)

Parameters:
tcpTCP connection
sack_seqSEQ for first selective acknowledgement (if any)

Transmits any outstanding data on the connection.

Note that even if an error is returned, the retransmission timer will have been started if necessary, and so the stack will eventually attempt to retransmit the failed packet.

Definition at line 608 of file tcp.c.

References tcp_header::ack, alloc_iob(), assert, tcp_header::csum, currticks(), io_buffer::data, DBGC, DBGC2, tcp_header::dest, tcp_header::flags, tcp_connection::flags, flags, tcp_header::hlen, htonl, htons, iob_len(), iob_push, iob_reserve, tcp_mss_option::kind, tcp_window_scale_option::kind, tcp_sack_permitted_option::kind, tcp_sack_option::kind, tcp_timestamp_option::kind, tcp_sack_block::left, len, tcp_mss_option::length, tcp_window_scale_option::length, tcp_sack_permitted_option::length, tcp_sack_option::length, tcp_timestamp_option::length, list_empty, tcp_connection::local_port, memset(), tcp_connection::mss, tcp_mss_option::mss, tcp_window_scale_padded_option::nop, tcp_sack_permitted_padded_option::nop, tcp_sack_padded_option::nop, tcp_timestamp_padded_option::nop, ntohl, ntohs, NULL, tcp_connection::peer, profile_start(), profile_stop(), rc, tcp_connection::rcv_ack, tcp_connection::rcv_win, tcp_connection::rcv_win_scale, tcp_sack_block::right, tcp_connection::rx_queue, tcp_connection::sack, tcp_sack_padded_option::sackopt, tcp_window_scale_option::scale, tcp_header::seq, tcp_connection::snd_sent, tcp_connection::snd_seq, tcp_sack_permitted_padded_option::spopt, tcp_header::src, sockaddr_tcpip::st_port, start_timer(), strerror(), TCP_ACK_PENDING, TCP_CAN_SEND_DATA, tcp_dump_flags(), TCP_FIN, TCP_FLAGS_SENDING, TCP_MAX_HEADER_LEN, TCP_MAX_WINDOW_SIZE, TCP_OPTION_MSS, TCP_OPTION_NOP, TCP_OPTION_SACK, TCP_OPTION_SACK_PERMITTED, TCP_OPTION_TS, TCP_OPTION_WS, tcp_process_tx_queue(), TCP_PSH, TCP_RX_WINDOW_SCALE, tcp_sack(), TCP_SACK_ENABLED, tcp_connection::tcp_state, TCP_SYN, TCP_TS_ENABLED, tcp_xmit_win(), tcpip_chksum(), tcpip_tx(), tcp_connection::timer, tcp_connection::ts_recent, tcp_timestamp_option::tsecr, tcp_timestamp_padded_option::tsopt, tcp_timestamp_option::tsval, tcp_header::win, tcp_window_scale_padded_option::wsopt, wsopt, tcp_connection::xfer, and xfer_window().

Referenced by tcp_rx(), and tcp_xmit().

                                                                            {
        struct io_buffer *iobuf;
        struct tcp_header *tcphdr;
        struct tcp_mss_option *mssopt;
        struct tcp_window_scale_padded_option *wsopt;
        struct tcp_timestamp_padded_option *tsopt;
        struct tcp_sack_permitted_padded_option *spopt;
        struct tcp_sack_padded_option *sackopt;
        struct tcp_sack_block *sack;
        void *payload;
        unsigned int flags;
        unsigned int sack_count;
        unsigned int i;
        size_t len = 0;
        size_t sack_len;
        uint32_t seq_len;
        uint32_t max_rcv_win;
        uint32_t max_representable_win;
        int rc;

        /* Start profiling */
        profile_start ( &tcp_tx_profiler );

        /* If retransmission timer is already running, do nothing */
        if ( timer_running ( &tcp->timer ) )
                return;

        /* Calculate both the actual (payload) and sequence space
         * lengths that we wish to transmit.
         */
        if ( TCP_CAN_SEND_DATA ( tcp->tcp_state ) ) {
                len = tcp_process_tx_queue ( tcp, tcp_xmit_win ( tcp ),
                                             NULL, 0 );
        }
        seq_len = len;
        flags = TCP_FLAGS_SENDING ( tcp->tcp_state );
        if ( flags & ( TCP_SYN | TCP_FIN ) ) {
                /* SYN or FIN consume one byte, and we can never send both */
                assert ( ! ( ( flags & TCP_SYN ) && ( flags & TCP_FIN ) ) );
                seq_len++;
        }
        tcp->snd_sent = seq_len;

        /* If we have nothing to transmit, stop now */
        if ( ( seq_len == 0 ) && ! ( tcp->flags & TCP_ACK_PENDING ) )
                return;

        /* If we are transmitting anything that requires
         * acknowledgement (i.e. consumes sequence space), start the
         * retransmission timer.  Do this before attempting to
         * allocate the I/O buffer, in case allocation itself fails.
         */
        if ( seq_len )
                start_timer ( &tcp->timer );

        /* Allocate I/O buffer */
        iobuf = alloc_iob ( len + TCP_MAX_HEADER_LEN );
        if ( ! iobuf ) {
                DBGC ( tcp, "TCP %p could not allocate iobuf for %08x..%08x "
                       "%08x\n", tcp, tcp->snd_seq, ( tcp->snd_seq + seq_len ),
                       tcp->rcv_ack );
                return;
        }
        iob_reserve ( iobuf, TCP_MAX_HEADER_LEN );

        /* Fill data payload from transmit queue */
        tcp_process_tx_queue ( tcp, len, iobuf, 0 );

        /* Expand receive window if possible */
        max_rcv_win = xfer_window ( &tcp->xfer );
        if ( max_rcv_win > TCP_MAX_WINDOW_SIZE )
                max_rcv_win = TCP_MAX_WINDOW_SIZE;
        max_representable_win = ( 0xffff << tcp->rcv_win_scale );
        if ( max_rcv_win > max_representable_win )
                max_rcv_win = max_representable_win;
        max_rcv_win &= ~0x03; /* Keep everything dword-aligned */
        if ( tcp->rcv_win < max_rcv_win )
                tcp->rcv_win = max_rcv_win;

        /* Fill up the TCP header */
        payload = iobuf->data;
        if ( flags & TCP_SYN ) {
                mssopt = iob_push ( iobuf, sizeof ( *mssopt ) );
                mssopt->kind = TCP_OPTION_MSS;
                mssopt->length = sizeof ( *mssopt );
                mssopt->mss = htons ( tcp->mss );
                wsopt = iob_push ( iobuf, sizeof ( *wsopt ) );
                wsopt->nop = TCP_OPTION_NOP;
                wsopt->wsopt.kind = TCP_OPTION_WS;
                wsopt->wsopt.length = sizeof ( wsopt->wsopt );
                wsopt->wsopt.scale = TCP_RX_WINDOW_SCALE;
                spopt = iob_push ( iobuf, sizeof ( *spopt ) );
                memset ( spopt->nop, TCP_OPTION_NOP, sizeof ( spopt->nop ) );
                spopt->spopt.kind = TCP_OPTION_SACK_PERMITTED;
                spopt->spopt.length = sizeof ( spopt->spopt );
        }
        if ( ( flags & TCP_SYN ) || ( tcp->flags & TCP_TS_ENABLED ) ) {
                tsopt = iob_push ( iobuf, sizeof ( *tsopt ) );
                memset ( tsopt->nop, TCP_OPTION_NOP, sizeof ( tsopt->nop ) );
                tsopt->tsopt.kind = TCP_OPTION_TS;
                tsopt->tsopt.length = sizeof ( tsopt->tsopt );
                tsopt->tsopt.tsval = htonl ( currticks() );
                tsopt->tsopt.tsecr = htonl ( tcp->ts_recent );
        }
        if ( ( tcp->flags & TCP_SACK_ENABLED ) &&
             ( ! list_empty ( &tcp->rx_queue ) ) &&
             ( ( sack_count = tcp_sack ( tcp, sack_seq ) ) != 0 ) ) {
                sack_len = ( sack_count * sizeof ( *sack ) );
                sackopt = iob_push ( iobuf, ( sizeof ( *sackopt ) + sack_len ));
                memset ( sackopt->nop, TCP_OPTION_NOP, sizeof ( sackopt->nop ));
                sackopt->sackopt.kind = TCP_OPTION_SACK;
                sackopt->sackopt.length =
                        ( sizeof ( sackopt->sackopt ) + sack_len );
                sack = ( ( ( void * ) sackopt ) + sizeof ( *sackopt ) );
                for ( i = 0 ; i < sack_count ; i++, sack++ ) {
                        sack->left = htonl ( tcp->sack[i].left );
                        sack->right = htonl ( tcp->sack[i].right );
                }
        }
        if ( len != 0 )
                flags |= TCP_PSH;
        tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) );
        memset ( tcphdr, 0, sizeof ( *tcphdr ) );
        tcphdr->src = htons ( tcp->local_port );
        tcphdr->dest = tcp->peer.st_port;
        tcphdr->seq = htonl ( tcp->snd_seq );
        tcphdr->ack = htonl ( tcp->rcv_ack );
        tcphdr->hlen = ( ( payload - iobuf->data ) << 2 );
        tcphdr->flags = flags;
        tcphdr->win = htons ( tcp->rcv_win >> tcp->rcv_win_scale );
        tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) );

        /* Dump header */
        DBGC2 ( tcp, "TCP %p TX %d->%d %08x..%08x           %08x %4zd",
                tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
                ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) + seq_len ),
                ntohl ( tcphdr->ack ), len );
        tcp_dump_flags ( tcp, tcphdr->flags );
        DBGC2 ( tcp, "\n" );

        /* Transmit packet */
        if ( ( rc = tcpip_tx ( iobuf, &tcp_protocol, NULL, &tcp->peer, NULL,
                               &tcphdr->csum ) ) != 0 ) {
                DBGC ( tcp, "TCP %p could not transmit %08x..%08x %08x: %s\n",
                       tcp, tcp->snd_seq, ( tcp->snd_seq + tcp->snd_sent ),
                       tcp->rcv_ack, strerror ( rc ) );
                return;
        }

        /* Clear ACK-pending flag */
        tcp->flags &= ~TCP_ACK_PENDING;

        profile_stop ( &tcp_tx_profiler );
}
static void tcp_xmit ( struct tcp_connection tcp) [static]

Transmit any outstanding data.

Parameters:
tcpTCP connection

Definition at line 768 of file tcp.c.

References tcp_connection::rcv_ack, and tcp_xmit_sack().

Referenced by tcp_expired(), tcp_keepalive_expired(), tcp_xfer_close(), and tcp_xfer_deliver().

                                                    {

        /* Transmit without an explicit first SACK */
        tcp_xmit_sack ( tcp, tcp->rcv_ack );
}
static int tcp_xmit_reset ( struct tcp_connection tcp,
struct sockaddr_tcpip st_dest,
struct tcp_header in_tcphdr 
) [static]

Send RST response to incoming packet.

Parameters:
in_tcphdrTCP header of incoming packet
Return values:
rcReturn status code

Definition at line 865 of file tcp.c.

References tcp_header::ack, alloc_iob(), tcp_header::csum, io_buffer::data, DBGC, DBGC2, tcp_header::dest, ENOMEM, tcp_header::flags, tcp_header::hlen, htons, iob_len(), iob_push, iob_reserve, memset(), ntohl, ntohs, NULL, rc, tcp_header::seq, tcp_header::src, strerror(), TCP_ACK, tcp_dump_flags(), TCP_MAX_HEADER_LEN, TCP_RST, tcpip_chksum(), tcpip_tx(), and tcp_header::win.

Referenced by tcp_rx().

                                                           {
        struct io_buffer *iobuf;
        struct tcp_header *tcphdr;
        int rc;

        /* Allocate space for dataless TX buffer */
        iobuf = alloc_iob ( TCP_MAX_HEADER_LEN );
        if ( ! iobuf ) {
                DBGC ( tcp, "TCP %p could not allocate iobuf for RST "
                       "%08x..%08x %08x\n", tcp, ntohl ( in_tcphdr->ack ),
                       ntohl ( in_tcphdr->ack ), ntohl ( in_tcphdr->seq ) );
                return -ENOMEM;
        }
        iob_reserve ( iobuf, TCP_MAX_HEADER_LEN );

        /* Construct RST response */
        tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) );
        memset ( tcphdr, 0, sizeof ( *tcphdr ) );
        tcphdr->src = in_tcphdr->dest;
        tcphdr->dest = in_tcphdr->src;
        tcphdr->seq = in_tcphdr->ack;
        tcphdr->ack = in_tcphdr->seq;
        tcphdr->hlen = ( ( sizeof ( *tcphdr ) / 4 ) << 4 );
        tcphdr->flags = ( TCP_RST | TCP_ACK );
        tcphdr->win = htons ( 0 );
        tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) );

        /* Dump header */
        DBGC2 ( tcp, "TCP %p TX %d->%d %08x..%08x           %08x %4d",
                tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
                ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) ),
                ntohl ( tcphdr->ack ), 0 );
        tcp_dump_flags ( tcp, tcphdr->flags );
        DBGC2 ( tcp, "\n" );

        /* Transmit packet */
        if ( ( rc = tcpip_tx ( iobuf, &tcp_protocol, NULL, st_dest,
                               NULL, &tcphdr->csum ) ) != 0 ) {
                DBGC ( tcp, "TCP %p could not transmit RST %08x..%08x %08x: "
                       "%s\n", tcp, ntohl ( in_tcphdr->ack ),
                       ntohl ( in_tcphdr->ack ), ntohl ( in_tcphdr->seq ),
                       strerror ( rc ) );
                return rc;
        }

        return 0;
}
static int tcp_rx_opts ( struct tcp_connection tcp,
const struct tcp_header tcphdr,
size_t  hlen,
struct tcp_options options 
) [static]

Parse TCP received options.

Parameters:
tcpTCP connection (may be NULL)
tcphdrTCP header
hlenTCP header length
optionsOptions structure to fill in
Return values:
rcReturn status code

Definition at line 947 of file tcp.c.

References assert, data, DBGC, EINVAL, end, tcp_option::kind, tcp_option::length, memset(), min, tcp_options::spopt, TCP_OPTION_END, TCP_OPTION_MSS, TCP_OPTION_NOP, TCP_OPTION_SACK, TCP_OPTION_SACK_PERMITTED, TCP_OPTION_TS, TCP_OPTION_WS, tcp_options::tsopt, and tcp_options::wsopt.

Referenced by tcp_rx().

                                                       {
        const void *data = ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) );
        const void *end = ( ( ( void * ) tcphdr ) + hlen );
        const struct tcp_option *option;
        unsigned int kind;
        size_t remaining;
        size_t min;

        /* Sanity check */
        assert ( hlen >= sizeof ( *tcphdr ) );

        /* Parse options */
        memset ( options, 0, sizeof ( *options ) );
        while ( ( remaining = ( end - data ) ) ) {

                /* Extract option code */
                option = data;
                kind = option->kind;

                /* Handle single-byte options */
                if ( kind == TCP_OPTION_END )
                        break;
                if ( kind == TCP_OPTION_NOP ) {
                        data++;
                        continue;
                }

                /* Handle multi-byte options */
                min = sizeof ( *option );
                switch ( kind ) {
                case TCP_OPTION_MSS:
                        /* Ignore received MSS */
                        break;
                case TCP_OPTION_WS:
                        options->wsopt = data;
                        min = sizeof ( *options->wsopt );
                        break;
                case TCP_OPTION_SACK_PERMITTED:
                        options->spopt = data;
                        min = sizeof ( *options->spopt );
                        break;
                case TCP_OPTION_SACK:
                        /* Ignore received SACKs */
                        break;
                case TCP_OPTION_TS:
                        options->tsopt = data;
                        min = sizeof ( *options->tsopt );
                        break;
                default:
                        DBGC ( tcp, "TCP %p received unknown option %d\n",
                               tcp, kind );
                        break;
                }
                if ( remaining < min ) {
                        DBGC ( tcp, "TCP %p received truncated option %d\n",
                               tcp, kind );
                        return -EINVAL;
                }
                if ( option->length < min ) {
                        DBGC ( tcp, "TCP %p received underlength option %d\n",
                               tcp, kind );
                        return -EINVAL;
                }
                if ( option->length > remaining ) {
                        DBGC ( tcp, "TCP %p received overlength option %d\n",
                               tcp, kind );
                        return -EINVAL;
                }
                data += option->length;
        }

        return 0;
}
static void tcp_rx_seq ( struct tcp_connection tcp,
uint32_t  seq_len 
) [static]

Consume received sequence space.

Parameters:
tcpTCP connection
seq_lenSequence space length to consume

Definition at line 1029 of file tcp.c.

References assert, tcp_connection::flags, tcp_sack_block::left, tcp_connection::rcv_ack, tcp_connection::rcv_win, tcp_sack_block::right, tcp_connection::sack, TCP_ACK_PENDING, tcp_cmp(), TCP_SACK_MAX, tcp_connection::ts_recent, and tcp_connection::ts_val.

Referenced by tcp_rx_data(), tcp_rx_fin(), and tcp_rx_syn().

                                                                        {
        unsigned int sack;

        /* Sanity check */
        assert ( seq_len > 0 );

        /* Update acknowledgement number */
        tcp->rcv_ack += seq_len;

        /* Update window */
        if ( tcp->rcv_win > seq_len ) {
                tcp->rcv_win -= seq_len;
        } else {
                tcp->rcv_win = 0;
        }

        /* Update timestamp */
        tcp->ts_recent = tcp->ts_val;

        /* Update SACK list */
        for ( sack = 0 ; sack < TCP_SACK_MAX ; sack++ ) {
                if ( tcp->sack[sack].left == tcp->sack[sack].right )
                        continue;
                if ( tcp_cmp ( tcp->sack[sack].left, tcp->rcv_ack ) < 0 )
                        tcp->sack[sack].left = tcp->rcv_ack;
                if ( tcp_cmp ( tcp->sack[sack].right, tcp->rcv_ack ) < 0 )
                        tcp->sack[sack].right = tcp->rcv_ack;
        }

        /* Mark ACK as pending */
        tcp->flags |= TCP_ACK_PENDING;
}
static int tcp_rx_syn ( struct tcp_connection tcp,
uint32_t  seq,
struct tcp_options options 
) [static]

Handle TCP received SYN.

Parameters:
tcpTCP connection
seqSEQ value (in host-endian order)
optionsTCP options
Return values:
rcReturn status code

Definition at line 1070 of file tcp.c.

References DBGC, tcp_connection::flags, tcp_connection::rcv_ack, tcp_connection::rcv_win_scale, tcp_window_scale_option::scale, seq, tcp_connection::snd_win_scale, tcp_options::spopt, TCP_ACK, tcp_rx_seq(), TCP_RX_WINDOW_SCALE, TCP_SACK_ENABLED, tcp_connection::tcp_state, TCP_STATE_RCVD, TCP_STATE_SENT, TCP_SYN, TCP_TS_ENABLED, tcp_options::tsopt, and tcp_options::wsopt.

Referenced by tcp_rx().

                                                      {

        /* Synchronise sequence numbers on first SYN */
        if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) {
                tcp->rcv_ack = seq;
                if ( options->tsopt )
                        tcp->flags |= TCP_TS_ENABLED;
                if ( options->spopt )
                        tcp->flags |= TCP_SACK_ENABLED;
                if ( options->wsopt ) {
                        tcp->snd_win_scale = options->wsopt->scale;
                        tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE;
                }
                DBGC ( tcp, "TCP %p using %stimestamps, %sSACK, TX window "
                       "x%d, RX window x%d\n", tcp,
                       ( ( tcp->flags & TCP_TS_ENABLED ) ? "" : "no " ),
                       ( ( tcp->flags & TCP_SACK_ENABLED ) ? "" : "no " ),
                       ( 1 << tcp->snd_win_scale ),
                       ( 1 << tcp->rcv_win_scale ) );
        }

        /* Ignore duplicate SYN */
        if ( seq != tcp->rcv_ack )
                return 0;

        /* Acknowledge SYN */
        tcp_rx_seq ( tcp, 1 );

        /* Mark SYN as received and start sending ACKs with each packet */
        tcp->tcp_state |= ( TCP_STATE_SENT ( TCP_ACK ) |
                            TCP_STATE_RCVD ( TCP_SYN ) );

        return 0;
}
static int tcp_rx_data ( struct tcp_connection tcp,
uint32_t  seq,
struct io_buffer iobuf 
) [static]

Handle TCP received data.

Parameters:
tcpTCP connection
seqSEQ value (in host-endian order)
iobufI/O buffer
Return values:
rcReturn status code

This function takes ownership of the I/O buffer.

Definition at line 1199 of file tcp.c.

References DBGC, free_iob(), iob_len(), iob_pull, len, profile_start(), profile_stop(), rc, tcp_connection::rcv_ack, seq, strerror(), tcp_rx_seq(), tcp_connection::xfer, and xfer_deliver_iob().

Referenced by tcp_process_rx_queue().

                                                   {
        uint32_t already_rcvd;
        uint32_t len;
        int rc;

        /* Ignore duplicate or out-of-order data */
        already_rcvd = ( tcp->rcv_ack - seq );
        len = iob_len ( iobuf );
        if ( already_rcvd >= len ) {
                free_iob ( iobuf );
                return 0;
        }
        iob_pull ( iobuf, already_rcvd );
        len -= already_rcvd;

        /* Acknowledge new data */
        tcp_rx_seq ( tcp, len );

        /* Deliver data to application */
        profile_start ( &tcp_xfer_profiler );
        if ( ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 ) {
                DBGC ( tcp, "TCP %p could not deliver %08x..%08x: %s\n",
                       tcp, seq, ( seq + len ), strerror ( rc ) );
                return rc;
        }
        profile_stop ( &tcp_xfer_profiler );

        return 0;
}
static int tcp_rx_fin ( struct tcp_connection tcp,
uint32_t  seq 
) [static]

Handle TCP received FIN.

Parameters:
tcpTCP connection
seqSEQ value (in host-endian order)
Return values:
rcReturn status code

Definition at line 1237 of file tcp.c.

References tcp_connection::rcv_ack, tcp_close(), TCP_FIN, tcp_rx_seq(), tcp_connection::tcp_state, and TCP_STATE_RCVD.

Referenced by tcp_process_rx_queue().

                                                                   {

        /* Ignore duplicate or out-of-order FIN */
        if ( seq != tcp->rcv_ack )
                return 0;

        /* Acknowledge FIN */
        tcp_rx_seq ( tcp, 1 );

        /* Mark FIN as received */
        tcp->tcp_state |= TCP_STATE_RCVD ( TCP_FIN );

        /* Close connection */
        tcp_close ( tcp, 0 );

        return 0;
}
static int tcp_rx_rst ( struct tcp_connection tcp,
uint32_t  seq 
) [static]

Handle TCP received RST.

Parameters:
tcpTCP connection
seqSEQ value (in host-endian order)
Return values:
rcReturn status code

Definition at line 1262 of file tcp.c.

References DBGC, ECONNRESET, tcp_connection::rcv_ack, tcp_connection::rcv_win, tcp_close(), TCP_CLOSED, tcp_dump_state(), tcp_in_window(), tcp_connection::tcp_state, TCP_STATE_ACKED, TCP_STATE_RCVD, and TCP_SYN.

Referenced by tcp_rx().

                                                                   {

        /* 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 ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) {
                if ( ! tcp_in_window ( seq, tcp->rcv_ack, tcp->rcv_win ) )
                        return 0;
        } else {
                if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
                        return 0;
        }

        /* Abort connection */
        tcp->tcp_state = TCP_CLOSED;
        tcp_dump_state ( tcp );
        tcp_close ( tcp, -ECONNRESET );

        DBGC ( tcp, "TCP %p connection reset by peer\n", tcp );
        return -ECONNRESET;
}
static void tcp_rx_enqueue ( struct tcp_connection tcp,
uint32_t  seq,
uint8_t  flags,
struct io_buffer iobuf 
) [static]

Enqueue received TCP packet.

Parameters:
tcpTCP connection
seqSEQ value (in host-endian order)
flagsTCP flags
iobufI/O buffer

Definition at line 1294 of file tcp.c.

References io_buffer::data, flags, tcp_rx_queued_header::flags, free_iob(), iob_len(), iob_push, len, io_buffer::list, list_add_tail, list_for_each_entry, tcp_rx_queued_header::nxt, tcp_connection::rcv_ack, tcp_connection::rcv_win, tcp_connection::rx_queue, tcp_rx_queued_header::seq, seq, tcp_cmp(), TCP_FIN, tcp_connection::tcp_state, TCP_STATE_RCVD, and TCP_SYN.

Referenced by tcp_rx().

                                                                      {
        struct tcp_rx_queued_header *tcpqhdr;
        struct io_buffer *queued;
        size_t len;
        uint32_t seq_len;
        uint32_t nxt;

        /* Calculate remaining flags and sequence length.  Note that
         * SYN, if present, has already been processed by this point.
         */
        flags &= TCP_FIN;
        len = iob_len ( iobuf );
        seq_len = ( len + ( flags ? 1 : 0 ) );
        nxt = ( seq + seq_len );

        /* Discard immediately (to save memory) if:
         *
         * a) we have not yet received a SYN (and so have no defined
         *    receive window), or
         * b) the packet lies entirely outside the receive window, or
         * c) there is no further content to process.
         */
        if ( ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) ||
             ( tcp_cmp ( seq, tcp->rcv_ack + tcp->rcv_win ) >= 0 ) ||
             ( tcp_cmp ( nxt, tcp->rcv_ack ) < 0 ) ||
             ( seq_len == 0 ) ) {
                free_iob ( iobuf );
                return;
        }

        /* Add internal header */
        tcpqhdr = iob_push ( iobuf, sizeof ( *tcpqhdr ) );
        tcpqhdr->seq = seq;
        tcpqhdr->nxt = nxt;
        tcpqhdr->flags = flags;

        /* Add to RX queue */
        list_for_each_entry ( queued, &tcp->rx_queue, list ) {
                tcpqhdr = queued->data;
                if ( tcp_cmp ( seq, tcpqhdr->seq ) < 0 )
                        break;
        }
        list_add_tail ( &iobuf->list, &queued->list );
}
static void tcp_process_rx_queue ( struct tcp_connection tcp) [static]

Process receive queue.

Parameters:
tcpTCP connection

Definition at line 1345 of file tcp.c.

References io_buffer::data, flags, tcp_rx_queued_header::flags, iob_disown, iob_len(), iob_pull, len, io_buffer::list, list_del, list_first_entry, tcp_connection::rcv_ack, tcp_connection::rx_queue, tcp_rx_queued_header::seq, seq, tcp_cmp(), TCP_FIN, tcp_rx_data(), and tcp_rx_fin().

Referenced by tcp_rx().

                                                                {
        struct io_buffer *iobuf;
        struct tcp_rx_queued_header *tcpqhdr;
        uint32_t seq;
        unsigned int flags;
        size_t len;

        /* Process all applicable received buffers.  Note that we
         * cannot use list_for_each_entry() to iterate over the RX
         * queue, since tcp_discard() may remove packets from the RX
         * queue while we are processing.
         */
        while ( ( iobuf = list_first_entry ( &tcp->rx_queue, struct io_buffer,
                                             list ) ) ) {

                /* Stop processing when we hit the first gap */
                tcpqhdr = iobuf->data;
                if ( tcp_cmp ( tcpqhdr->seq, tcp->rcv_ack ) > 0 )
                        break;

                /* Strip internal header and remove from RX queue */
                list_del ( &iobuf->list );
                seq = tcpqhdr->seq;
                flags = tcpqhdr->flags;
                iob_pull ( iobuf, sizeof ( *tcpqhdr ) );
                len = iob_len ( iobuf );

                /* Handle new data, if any */
                tcp_rx_data ( tcp, seq, iob_disown ( iobuf ) );
                seq += len;

                /* Handle FIN, if present */
                if ( flags & TCP_FIN ) {
                        tcp_rx_fin ( tcp, seq );
                        seq++;
                }
        }
}
static int tcp_rx ( struct io_buffer iobuf,
struct net_device *netdev  __unused,
struct sockaddr_tcpip st_src,
struct sockaddr_tcpip *st_dest  __unused,
uint16_t  pshdr_csum 
) [static]

Process received packet.

Parameters:
iobufI/O buffer
netdevNetwork device
st_srcPartially-filled source address
st_destPartially-filled destination address
pshdr_csumPseudo-header checksum
Return values:
rcReturn status code

Definition at line 1394 of file tcp.c.

References tcp_header::ack, io_buffer::data, DBG, DBGC2, tcp_header::dest, EINVAL, ENOTCONN, tcp_header::flags, tcp_connection::flags, flags, free_iob(), tcp_header::hlen, iob_disown, iob_len(), iob_pull, len, list_empty, ntohl, ntohs, tcp_connection::process, process_add(), profile_start(), profile_stop(), rc, tcp_connection::rcv_ack, tcp_connection::rx_queue, tcp_header::seq, seq, tcp_connection::snd_win_scale, tcp_header::src, start_timer_fixed(), stop_timer(), TCP_ACK, TCP_ACK_PENDING, TCP_CLOSED_GRACEFULLY, tcp_demux(), tcp_dump_flags(), tcp_dump_state(), TCP_FIN, TCP_MASK_HLEN, TCP_MSL, tcp_process_rx_queue(), TCP_RST, tcp_rx_ack(), tcp_rx_enqueue(), tcp_rx_opts(), tcp_rx_rst(), tcp_rx_syn(), tcp_connection::tcp_state, TCP_STATE_RCVD, TCP_SYN, tcp_xfer_window(), tcp_xmit_reset(), tcp_xmit_sack(), tcpip_continue_chksum(), tcp_connection::ts_val, tcp_options::tsopt, tcp_timestamp_option::tsval, tcp_connection::wait, tcp_header::win, tcp_connection::xfer, and xfer_window_changed().

                                          {
        struct tcp_header *tcphdr = iobuf->data;
        struct tcp_connection *tcp;
        struct tcp_options options;
        size_t hlen;
        uint16_t csum;
        uint32_t seq;
        uint32_t ack;
        uint16_t raw_win;
        uint32_t win;
        unsigned int flags;
        size_t len;
        uint32_t seq_len;
        size_t old_xfer_window;
        int rc;

        /* Start profiling */
        profile_start ( &tcp_rx_profiler );

        /* Sanity check packet */
        if ( iob_len ( iobuf ) < sizeof ( *tcphdr ) ) {
                DBG ( "TCP packet too short at %zd bytes (min %zd bytes)\n",
                      iob_len ( iobuf ), sizeof ( *tcphdr ) );
                rc = -EINVAL;
                goto discard;
        }
        hlen = ( ( tcphdr->hlen & TCP_MASK_HLEN ) / 16 ) * 4;
        if ( hlen < sizeof ( *tcphdr ) ) {
                DBG ( "TCP header too short at %zd bytes (min %zd bytes)\n",
                      hlen, sizeof ( *tcphdr ) );
                rc = -EINVAL;
                goto discard;
        }
        if ( hlen > iob_len ( iobuf ) ) {
                DBG ( "TCP header too long at %zd bytes (max %zd bytes)\n",
                      hlen, iob_len ( iobuf ) );
                rc = -EINVAL;
                goto discard;
        }
        csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data,
                                       iob_len ( iobuf ) );
        if ( csum != 0 ) {
                DBG ( "TCP checksum incorrect (is %04x including checksum "
                      "field, should be 0000)\n", csum );
                rc = -EINVAL;
                goto discard;
        }
        
        /* Parse parameters from header and strip header */
        tcp = tcp_demux ( ntohs ( tcphdr->dest ) );
        seq = ntohl ( tcphdr->seq );
        ack = ntohl ( tcphdr->ack );
        raw_win = ntohs ( tcphdr->win );
        flags = tcphdr->flags;
        if ( ( rc = tcp_rx_opts ( tcp, tcphdr, hlen, &options ) ) != 0 )
                goto discard;
        if ( tcp && options.tsopt )
                tcp->ts_val = ntohl ( options.tsopt->tsval );
        iob_pull ( iobuf, hlen );
        len = iob_len ( iobuf );
        seq_len = ( len + ( ( flags & TCP_SYN ) ? 1 : 0 ) +
                    ( ( flags & TCP_FIN ) ? 1 : 0 ) );

        /* Dump header */
        DBGC2 ( tcp, "TCP %p RX %d<-%d           %08x %08x..%08x %4zd",
                tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ),
                ntohl ( tcphdr->ack ), ntohl ( tcphdr->seq ),
                ( ntohl ( tcphdr->seq ) + seq_len ), len );
        tcp_dump_flags ( tcp, tcphdr->flags );
        DBGC2 ( tcp, "\n" );

        /* If no connection was found, silently drop packet */
        if ( ! tcp ) {
                rc = -ENOTCONN;
                goto discard;
        }

        /* Record old data-transfer window */
        old_xfer_window = tcp_xfer_window ( tcp );

        /* Handle ACK, if present */
        if ( flags & TCP_ACK ) {
                win = ( raw_win << tcp->snd_win_scale );
                if ( ( rc = tcp_rx_ack ( tcp, ack, win ) ) != 0 ) {
                        tcp_xmit_reset ( tcp, st_src, tcphdr );
                        goto discard;
                }
        }

        /* Force an ACK if this packet is out of order */
        if ( ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) &&
             ( seq != tcp->rcv_ack ) ) {
                tcp->flags |= TCP_ACK_PENDING;
        }

        /* Handle SYN, if present */
        if ( flags & TCP_SYN ) {
                tcp_rx_syn ( tcp, seq, &options );
                seq++;
        }

        /* Handle RST, if present */
        if ( flags & TCP_RST ) {
                if ( ( rc = tcp_rx_rst ( tcp, seq ) ) != 0 )
                        goto discard;
        }

        /* Enqueue received data */
        tcp_rx_enqueue ( tcp, seq, flags, iob_disown ( iobuf ) );

        /* Process receive queue */
        tcp_process_rx_queue ( tcp );

        /* Dump out any state change as a result of the received packet */
        tcp_dump_state ( tcp );

        /* Schedule transmission of ACK (and any pending data).  If we
         * have received any out-of-order packets (i.e. if the receive
         * queue remains non-empty after processing) then send the ACK
         * immediately in order to trigger Fast Retransmission.
         */
        if ( list_empty ( &tcp->rx_queue ) ) {
                process_add ( &tcp->process );
        } else {
                tcp_xmit_sack ( tcp, 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 ( tcp->tcp_state ) ) {
                stop_timer ( &tcp->wait );
                start_timer_fixed ( &tcp->wait, ( 2 * TCP_MSL ) );
        }

        /* Notify application if window has changed */
        if ( tcp_xfer_window ( tcp ) != old_xfer_window )
                xfer_window_changed ( &tcp->xfer );

        profile_stop ( &tcp_rx_profiler );
        return 0;

 discard:
        /* Free received packet */
        free_iob ( iobuf );
        return rc;
}
static unsigned int tcp_discard ( void  ) [static]

Discard some cached TCP data.

Return values:
discardedNumber of cached items discarded

Definition at line 1558 of file tcp.c.

References free_iob(), io_buffer::list, list_del, list_for_each_entry, list_for_each_entry_reverse, and tcp_connection::rx_queue.

                                         {
        struct tcp_connection *tcp;
        struct io_buffer *iobuf;
        unsigned int discarded = 0;

        /* Try to drop one queued RX packet from each connection */
        list_for_each_entry ( tcp, &tcp_conns, list ) {
                list_for_each_entry_reverse ( iobuf, &tcp->rx_queue, list ) {

                        /* Remove packet from queue */
                        list_del ( &iobuf->list );
                        free_iob ( iobuf );

                        /* Report discard */
                        discarded++;
                        break;
                }
        }

        return discarded;
}
struct cache_discarder tcp_discarder __cache_discarder ( CACHE_NORMAL  ) [read]

TCP cache discarder.

static struct tcp_connection* tcp_first_unclosed ( void  ) [static, read]

Find first TCP connection that has not yet been closed.

Return values:
tcpFirst unclosed connection, or NULL

Definition at line 1590 of file tcp.c.

References tcp_connection::flags, tcp_connection::list, list_for_each_entry, NULL, and TCP_XFER_CLOSED.

Referenced by tcp_shutdown().

                                                           {
        struct tcp_connection *tcp;

        /* Find first connection which has not yet been closed */
        list_for_each_entry ( tcp, &tcp_conns, list ) {
                if ( ! ( tcp->flags & TCP_XFER_CLOSED ) )
                        return tcp;
        }
        return NULL;
}
static struct tcp_connection* tcp_first_unfinished ( void  ) [static, read]

Find first TCP connection that has not yet finished all operations.

Return values:
tcpFirst unfinished connection, or NULL

Definition at line 1606 of file tcp.c.

References tcp_connection::list, list_for_each_entry, NULL, tcp_connection::process, process_running(), TCP_CLOSED_GRACEFULLY, and tcp_connection::tcp_state.

Referenced by tcp_shutdown().

                                                             {
        struct tcp_connection *tcp;

        /* Find first connection which has not yet closed gracefully,
         * or which still has a pending transmission (e.g. to ACK the
         * received FIN).
         */
        list_for_each_entry ( tcp, &tcp_conns, list ) {
                if ( ( ! TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) ||
                     process_running ( &tcp->process ) ) {
                        return tcp;
                }
        }
        return NULL;
}
static void tcp_shutdown ( int booting  __unused) [static]

Shut down all TCP connections.

Definition at line 1626 of file tcp.c.

References currticks(), DBGC, ECANCELED, tcp_connection::list, list_first_entry, NULL, start, step(), tcp_close(), TCP_CLOSED, tcp_dump_state(), TCP_FINISH_TIMEOUT, tcp_first_unclosed(), tcp_first_unfinished(), and tcp_connection::tcp_state.

                                                  {
        struct tcp_connection *tcp;
        unsigned long start;
        unsigned long elapsed;

        /* Initiate a graceful close of all connections, allowing for
         * the fact that the connection list may change as we do so.
         */
        while ( ( tcp = tcp_first_unclosed() ) ) {
                DBGC ( tcp, "TCP %p closing for shutdown\n", tcp );
                tcp_close ( tcp, -ECANCELED );
        }

        /* Wait for all connections to finish closing gracefully */
        start = currticks();
        while ( ( tcp = tcp_first_unfinished() ) &&
                ( ( elapsed = ( currticks() - start ) ) < TCP_FINISH_TIMEOUT )){
                step();
        }

        /* Forcibly close any remaining connections */
        while ( ( tcp = list_first_entry ( &tcp_conns, struct tcp_connection,
                                           list ) ) != NULL ) {
                tcp->tcp_state = TCP_CLOSED;
                tcp_dump_state ( tcp );
                tcp_close ( tcp, -ECANCELED );
        }
}
struct startup_fn tcp_startup_fn __startup_fn ( STARTUP_LATE  ) [read]

TCP shutdown function.

static void tcp_xfer_close ( struct tcp_connection tcp,
int  rc 
) [static]

Close interface.

Parameters:
tcpTCP connection
rcReason for close

Definition at line 1673 of file tcp.c.

References tcp_close(), and tcp_xmit().

                                                                  {

        /* Close data transfer interface */
        tcp_close ( tcp, rc );

        /* Transmit FIN, if possible */
        tcp_xmit ( tcp );
}
static int tcp_xfer_deliver ( struct tcp_connection tcp,
struct io_buffer iobuf,
struct xfer_metadata *meta  __unused 
) [static]

Deliver datagram as I/O buffer.

Parameters:
tcpTCP connection
iobufDatagram I/O buffer
metaData transfer metadata
Return values:
rcReturn status code

Definition at line 1690 of file tcp.c.

References io_buffer::list, list_add_tail, tcp_connection::pending_data, pending_get(), tcp_xmit(), and tcp_connection::tx_queue.

                                                                    {

        /* Enqueue packet */
        list_add_tail ( &iobuf->list, &tcp->tx_queue );

        /* Each enqueued packet is a pending operation */
        pending_get ( &tcp->pending_data );

        /* Transmit data, if possible */
        tcp_xmit ( tcp );

        return 0;
}
static int tcp_open_uri ( struct interface xfer,
struct uri uri 
) [static]

Open TCP URI.

Parameters:
xferData transfer interface
uriURI
Return values:
rcReturn status code

Definition at line 1748 of file tcp.c.

References EINVAL, uri::host, htons, memset(), NULL, SOCK_STREAM, sockaddr_tcpip::st_port, uri_port(), and xfer_open_named_socket().

                                                                    {
        struct sockaddr_tcpip peer;

        /* Sanity check */
        if ( ! uri->host )
                return -EINVAL;

        memset ( &peer, 0, sizeof ( peer ) );
        peer.st_port = htons ( uri_port ( uri, 0 ) );
        return xfer_open_named_socket ( xfer, SOCK_STREAM,
                                        ( struct sockaddr * ) &peer,
                                        uri->host, NULL );
}

Variable Documentation

struct profiler tcp_xfer_profiler __profiler = { .name = "tcp.tx" } [static]

Transmit profiler.

Data transfer profiler.

Receive profiler.

Definition at line 170 of file tcp.c.

static struct process_descriptor tcp_process_desc [static]
Initial value:

TCP process descriptor.

Definition at line 179 of file tcp.c.

Referenced by tcp_open().

static struct interface_descriptor tcp_xfer_desc [static]
Initial value:

TCP data transfer interface descriptor.

Definition at line 180 of file tcp.c.

Referenced by tcp_open().

struct tcpip_protocol tcp_protocol __tcpip_protocol
Initial value:
 {
        .name = "TCP",
        .rx = tcp_rx,
        .tcpip_proto = IP_TCP,
}

TCP protocol.

ICMPv4 TCP/IP protocol.

Definition at line 1547 of file tcp.c.

Initial value:

TCP data transfer interface operations.

Definition at line 1707 of file tcp.c.

struct socket_opener tcp_ipv6_socket_opener __socket_opener
Initial value:
 {
        .semantics      = TCP_SOCK_STREAM,
        .family         = AF_INET,
        .open           = tcp_open,
}

TCP IPv4 socket opener.

TCP IPv6 socket opener.

Definition at line 1725 of file tcp.c.

struct uri_opener tcp_uri_opener __uri_opener
Initial value:
 {
        .scheme         = "tcp",
        .open           = tcp_open_uri,
}

TCP URI opener.

Definition at line 1763 of file tcp.c.