diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/ExpireRoutersJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/ExpireRoutersJob.java index 7b7d908aad..207d8896a2 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/ExpireRoutersJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/ExpireRoutersJob.java @@ -17,6 +17,7 @@ import net.i2p.data.router.RouterKeyGenerator; import net.i2p.router.CommSystemFacade.Status; import net.i2p.router.JobImpl; +import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.util.Log; import net.i2p.util.SystemVersion; @@ -75,6 +76,8 @@ private int expireKeys() { RouterKeyGenerator gen = getContext().routerKeyGenerator(); long now = getContext().clock().now(); long cutoff = now - 30*60*1000; + // for U routers + long ucutoff = now - 15*60*1000; boolean almostMidnight = gen.getTimeTillMidnight() < FloodfillNetworkDatabaseFacade.NEXT_RKEY_RI_ADVANCE_TIME - 30*60*1000; Hash us = getContext().routerHash(); boolean isFF = _facade.floodfillEnabled(); @@ -99,7 +102,9 @@ private int expireKeys() { continue; if (count > LIMIT_ROUTERS) { // aggressive drop strategy - if (e.getDate() < cutoff) { + long pub = e.getDate(); + if (pub < cutoff || + (pub < ucutoff && ((RouterInfo) e).getCapabilities().indexOf(Router.CAPABILITY_UNREACHABLE) >= 0)) { if (isFF) { // don't drop very close to us byte[] rkey = gen.getRoutingKey(key).getData(); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java index 5e3ac202dd..fd1f871ee5 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java @@ -183,7 +183,6 @@ List selectFloodfillParticipants(Hash key, int maxNumRouters, KBucketSet selectFloodfillParticipantsIncludingUs(Hash key, int howMany, int found = 0; long now = _context.clock().now(); - long installed = _context.getProperty("router.firstInstalled", 0L); - boolean enforceHeard = installed > 0 && (now - installed) > INSTALL_AGE; + boolean enforceHeard; double maxFailRate = 0.95; if (_context.router().getUptime() > 60*60*1000) { + enforceHeard = true; RateStat rs = _context.statManager().getRate("peer.failedLookupRate"); if (rs != null) { Rate r = rs.getRate(60*60*1000); @@ -233,6 +232,9 @@ private List selectFloodfillParticipantsIncludingUs(Hash key, int howMany, maxFailRate = Math.min(0.95d, Math.max(0.20d, 1.25d * currentFailRate)); } } + } else { + long down = _context.router().getEstimatedDowntime(); + enforceHeard = down > 0 && down < 30*60*60*1000L; } // 5 == FNDF.MAX_TO_FLOOD + 1 @@ -281,7 +283,9 @@ private List selectFloodfillParticipantsIncludingUs(Hash key, int howMany, maxGoodRespTime = 2 * tunnelTestTime.getAverageValue(); } if (prof != null) { - if (enforceHeard && prof.getFirstHeardAbout() > now - HEARD_AGE) { + if (enforceHeard && + (prof.getLastHeardFrom() <= 0 || + prof.getFirstHeardAbout() > now - HEARD_AGE)) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Bad (new): " + entry); badff.add(entry); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java index 7758c0fb85..eea7847f77 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java @@ -263,6 +263,10 @@ else if (_fromHash.equals(key)) // actually new int count = _facade.getDataStore().size(); if (count > LIMIT_ROUTERS) { + String caps = ri.getCapabilities(); + boolean isU = caps.indexOf(Router.CAPABILITY_UNREACHABLE) >= 0; + boolean isFF = caps.indexOf(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL) >= 0; + boolean notFrom = !key.equals(_fromHash); if (_facade.floodfillEnabled()) { // determine if they're "close enough" // we will still ack and flood by setting wasNew = true even if we don't store locally @@ -278,7 +282,11 @@ else if (_fromHash.equals(key)) if (until > FloodfillNetworkDatabaseFacade.NEXT_RKEY_RI_ADVANCE_TIME) { // appx. 90% max drop rate so even just-reseeded new routers will make it eventually int pdrop = Math.min(110, (128 * count / LIMIT_ROUTERS) - 128); - if (ri.getCapabilities().indexOf(Router.CAPABILITY_UNREACHABLE) >= 0) + if (isU) + pdrop *= 3; + if (isFF) + pdrop *= 3; + if (notFrom) pdrop *= 3; if (pdrop > 0 && (pdrop >= 128 || getContext().random().nextInt(128) < pdrop)) { if (_log.shouldWarn()) @@ -299,7 +307,11 @@ else if (_fromHash.equals(key)) ((rkey[1] ^ ourRKey[1]) & 0xff); if (distance >= 256) { int pdrop = Math.min(110, (128 * count / LIMIT_ROUTERS) - 128); - if (ri.getCapabilities().indexOf(Router.CAPABILITY_UNREACHABLE) >= 0) + if (isU) + pdrop *= 3; + if (isFF) + pdrop *= 3; + if (notFrom) pdrop *= 3; if (pdrop > 0 && (pdrop >= 128 || getContext().random().nextInt(128) < pdrop)) { if (_log.shouldWarn()) @@ -322,7 +334,11 @@ else if (_fromHash.equals(key)) // non-ff // up to 100% drop rate int pdrop = (128 * count / LIMIT_ROUTERS) - 128; - if (ri.getCapabilities().indexOf(Router.CAPABILITY_UNREACHABLE) >= 0) + if (isU) + pdrop *= 3; + if (isFF) + pdrop *= 3; + if (notFrom) pdrop *= 3; if (pdrop > 0 && (pdrop >= 128 || getContext().random().nextInt(128) < pdrop)) { if (_log.shouldWarn()) diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index a16ed22f60..eb22097b6b 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -1302,7 +1302,7 @@ String validate(RouterInfo routerInfo) throws IllegalArgumentException { if (existing >= MIN_REMAINING_ROUTERS) { if (_log.shouldLog(Log.INFO)) _log.info("Expired RI " + routerInfo.getIdentity().getHash(), new Exception()); - return "Peer expired " + DataHelper.formatDuration(age) + " ago"; + return "RI expired " + DataHelper.formatDuration(age) + " ago"; } else { if (_log.shouldLog(Log.WARN)) _log.warn("Even though the peer is old, we have only " + existing @@ -1313,35 +1313,42 @@ String validate(RouterInfo routerInfo) throws IllegalArgumentException { if (age < 0 - 2*Router.CLOCK_FUDGE_FACTOR) { String skewString = DataHelper.formatDuration(0 - age); if (_log.shouldLog(Log.INFO)) - _log.info("Peer " + routerInfo.getIdentity().getHash() + " published their routerInfo in the future?! [" + _log.info("RI " + routerInfo.getIdentity().getHash() + " published in the future?! [" + skewString + ']', new Exception()); if (upLongEnough && _context.commSystem().countActivePeers() >= 50) { // we can be fairly confident that his clock is in the future, // not that ours is in the past, so ban him for a while _context.banlist().banlistRouter(routerInfo.getHash(), "Excessive clock skew: {0}", skewString, null, now + 60*60*1000); } - return "Peer published " + skewString + " in the future?!"; + return "RI published " + skewString + " in the future?!"; } if (!routerInfo.isCurrent(ROUTER_INFO_EXPIRATION_INTRODUCED)) { if (routerInfo.getAddresses().isEmpty()) - return "Old peer with no addresses"; + return "Old RI with no addresses"; // This should cover the introducers case below too // And even better, catches the case where the router is unreachable but knows no introducers if (routerInfo.getCapabilities().indexOf(Router.CAPABILITY_UNREACHABLE) >= 0) - return "Old peer and thinks it is unreachable"; + return "Old RI and thinks it is unreachable"; // Just check all the addresses, faster than getting just the SSU ones for (RouterAddress ra : routerInfo.getAddresses()) { // Introducers change often, introducee will ping introducer for 2 hours if (ra.getOption("itag0") != null) - return "Old peer with SSU Introducers"; + return "Old RI with SSU Introducers"; } } if (upLongEnough && age > 2*24*60*60*1000l) { - return "Peer published " + DataHelper.formatDuration(age) + " ago"; + return "RI published " + DataHelper.formatDuration(age) + " ago"; } if (upLongEnough && !routerInfo.isCurrent(ROUTER_INFO_EXPIRATION_SHORT)) { if (routerInfo.getTargetAddresses("NTCP", "NTCP2").isEmpty()) - return "Peer published > 75m ago, SSU only without introducers"; + return "RI published > 75m ago, SSU only without introducers"; + } + for (RouterAddress ra : routerInfo.getTargetAddresses("NTCP2")) { + String i = ra.getOption("i"); + if (i != null && i.length() != 24) { + _context.banlist().banlistRouter(routerInfo.getIdentity().calculateHash(), "Bad address", null, null, now + 15*60*1000L); + return "Bad NTCP2 address"; + } } return null; } @@ -1395,7 +1402,7 @@ RouterInfo store(Hash key, RouterInfo routerInfo, boolean persist) throws Illega String err = validate(key, routerInfo); if (err != null) - throw new IllegalArgumentException("Invalid store attempt - " + err); + throw new IllegalArgumentException("Invalid store attempt of RI " + key.toBase64() + " - " + err); //if (_log.shouldLog(Log.DEBUG)) // _log.debug("RouterInfo " + key.toBase64() + " is stored with " diff --git a/router/java/src/net/i2p/router/sybil/Analysis.java b/router/java/src/net/i2p/router/sybil/Analysis.java index d25c4ceadc..edaa92d921 100644 --- a/router/java/src/net/i2p/router/sybil/Analysis.java +++ b/router/java/src/net/i2p/router/sybil/Analysis.java @@ -79,11 +79,11 @@ public class Analysis extends JobImpl implements RouterApp, Runnable { public static final int PAIRMAX = 20; public static final int MAX = 10; // multiplied by size - 1, will also get POINTS24 added - private static final double POINTS32 = 5.0; + private static final double POINTS32 = 0; //5.0; // multiplied by size - 1, will also get POINTS16 added - private static final double POINTS24 = 4.0; + private static final double POINTS24 = 0; //4.0; // multiplied by size - 1 - private static final double POINTS16 = 0.25; + private static final double POINTS16 = 0; //0.25; private static final double POINTS_US32 = 25.0; private static final double POINTS_US24 = 20.0; private static final double POINTS_US16 = 10.0; @@ -91,8 +91,8 @@ public class Analysis extends JobImpl implements RouterApp, Runnable { // IPv6 since 0.9.57, likely to be on top of IPv4, so make lower private static final double POINTS_V6_US64 = 12.5; private static final double POINTS_V6_US48 = 5.0; - private static final double POINTS64 = 2.0; - private static final double POINTS48 = 0.5; + private static final double POINTS64 = 0; //2.0; + private static final double POINTS48 = 0; //0.5; private static final double POINTS_FAMILY = -10.0; private static final double POINTS_FAMILY_VERIFIED = POINTS_FAMILY * 4; diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java index c27b451829..4c812803e4 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -395,6 +395,7 @@ protected void outboundMessageReady() { } catch (DataFormatException dfe) { if (_log.shouldWarn()) _log.warn("bad address? " + target, dfe); + _context.banlist().banlistRouter(ih, "Bad address", null, null, _context.clock().now() + 15*60*1000L); fail = true; } } else { @@ -410,7 +411,7 @@ protected void outboundMessageReady() { if (fail) { // race, RI changed out from under us, maybe SSU can handle it if (_log.shouldLog(Log.WARN)) - _log.warn("we bid on a peer who doesn't have an ntcp address? " + target); + _log.warn("we bid on a peer with a bad address? " + target); afterSend(msg, false); return; } 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 ff7410176d..90556119a2 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -44,6 +44,8 @@ import net.i2p.router.OutNetMessage; import net.i2p.router.Router; import net.i2p.router.RouterContext; +import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade; +import net.i2p.router.peermanager.PeerProfile; import net.i2p.router.transport.Transport; import static net.i2p.router.transport.Transport.AddressSource.*; import net.i2p.router.transport.TransportBid; @@ -2483,6 +2485,13 @@ public TransportBid bid(RouterInfo toAddress, int dataSize) { if (isUnreachable(to)) return null; + // temp, let NTCP2 deal with him (prop. 165) + if (toAddress.getCapabilities().indexOf(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL) >= 0) { + PeerProfile prof = _context.profileOrganizer().getProfileNonblocking(to); + if (prof == null || prof.getLastHeardFrom() <= 0) + return null; + } + // Validate his SSU address RouterAddress addr = getTargetAddress(toAddress); if (addr == null) { diff --git a/router/java/src/net/i2p/router/tunnel/OutboundTunnelEndpoint.java b/router/java/src/net/i2p/router/tunnel/OutboundTunnelEndpoint.java index b8e6f238a8..e468050a56 100644 --- a/router/java/src/net/i2p/router/tunnel/OutboundTunnelEndpoint.java +++ b/router/java/src/net/i2p/router/tunnel/OutboundTunnelEndpoint.java @@ -1,7 +1,9 @@ package net.i2p.router.tunnel; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.TunnelId; +import net.i2p.data.i2np.DatabaseStoreMessage; import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.TunnelDataMessage; import net.i2p.router.OutNetMessage; @@ -64,6 +66,26 @@ public void receiveComplete(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) { _log.warn("Dropping msg at OBEP with unsupported delivery instruction type LOCAL"); return; } + + int type = msg.getType(); + if (type == DatabaseStoreMessage.MESSAGE_TYPE) { + DatabaseStoreMessage dsm = (DatabaseStoreMessage) msg; + DatabaseEntry entry = dsm.getEntry(); + if (entry.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { + long now = _context.clock().now(); + long date = entry.getDate(); + if (date < now - 60*60*1000L) { + if (_log.shouldWarn()) + _log.warn("Dropping DSM of old RI at OBEP, direct? " + (toTunnel == null) + " to router: " + toRouter.toBase64() + " key: " + dsm.getKey().toBase64()); + return; + } else if (date > now + 2*60*1000L) { + if (_log.shouldWarn()) + _log.warn("Dropping DSM of future RI at OBEP, direct? " + (toTunnel == null) + " to router: " + toRouter.toBase64() + " key: " + dsm.getKey().toBase64()); + return; + } + } + } + if (_log.shouldLog(Log.DEBUG)) _log.debug("outbound tunnel " + _config + " received a full message: " + msg + " to be forwarded on to " @@ -73,7 +95,7 @@ public void receiveComplete(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) { // don't drop it if we are the target boolean toUs = _context.routerHash().equals(toRouter); if ((!toUs) && - _context.tunnelDispatcher().shouldDropParticipatingMessage(TunnelDispatcher.Location.OBEP, msg.getType(), size)) + _context.tunnelDispatcher().shouldDropParticipatingMessage(TunnelDispatcher.Location.OBEP, type, size)) return; // this overstates the stat somewhat, but ok for now //int kb = (size + 1023) / 1024;