+static inline uint32_t absdiff(uint32_t a, uint32_t b) {
+ if(a > b) {
+ return a - b;
+ } else {
+ return b - a;
+ }
+}
+
+// Update RTT variables. See RFC 6298.
+static void update_rtt(struct utcp_connection *c, uint32_t rtt) {
+ if(!rtt) {
+ debug(c, "invalid rtt\n");
+ return;
+ }
+
+ if(!c->srtt) {
+ c->srtt = rtt;
+ c->rttvar = rtt / 2;
+ } else {
+ c->rttvar = (c->rttvar * 3 + absdiff(c->srtt, rtt)) / 4;
+ c->srtt = (c->srtt * 7 + rtt) / 8;
+ }
+
+ c->rto = c->srtt + max(4 * c->rttvar, CLOCK_GRANULARITY);
+
+ if(c->rto > MAX_RTO) {
+ c->rto = MAX_RTO;
+ }
+
+ debug(c, "rtt %u srtt %u rttvar %u rto %u\n", rtt, c->srtt, c->rttvar, c->rto);
+}
+
+static void start_retransmit_timer(struct utcp_connection *c) {
+ clock_gettime(UTCP_CLOCK, &c->rtrx_timeout);
+
+ uint32_t rto = c->rto;
+
+ while(rto > USEC_PER_SEC) {
+ c->rtrx_timeout.tv_sec++;
+ rto -= USEC_PER_SEC;
+ }
+
+ c->rtrx_timeout.tv_nsec += rto * 1000;
+
+ if(c->rtrx_timeout.tv_nsec >= NSEC_PER_SEC) {
+ c->rtrx_timeout.tv_nsec -= NSEC_PER_SEC;
+ c->rtrx_timeout.tv_sec++;
+ }
+
+ debug(c, "rtrx_timeout %ld.%06lu\n", c->rtrx_timeout.tv_sec, c->rtrx_timeout.tv_nsec);
+}
+
+static void stop_retransmit_timer(struct utcp_connection *c) {
+ timespec_clear(&c->rtrx_timeout);
+ debug(c, "rtrx_timeout cleared\n");
+}
+
+struct utcp_connection *utcp_connect_ex(struct utcp *utcp, uint16_t dst, utcp_recv_t recv, void *priv, uint32_t flags) {