From 7db6cac1f603d917a34a22e3a6af56b6a435855c Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 22 Apr 2024 19:41:58 +0000 Subject: [PATCH] Transport: SSU1 removal part 4/n --- .../transport/udp/IntroductionManager.java | 372 +----------------- 1 file changed, 21 insertions(+), 351 deletions(-) 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 4dae9fd65..97f298266 100644 --- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java +++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java @@ -85,7 +85,6 @@ class IntroductionManager { private final RouterContext _context; private final Log _log; private final UDPTransport _transport; - private final PacketBuilder _builder; private final PacketBuilder2 _builder2; /** map of relay tag to Charlie PeerState that should receive the introduction (we are Bob) */ private final Map _outbound; @@ -115,15 +114,11 @@ 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 = true; public IntroductionManager(RouterContext ctx, UDPTransport transport) { _context = ctx; _log = ctx.logManager().getLog(IntroductionManager.class); _transport = transport; - _builder = transport.getBuilder(); _builder2 = transport.getBuilder2(); _outbound = new ConcurrentHashMap(MAX_OUTBOUND); _inbound = new ConcurrentHashMap(MAX_INBOUND); @@ -150,9 +145,7 @@ class IntroductionManager { if (added) _outbound.put(Long.valueOf(id), peer); long id2 = peer.getTheyRelayToUsAs(); - //if (id2 > 0 && _inbound.size() < MAX_INBOUND) { - // test - if (id2 > 0 && (_inbound.size() < MAX_INBOUND || peer.getVersion() == 2)) { + if (id2 > 0 && _inbound.size() < MAX_INBOUND) { added = true; _inbound.put(Long.valueOf(id2), peer); } @@ -210,8 +203,7 @@ class IntroductionManager { if (_inbound.isEmpty()) return 0; List peers = new ArrayList(_inbound.values()); int sz = peers.size(); - boolean preferV2 = _builder2 != null && _context.getProperty(PROP_PREFER_SSU2, DEFAULT_PREFER_SSU2); - Collections.sort(peers, new PeerStateComparator(preferV2)); + Collections.sort(peers, new PeerStateComparator()); int found = 0; long now = _context.clock().now(); long inactivityCutoff = now - (UDPTransport.EXPIRE_TIMEOUT / 2); // 15 min @@ -221,8 +213,6 @@ class IntroductionManager { List introducers = new ArrayList(howMany); String exp = Long.toString((now + INTRODUCER_EXPIRATION) / 1000); - // try to keep a mix of v1 and v2 - int ssu1count = 0; int ssu2count = 0; // reuse old ones if ok if (current != null) { @@ -235,29 +225,10 @@ class IntroductionManager { if (!isInboundTagValid(tag)) continue; String sexp = Long.toString(ua.getIntroducerExpiration(i) / 1000); - Introducer intro; - byte[] key = ua.getIntroducerKey(i); - if (key != null) { - // SSU 1 - //// Replace SSU 1 with SSU 2 if available for slot 2 - //// leave slots 0 and 1 for SSU 1 - //// this will churn the SSU 1 introducers, oh well - if (preferV2 && ssu1count >= 2) - continue; - intro = new Introducer(ua.getIntroducerHost(i).getAddress(), - ua.getIntroducerPort(i), key, tag, sexp); - ssu1count++; - if (_log.shouldInfo()) - _log.info("Reusing introducer: " + ua.getIntroducerHost(i)); - } else { - // SSU 2 - if (_builder != null && ssu2count >= 2) - continue; - intro = new Introducer(ua.getIntroducerHash(i), tag, sexp); - ssu2count++; - if (_log.shouldInfo()) - _log.info("Reusing introducer: " + ua.getIntroducerHash(i)); - } + Introducer intro = new Introducer(ua.getIntroducerHash(i), tag, sexp); + ssu2count++; + if (_log.shouldInfo()) + _log.info("Reusing introducer: " + ua.getIntroducerHash(i)); introducers.add(intro); found++; } @@ -270,14 +241,10 @@ class IntroductionManager { continue; Hash hash = cur.getRemotePeer(); // dup check of reused SSU2 introducers - if (cur.getVersion() > 1) { - String b64 = hash.toBase64(); - for (Introducer intro : introducers) { - if (b64.equals(intro.shash)) - continue outerloop; - } - if (_builder != null && ssu2count >= 2) - continue; + String b64 = hash.toBase64(); + for (Introducer intro : introducers) { + if (b64.equals(intro.shash)) + continue outerloop; } RouterInfo ri = _context.netDb().lookupRouterInfoLocally(hash); if (ri == null) { @@ -318,7 +285,6 @@ class IntroductionManager { continue; } int oldFound = found; - loop: for (RouterAddress ra : ras) { byte[] ip = ra.getIP(); if (ip == null) @@ -327,11 +293,6 @@ class IntroductionManager { String host = ip.length == 4 ? ra.getHost() : Addresses.toString(ip); if (host == null) continue; - // dup check of reused introducers - for (Introducer intro : introducers) { - if (host.equals(intro.sip)) - continue loop; - } int port = ra.getPort(); if (!isValid(ip, port, true)) continue; @@ -343,23 +304,11 @@ class IntroductionManager { continue; } cur.setIntroducerTime(); - Introducer intro; - if (cur.getVersion() == 1) { - UDPAddress ura = new UDPAddress(ra); - byte[] ikey = ura.getIntroKey(); - if (ikey == null) - continue; - intro = new Introducer(ip, port, ikey, cur.getTheyRelayToUsAs(), exp); - ssu1count++; - } else { - intro = new Introducer(hash, cur.getTheyRelayToUsAs(), exp); - ssu2count++; - } + Introducer intro = new Introducer(hash, cur.getTheyRelayToUsAs(), exp); + ssu2count++; introducers.add(intro); found++; - // two per router max, one for SSU 2 - if (found - oldFound >= 2 || cur.getVersion() > 1) - break; + break; } if (oldFound != found && _log.shouldLog(Log.INFO)) _log.info("Picking introducer: " + cur); @@ -369,13 +318,7 @@ class IntroductionManager { Collections.sort(introducers); for (int i = 0; i < found; i++) { Introducer in = introducers.get(i); - if (in.version == 1) { - ssuOptions.setProperty(UDPAddress.PROP_INTRO_HOST_PREFIX + i, in.sip); - ssuOptions.setProperty(UDPAddress.PROP_INTRO_PORT_PREFIX + i, in.sport); - ssuOptions.setProperty(UDPAddress.PROP_INTRO_KEY_PREFIX + i, in.skey); - } else { - ssuOptions.setProperty(UDPAddress.PROP_INTRO_HASH_PREFIX + i, in.shash); - } + ssuOptions.setProperty(UDPAddress.PROP_INTRO_HASH_PREFIX + i, in.shash); ssuOptions.setProperty(UDPAddress.PROP_INTRO_TAG_PREFIX + i, in.stag); String sexp = in.sexp; // look for existing expiration in current published @@ -383,20 +326,10 @@ class IntroductionManager { if (current != null) { for (int j = 0; j < UDPTransport.PUBLIC_RELAY_COUNT; j++) { String oexp = null; - if (in.version == 1) { - if (in.sip.equals(current.getOption(UDPAddress.PROP_INTRO_HOST_PREFIX + j)) && - in.sport.equals(current.getOption(UDPAddress.PROP_INTRO_PORT_PREFIX + j)) && - in.skey.equals(current.getOption(UDPAddress.PROP_INTRO_KEY_PREFIX + j)) && - in.stag.equals(current.getOption(UDPAddress.PROP_INTRO_TAG_PREFIX + j))) { - // found old one - oexp = current.getOption(UDPAddress.PROP_INTRO_EXP_PREFIX + j); - } - } else { - if (in.shash.equals(current.getOption(UDPAddress.PROP_INTRO_HASH_PREFIX + j)) && - in.stag.equals(current.getOption(UDPAddress.PROP_INTRO_TAG_PREFIX + j))) { - // found old one - oexp = current.getOption(UDPAddress.PROP_INTRO_EXP_PREFIX + j); - } + if (in.shash.equals(current.getOption(UDPAddress.PROP_INTRO_HASH_PREFIX + j)) && + in.stag.equals(current.getOption(UDPAddress.PROP_INTRO_TAG_PREFIX + j))) { + // found old one + oexp = current.getOption(UDPAddress.PROP_INTRO_EXP_PREFIX + j); } if (oexp != null) { try { @@ -421,25 +354,14 @@ class IntroductionManager { /** * For picking introducers. - * Reverse sort, version 2 first, for testing - * Then lowest uptime first, to reduce idle timeout and disconnect, + * Lowest uptime first, to reduce idle timeout and disconnect, * and ensure variety. * * @since 0.9.55 */ private static class PeerStateComparator implements Comparator { - 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; @@ -454,21 +376,7 @@ class IntroductionManager { * @since 0.9.18 */ private static class Introducer implements Comparable { - public final String sip, sport, skey, stag, sexp, shash; - public final int version; - - /** - * SSU 1 - */ - public Introducer(byte[] ip, int port, byte[] key, long tag, String exp) { - sip = Addresses.toString(ip); - sport = String.valueOf(port); - skey = Base64.encode(key); - stag = String.valueOf(tag); - sexp = exp; - version = 1; - shash = null; - } + public final String stag, sexp, shash; /** * SSU 2 @@ -478,18 +386,10 @@ class IntroductionManager { stag = String.valueOf(tag); sexp = exp; shash = h.toBase64(); - version = 2; - sip = null; - sport = null; - skey = null; } @Override public int compareTo(Introducer i) { - // put SSU 2 at the end to not confuse SSU 1 - int diff = version - i.version; - if (diff != 0) - return diff; return stag.compareTo(i.stag); } @@ -530,10 +430,7 @@ class IntroductionManager { cur.setLastPingTime(now); UDPPacket ping; try { - if (cur.getVersion() == 2) - ping = _builder2.buildPing((PeerState2) cur); - else - ping = _builder.buildPing(cur); + ping = _builder2.buildPing((PeerState2) cur); _transport.send(ping); } catch (IOException ioe) { iter.remove(); @@ -569,233 +466,6 @@ class IntroductionManager { return _outbound.size(); } - /** - * We are Charlie and we got this from Bob. - * Send a HolePunch to Alice, who will soon be sending us a SessionRequest. - * We should already have a session with Bob, but probably not with Alice. - * - * If we don't have a session with Bob, we removed the relay tag from - * our _outbound table, so this won't work. - * - * We do some throttling here. - * - * SSU 1 only. - */ - void receiveRelayIntro(RemoteHostId bob, UDPPacketReader reader) { - if (_context.router().isHidden()) - return; - _context.statManager().addRateData("udp.receiveRelayIntro", 1); - - if (!_transport.allowConnection()) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Dropping RelayIntro, over conn limit"); - return; - } - - int ipSize = reader.getRelayIntroReader().readIPSize(); - byte ip[] = new byte[ipSize]; - reader.getRelayIntroReader().readIP(ip, 0); - int port = reader.getRelayIntroReader().readPort(); - - // allow IPv6 as of 0.9.50 - // validate alice IP/port here. We don't need to validate Bob, we have a session with him. - if (!isValid(ip, port, true)) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Invalid relay intro for alice " + Addresses.toString(ip, port) + " via bob " + bob); - _context.statManager().addRateData("udp.relayBadIP", 1); - return; - } - - if (_log.shouldDebug()) - _log.debug("Receive relay intro from " + bob + " for " + Addresses.toString(ip, port)); - - InetAddress to = null; - try { - to = InetAddress.getByAddress(ip); - } catch (UnknownHostException uhe) { - // banlist Bob? - if (_log.shouldLog(Log.WARN)) - _log.warn("IP for alice to hole punch to is invalid", uhe); - _context.statManager().addRateData("udp.relayBadIP", 1); - return; - } - - RemoteHostId alice = new RemoteHostId(ip, port); - if (_transport.getPeerState(alice) != null) { - if (_log.shouldLog(Log.INFO)) - _log.info("Ignoring RelayIntro, already have a session to " + to); - return; - } - EstablishmentManager establisher = _transport.getEstablisher(); - if (establisher != null) { - if (establisher.getInboundState(alice) != null) { - // This check may be common, as Alice sends RelayRequests to - // several introducers at once. - if (_log.shouldLog(Log.INFO)) - _log.info("Ignoring RelayIntro, establishment in progress to " + to); - return; - } - if (!establisher.shouldAllowInboundEstablishment()) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Dropping RelayIntro, too many establishments in progress - for " + to); - return; - } - } - - // basic throttle, don't bother saving per-peer send times - // we throttle on IP only, ignoring port - boolean tooMany = false; - boolean already = false; - synchronized (_recentHolePunches) { - long now = _context.clock().now(); - if (now > _lastHolePunchClean + PUNCH_CLEAN_TIME) { - _recentHolePunches.clear(); - _lastHolePunchClean = now; - _recentHolePunches.add(to); - } else { - tooMany = _recentHolePunches.size() >= MAX_PUNCHES; - if (!tooMany) - already = !_recentHolePunches.add(to); - } - } - if (tooMany) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Dropping - too many - RelayIntro for " + to); - return; - } - if (already) { - // This check will trigger a lot, as Alice sends RelayRequests to - // several introducers at once. - if (_log.shouldLog(Log.INFO)) - _log.info("Ignoring dup RelayIntro for " + to); - return; - } - - _transport.send(_builder.buildHolePunch(to, port)); - } - - /** - * We are Bob and we got this from Alice. - * Send a RelayIntro to Charlie and a RelayResponse to Alice. - * We should already have a session with Charlie, but not necessarily with Alice. - * - * SSU 1 only. - */ - void receiveRelayRequest(RemoteHostId alice, UDPPacketReader reader) { - if (_context.router().isHidden()) - return; - UDPPacketReader.RelayRequestReader rrReader = reader.getRelayRequestReader(); - long tag = rrReader.readTag(); - int ipSize = rrReader.readIPSize(); - int port = rrReader.readPort(); - - byte[] aliceIP = alice.getIP(); - int alicePort = alice.getPort(); - boolean ipIncluded = ipSize != 0; - // here we allow IPv6 - if (!isValid(aliceIP, alicePort, true)) { - // not necessarily invalid ip/port, could be blocklisted - if (_log.shouldWarn()) - _log.warn("Rejecting relay req from " + alice + " for " + Addresses.toString(aliceIP, alicePort)); - _context.statManager().addRateData("udp.relayBadIP", 1); - return; - } - // prior to 0.9.24 we rejected any non-zero-length ip - // here we reject anything different if it's the same size - // As of 0.9.50 we allow relay request over IPv6 - if (ipIncluded) { - byte ip[] = new byte[ipSize]; - rrReader.readIP(ip, 0); - if (ipSize == aliceIP.length && !Arrays.equals(aliceIP, ip)) { - if (_log.shouldWarn()) - _log.warn("Bad relay req from " + alice + " for " + Addresses.toString(ip, port)); - _context.statManager().addRateData("udp.relayBadIP", 1); - return; - } - aliceIP = ip; - } - // prior to 0.9.24 we rejected any nonzero port - // here we reject anything different - // As of 0.9.50 we allow it if the IP was included - if (port != 0) { - if (ipIncluded) { - alicePort = port; - } else if (port != alicePort) { - if (_log.shouldWarn()) - _log.warn("Bad relay req from " + alice + " for " + Addresses.toString(aliceIP, port)); - _context.statManager().addRateData("udp.relayBadIP", 1); - } - return; - } - // check again if IP was provided - // allow IPv6 as of 0.9.50 - RemoteHostId aliceRelayID; - if (ipIncluded) { - if (!isValid(aliceIP, alicePort, true)) { - if (_log.shouldWarn()) - _log.warn("Bad relay req from " + alice + " for " + Addresses.toString(aliceIP, alicePort)); - _context.statManager().addRateData("udp.relayBadIP", 1); - return; - } - aliceRelayID = new RemoteHostId(aliceIP, alicePort); - } else { - aliceRelayID = alice; - } - - PeerState charlie = get(tag); - if (charlie == null) { - if (_log.shouldDebug()) - _log.debug("Receive relay request from " + alice - + " with unknown tag " + tag); - _context.statManager().addRateData("udp.receiveRelayRequestBadTag", 1); - return; - } - if (charlie.getVersion() != 1) { - if (_log.shouldWarn()) - _log.warn("Receive SSU1 relay request from " + alice + " for SSU2 " + charlie); - return; - } - if (_log.shouldDebug()) - _log.debug("Receive relay request from " + alice - + " for tag " + tag - + " and relaying with " + charlie); - - // TODO throttle based on alice identity and/or intro tag? - - _context.statManager().addRateData("udp.receiveRelayRequest", 1); - - // send that peer an introduction for alice - _transport.send(_builder.buildRelayIntro(aliceRelayID, charlie, rrReader)); - long now = _context.clock().now(); - charlie.setLastSendTime(now); - - // send alice back charlie's info - // lookup session so we can use session key if available - SessionKey cipherKey = null; - SessionKey macKey = null; - PeerState aliceState = _transport.getPeerState(alice); - if (aliceState != null) { - // established session (since 0.9.12) - cipherKey = aliceState.getCurrentCipherKey(); - macKey = aliceState.getCurrentMACKey(); - } - if (cipherKey == null || macKey == null) { - // no session, use intro key (was only way before 0.9.12) - byte key[] = new byte[SessionKey.KEYSIZE_BYTES]; - reader.getRelayRequestReader().readAliceIntroKey(key, 0); - cipherKey = new SessionKey(key); - macKey = cipherKey; - if (_log.shouldDebug()) - _log.debug("Sending relay response (w/ intro key) to " + alice); - } else { - if (_log.shouldDebug()) - _log.debug("Sending relay response (in-session) to " + alice); - aliceState.setLastSendTime(now); - } - _transport.send(_builder.buildRelayResponse(alice, charlie, rrReader.readNonce(), - cipherKey, macKey)); - } - /** * We are Bob and we got this from Alice. * Send Alice's RI and a RelayIntro to Charlie, or reject with a RelayResponse to Alice.