From e7e607d6d1b4e53f6f2a6094c02240e4fd8f59d4 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sun, 18 Oct 2015 20:24:50 +0200 Subject: [PATCH] Measure RTT and calculate RTO. 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 | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ utcp_priv.h | 13 ++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/utcp.c b/utcp.c index c026ce7..4f3510e 100644 --- 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; } diff --git a/utcp_priv.h b/utcp_priv.h index 3a119f1..24f4158 100644 --- a/utcp_priv.h +++ b/utcp_priv.h @@ -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 -- 2.39.5