176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <string.h> 276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdlib.h> 376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdio.h> 476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <assert.h> 576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <errno.h> 676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <byteswap.h> 776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/timer.h> 876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/iobuf.h> 976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/malloc.h> 1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/retry.h> 1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/refcnt.h> 1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/xfer.h> 1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/open.h> 1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/uri.h> 1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/tcpip.h> 1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/tcp.h> 1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** @file 1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * TCP protocol 2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 2476d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFILE_LICENCE ( GPL2_OR_LATER ); 2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** A TCP connection */ 2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct tcp_connection { 2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Reference counter */ 2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct refcnt refcnt; 3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** List of TCP connections */ 3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct list_head list; 3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Data transfer interface */ 3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct xfer_interface xfer; 3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Data transfer interface closed flag */ 3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int xfer_closed; 3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Remote socket address */ 3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_tcpip peer; 4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Local port, in network byte order */ 4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int local_port; 4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Current TCP state */ 4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int tcp_state; 4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Previous TCP state 4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Maintained only for debug messages 4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int prev_tcp_state; 5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Current sequence number 5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Equivalent to SND.UNA in RFC 793 terminology. 5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t snd_seq; 5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Unacknowledged sequence count 5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Equivalent to (SND.NXT-SND.UNA) in RFC 793 terminology. 5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t snd_sent; 6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Send window 6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Equivalent to SND.WND in RFC 793 terminology 6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t snd_win; 6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Current acknowledgement number 6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Equivalent to RCV.NXT in RFC 793 terminology. 6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t rcv_ack; 7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Receive window 7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Equivalent to RCV.WND in RFC 793 terminology. 7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t rcv_win; 7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Most recent received timestamp 7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Equivalent to TS.Recent in RFC 1323 terminology. 7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t ts_recent; 8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Timestamps enabled */ 8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int timestamps; 8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Transmit queue */ 8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct list_head queue; 8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Retransmission timer */ 8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct retry_timer timer; 8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * List of registered TCP connections 9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic LIST_HEAD ( tcp_conns ); 9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* Forward declarations */ 9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct xfer_interface_operations tcp_xfer_operations; 9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void tcp_expired ( struct retry_timer *timer, int over ); 9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, 9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t win ); 9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Name TCP state 10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v state TCP state 10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret name Name of TCP state 10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic inline __attribute__ (( always_inline )) const char * 10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmantcp_state ( int state ) { 10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman switch ( state ) { 10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_CLOSED: return "CLOSED"; 11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_LISTEN: return "LISTEN"; 11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_SYN_SENT: return "SYN_SENT"; 11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_SYN_RCVD: return "SYN_RCVD"; 11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_ESTABLISHED: return "ESTABLISHED"; 11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_FIN_WAIT_1: return "FIN_WAIT_1"; 11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_FIN_WAIT_2: return "FIN_WAIT_2"; 11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_CLOSING_OR_LAST_ACK: return "CLOSING/LAST_ACK"; 11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_TIME_WAIT: return "TIME_WAIT"; 11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_CLOSE_WAIT: return "CLOSE_WAIT"; 11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman default: return "INVALID"; 12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Dump TCP state transition 12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic inline __attribute__ (( always_inline )) void 12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmantcp_dump_state ( struct tcp_connection *tcp ) { 13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( tcp->tcp_state != tcp->prev_tcp_state ) { 13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p transitioned from %s to %s\n", tcp, 13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_state ( tcp->prev_tcp_state ), 13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_state ( tcp->tcp_state ) ); 13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->prev_tcp_state = tcp->tcp_state; 13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Dump TCP flags 14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v flags TCP flags 14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic inline __attribute__ (( always_inline )) void 14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmantcp_dump_flags ( struct tcp_connection *tcp, unsigned int flags ) { 14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & TCP_RST ) 14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, " RST" ); 14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & TCP_SYN ) 14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, " SYN" ); 15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & TCP_PSH ) 15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, " PSH" ); 15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & TCP_FIN ) 15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, " FIN" ); 15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & TCP_ACK ) 15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, " ACK" ); 15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*************************************************************************** 15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Open and close 16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *************************************************************************** 16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Bind TCP connection to local port 16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v port Local port number, in network-endian order 17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * If the port is 0, the connection is assigned an available port 17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * between 1024 and 65535. 17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_bind ( struct tcp_connection *tcp, unsigned int port ) { 17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_connection *existing; 17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman static uint16_t try_port = 1023; 17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If no port specified, find the first available port */ 18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! port ) { 18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( try_port ) { 18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman try_port++; 18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( try_port < 1024 ) 18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman continue; 18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( tcp_bind ( tcp, htons ( try_port ) ) == 0 ) 18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p could not bind: no free ports\n", tcp ); 18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -EADDRINUSE; 19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Attempt bind to local port */ 19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_for_each_entry ( existing, &tcp_conns, list ) { 19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( existing->local_port == port ) { 19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p could not bind: port %d in use\n", 19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp, ntohs ( port ) ); 19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -EADDRINUSE; 19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->local_port = port; 20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p bound to port %d\n", tcp, ntohs ( port ) ); 20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Open a TCP connection 20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v xfer Data transfer interface 21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v peer Peer socket address 21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v local Local socket address, or NULL 21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_open ( struct xfer_interface *xfer, struct sockaddr *peer, 21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr *local ) { 21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; 21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; 21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_connection *tcp; 21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int bind_port; 22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int rc; 22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Allocate and initialise structure */ 22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp = zalloc ( sizeof ( *tcp ) ); 22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! tcp ) 22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -ENOMEM; 22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p allocated\n", tcp ); 22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman xfer_init ( &tcp->xfer, &tcp_xfer_operations, &tcp->refcnt ); 22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->prev_tcp_state = TCP_CLOSED; 22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN ); 23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_dump_state ( tcp ); 23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->snd_seq = random(); 23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman INIT_LIST_HEAD ( &tcp->queue ); 23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->timer.expired = tcp_expired; 23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) ); 23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Bind to local port */ 23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman bind_port = ( st_local ? st_local->st_port : 0 ); 23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = tcp_bind ( tcp, bind_port ) ) != 0 ) 23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err; 24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Start timer to initiate SYN */ 24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman start_timer_nodelay ( &tcp->timer ); 24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Attach parent interface, transfer reference to connection 24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * list and return 24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman xfer_plug_plug ( &tcp->xfer, xfer ); 24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_add ( &tcp->list, &tcp_conns ); 24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err: 25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ref_put ( &tcp->refcnt ); 25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Close TCP connection 25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v rc Reason for close 26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Closes the data transfer interface. If the TCP state machine is in 26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * a suitable state, the connection will be deleted. 26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void tcp_close ( struct tcp_connection *tcp, int rc ) { 26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct io_buffer *iobuf; 26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct io_buffer *tmp; 26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Close data transfer interface */ 27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman xfer_nullify ( &tcp->xfer ); 27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman xfer_close ( &tcp->xfer, rc ); 27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->xfer_closed = 1; 27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If we are in CLOSED, or have otherwise not yet received a 27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * SYN (i.e. we are in LISTEN or SYN_SENT), just delete the 27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * connection. 27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) { 27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Transition to CLOSED for the sake of debugging messages */ 28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->tcp_state = TCP_CLOSED; 28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_dump_state ( tcp ); 28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Free any unsent I/O buffers */ 28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_for_each_entry_safe ( iobuf, tmp, &tcp->queue, list ) { 28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_del ( &iobuf->list ); 28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_iob ( iobuf ); 28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Remove from list and drop reference */ 29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman stop_timer ( &tcp->timer ); 29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_del ( &tcp->list ); 29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ref_put ( &tcp->refcnt ); 29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p connection deleted\n", tcp ); 29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return; 29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If we have not had our SYN acknowledged (i.e. we are in 29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * SYN_RCVD), pretend that it has been acknowledged so that we 30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * can send a FIN without breaking things. 30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) ) 30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 ); 30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If we have no data remaining to send, start sending FIN */ 30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( list_empty ( &tcp->queue ) ) { 30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN ); 30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_dump_state ( tcp ); 30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*************************************************************************** 31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Transmit data path 31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *************************************************************************** 31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Calculate transmission window 32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 32376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret len Maximum length that can be sent in a single packet 32476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 32576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic size_t tcp_xmit_win ( struct tcp_connection *tcp ) { 32676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t len; 32776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 32876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Not ready if we're not in a suitable connection state */ 32976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! TCP_CAN_SEND_DATA ( tcp->tcp_state ) ) 33076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 33176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 33276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Length is the minimum of the receiver's window and the path MTU */ 33376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len = tcp->snd_win; 33476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( len > TCP_PATH_MTU ) 33576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len = TCP_PATH_MTU; 33676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 33776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return len; 33876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 33976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 34076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 34176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Process TCP transmit queue 34276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 34376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 34476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v max_len Maximum length to process 34576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v dest I/O buffer to fill with data, or NULL 34676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v remove Remove data from queue 34776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret len Length of data processed 34876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 34976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This processes at most @c max_len bytes from the TCP connection's 35076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * transmit queue. Data will be copied into the @c dest I/O buffer 35176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * (if provided) and, if @c remove is true, removed from the transmit 35276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * queue. 35376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 35476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic size_t tcp_process_queue ( struct tcp_connection *tcp, size_t max_len, 35576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct io_buffer *dest, int remove ) { 35676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct io_buffer *iobuf; 35776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct io_buffer *tmp; 35876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t frag_len; 35976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t len = 0; 36076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 36176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_for_each_entry_safe ( iobuf, tmp, &tcp->queue, list ) { 36276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman frag_len = iob_len ( iobuf ); 36376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( frag_len > max_len ) 36476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman frag_len = max_len; 36576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( dest ) { 36676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memcpy ( iob_put ( dest, frag_len ), iobuf->data, 36776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman frag_len ); 36876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 36976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( remove ) { 37076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_pull ( iobuf, frag_len ); 37176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! iob_len ( iobuf ) ) { 37276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_del ( &iobuf->list ); 37376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_iob ( iobuf ); 37476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 37576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 37676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len += frag_len; 37776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman max_len -= frag_len; 37876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 37976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return len; 38076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 38176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 38276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 38376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Transmit any outstanding data 38476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 38576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 38676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v force_send Force sending of packet 38776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 38876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Transmits any outstanding data on the connection. 38976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 39076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Note that even if an error is returned, the retransmission timer 39176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * will have been started if necessary, and so the stack will 39276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * eventually attempt to retransmit the failed packet. 39376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 39476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_xmit ( struct tcp_connection *tcp, int force_send ) { 39576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct io_buffer *iobuf; 39676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_header *tcphdr; 39776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_mss_option *mssopt; 39876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_timestamp_padded_option *tsopt; 39976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman void *payload; 40076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int flags; 40176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t len = 0; 40276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t seq_len; 40376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t app_win; 40476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t max_rcv_win; 40576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int rc; 40676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 40776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If retransmission timer is already running, do nothing */ 40876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( timer_running ( &tcp->timer ) ) 40976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 41076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 41176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Calculate both the actual (payload) and sequence space 41276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * lengths that we wish to transmit. 41376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 41476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( TCP_CAN_SEND_DATA ( tcp->tcp_state ) ) { 41576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len = tcp_process_queue ( tcp, tcp_xmit_win ( tcp ), 41676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman NULL, 0 ); 41776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 41876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman seq_len = len; 41976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman flags = TCP_FLAGS_SENDING ( tcp->tcp_state ); 42076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & ( TCP_SYN | TCP_FIN ) ) { 42176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* SYN or FIN consume one byte, and we can never send both */ 42276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman assert ( ! ( ( flags & TCP_SYN ) && ( flags & TCP_FIN ) ) ); 42376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman seq_len++; 42476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 42576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->snd_sent = seq_len; 42676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 42776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If we have nothing to transmit, stop now */ 42876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( seq_len == 0 ) && ! force_send ) 42976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 43076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 43176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If we are transmitting anything that requires 43276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * acknowledgement (i.e. consumes sequence space), start the 43376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * retransmission timer. Do this before attempting to 43476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * allocate the I/O buffer, in case allocation itself fails. 43576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 43676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( seq_len ) 43776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman start_timer ( &tcp->timer ); 43876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 43976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Allocate I/O buffer */ 44076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iobuf = alloc_iob ( len + MAX_HDR_LEN ); 44176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! iobuf ) { 44276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p could not allocate iobuf for %08x..%08x " 44376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "%08x\n", tcp, tcp->snd_seq, ( tcp->snd_seq + seq_len ), 44476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->rcv_ack ); 44576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -ENOMEM; 44676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 44776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_reserve ( iobuf, MAX_HDR_LEN ); 44876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 44976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Fill data payload from transmit queue */ 45076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_process_queue ( tcp, len, iobuf, 0 ); 45176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 45276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Expand receive window if possible */ 45376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman max_rcv_win = ( ( freemem * 3 ) / 4 ); 45476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( max_rcv_win > TCP_MAX_WINDOW_SIZE ) 45576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman max_rcv_win = TCP_MAX_WINDOW_SIZE; 45676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman app_win = xfer_window ( &tcp->xfer ); 45776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( max_rcv_win > app_win ) 45876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman max_rcv_win = app_win; 45976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman max_rcv_win &= ~0x03; /* Keep everything dword-aligned */ 46076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( tcp->rcv_win < max_rcv_win ) 46176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->rcv_win = max_rcv_win; 46276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 46376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Fill up the TCP header */ 46476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman payload = iobuf->data; 46576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & TCP_SYN ) { 46676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman mssopt = iob_push ( iobuf, sizeof ( *mssopt ) ); 46776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman mssopt->kind = TCP_OPTION_MSS; 46876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman mssopt->length = sizeof ( *mssopt ); 46976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman mssopt->mss = htons ( TCP_MSS ); 47076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 47176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( flags & TCP_SYN ) || tcp->timestamps ) { 47276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tsopt = iob_push ( iobuf, sizeof ( *tsopt ) ); 47376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memset ( tsopt->nop, TCP_OPTION_NOP, sizeof ( tsopt->nop ) ); 47476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tsopt->tsopt.kind = TCP_OPTION_TS; 47576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tsopt->tsopt.length = sizeof ( tsopt->tsopt ); 47676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tsopt->tsopt.tsval = ntohl ( currticks() ); 47776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tsopt->tsopt.tsecr = ntohl ( tcp->ts_recent ); 47876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 47976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! ( flags & TCP_SYN ) ) 48076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman flags |= TCP_PSH; 48176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) ); 48276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memset ( tcphdr, 0, sizeof ( *tcphdr ) ); 48376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->src = tcp->local_port; 48476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->dest = tcp->peer.st_port; 48576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->seq = htonl ( tcp->snd_seq ); 48676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->ack = htonl ( tcp->rcv_ack ); 48776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->hlen = ( ( payload - iobuf->data ) << 2 ); 48876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->flags = flags; 48976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->win = htons ( tcp->rcv_win ); 49076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) ); 49176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 49276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Dump header */ 49376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, "TCP %p TX %d->%d %08x..%08x %08x %4zd", 49476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ), 49576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) + seq_len ), 49676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohl ( tcphdr->ack ), len ); 49776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_dump_flags ( tcp, tcphdr->flags ); 49876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, "\n" ); 49976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 50076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Transmit packet */ 50176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = tcpip_tx ( iobuf, &tcp_protocol, NULL, &tcp->peer, NULL, 50276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman &tcphdr->csum ) ) != 0 ) { 50376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p could not transmit %08x..%08x %08x: %s\n", 50476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp, tcp->snd_seq, ( tcp->snd_seq + tcp->snd_sent ), 50576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->rcv_ack, strerror ( rc ) ); 50676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 50776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 50876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 50976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 51076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 51176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 51276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 51376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Retransmission timer expired 51476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 51576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v timer Retry timer 51676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v over Failure indicator 51776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 51876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void tcp_expired ( struct retry_timer *timer, int over ) { 51976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_connection *tcp = 52076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman container_of ( timer, struct tcp_connection, timer ); 52176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int graceful_close = TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ); 52276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 52376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p timer %s in %s for %08x..%08x %08x\n", tcp, 52476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( over ? "expired" : "fired" ), tcp_state ( tcp->tcp_state ), 52576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->snd_seq, ( tcp->snd_seq + tcp->snd_sent ), tcp->rcv_ack ); 52676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 52776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman assert ( ( tcp->tcp_state == TCP_SYN_SENT ) || 52876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( tcp->tcp_state == TCP_SYN_RCVD ) || 52976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( tcp->tcp_state == TCP_ESTABLISHED ) || 53076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( tcp->tcp_state == TCP_FIN_WAIT_1 ) || 53176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( tcp->tcp_state == TCP_TIME_WAIT ) || 53276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( tcp->tcp_state == TCP_CLOSE_WAIT ) || 53376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( tcp->tcp_state == TCP_CLOSING_OR_LAST_ACK ) ); 53476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 53576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( over || graceful_close ) { 53676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If we have finally timed out and given up, or if 53776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * this is the result of a graceful close, terminate 53876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the connection 53976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 54076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->tcp_state = TCP_CLOSED; 54176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_dump_state ( tcp ); 54276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_close ( tcp, -ETIMEDOUT ); 54376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else { 54476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Otherwise, retransmit the packet */ 54576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_xmit ( tcp, 0 ); 54676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 54776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 54876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 54976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 55076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Send RST response to incoming packet 55176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 55276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v in_tcphdr TCP header of incoming packet 55376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 55476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 55576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_xmit_reset ( struct tcp_connection *tcp, 55676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_tcpip *st_dest, 55776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_header *in_tcphdr ) { 55876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct io_buffer *iobuf; 55976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_header *tcphdr; 56076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int rc; 56176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 56276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Allocate space for dataless TX buffer */ 56376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iobuf = alloc_iob ( MAX_HDR_LEN ); 56476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! iobuf ) { 56576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p could not allocate iobuf for RST " 56676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "%08x..%08x %08x\n", tcp, ntohl ( in_tcphdr->ack ), 56776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohl ( in_tcphdr->ack ), ntohl ( in_tcphdr->seq ) ); 56876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -ENOMEM; 56976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 57076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_reserve ( iobuf, MAX_HDR_LEN ); 57176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 57276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Construct RST response */ 57376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) ); 57476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memset ( tcphdr, 0, sizeof ( *tcphdr ) ); 57576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->src = in_tcphdr->dest; 57676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->dest = in_tcphdr->src; 57776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->seq = in_tcphdr->ack; 57876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->ack = in_tcphdr->seq; 57976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->hlen = ( ( sizeof ( *tcphdr ) / 4 ) << 4 ); 58076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->flags = ( TCP_RST | TCP_ACK ); 58176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->win = htons ( TCP_MAX_WINDOW_SIZE ); 58276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) ); 58376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 58476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Dump header */ 58576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, "TCP %p TX %d->%d %08x..%08x %08x %4d", 58676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ), 58776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) ), 58876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohl ( tcphdr->ack ), 0 ); 58976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_dump_flags ( tcp, tcphdr->flags ); 59076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, "\n" ); 59176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 59276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Transmit packet */ 59376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = tcpip_tx ( iobuf, &tcp_protocol, NULL, st_dest, 59476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman NULL, &tcphdr->csum ) ) != 0 ) { 59576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p could not transmit RST %08x..%08x %08x: " 59676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "%s\n", tcp, ntohl ( in_tcphdr->ack ), 59776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohl ( in_tcphdr->ack ), ntohl ( in_tcphdr->seq ), 59876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman strerror ( rc ) ); 59976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 60076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 60176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 60276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 60376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 60476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 60576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*************************************************************************** 60676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 60776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Receive data path 60876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 60976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *************************************************************************** 61076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 61176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 61276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 61376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Identify TCP connection by local port number 61476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 61576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v local_port Local port (in network-endian order) 61676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret tcp TCP connection, or NULL 61776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 61876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct tcp_connection * tcp_demux ( unsigned int local_port ) { 61976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_connection *tcp; 62076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 62176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_for_each_entry ( tcp, &tcp_conns, list ) { 62276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( tcp->local_port == local_port ) 62376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return tcp; 62476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 62576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return NULL; 62676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 62776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 62876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 62976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Parse TCP received options 63076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 63176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 63276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v data Raw options data 63376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v len Raw options length 63476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v options Options structure to fill in 63576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 63676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void tcp_rx_opts ( struct tcp_connection *tcp, const void *data, 63776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t len, struct tcp_options *options ) { 63876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const void *end = ( data + len ); 63976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const struct tcp_option *option; 64076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int kind; 64176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 64276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memset ( options, 0, sizeof ( *options ) ); 64376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( data < end ) { 64476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman option = data; 64576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman kind = option->kind; 64676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( kind == TCP_OPTION_END ) 64776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return; 64876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( kind == TCP_OPTION_NOP ) { 64976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman data++; 65076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman continue; 65176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 65276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman switch ( kind ) { 65376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_OPTION_MSS: 65476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman options->mssopt = data; 65576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman break; 65676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case TCP_OPTION_TS: 65776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman options->tsopt = data; 65876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman break; 65976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman default: 66076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p received unknown option %d\n", 66176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp, kind ); 66276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman break; 66376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 66476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman data += option->length; 66576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 66676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 66776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 66876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 66976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Consume received sequence space 67076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 67176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 67276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v seq_len Sequence space length to consume 67376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 67476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void tcp_rx_seq ( struct tcp_connection *tcp, uint32_t seq_len ) { 67576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->rcv_ack += seq_len; 67676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( tcp->rcv_win > seq_len ) { 67776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->rcv_win -= seq_len; 67876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else { 67976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->rcv_win = 0; 68076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 68176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 68276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 68376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 68476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Handle TCP received SYN 68576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 68676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 68776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v seq SEQ value (in host-endian order) 68876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v options TCP options 68976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 69076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 69176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq, 69276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_options *options ) { 69376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 69476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Synchronise sequence numbers on first SYN */ 69576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) { 69676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->rcv_ack = seq; 69776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( options->tsopt ) 69876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->timestamps = 1; 69976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 70076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 70176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Ignore duplicate SYN */ 70276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( tcp->rcv_ack - seq ) > 0 ) 70376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 70476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 70576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Mark SYN as received and start sending ACKs with each packet */ 70676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->tcp_state |= ( TCP_STATE_SENT ( TCP_ACK ) | 70776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman TCP_STATE_RCVD ( TCP_SYN ) ); 70876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 70976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Acknowledge SYN */ 71076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_rx_seq ( tcp, 1 ); 71176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 71276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 71376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 71476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 71576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 71676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Handle TCP received ACK 71776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 71876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 71976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ack ACK value (in host-endian order) 72076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v win WIN value (in host-endian order) 72176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 72276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 72376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, 72476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t win ) { 72576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t ack_len = ( ack - tcp->snd_seq ); 72676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t len; 72776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int acked_flags; 72876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 72976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Check for out-of-range or old duplicate ACKs */ 73076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ack_len > tcp->snd_sent ) { 73176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p received ACK for %08x..%08x, " 73276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "sent only %08x..%08x\n", tcp, tcp->snd_seq, 73376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( tcp->snd_seq + ack_len ), tcp->snd_seq, 73476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( tcp->snd_seq + tcp->snd_sent ) ); 73576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 73676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( TCP_HAS_BEEN_ESTABLISHED ( tcp->tcp_state ) ) { 73776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Just ignore what might be old duplicate ACKs */ 73876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 73976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else { 74076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Send RST if an out-of-range ACK is received 74176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * on a not-yet-established connection, as per 74276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * RFC 793. 74376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 74476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -EINVAL; 74576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 74676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 74776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 74876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Ignore ACKs that don't actually acknowledge any new data. 74976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * (In particular, do not stop the retransmission timer; this 75076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * avoids creating a sorceror's apprentice syndrome when a 75176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * duplicate ACK is received and we still have data in our 75276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * transmit queue.) 75376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 75476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ack_len == 0 ) 75576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 75676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 75776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Stop the retransmission timer */ 75876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman stop_timer ( &tcp->timer ); 75976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 76076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Determine acknowledged flags and data length */ 76176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len = ack_len; 76276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman acked_flags = ( TCP_FLAGS_SENDING ( tcp->tcp_state ) & 76376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( TCP_SYN | TCP_FIN ) ); 76476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( acked_flags ) 76576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len--; 76676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 76776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Update SEQ and sent counters, and window size */ 76876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->snd_seq = ack; 76976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->snd_sent = 0; 77076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->snd_win = win; 77176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 77276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Remove any acknowledged data from transmit queue */ 77376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_process_queue ( tcp, len, NULL, 1 ); 77476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 77576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Mark SYN/FIN as acknowledged if applicable. */ 77676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( acked_flags ) 77776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->tcp_state |= TCP_STATE_ACKED ( acked_flags ); 77876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 77976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Start sending FIN if we've had all possible data ACKed */ 78076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( list_empty ( &tcp->queue ) && tcp->xfer_closed ) 78176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN ); 78276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 78376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 78476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 78576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 78676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 78776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Handle TCP received data 78876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 78976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 79076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v seq SEQ value (in host-endian order) 79176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf I/O buffer 79276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 79376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 79476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This function takes ownership of the I/O buffer. 79576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 79676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_rx_data ( struct tcp_connection *tcp, uint32_t seq, 79776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct io_buffer *iobuf ) { 79876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t already_rcvd; 79976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t len; 80076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int rc; 80176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 80276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Ignore duplicate or out-of-order data */ 80376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman already_rcvd = ( tcp->rcv_ack - seq ); 80476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len = iob_len ( iobuf ); 80576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( already_rcvd >= len ) { 80676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_iob ( iobuf ); 80776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 80876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 80976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_pull ( iobuf, already_rcvd ); 81076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len -= already_rcvd; 81176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 81276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Deliver data to application */ 81376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 ) { 81476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p could not deliver %08x..%08x: %s\n", 81576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp, seq, ( seq + len ), strerror ( rc ) ); 81676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 81776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 81876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 81976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Acknowledge new data */ 82076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_rx_seq ( tcp, len ); 82176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 82276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 82376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 82476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 82576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 82676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Handle TCP received FIN 82776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 82876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 82976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v seq SEQ value (in host-endian order) 83076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 83176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 83276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_rx_fin ( struct tcp_connection *tcp, uint32_t seq ) { 83376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 83476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Ignore duplicate or out-of-order FIN */ 83576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( tcp->rcv_ack - seq ) > 0 ) 83676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 83776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 83876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Mark FIN as received and acknowledge it */ 83976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->tcp_state |= TCP_STATE_RCVD ( TCP_FIN ); 84076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_rx_seq ( tcp, 1 ); 84176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 84276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Close connection */ 84376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_close ( tcp, 0 ); 84476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 84576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 84676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 84776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 84876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 84976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Handle TCP received RST 85076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 85176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcp TCP connection 85276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v seq SEQ value (in host-endian order) 85376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 85476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 85576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_rx_rst ( struct tcp_connection *tcp, uint32_t seq ) { 85676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 85776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Accept RST only if it falls within the window. If we have 85876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * not yet received a SYN, then we have no window to test 85976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * against, so fall back to checking that our SYN has been 86076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ACKed. 86176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 86276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) { 86376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( seq - tcp->rcv_ack ) >= tcp->rcv_win ) 86476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 86576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else { 86676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) ) 86776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 86876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 86976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 87076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Abort connection */ 87176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->tcp_state = TCP_CLOSED; 87276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_dump_state ( tcp ); 87376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_close ( tcp, -ECONNRESET ); 87476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 87576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( tcp, "TCP %p connection reset by peer\n", tcp ); 87676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -ECONNRESET; 87776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 87876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 87976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 88076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Process received packet 88176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 88276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf I/O buffer 88376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v st_src Partially-filled source address 88476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v st_dest Partially-filled destination address 88576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v pshdr_csum Pseudo-header checksum 88676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 88776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 88876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_rx ( struct io_buffer *iobuf, 88976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_tcpip *st_src, 89076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_tcpip *st_dest __unused, 89176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint16_t pshdr_csum ) { 89276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_header *tcphdr = iobuf->data; 89376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_connection *tcp; 89476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_options options; 89576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t hlen; 89676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint16_t csum; 89776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t start_seq; 89876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t seq; 89976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t ack; 90076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint32_t win; 90176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int flags; 90276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t len; 90376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int rc; 90476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 90576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Sanity check packet */ 90676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( iob_len ( iobuf ) < sizeof ( *tcphdr ) ) { 90776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "TCP packet too short at %zd bytes (min %zd bytes)\n", 90876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_len ( iobuf ), sizeof ( *tcphdr ) ); 90976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -EINVAL; 91076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto discard; 91176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 91276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman hlen = ( ( tcphdr->hlen & TCP_MASK_HLEN ) / 16 ) * 4; 91376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( hlen < sizeof ( *tcphdr ) ) { 91476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "TCP header too short at %zd bytes (min %zd bytes)\n", 91576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman hlen, sizeof ( *tcphdr ) ); 91676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -EINVAL; 91776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto discard; 91876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 91976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( hlen > iob_len ( iobuf ) ) { 92076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "TCP header too long at %zd bytes (max %zd bytes)\n", 92176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman hlen, iob_len ( iobuf ) ); 92276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -EINVAL; 92376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto discard; 92476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 92576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, 92676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_len ( iobuf ) ); 92776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( csum != 0 ) { 92876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "TCP checksum incorrect (is %04x including checksum " 92976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "field, should be 0000)\n", csum ); 93076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -EINVAL; 93176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto discard; 93276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 93376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 93476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Parse parameters from header and strip header */ 93576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp = tcp_demux ( tcphdr->dest ); 93676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman start_seq = seq = ntohl ( tcphdr->seq ); 93776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ack = ntohl ( tcphdr->ack ); 93876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman win = ntohs ( tcphdr->win ); 93976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman flags = tcphdr->flags; 94076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ), 94176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( hlen - sizeof ( *tcphdr ) ), &options ); 94276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_pull ( iobuf, hlen ); 94376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len = iob_len ( iobuf ); 94476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 94576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Dump header */ 94676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, "TCP %p RX %d<-%d %08x %08x..%08zx %4zd", 94776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ), 94876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohl ( tcphdr->ack ), ntohl ( tcphdr->seq ), 94976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( ntohl ( tcphdr->seq ) + len + 95076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( ( tcphdr->flags & ( TCP_SYN | TCP_FIN ) ) ? 1 : 0 )), len); 95176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_dump_flags ( tcp, tcphdr->flags ); 95276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC2 ( tcp, "\n" ); 95376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 95476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If no connection was found, send RST */ 95576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! tcp ) { 95676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_xmit_reset ( tcp, st_src, tcphdr ); 95776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -ENOTCONN; 95876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto discard; 95976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 96076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 96176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Handle ACK, if present */ 96276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & TCP_ACK ) { 96376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = tcp_rx_ack ( tcp, ack, win ) ) != 0 ) { 96476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_xmit_reset ( tcp, st_src, tcphdr ); 96576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto discard; 96676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 96776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 96876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 96976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Handle SYN, if present */ 97076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & TCP_SYN ) { 97176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_rx_syn ( tcp, seq, &options ); 97276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman seq++; 97376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 97476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 97576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Handle RST, if present */ 97676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & TCP_RST ) { 97776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = tcp_rx_rst ( tcp, seq ) ) != 0 ) 97876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto discard; 97976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 98076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 98176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Handle new data, if any */ 98276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_rx_data ( tcp, seq, iobuf ); 98376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman seq += len; 98476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 98576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Handle FIN, if present */ 98676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( flags & TCP_FIN ) { 98776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_rx_fin ( tcp, seq ); 98876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman seq++; 98976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 99076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 99176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Update timestamp, if present and applicable */ 99276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( seq == tcp->rcv_ack ) && options.tsopt ) 99376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->ts_recent = ntohl ( options.tsopt->tsval ); 99476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 99576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Dump out any state change as a result of the received packet */ 99676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_dump_state ( tcp ); 99776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 99876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Send out any pending data. We force sending a reply if either 99976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 100076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * a) the peer is expecting an ACK (i.e. consumed sequence space), or 100176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * b) either end of the packet was outside the receive window 100276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 100376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Case (b) enables us to support TCP keepalives using 100476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * zero-length packets, which we would otherwise ignore. Note 100576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * that for case (b), we need *only* consider zero-length 100676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * packets, since non-zero-length packets will already be 100776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * caught by case (a). 100876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 100976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_xmit ( tcp, ( ( start_seq != seq ) || 101076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( ( seq - tcp->rcv_ack ) > tcp->rcv_win ) ) ); 101176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 101276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If this packet was the last we expect to receive, set up 101376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * timer to expire and cause the connection to be freed. 101476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 101576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) { 101676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp->timer.timeout = ( 2 * TCP_MSL ); 101776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman start_timer ( &tcp->timer ); 101876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 101976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 102076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 102176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 102276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman discard: 102376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Free received packet */ 102476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_iob ( iobuf ); 102576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 102676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 102776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 102876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** TCP protocol */ 102976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct tcpip_protocol tcp_protocol __tcpip_protocol = { 103076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .name = "TCP", 103176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .rx = tcp_rx, 103276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .tcpip_proto = IP_TCP, 103376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 103476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 103576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*************************************************************************** 103676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 103776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Data transfer interface 103876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 103976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *************************************************************************** 104076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 104176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 104276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 104376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Close interface 104476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 104576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v xfer Data transfer interface 104676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v rc Reason for close 104776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 104876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void tcp_xfer_close ( struct xfer_interface *xfer, int rc ) { 104976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_connection *tcp = 105076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman container_of ( xfer, struct tcp_connection, xfer ); 105176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 105276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Close data transfer interface */ 105376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_close ( tcp, rc ); 105476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 105576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Transmit FIN, if possible */ 105676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_xmit ( tcp, 0 ); 105776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 105876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 105976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 106076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check flow control window 106176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 106276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v xfer Data transfer interface 106376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret len Length of window 106476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 106576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic size_t tcp_xfer_window ( struct xfer_interface *xfer ) { 106676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_connection *tcp = 106776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman container_of ( xfer, struct tcp_connection, xfer ); 106876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 106976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Not ready if data queue is non-empty. This imposes a limit 107076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * of only one unACKed packet in the TX queue at any time; we 107176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * do this to conserve memory usage. 107276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 107376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! list_empty ( &tcp->queue ) ) 107476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 107576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 107676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Return TCP window length */ 107776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return tcp_xmit_win ( tcp ); 107876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 107976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 108076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 108176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Deliver datagram as I/O buffer 108276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 108376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v xfer Data transfer interface 108476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf Datagram I/O buffer 108576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v meta Data transfer metadata 108676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 108776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 108876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_xfer_deliver_iob ( struct xfer_interface *xfer, 108976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct io_buffer *iobuf, 109076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct xfer_metadata *meta __unused ) { 109176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcp_connection *tcp = 109276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman container_of ( xfer, struct tcp_connection, xfer ); 109376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 109476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Enqueue packet */ 109576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_add_tail ( &iobuf->list, &tcp->queue ); 109676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 109776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Transmit data, if possible */ 109876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman tcp_xmit ( tcp, 0 ); 109976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 110076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 110176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 110276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 110376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** TCP data transfer interface operations */ 110476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct xfer_interface_operations tcp_xfer_operations = { 110576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .close = tcp_xfer_close, 110676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .vredirect = ignore_xfer_vredirect, 110776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .window = tcp_xfer_window, 110876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .alloc_iob = default_xfer_alloc_iob, 110976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .deliver_iob = tcp_xfer_deliver_iob, 111076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .deliver_raw = xfer_deliver_as_iob, 111176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 111276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 111376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*************************************************************************** 111476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 111576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Openers 111676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 111776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *************************************************************************** 111876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 111976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 112076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** TCP socket opener */ 112176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct socket_opener tcp_socket_opener __socket_opener = { 112276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .semantics = TCP_SOCK_STREAM, 112376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .family = AF_INET, 112476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .open = tcp_open, 112576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 112676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 112776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Linkage hack */ 112876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint tcp_sock_stream = TCP_SOCK_STREAM; 112976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 113076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 113176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Open TCP URI 113276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 113376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v xfer Data transfer interface 113476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v uri URI 113576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 113676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 113776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int tcp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) { 113876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_tcpip peer; 113976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 114076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Sanity check */ 114176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! uri->host ) 114276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -EINVAL; 114376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 114476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memset ( &peer, 0, sizeof ( peer ) ); 114576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman peer.st_port = htons ( uri_port ( uri, 0 ) ); 114676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return xfer_open_named_socket ( xfer, SOCK_STREAM, 114776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( struct sockaddr * ) &peer, 114876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uri->host, NULL ); 114976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 115076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 115176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** TCP URI opener */ 115276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct uri_opener tcp_uri_opener __uri_opener = { 115376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .scheme = "tcp", 115476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .open = tcp_open_uri, 115576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 115676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 1157