From: Guus Sliepen <guus@meshlink.io>
Date: Sun, 18 Oct 2015 18:24:50 +0000 (+0200)
Subject: Measure RTT and calculate RTO.
X-Git-Url: http://git.meshlink.io/?a=commitdiff_plain;h=e7e607d6d1b4e53f6f2a6094c02240e4fd8f59d4;p=utcp

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.
---

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