Add support for TCP timestamps
authorMichael Brown <mcb30@etherboot.org>
Fri, 13 Jul 2007 10:32:53 +0000 (11:32 +0100)
committerMichael Brown <mcb30@etherboot.org>
Fri, 13 Jul 2007 10:32:53 +0000 (11:32 +0100)
src/include/gpxe/tcp.h
src/net/tcp.c

index d927d24..d967791 100644 (file)
@@ -27,18 +27,59 @@ struct tcp_header {
        uint16_t urg;           /* Urgent pointer */
 };
 
-/**
- * TCP MSS option
+/** @defgroup tcpopts TCP options
+ * @{
  */
+
+/** End of TCP options list */
+#define TCP_OPTION_END 0
+
+/** TCP option pad */
+#define TCP_OPTION_NOP 1
+
+/** Generic TCP option */
+struct tcp_option {
+       uint8_t kind;
+       uint8_t length;
+} __attribute__ (( packed ));
+
+/** TCP MSS option */
 struct tcp_mss_option {
        uint8_t kind;
        uint8_t length;
        uint16_t mss;
-};
+} __attribute__ (( packed ));
 
 /** Code for the TCP MSS option */
 #define TCP_OPTION_MSS 2
 
+/** TCP timestamp option */
+struct tcp_timestamp_option {
+       uint8_t kind;
+       uint8_t length;
+       uint32_t tsval;
+       uint32_t tsecr;
+} __attribute__ (( packed ));
+
+/** Padded TCP timestamp option (used for sending) */
+struct tcp_timestamp_padded_option {
+       uint8_t nop[2];
+       struct tcp_timestamp_option tsopt;
+} __attribute__ (( packed ));
+
+/** Code for the TCP timestamp option */
+#define TCP_OPTION_TS 8
+
+/** Parsed TCP options */
+struct tcp_options {
+       /** MSS option, if present */
+       const struct tcp_mss_option *mssopt;
+       /** Timestampe option, if present */
+       const struct tcp_timestamp_option *tsopt;
+};
+
+/** @} */
+
 /*
  * TCP flags
  */
index 98db274..b2d9b2d 100644 (file)
@@ -65,6 +65,13 @@ struct tcp_connection {
         * Equivalent to RCV.NXT in RFC 793 terminology.
         */
        uint32_t rcv_ack;
+       /** Most recent received timestamp
+        *
+        * Equivalent to TS.Recent in RFC 1323 terminology.
+        */
+       uint32_t ts_recent;
+       /** Timestamps enabled */
+       int timestamps;
 
        /** Transmit queue */
        struct list_head queue;
@@ -381,6 +388,7 @@ static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
        struct io_buffer *iobuf;
        struct tcp_header *tcphdr;
        struct tcp_mss_option *mssopt;
+       struct tcp_timestamp_padded_option *tsopt;
        void *payload;
        unsigned int flags;
        size_t len = 0;
@@ -449,6 +457,14 @@ static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
                mssopt->length = sizeof ( *mssopt );
                mssopt->mss = htons ( TCP_MSS );
        }
+       if ( ( flags & TCP_SYN ) || tcp->timestamps ) {
+               tsopt = iob_push ( iobuf, sizeof ( *tsopt ) );
+               memset ( tsopt->nop, TCP_OPTION_NOP, sizeof ( tsopt->nop ) );
+               tsopt->tsopt.kind = TCP_OPTION_TS;
+               tsopt->tsopt.length = sizeof ( tsopt->tsopt );
+               tsopt->tsopt.tsval = ntohl ( currticks() );
+               tsopt->tsopt.tsecr = ntohl ( tcp->ts_recent );
+       }
        tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) );
        memset ( tcphdr, 0, sizeof ( *tcphdr ) );
        tcphdr->src = tcp->local_port;
@@ -594,18 +610,63 @@ static struct tcp_connection * tcp_demux ( unsigned int local_port ) {
        return NULL;
 }
 
+/**
+ * Parse TCP received options
+ *
+ * @v tcp              TCP connection
+ * @v data             Raw options data
+ * @v len              Raw options length
+ * @v options          Options structure to fill in
+ */
+static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data,
+                         size_t len, struct tcp_options *options ) {
+       const void *end = ( data + len );
+       const struct tcp_option *option;
+       unsigned int kind;
+
+       memset ( options, 0, sizeof ( *options ) );
+       while ( data < end ) {
+               option = data;
+               kind = option->kind;
+               if ( kind == TCP_OPTION_END )
+                       return;
+               if ( kind == TCP_OPTION_NOP ) {
+                       data++;
+                       continue;
+               }
+               switch ( kind ) {
+               case TCP_OPTION_MSS:
+                       options->mssopt = data;
+                       break;
+               case TCP_OPTION_TS:
+                       options->tsopt = data;
+                       break;
+               default:
+                       DBGC ( tcp, "TCP %p received unknown option %d\n",
+                              tcp, kind );
+                       break;
+               }
+               data += option->length;
+       }
+}
+
 /**
  * Handle TCP received SYN
  *
  * @v tcp              TCP connection
  * @v seq              SEQ value (in host-endian order)
+ * @v options          TCP options
  * @ret rc             Return status code
  */
-static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq ) {
+static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq,
+                       struct tcp_options *options ) {
 
        /* Synchronise sequence numbers on first SYN */
-       if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) )
+       if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) {
                tcp->rcv_ack = seq;
+               if ( options->tsopt )
+                       tcp->timestamps = 1;
+       }
 
        /* Ignore duplicate SYN */
        if ( ( tcp->rcv_ack - seq ) > 0 )
@@ -776,6 +837,7 @@ static int tcp_rx ( struct io_buffer *iobuf,
                    uint16_t pshdr_csum ) {
        struct tcp_header *tcphdr = iobuf->data;
        struct tcp_connection *tcp;
+       struct tcp_options options;
        unsigned int hlen;
        uint16_t csum;
        uint32_t start_seq;
@@ -820,6 +882,8 @@ static int tcp_rx ( struct io_buffer *iobuf,
        ack = ntohl ( tcphdr->ack );
        win = ntohs ( tcphdr->win );
        flags = tcphdr->flags;
+       tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ),
+                     ( hlen - sizeof ( *tcphdr ) ), &options );
        iob_pull ( iobuf, hlen );
        len = iob_len ( iobuf );
 
@@ -849,7 +913,7 @@ static int tcp_rx ( struct io_buffer *iobuf,
 
        /* Handle SYN, if present */
        if ( flags & TCP_SYN ) {
-               tcp_rx_syn ( tcp, seq );
+               tcp_rx_syn ( tcp, seq, &options );
                seq++;
        }
 
@@ -869,6 +933,10 @@ static int tcp_rx ( struct io_buffer *iobuf,
                seq++;
        }
 
+       /* Update timestamp, if present and applicable */
+       if ( ( seq == tcp->rcv_ack ) && options.tsopt )
+               tcp->ts_recent = ntohl ( options.tsopt->tsval );
+
        /* Dump out any state change as a result of the received packet */
        tcp_dump_state ( tcp );