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 60976d285b199c4551c5f01422e9be6decbf8161..6f01a3f6251c03c4851944edec45673c61936d5c 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 d0b999604f2be9d444f52d5f29018454af53abb9..94be6ca25ed8e0c54affd3cbc2b23ae2da862a43 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 418876cc092bed9bf70df7f13ab3678562c941b6..7e64a9528052eaa708c68dda5fe8832576302c11 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 dd8fb6ac8b161a120aad200642aa0e2e3d9addb9..bf9c284bd3a032acb6099494256573d333b0c994 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 aa08bd9459ad34f716b8b0d699074fd4af65eee1..8bedd2ff3838f5036fc129785851d2b7dc56f380 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 0681fd8a1fc9e5d62a3308dbc77cae10e0370842..0bc4de39b173e2ee907778107f4890aec1e6bde4 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));
         }