]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[tcp] Add support for TCP window scaling
authorMichael Brown <mcb30@ipxe.org>
Fri, 29 Jun 2012 13:30:18 +0000 (14:30 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 29 Jun 2012 14:05:33 +0000 (15:05 +0100)
The maximum unscaled TCP window (64kB) implies a maximum bandwidth of
around 300kB/s on a WAN link with an RTT of 200ms.  Add support for
the TCP window scaling option to remove this upper limit.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/tcp.h
src/net/tcp.c

index 7084af60efab8ea00c3d40697dafcd528e65a05e..cb3309f2f9ad2df06489e4d2bf8629a74ea964aa 100644 (file)
@@ -54,6 +54,31 @@ struct tcp_mss_option {
 /** 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;
@@ -75,7 +100,9 @@ struct tcp_timestamp_padded_option {
 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;
 };
 
@@ -316,6 +343,7 @@ struct tcp_options {
        ( 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 ) )
 
 /**
index c6bb4a6625a257811c5dcbe74826b802818056b5..b30fb1b0a49c230543412625031d0d76a0e900e3 100644 (file)
@@ -87,6 +87,16 @@ struct tcp_connection {
         * 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;
@@ -490,6 +500,7 @@ static int tcp_xmit ( struct tcp_connection *tcp ) {
        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;
@@ -497,6 +508,7 @@ static int tcp_xmit ( struct tcp_connection *tcp ) {
        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 */
@@ -551,6 +563,9 @@ static int tcp_xmit ( struct tcp_connection *tcp ) {
        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;
@@ -562,6 +577,11 @@ static int tcp_xmit ( struct tcp_connection *tcp ) {
                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 ) );
@@ -581,7 +601,7 @@ static int tcp_xmit ( struct tcp_connection *tcp ) {
        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 */
@@ -769,6 +789,9 @@ static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data,
                case TCP_OPTION_MSS:
                        options->mssopt = data;
                        break;
+               case TCP_OPTION_WS:
+                       options->wsopt = data;
+                       break;
                case TCP_OPTION_TS:
                        options->tsopt = data;
                        break;
@@ -825,6 +848,10 @@ static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq,
                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 */
@@ -1168,7 +1195,7 @@ static int tcp_rx ( struct io_buffer *iobuf,
        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 );