]> git.meshlink.io Git - utcp/commitdiff
Measure RTT and calculate RTO.
authorGuus Sliepen <guus@meshlink.io>
Sun, 18 Oct 2015 18:24:50 +0000 (20:24 +0200)
committerGuus Sliepen <guus@sliepen.org>
Sun, 2 Jul 2017 10:03:59 +0000 (12:03 +0200)
Opportunistically measure RTT using only a single timer, without requiring
timestamps to be added to packets. Use the method described in RFC 6298 to
smoothly update the value of RTO.

utcp.c
utcp_priv.h

diff --git a/utcp.c b/utcp.c
index c026ce7b9de58b2bdefb71ece87bac739e2c04e4..4f3510e8c0da3f8ee130e5089d23aa244f22cb0c 100644 (file)
--- a/utcp.c
+++ b/utcp.c
@@ -333,6 +333,31 @@ static struct utcp_connection *allocate_connection(struct utcp *utcp, uint16_t s
        return c;
 }
 
+// Update RTT variables. See RFC 6298.
+static void update_rtt(struct utcp_connection *c, uint32_t rtt) {
+       if(!rtt) {
+               debug("invalid rtt\n");
+               return;
+       }
+
+       struct utcp *utcp = c->utcp;
+
+       if(!utcp->srtt) {
+               utcp->srtt = rtt;
+               utcp->rttvar = rtt / 2;
+               utcp->rto = rtt + max(2 * rtt, CLOCK_GRANULARITY);
+       } else {
+               utcp->rttvar = (utcp->rttvar * 3 + abs(utcp->srtt - rtt)) / 4;
+               utcp->srtt = (utcp->srtt * 7 + rtt) / 8;
+               utcp->rto = utcp->srtt + max(utcp->rttvar, CLOCK_GRANULARITY);
+       }
+
+       if(utcp->rto > MAX_RTO)
+               utcp->rto = MAX_RTO;
+
+       debug("rtt %u srtt %u rttvar %u rto %u\n", rtt, utcp->srtt, utcp->rttvar, utcp->rto);
+}
+
 struct utcp_connection *utcp_connect(struct utcp *utcp, uint16_t dst, utcp_recv_t recv, void *priv) {
        struct utcp_connection *c = allocate_connection(utcp, 0, dst);
        if(!c)
@@ -420,6 +445,13 @@ static void ack(struct utcp_connection *c, bool sendatleastone) {
                        pkt->hdr.ctl |= FIN;
                }
 
+               if(!c->rtt_start.tv_sec) {
+                       // Start RTT measurement
+                       gettimeofday(&c->rtt_start, NULL);
+                       c->rtt_seq = pkt->hdr.seq + seglen;
+                       debug("Starting RTT measurement, expecting ack %u\n", c->rtt_seq);
+               }
+
                print_packet(c->utcp, "send", pkt, sizeof pkt->hdr + seglen);
                c->utcp->send(c->utcp, pkt, sizeof pkt->hdr + seglen);
        } while(left);
@@ -555,6 +587,10 @@ static void retransmit(struct utcp_connection *c) {
                        break;
        }
 
+       utcp->rto *= 2;
+       if(utcp->rto > MAX_RTO)
+               utcp->rto = MAX_RTO;
+       c->rtt_start.tv_sec = 0; // invalidate RTT timer
        free(pkt);
 }
 
@@ -893,6 +929,20 @@ ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) {
        prevrcvnxt = c->rcv.nxt;
 
        if(advanced) {
+               // RTT measurement
+               if(c->rtt_start.tv_sec) {
+                       if(c->rtt_seq == hdr.ack) {
+                               struct timeval now, diff;
+                               gettimeofday(&now, NULL);
+                               timersub(&now, &c->rtt_start, &diff);
+                               update_rtt(c, diff.tv_sec * 1000000 + diff.tv_usec);
+                               c->rtt_start.tv_sec = 0;
+                       } else if(c->rtt_seq < hdr.ack) {
+                               debug("Cancelling RTT measurement: %u < %u\n", c->rtt_seq, hdr.ack);
+                               c->rtt_start.tv_sec = 0;
+                       }
+               }
+
                int32_t data_acked = advanced;
 
                switch(c->state) {
@@ -1323,6 +1373,7 @@ struct utcp *utcp_init(utcp_accept_t accept, utcp_pre_accept_t pre_accept, utcp_
        utcp->priv = priv;
        utcp->mtu = DEFAULT_MTU;
        utcp->timeout = DEFAULT_USER_TIMEOUT; // s
+       utcp->rto = START_RTO; // us
 
        return utcp;
 }
index 3a119f16cf60a878e327e0b9ebe49efe3aff2f62..24f4158318ddccd8a4a586b72420fbe87de172c2 100644 (file)
@@ -39,6 +39,9 @@
 #define DEFAULT_MTU 1000
 
 #define DEFAULT_USER_TIMEOUT 60 // s
+#define CLOCK_GRANULARITY 1000 // us
+#define START_RTO 1000000 // us
+#define MAX_RTO 3000000 // us
 
 struct hdr {
        uint16_t src; // Source port
@@ -129,6 +132,8 @@ struct utcp_connection {
 
        struct timeval conn_timeout;
        struct timeval rtrx_timeout;
+       struct timeval rtt_start;
+       uint32_t rtt_seq;
 
        // Buffers
 
@@ -159,7 +164,13 @@ struct utcp {
        // Global socket options
 
        uint16_t mtu;
-       int timeout;
+       int timeout; // s
+
+       // RTT variables
+
+       uint32_t srtt; // us
+       uint32_t rttvar; // us
+       uint32_t rto; // us
 
        // Connection management