/** Code for the TCP MSS option */
#define TCP_OPTION_MSS 2
+/** TCP window scale option */
+struct tcp_window_scale_option {
+ uint8_t kind;
+ uint8_t length;
+ uint8_t scale;
+} __attribute__ (( packed ));
+
+/** Padded TCP window scale option (used for sending) */
+struct tcp_window_scale_padded_option {
+ uint8_t nop;
+ struct tcp_window_scale_option wsopt;
+} __attribute (( packed ));
+
+/** Code for the TCP window scale option */
+#define TCP_OPTION_WS 3
+
+/** Advertised TCP window scale
+ *
+ * Using a scale factor of 2**9 provides for a maximum window of 32MB,
+ * which is sufficient to allow Gigabit-speed transfers with a 200ms
+ * RTT. The minimum advertised window is 512 bytes, which is still
+ * less than a single packet.
+ */
+#define TCP_RX_WINDOW_SCALE 9
+
/** TCP timestamp option */
struct tcp_timestamp_option {
uint8_t kind;
struct tcp_options {
/** MSS option, if present */
const struct tcp_mss_option *mssopt;
- /** Timestampe option, if present */
+ /** Window scale option, if present */
+ const struct tcp_window_scale_option *wsopt;
+ /** Timestamp option, if present */
const struct tcp_timestamp_option *tsopt;
};
( MAX_LL_NET_HEADER_LEN + \
sizeof ( struct tcp_header ) + \
sizeof ( struct tcp_mss_option ) + \
+ sizeof ( struct tcp_window_scale_padded_option ) + \
sizeof ( struct tcp_timestamp_padded_option ) )
/**
* Equivalent to TS.Recent in RFC 1323 terminology.
*/
uint32_t ts_recent;
+ /** Send window scale
+ *
+ * Equivalent to Snd.Wind.Scale in RFC 1323 terminology
+ */
+ uint8_t snd_win_scale;
+ /** Receive window scale
+ *
+ * Equivalent to Rcv.Wind.Scale in RFC 1323 terminology
+ */
+ uint8_t rcv_win_scale;
/** Transmit queue */
struct list_head tx_queue;
struct io_buffer *iobuf;
struct tcp_header *tcphdr;
struct tcp_mss_option *mssopt;
+ struct tcp_window_scale_padded_option *wsopt;
struct tcp_timestamp_padded_option *tsopt;
void *payload;
unsigned int flags;
uint32_t seq_len;
uint32_t app_win;
uint32_t max_rcv_win;
+ uint32_t max_representable_win;
int rc;
/* If retransmission timer is already running, do nothing */
app_win = xfer_window ( &tcp->xfer );
if ( max_rcv_win > app_win )
max_rcv_win = app_win;
+ max_representable_win = ( 0xffff << tcp->rcv_win_scale );
+ if ( max_rcv_win > max_representable_win )
+ max_rcv_win = max_representable_win;
max_rcv_win &= ~0x03; /* Keep everything dword-aligned */
if ( tcp->rcv_win < max_rcv_win )
tcp->rcv_win = max_rcv_win;
mssopt->kind = TCP_OPTION_MSS;
mssopt->length = sizeof ( *mssopt );
mssopt->mss = htons ( TCP_MSS );
+ wsopt = iob_push ( iobuf, sizeof ( *wsopt ) );
+ wsopt->nop = TCP_OPTION_NOP;
+ wsopt->wsopt.kind = TCP_OPTION_WS;
+ wsopt->wsopt.length = sizeof ( wsopt->wsopt );
+ wsopt->wsopt.scale = TCP_RX_WINDOW_SCALE;
}
if ( ( flags & TCP_SYN ) || ( tcp->flags & TCP_TS_ENABLED ) ) {
tsopt = iob_push ( iobuf, sizeof ( *tsopt ) );
tcphdr->ack = htonl ( tcp->rcv_ack );
tcphdr->hlen = ( ( payload - iobuf->data ) << 2 );
tcphdr->flags = flags;
- tcphdr->win = htons ( tcp->rcv_win );
+ tcphdr->win = htons ( tcp->rcv_win >> tcp->rcv_win_scale );
tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) );
/* Dump header */
case TCP_OPTION_MSS:
options->mssopt = data;
break;
+ case TCP_OPTION_WS:
+ options->wsopt = data;
+ break;
case TCP_OPTION_TS:
options->tsopt = data;
break;
tcp->rcv_ack = seq;
if ( options->tsopt )
tcp->flags |= TCP_TS_ENABLED;
+ if ( options->wsopt ) {
+ tcp->snd_win_scale = options->wsopt->scale;
+ tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE;
+ }
}
/* Ignore duplicate SYN */
tcp = tcp_demux ( ntohs ( tcphdr->dest ) );
seq = ntohl ( tcphdr->seq );
ack = ntohl ( tcphdr->ack );
- win = ntohs ( tcphdr->win );
+ win = ( ntohs ( tcphdr->win ) << tcp->snd_win_scale );
flags = tcphdr->flags;
tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ),
( hlen - sizeof ( *tcphdr ) ), &options );