From cf1c0cb3ed0492f80d338c8e1390048737dd1d5c Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Tue, 26 Feb 2019 17:17:00 +0000
Subject: [PATCH] SSU: Fix RTT/RTO calculations (ticket #2443)

---
 .../i2p/router/transport/udp/ACKSender.java   |  2 +-
 .../transport/udp/EstablishmentManager.java   |  4 +-
 .../transport/udp/InboundEstablishState.java  |  8 ++++
 .../transport/udp/OutboundEstablishState.java |  7 +++
 .../i2p/router/transport/udp/PeerState.java   | 44 ++++++++++++-------
 .../router/transport/udp/UDPTransport.java    |  2 +-
 6 files changed, 47 insertions(+), 20 deletions(-)

diff --git a/router/java/src/net/i2p/router/transport/udp/ACKSender.java b/router/java/src/net/i2p/router/transport/udp/ACKSender.java
index 60976d285b..6f01a3f625 100644
--- a/router/java/src/net/i2p/router/transport/udp/ACKSender.java
+++ b/router/java/src/net/i2p/router/transport/udp/ACKSender.java
@@ -62,7 +62,7 @@ class ACKSender implements Runnable {
     
     public synchronized void shutdown() { 
         _alive = false;
-        PeerState poison = new PeerState(_context, _transport, new byte[4], 0, null, false);
+        PeerState poison = new PeerState(_context, _transport, new byte[4], 0, null, false, 0);
         poison.setTheyRelayToUsAs(POISON_PS);
         _peersToACK.offer(poison);
         for (int i = 1; i <= 5 && !_peersToACK.isEmpty(); i++) {
diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
index d0b999604f..94be6ca25e 100644
--- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
@@ -694,7 +694,7 @@ class EstablishmentManager {
         
         RouterIdentity remote = state.getConfirmedIdentity();
         PeerState peer = new PeerState(_context, _transport,
-                                       state.getSentIP(), state.getSentPort(), remote.calculateHash(), true);
+                                       state.getSentIP(), state.getSentPort(), remote.calculateHash(), true, state.getRTT());
         peer.setCurrentCipherKey(state.getCipherKey());
         peer.setCurrentMACKey(state.getMACKey());
         peer.setWeRelayToThemAs(state.getSentRelayTag());
@@ -813,7 +813,7 @@ class EstablishmentManager {
             _outboundByClaimedAddress.remove(claimed, state);
         _outboundByHash.remove(remote.calculateHash(), state);
         PeerState peer = new PeerState(_context, _transport,
-                                       state.getSentIP(), state.getSentPort(), remote.calculateHash(), false);
+                                       state.getSentIP(), state.getSentPort(), remote.calculateHash(), false, state.getRTT());
         peer.setCurrentCipherKey(state.getCipherKey());
         peer.setCurrentMACKey(state.getMACKey());
         peer.setTheyRelayToUsAs(state.getReceivedRelayTag());
diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java
index 418876cc09..7e64a95280 100644
--- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java
+++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java
@@ -64,6 +64,8 @@ class InboundEstablishState {
     private int _createdSentCount;
     // default true
     private boolean _introductionRequested = true;
+
+    private int _rtt;
     
     public enum InboundState {
         /** nothin known yet */
@@ -296,6 +298,8 @@ class InboundEstablishState {
      */
     public synchronized long getNextSendTime() { return _nextSend; }
 
+    synchronized int getRTT() { return _rtt; }
+
     /** RemoteHostId, uniquely identifies an attempt */
     RemoteHostId getRemoteHostId() { return _remoteHostId; }
 
@@ -356,6 +360,10 @@ class InboundEstablishState {
                 _currentState = InboundState.IB_STATE_CONFIRMED_PARTIALLY;
         }
         
+        if (_createdSentCount == 1) {
+            _rtt = (int) ( _context.clock().now() - _lastSend );
+        }	
+
         packetReceived();
     }
     
diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java
index dd8fb6ac8b..bf9c284bd3 100644
--- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java
+++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java
@@ -74,6 +74,7 @@ class OutboundEstablishState {
     private long _confirmedSentTime;
     private long _requestSentTime;
     private long _introSentTime;
+    private int _rtt;
     
     public enum OutboundState {
         /** nothin sent yet */
@@ -179,6 +180,8 @@ class OutboundEstablishState {
      *  @since 0.9.24
      */
     public boolean needIntroduction() { return _needIntroduction; }
+
+    synchronized int getRTT() { return _rtt; }
     
     /**
      *  Queue a message to be sent after the session is established.
@@ -304,6 +307,10 @@ class OutboundEstablishState {
             _currentState == OutboundState.OB_STATE_INTRODUCED ||
             _currentState == OutboundState.OB_STATE_PENDING_INTRO)
             _currentState = OutboundState.OB_STATE_CREATED_RECEIVED;
+
+        if (_requestSentCount == 1) {
+            _rtt = (int) (_context.clock().now() - _requestSentTime);
+        }
         packetReceived();
     }
     
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java
index aa08bd9459..8bedd2ff38 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerState.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java
@@ -314,10 +314,10 @@ public class PeerState {
      */
     public static final int MAX_MTU = Math.max(LARGE_MTU, MAX_IPV6_MTU);
     
-    private static final int MIN_RTO = 100 + ACKSender.ACK_FREQUENCY;
-    private static final int INIT_RTO = 3*1000;
-    public static final int INIT_RTT = INIT_RTO / 2;
-    private static final int MAX_RTO = 15*1000;
+    private static final int MIN_RTO = 1000;
+    private static final int INIT_RTO = 1000;
+    private static final int INIT_RTT = 0;
+    private static final int MAX_RTO = 60*1000;
     private static final int CLOCK_SKEW_FUDGE = (ACKSender.ACK_FREQUENCY * 2) / 3;
     
     /**
@@ -336,8 +336,11 @@ public class PeerState {
     private static final long RESEND_ACK_TIMEOUT = 5*60*1000;
 
     
+    /**
+     *  @param rtt from the EstablishState, or 0 if not available
+     */
     public PeerState(RouterContext ctx, UDPTransport transport,
-                     byte[] remoteIP, int remotePort, Hash remotePeer, boolean isInbound) {
+                     byte[] remoteIP, int remotePort, Hash remotePeer, boolean isInbound, int rtt) {
         _context = ctx;
         _log = ctx.logManager().getLog(PeerState.class);
         _transport = transport;
@@ -366,9 +369,14 @@ public class PeerState {
         }
         //_mtuLastChecked = -1;
         _lastACKSend = -1;
+
         _rto = INIT_RTO;
         _rtt = INIT_RTT;
-        _rttDeviation = _rtt;
+        if (rtt > 0)  
+            recalculateTimeouts(rtt);
+        else
+            _rttDeviation = _rtt;
+            
         _inboundMessages = new HashMap<Long, InboundMessageState>(8);
         _outboundMessages = new CachedIteratorCollection<OutboundMessageState>();
         //_outboundQueue = new CoDelPriorityBlockingQueue(ctx, "UDP-PeerState", 32);
@@ -1226,16 +1234,18 @@ public class PeerState {
      *  Caller should synch on this
      */
     private void recalculateTimeouts(long lifetime) {
-        // the rttDev calculation matches that recommended in RFC 2988 (beta = 1/4)
-        _rttDeviation = _rttDeviation + (int)(0.25d*(Math.abs(lifetime-_rtt)-_rttDeviation));
+        if (_rtt <= 0) {
+            // first measurement
+            _rtt = (int) lifetime;
+            _rttDeviation = (int)(lifetime / 2);
+        } else {
+            // the rttDev calculation matches that recommended in RFC 2988 (beta = 1/4)
+            _rttDeviation = (int)( 0.75*_rttDeviation + 0.25*Math.abs(lifetime-_rtt) );
         
-        float scale = RTT_DAMPENING;
-        // the faster we are going, the slower we want to reduce the rtt
-        //if (_sendBps > 0)
-        //    scale = lifetime / ((float)lifetime + (float)_sendBps);
-        //if (scale < 0.001f) scale = 0.001f;
+            float scale = RTT_DAMPENING;
         
-        _rtt = (int)(_rtt*(1.0f-scale) + (scale)*lifetime);
+            _rtt = (int)(_rtt*(1.0f-scale) + (scale)*lifetime);
+        }
         // K = 4
         _rto = Math.min(MAX_RTO, Math.max(minRTO(), _rtt + (_rttDeviation<<2)));
         //if (_log.shouldLog(Log.DEBUG))
@@ -1821,13 +1831,15 @@ public class PeerState {
                               + " remaining"
                               + " for message " + state.getMessageId() + ": " + state);
 
-                if (state.getPushCount() > 0)
+                int rto = getRTO();
+                if (state.getPushCount() > 0) {
                     _retransmitter = state;
+                    rto = Math.min(MAX_RTO, rto << state.getPushCount()); // Section 5.5 RFC 6298
+                }
 
                 if (state.push())
                     _messagesSent++;
             
-                int rto = getRTO();
                 state.setNextSendTime(now + rto);
 
                 //if (peer.getSendWindowBytesRemaining() > 0)
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
index 0681fd8a1f..0bc4de39b1 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
@@ -2723,7 +2723,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         for (PeerState peer : _peersByIdent.values()) {
             if ((!includeEverybody) && now - peer.getLastReceiveTime() > 5*60*1000)
                 continue; // skip old peers
-            if (peer.getRTT() > PeerState.INIT_RTT - 250)
+            if (peer.getRTT() > 1250)
                 continue; // Big RTT makes for a poor calculation
             skews.addElement(Long.valueOf(peer.getClockSkew() / 1000));
         }
-- 
GitLab