From 4a9f8240db2d97485fcf4d05e81a8d5265f873c9 Mon Sep 17 00:00:00 2001
From: zzz <zzz@i2pmail.org>
Date: Sun, 5 Jun 2022 11:21:04 -0400
Subject: [PATCH] SSU2: Relay WIP part 6

Fix saving relay tag as Bob
Select introducers by newest connection instead of random
Ping introducers more often
Check and set ping time when pinging introducers
Remove unused nextCipherKey
Debug: Prefer selecting SSU2 introducers
Log tweaks
---
 .../transport/udp/EstablishmentManager.java   |  2 +-
 .../transport/udp/IntroductionManager.java    | 63 ++++++++++++++-----
 .../i2p/router/transport/udp/PeerState.java   | 17 ++---
 .../router/transport/udp/UDPTransport.java    |  4 +-
 4 files changed, 61 insertions(+), 25 deletions(-)

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 0c00148b57..c2828110b6 100644
--- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
@@ -949,11 +949,11 @@ class EstablishmentManager {
             peer = new PeerState(_context, _transport,
                                  state.getSentIP(), state.getSentPort(), remote.calculateHash(), true, state.getRTT(),
                                  state.getCipherKey(), state.getMACKey());
-            peer.setWeRelayToThemAs(state.getSentRelayTag());
         } else {
             InboundEstablishState2 state2 = (InboundEstablishState2) state;
             peer = state2.getPeerState();
         }
+        peer.setWeRelayToThemAs(state.getSentRelayTag());
 
         if (version == 1) {
             // Lookup the peer's MTU from the netdb, since it isn't included in the protocol setup (yet)
diff --git a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
index d504b78e04..031a1bc0a4 100644
--- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
@@ -5,6 +5,7 @@ import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -110,6 +111,9 @@ class IntroductionManager {
     private static final long INTRODUCER_EXPIRATION = 80*60*1000L;
     private static final String MIN_IPV6_INTRODUCER_VERSION = "0.9.50";
     private static final long MAX_SKEW = 2*60*1000;
+    /** testing */
+    private static final String PROP_PREFER_SSU2 = "i2np.ssu2.preferSSU2Introducers";
+    private static final boolean DEFAULT_PREFER_SSU2 = SSU2Util.ENABLE_RELAY && true;
 
     public IntroductionManager(RouterContext ctx, UDPTransport transport) {
         _context = ctx;
@@ -150,8 +154,7 @@ class IntroductionManager {
             _inbound.put(Long.valueOf(id2), peer);
         }
         if (added &&_log.shouldLog(Log.DEBUG))
-            _log.debug("adding peer " + peer.getRemotePeer() + ' ' + peer.getRemoteHostId() + ", weRelayToThemAs "
-                       + id + ", theyRelayToUsAs " + id2);
+            _log.debug("adding peer " + peer);
     }
     
     public void remove(PeerState peer) {
@@ -164,8 +167,7 @@ class IntroductionManager {
             _inbound.remove(Long.valueOf(id2));
         }
         if ((id > 0 || id2 > 0) &&_log.shouldLog(Log.DEBUG))
-            _log.debug("removing peer " + peer.getRemotePeer() + ' ' + peer.getRemoteHostId() + ", weRelayToThemAs "
-                       + id + ", theyRelayToUsAs " + id2);
+            _log.debug("removing peer " + peer);
     }
     
     /**
@@ -200,13 +202,13 @@ class IntroductionManager {
      * @return number of introducers added
      */
     public int pickInbound(RouterAddress current, boolean ipv6, Properties ssuOptions, int howMany) {
-        int start = _context.random().nextInt();
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Picking inbound out of " + _inbound.size());
         if (_inbound.isEmpty()) return 0;
         List<PeerState> peers = new ArrayList<PeerState>(_inbound.values());
         int sz = peers.size();
-        start = start % sz;
+        boolean preferV2 = _context.getProperty(PROP_PREFER_SSU2, DEFAULT_PREFER_SSU2);
+        Collections.sort(peers, new PeerStateComparator(preferV2));
         int found = 0;
         long now = _context.clock().now();
         long inactivityCutoff = now - (UDPTransport.EXPIRE_TIMEOUT / 2);    // 15 min
@@ -238,7 +240,7 @@ class IntroductionManager {
         }
 
         for (int i = 0; i < sz && found < howMany; i++) {
-            PeerState cur = peers.get((start + i) % sz);
+            PeerState cur = peers.get(i);
             if (cur.isIPv6() != ipv6)
                 continue;
             RouterInfo ri = _context.netDb().lookupRouterInfoLocally(cur.getRemotePeer());
@@ -372,6 +374,36 @@ class IntroductionManager {
         return found;
     }
 
+    /**
+     *  For picking introducers.
+     *  Reverse sort, version 2 first, for testing
+     *  Then lowest uptime first, to reduce idle timeout and disconnect,
+     *  and ensure variety.
+     *
+     *  @since 0.9.55
+     */
+    private static class PeerStateComparator implements Comparator<PeerState> {
+        private final boolean _v2;
+
+        public PeerStateComparator(boolean preferV2) {
+            _v2 = preferV2;
+        }
+
+        public int compare(PeerState l, PeerState r) {
+            if (_v2) {
+                int rv = r.getVersion() - l.getVersion();
+                if (rv != 0)
+                    return rv;
+            }
+            long d = r.getKeyEstablishedTime() - l.getKeyEstablishedTime();
+            if (d < 0)
+                return -1;
+            if (d > 0)
+                return 1;
+            return 0;
+        }
+    }
+
     /**
      *  So we can sort them
      *  @since 0.9.18
@@ -443,13 +475,13 @@ class IntroductionManager {
         // Try to keep the connection up for two hours after we made anybody an introducer
         long now = _context.clock().now();
         long pingCutoff = now - (105 * 60 * 1000);
-        long inactivityCutoff = now - UDPTransport.MIN_EXPIRE_TIMEOUT;
+        long inactivityCutoff = now - (UDPTransport.MIN_EXPIRE_TIMEOUT / 2);
         for (PeerState cur : _inbound.values()) {
             if (cur.getIntroducerTime() > pingCutoff &&
-                cur.getLastSendTime() < inactivityCutoff) {
+                cur.getLastSendOrPingTime() < inactivityCutoff) {
                 if (_log.shouldLog(Log.INFO))
                     _log.info("Pinging introducer: " + cur);
-                cur.setLastSendTime(now);
+                cur.setLastPingTime(now);
                 UDPPacket ping;
                 if (cur.getVersion() == 2)
                     ping = _builder2.buildPing((PeerState2) cur);
@@ -770,8 +802,11 @@ class IntroductionManager {
         UDPPacket packet;
         if (rcode == SSU2Util.RELAY_ACCEPT) {
             // Send Alice RI and forward data in a Relay Intro to Charlie
-            if (_log.shouldDebug())
-                _log.debug("Send alice RI and relay intro to " + charlie);
+            if (_log.shouldInfo())
+                _log.info("Receive relay request from " + alice 
+                      + " for tag " + tag
+                      + " nonce " + nonce
+                      + " and relaying with " + charlie);
             DatabaseStoreMessage dbsm = new DatabaseStoreMessage(_context);
             dbsm.setEntry(aliceRI);
             dbsm.setMessageExpiration(now + 10*1000);
@@ -796,8 +831,8 @@ class IntroductionManager {
                     _log.warn("sig fail");
                  return;
             }
-            if (_log.shouldDebug())
-                _log.debug("Send relay response rejection " + rcode + " to " + alice);
+            if (_log.shouldInfo())
+                _log.info("Send relay response rejection " + rcode + " to " + alice);
             packet = _builder2.buildRelayResponse(data, alice);
         }
         _transport.send(packet);
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 01e43175d4..2cde0fd19d 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerState.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java
@@ -62,12 +62,6 @@ public class PeerState {
      * connection, or null if we are not in the process of rekeying.
      */
     private SessionKey _nextMACKey;
-    /**
-     * The pending AES key for encrypting/decrypting packets if we are
-     * rekeying the connection, or null if we are not in the process
-     * of rekeying.
-     */
-    private SessionKey _nextCipherKey;
 
     /** when were the current cipher and MAC keys established/rekeyed? */
     protected final long _keyEstablishedTime;
@@ -489,9 +483,12 @@ public class PeerState {
      *
      * @return null always, rekeying unimplemented
      */
-    SessionKey getNextCipherKey() { return _nextCipherKey; }
+    SessionKey getNextCipherKey() { return null; }
 
-    /** when were the current cipher and MAC keys established/rekeyed? */
+    /**
+     * When were the current cipher and MAC keys established/rekeyed?
+     * This is the connection uptime.
+     */
     public long getKeyEstablishedTime() { return _keyEstablishedTime; }
 
     /**
@@ -2402,6 +2399,10 @@ public class PeerState {
         buf.append(" IBM: ").append(_inboundMessages.size());
         buf.append(" OBQ: ").append(_outboundQueue.size());
         buf.append(" OBL: ").append(_outboundMessages.size());
+        if (_weRelayToThemAs > 0)
+            buf.append(" weRelayToThemAs: ").append(_weRelayToThemAs);
+        if (_theyRelayToUsAs > 0)
+            buf.append(" theyRelayToUsAs: ").append(_theyRelayToUsAs);
         return buf.toString();
     }
 }
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 5f3a3bbc8f..28ea0f56e4 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
@@ -2040,8 +2040,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
                 //        _log.warn((ipv6 ? "IPv6" : "IPv4") + " introducers valid, haven't changed in " + DataHelper.formatDuration(sinceSelected) + ", reselecting");
                 //    return true;
                 //} else {
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info((ipv6 ? "IPv6" : "IPv4") + " introducers valid, selected " + DataHelper.formatDuration(sinceSelected) + " ago");
+                    if (_log.shouldDebug())
+                        _log.debug((ipv6 ? "IPv6" : "IPv4") + " introducers valid, selected " + DataHelper.formatDuration(sinceSelected) + " ago");
                     return false;
                 //}
             } else if (sinceSelected > 2*60*1000) {
-- 
GitLab