|  | @@ -0,0 +1,206 @@
 | 
		
	
		
			
			|  | 1 | +#include <string.h>
 | 
		
	
		
			
			|  | 2 | +#include <assert.h>
 | 
		
	
		
			
			|  | 3 | +#include <byteswap.h>
 | 
		
	
		
			
			|  | 4 | +#include <gpxe/tcp.h>
 | 
		
	
		
			
			|  | 5 | +#include "uip/uip.h"
 | 
		
	
		
			
			|  | 6 | +#include "uip/uip_arp.h"
 | 
		
	
		
			
			|  | 7 | +
 | 
		
	
		
			
			|  | 8 | +/** @file
 | 
		
	
		
			
			|  | 9 | + *
 | 
		
	
		
			
			|  | 10 | + * TCP protocol
 | 
		
	
		
			
			|  | 11 | + *
 | 
		
	
		
			
			|  | 12 | + * The gPXE TCP stack is currently implemented on top of the uIP
 | 
		
	
		
			
			|  | 13 | + * protocol stack.  This file provides wrappers around uIP so that
 | 
		
	
		
			
			|  | 14 | + * higher-level protocol implementations do not need to talk directly
 | 
		
	
		
			
			|  | 15 | + * to uIP (which has a somewhat baroque API).
 | 
		
	
		
			
			|  | 16 | + *
 | 
		
	
		
			
			|  | 17 | + * Basic operation is to create a #tcp_connection structure, call
 | 
		
	
		
			
			|  | 18 | + * tcp_connect() and then call run_tcpip() in a loop until the
 | 
		
	
		
			
			|  | 19 | + * operation has completed.  The TCP stack will call the various
 | 
		
	
		
			
			|  | 20 | + * methods defined in the #tcp_operations structure in order to send
 | 
		
	
		
			
			|  | 21 | + * and receive data.
 | 
		
	
		
			
			|  | 22 | + *
 | 
		
	
		
			
			|  | 23 | + * See hello.c for a trivial example of a TCP protocol using this
 | 
		
	
		
			
			|  | 24 | + * API.
 | 
		
	
		
			
			|  | 25 | + *
 | 
		
	
		
			
			|  | 26 | + */
 | 
		
	
		
			
			|  | 27 | +
 | 
		
	
		
			
			|  | 28 | +/**
 | 
		
	
		
			
			|  | 29 | + * Initialise TCP/IP stack
 | 
		
	
		
			
			|  | 30 | + *
 | 
		
	
		
			
			|  | 31 | + */
 | 
		
	
		
			
			|  | 32 | +void init_tcpip ( void ) {
 | 
		
	
		
			
			|  | 33 | +	uip_init();
 | 
		
	
		
			
			|  | 34 | +	uip_arp_init();
 | 
		
	
		
			
			|  | 35 | +}
 | 
		
	
		
			
			|  | 36 | +
 | 
		
	
		
			
			|  | 37 | +#define UIP_HLEN ( 40 + UIP_LLH_LEN )
 | 
		
	
		
			
			|  | 38 | +
 | 
		
	
		
			
			|  | 39 | +/**
 | 
		
	
		
			
			|  | 40 | + * Transmit TCP data
 | 
		
	
		
			
			|  | 41 | + *
 | 
		
	
		
			
			|  | 42 | + * This is a wrapper around netdev_transmit().  It gathers up the
 | 
		
	
		
			
			|  | 43 | + * packet produced by uIP, and then passes it to netdev_transmit() as
 | 
		
	
		
			
			|  | 44 | + * a single buffer.
 | 
		
	
		
			
			|  | 45 | + */
 | 
		
	
		
			
			|  | 46 | +static void uip_transmit ( void ) {
 | 
		
	
		
			
			|  | 47 | +	uip_arp_out();
 | 
		
	
		
			
			|  | 48 | +	if ( uip_len > UIP_HLEN ) {
 | 
		
	
		
			
			|  | 49 | +		memcpy ( uip_buf + UIP_HLEN, ( void * ) uip_appdata,
 | 
		
	
		
			
			|  | 50 | +			 uip_len - UIP_HLEN );
 | 
		
	
		
			
			|  | 51 | +	}
 | 
		
	
		
			
			|  | 52 | +	netdev_transmit ( uip_buf, uip_len );
 | 
		
	
		
			
			|  | 53 | +	uip_len = 0;
 | 
		
	
		
			
			|  | 54 | +}
 | 
		
	
		
			
			|  | 55 | +
 | 
		
	
		
			
			|  | 56 | +/**
 | 
		
	
		
			
			|  | 57 | + * Run the TCP/IP stack
 | 
		
	
		
			
			|  | 58 | + *
 | 
		
	
		
			
			|  | 59 | + * Call this function in a loop in order to allow TCP/IP processing to
 | 
		
	
		
			
			|  | 60 | + * take place.  This call takes the stack through a single iteration;
 | 
		
	
		
			
			|  | 61 | + * it will typically be used in a loop such as
 | 
		
	
		
			
			|  | 62 | + *
 | 
		
	
		
			
			|  | 63 | + * @code
 | 
		
	
		
			
			|  | 64 | + *
 | 
		
	
		
			
			|  | 65 | + * struct tcp_connection *my_connection;
 | 
		
	
		
			
			|  | 66 | + * ...
 | 
		
	
		
			
			|  | 67 | + * tcp_connect ( my_connection );
 | 
		
	
		
			
			|  | 68 | + * while ( ! my_connection->finished ) {
 | 
		
	
		
			
			|  | 69 | + *   run_tcpip();
 | 
		
	
		
			
			|  | 70 | + * }
 | 
		
	
		
			
			|  | 71 | + *
 | 
		
	
		
			
			|  | 72 | + * @endcode
 | 
		
	
		
			
			|  | 73 | + *
 | 
		
	
		
			
			|  | 74 | + * where @c my_connection->finished is set by one of the connection's
 | 
		
	
		
			
			|  | 75 | + * #tcp_operations methods to indicate completion.
 | 
		
	
		
			
			|  | 76 | + */
 | 
		
	
		
			
			|  | 77 | +void run_tcpip ( void ) {
 | 
		
	
		
			
			|  | 78 | +	void *data;
 | 
		
	
		
			
			|  | 79 | +	size_t len;
 | 
		
	
		
			
			|  | 80 | +	uint16_t type;
 | 
		
	
		
			
			|  | 81 | +	int i;
 | 
		
	
		
			
			|  | 82 | +	
 | 
		
	
		
			
			|  | 83 | +	if ( netdev_poll ( 1, &data, &len ) ) {
 | 
		
	
		
			
			|  | 84 | +		/* We have data */
 | 
		
	
		
			
			|  | 85 | +		memcpy ( uip_buf, data, len );
 | 
		
	
		
			
			|  | 86 | +		uip_len = len;
 | 
		
	
		
			
			|  | 87 | +		type = ntohs ( *( ( uint16_t * ) ( uip_buf + 12 ) ) );
 | 
		
	
		
			
			|  | 88 | +		if ( type == UIP_ETHTYPE_ARP ) {
 | 
		
	
		
			
			|  | 89 | +			uip_arp_arpin();
 | 
		
	
		
			
			|  | 90 | +		} else {
 | 
		
	
		
			
			|  | 91 | +			uip_arp_ipin();
 | 
		
	
		
			
			|  | 92 | +			uip_input();
 | 
		
	
		
			
			|  | 93 | +		}
 | 
		
	
		
			
			|  | 94 | +		if ( uip_len > 0 )
 | 
		
	
		
			
			|  | 95 | +			uip_transmit();
 | 
		
	
		
			
			|  | 96 | +	} else {
 | 
		
	
		
			
			|  | 97 | +		for ( i = 0 ; i < UIP_CONNS ; i++ ) {
 | 
		
	
		
			
			|  | 98 | +			uip_periodic ( i );
 | 
		
	
		
			
			|  | 99 | +			if ( uip_len > 0 )
 | 
		
	
		
			
			|  | 100 | +				uip_transmit();
 | 
		
	
		
			
			|  | 101 | +		}
 | 
		
	
		
			
			|  | 102 | +	}
 | 
		
	
		
			
			|  | 103 | +}
 | 
		
	
		
			
			|  | 104 | +
 | 
		
	
		
			
			|  | 105 | +/**
 | 
		
	
		
			
			|  | 106 | + * Open a TCP connection
 | 
		
	
		
			
			|  | 107 | + *
 | 
		
	
		
			
			|  | 108 | + * @v conn	TCP connection
 | 
		
	
		
			
			|  | 109 | + * @ret 0	Success
 | 
		
	
		
			
			|  | 110 | + * @ret <0	Failure
 | 
		
	
		
			
			|  | 111 | + * 
 | 
		
	
		
			
			|  | 112 | + * This sets up a new TCP connection to the remote host specified in
 | 
		
	
		
			
			|  | 113 | + * tcp_connection::sin.  The actual SYN packet will not be sent out
 | 
		
	
		
			
			|  | 114 | + * until run_tcpip() is called for the first time.
 | 
		
	
		
			
			|  | 115 | + *
 | 
		
	
		
			
			|  | 116 | + * @todo Use linked lists instead of a static buffer, and thereby
 | 
		
	
		
			
			|  | 117 | + *       remove the only potential failure case, giving this function
 | 
		
	
		
			
			|  | 118 | + *       a void return type.
 | 
		
	
		
			
			|  | 119 | + */
 | 
		
	
		
			
			|  | 120 | +int tcp_connect ( struct tcp_connection *conn ) {
 | 
		
	
		
			
			|  | 121 | +	struct uip_conn *uip_conn;
 | 
		
	
		
			
			|  | 122 | +	u16_t ipaddr[2];
 | 
		
	
		
			
			|  | 123 | +
 | 
		
	
		
			
			|  | 124 | +	assert ( conn->sin.sin_addr.s_addr != 0 );
 | 
		
	
		
			
			|  | 125 | +	assert ( conn->sin.sin_port != 0 );
 | 
		
	
		
			
			|  | 126 | +	assert ( conn->tcp_op != NULL );
 | 
		
	
		
			
			|  | 127 | +	assert ( sizeof ( uip_conn->appstate ) == sizeof ( conn ) );
 | 
		
	
		
			
			|  | 128 | +
 | 
		
	
		
			
			|  | 129 | +	* ( ( uint32_t * ) ipaddr ) = conn->sin.sin_addr.s_addr;
 | 
		
	
		
			
			|  | 130 | +	uip_conn = uip_connect ( ipaddr, conn->sin.sin_port );
 | 
		
	
		
			
			|  | 131 | +	if ( ! uip_conn )
 | 
		
	
		
			
			|  | 132 | +		return -1;
 | 
		
	
		
			
			|  | 133 | +
 | 
		
	
		
			
			|  | 134 | +	*( ( void ** ) uip_conn->appstate ) = conn;
 | 
		
	
		
			
			|  | 135 | +	return 0;
 | 
		
	
		
			
			|  | 136 | +}
 | 
		
	
		
			
			|  | 137 | +
 | 
		
	
		
			
			|  | 138 | +/**
 | 
		
	
		
			
			|  | 139 | + * Send data via a TCP connection
 | 
		
	
		
			
			|  | 140 | + *
 | 
		
	
		
			
			|  | 141 | + * @v conn	TCP connection
 | 
		
	
		
			
			|  | 142 | + * @v data	Data to send
 | 
		
	
		
			
			|  | 143 | + * @v len	Length of data
 | 
		
	
		
			
			|  | 144 | + *
 | 
		
	
		
			
			|  | 145 | + * Data will be automatically limited to the current TCP window size.
 | 
		
	
		
			
			|  | 146 | + *
 | 
		
	
		
			
			|  | 147 | + * If retransmission is required, the connection's
 | 
		
	
		
			
			|  | 148 | + * tcp_operations::newdata() method will be called again in order to
 | 
		
	
		
			
			|  | 149 | + * regenerate the data.
 | 
		
	
		
			
			|  | 150 | + */
 | 
		
	
		
			
			|  | 151 | +void tcp_send ( struct tcp_connection *conn __unused,
 | 
		
	
		
			
			|  | 152 | +		const void *data, size_t len ) {
 | 
		
	
		
			
			|  | 153 | +	assert ( conn = *( ( void ** ) uip_conn->appstate ) );
 | 
		
	
		
			
			|  | 154 | +	uip_send ( ( void * ) data, len );
 | 
		
	
		
			
			|  | 155 | +}
 | 
		
	
		
			
			|  | 156 | +
 | 
		
	
		
			
			|  | 157 | +/**
 | 
		
	
		
			
			|  | 158 | + * Close a TCP connection
 | 
		
	
		
			
			|  | 159 | + *
 | 
		
	
		
			
			|  | 160 | + * @v conn	TCP connection
 | 
		
	
		
			
			|  | 161 | + */
 | 
		
	
		
			
			|  | 162 | +void tcp_close ( struct tcp_connection *conn __unused ) {
 | 
		
	
		
			
			|  | 163 | +	assert ( conn = *( ( void ** ) uip_conn->appstate ) );
 | 
		
	
		
			
			|  | 164 | +	uip_close();
 | 
		
	
		
			
			|  | 165 | +}
 | 
		
	
		
			
			|  | 166 | +
 | 
		
	
		
			
			|  | 167 | +/**
 | 
		
	
		
			
			|  | 168 | + * uIP TCP application call interface
 | 
		
	
		
			
			|  | 169 | + *
 | 
		
	
		
			
			|  | 170 | + * This is the entry point of gPXE from the point of view of the uIP
 | 
		
	
		
			
			|  | 171 | + * protocol stack.  This function calls the appropriate methods from
 | 
		
	
		
			
			|  | 172 | + * the connection's @tcp_operations table in order to process received
 | 
		
	
		
			
			|  | 173 | + * data, transmit new data etc.
 | 
		
	
		
			
			|  | 174 | + */
 | 
		
	
		
			
			|  | 175 | +void uip_tcp_appcall ( void ) {
 | 
		
	
		
			
			|  | 176 | +	struct tcp_connection *conn = *( ( void ** ) uip_conn->appstate );
 | 
		
	
		
			
			|  | 177 | +	struct tcp_operations *op = conn->tcp_op;
 | 
		
	
		
			
			|  | 178 | +
 | 
		
	
		
			
			|  | 179 | +	assert ( conn->tcp_op->closed != NULL );
 | 
		
	
		
			
			|  | 180 | +	assert ( conn->tcp_op->connected != NULL );
 | 
		
	
		
			
			|  | 181 | +	assert ( conn->tcp_op->acked != NULL );
 | 
		
	
		
			
			|  | 182 | +	assert ( conn->tcp_op->newdata != NULL );
 | 
		
	
		
			
			|  | 183 | +	assert ( conn->tcp_op->senddata != NULL );
 | 
		
	
		
			
			|  | 184 | +
 | 
		
	
		
			
			|  | 185 | +	if ( uip_aborted() && op->aborted ) /* optional method */
 | 
		
	
		
			
			|  | 186 | +		op->aborted ( conn );
 | 
		
	
		
			
			|  | 187 | +	if ( uip_timedout() && op->timedout ) /* optional method */
 | 
		
	
		
			
			|  | 188 | +		op->timedout ( conn );
 | 
		
	
		
			
			|  | 189 | +	if ( uip_closed() && op->closed ) /* optional method */
 | 
		
	
		
			
			|  | 190 | +		op->closed ( conn );
 | 
		
	
		
			
			|  | 191 | +	if ( uip_connected() )
 | 
		
	
		
			
			|  | 192 | +		op->connected ( conn );
 | 
		
	
		
			
			|  | 193 | +	if ( uip_acked() )
 | 
		
	
		
			
			|  | 194 | +		op->acked ( conn, uip_conn->len );
 | 
		
	
		
			
			|  | 195 | +	if ( uip_newdata() )
 | 
		
	
		
			
			|  | 196 | +		op->newdata ( conn, ( void * ) uip_appdata, uip_len );
 | 
		
	
		
			
			|  | 197 | +	if ( uip_rexmit() || uip_newdata() || uip_acked() ||
 | 
		
	
		
			
			|  | 198 | +	     uip_connected() || uip_poll() )
 | 
		
	
		
			
			|  | 199 | +		op->senddata ( conn );
 | 
		
	
		
			
			|  | 200 | +}
 | 
		
	
		
			
			|  | 201 | +
 | 
		
	
		
			
			|  | 202 | +/* Present here to allow everything to link.  Will go into separate
 | 
		
	
		
			
			|  | 203 | + * udp.c file
 | 
		
	
		
			
			|  | 204 | + */
 | 
		
	
		
			
			|  | 205 | +void uip_udp_appcall ( void ) {
 | 
		
	
		
			
			|  | 206 | +}
 |