public void load(Properties props) { _successfulLookups = getLong(props, "dbHistory.successfulLookups"); _failedLookups = getLong(props, "dbHistory.failedLookups"); _lookupsReceived = getLong(props, "dbHistory.lookupsReceived"); _lookupReplyDuplicate = getLong(props, "dbHistory.lookupReplyDuplicate"); _lookupReplyInvalid = getLong(props, "dbHistory.lookupReplyInvalid"); _lookupReplyNew = getLong(props, "dbHistory.lookupReplyNew"); _lookupReplyOld = getLong(props, "dbHistory.lookupReplyOld"); _unpromptedDbStoreNew = getLong(props, "dbHistory.unpromptedDbStoreNew"); _unpromptedDbStoreOld = getLong(props, "dbHistory.unpromptedDbStoreOld"); _lastLookupReceived = getLong(props, "dbHistory.lastLookupReceived"); _avgDelayBetweenLookupsReceived = getLong(props, "dbHistory.avgDelayBetweenLookupsReceived"); try { _failedLookupRate.load(props, "dbHistory.failedLookupRate", true); _log.debug("Loading dbHistory.failedLookupRate"); } catch (IllegalArgumentException iae) { _log.warn("DB History failed lookup rate is corrupt, resetting", iae); } try { _invalidReplyRate.load(props, "dbHistory.invalidReplyRate", true); } catch (IllegalArgumentException iae) { _log.warn("DB History invalid reply rate is corrupt, resetting", iae); createRates(_statGroup); } }
/** * Note that we successfully stored to a floodfill peer and verified the result by asking another * floodfill peer */ public void storeSuccessful() { // Fixme, redefined this to include both lookup and store fails, // need to fix the javadocs _failedLookupRate.addData(0, 0); _context.statManager().addRateData("peer.failedLookupRate", 0, 0); _lastStoreSuccessful = _context.clock().now(); }
private void createRates(String statGroup) { if (_failedLookupRate == null) _failedLookupRate = new RateStat( "dbHistory.failedLookupRate", "How often does this peer to respond to a lookup?", statGroup, new long[] {10 * 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l}); if (_invalidReplyRate == null) _invalidReplyRate = new RateStat( "dbHistory.invalidReplyRate", "How often does this peer give us a bad (nonexistant, forged, etc) peer?", statGroup, new long[] {30 * 60 * 1000l}); _failedLookupRate.setStatLog(_context.statManager().getStatLog()); _invalidReplyRate.setStatLog(_context.statManager().getStatLog()); }
/** * Receive a lookup reply from the peer, where they gave us the specified info * * @param newPeers number of peers we have never seen before * @param oldPeers number of peers we have seen before * @param invalid number of peers that are invalid / out of date / otherwise b0rked * @param duplicate number of peers we asked them not to give us (though they're allowed to send * us themselves if they don't know anyone else) */ public void lookupReply(int newPeers, int oldPeers, int invalid, int duplicate) { _lookupReplyNew += newPeers; _lookupReplyOld += oldPeers; _lookupReplyInvalid += invalid; _lookupReplyDuplicate += duplicate; if (invalid > 0) { _invalidReplyRate.addData(invalid, 0); } }
/** * move the cursor to the next known stat, returning true if a valid * stat is available. * * @return true if a valid stat is available, otherwise false */ public boolean hasMoreStats() { if (_stats.isEmpty()) return false; _currentIsGraphed = false; _currentStatName = (String)_stats.remove(0); RateStat rs = _context.statManager().getRate(_currentStatName); if (rs != null) { _currentStatDescription = rs.getDescription(); if (_currentGroup == null) _currentIsFirstInGroup = true; else if (!rs.getGroupName().equals(_currentGroup)) _currentIsFirstInGroup = true; else _currentIsFirstInGroup = false; _currentGroup = rs.getGroupName(); long period = rs.getPeriods()[0]; // should be the minimum if (period <= 10*60*1000) { Rate r = rs.getRate(period); _currentCanBeGraphed = r != null; if (_currentCanBeGraphed) { // see above //_currentIsGraphed = r.getSummaryListener() != null; _currentGraphName = _currentStatName + "." + period; _currentIsGraphed = _graphs.contains(_currentGraphName); } } else { _currentCanBeGraphed = false; } } else { FrequencyStat fs = _context.statManager().getFrequency(_currentStatName); if (fs != null) { _currentStatDescription = fs.getDescription(); if (_currentGroup == null) _currentIsFirstInGroup = true; else if (!fs.getGroupName().equals(_currentGroup)) _currentIsFirstInGroup = true; else _currentIsFirstInGroup = false; _currentGroup = fs.getGroupName(); _currentCanBeGraphed = false; } else { if (_log.shouldLog(Log.ERROR)) _log.error("Stat does not exist?! [" + _currentStatName + "]"); return false; } } if (_filters.contains("*") || _filters.contains(_currentStatName)) _currentIsLogged = true; else _currentIsLogged = false; return true; }
public void store(OutputStream out) throws IOException { StringBuilder buf = new StringBuilder(512); buf.append(NL); buf.append("#################").append(NL); buf.append("# DB history").append(NL); buf.append("###").append(NL); add( buf, "successfulLookups", _successfulLookups, "How many times have they successfully given us what we wanted when looking for it?"); add( buf, "failedLookups", _failedLookups, "How many times have we sent them a db lookup and they didn't reply?"); add(buf, "lookupsReceived", _lookupsReceived, "How many lookups have they sent us?"); add( buf, "lookupReplyDuplicate", _lookupReplyDuplicate, "How many of their reply values to our lookups were something we asked them not to send us?"); add( buf, "lookupReplyInvalid", _lookupReplyInvalid, "How many of their reply values to our lookups were invalid (expired, forged, corrupted)?"); add( buf, "lookupReplyNew", _lookupReplyNew, "How many of their reply values to our lookups were brand new to us?"); add( buf, "lookupReplyOld", _lookupReplyOld, "How many of their reply values to our lookups were something we had seen before?"); add( buf, "unpromptedDbStoreNew", _unpromptedDbStoreNew, "How times have they sent us something we didn't ask for and hadn't seen before?"); add( buf, "unpromptedDbStoreOld", _unpromptedDbStoreOld, "How times have they sent us something we didn't ask for but have seen before?"); add( buf, "lastLookupReceived", _lastLookupReceived, "When was the last time they send us a lookup? (milliseconds since the epoch)"); add( buf, "avgDelayBetweenLookupsReceived", _avgDelayBetweenLookupsReceived, "How long is it typically between each db lookup they send us? (in milliseconds)"); out.write(buf.toString().getBytes()); _failedLookupRate.store(out, "dbHistory.failedLookupRate"); _invalidReplyRate.store(out, "dbHistory.invalidReplyRate"); }
public void coalesceStats() { if (_log.shouldLog(Log.DEBUG)) _log.debug("Coallescing stats"); _failedLookupRate.coalesceStats(); _invalidReplyRate.coalesceStats(); }
/** Note that floodfill verify failed */ public void storeFailed() { // Fixme, redefined this to include both lookup and store fails, // need to fix the javadocs _failedLookupRate.addData(1, 0); _lastStoreFailed = _context.clock().now(); }
/** Note that the peer failed to respond to the db lookup in any way */ public void lookupFailed() { _failedLookups++; _failedLookupRate.addData(1, 0); _context.statManager().addRateData("peer.failedLookupRate", 1, 0); _lastLookupFailed = _context.clock().now(); }
/** * Note that the peer was not only able to respond to the lookup, but sent us the data we wanted! */ public void lookupSuccessful() { _successfulLookups++; _failedLookupRate.addData(0, 0); _context.statManager().addRateData("peer.failedLookupRate", 0, 0); _lastLookupSuccessful = _context.clock().now(); }
private boolean shouldBeFloodfill() { if (!SigType.ECDSA_SHA256_P256.isAvailable()) return false; // Hidden trumps netDb.floodfillParticipant=true if (getContext().router().isHidden()) return false; String enabled = getContext().getProperty(PROP_FLOODFILL_PARTICIPANT, "auto"); if ("true".equals(enabled)) return true; if ("false".equals(enabled)) return false; // auto from here down // Only if not shutting down... if (getContext().router().gracefulShutdownInProgress()) return false; // ARM ElG decrypt is too slow if (SystemVersion.isARM() || SystemVersion.isAndroid()) return false; if (getContext().getBooleanProperty(UDPTransport.PROP_LAPTOP_MODE)) return false; if (getContext().commSystem().isInBadCountry()) return false; String country = getContext().commSystem().getOurCountry(); // anonymous proxy, satellite provider (not in bad country list) if ("a1".equals(country) || "a2".equals(country)) return false; // Only if up a while... if (getContext().router().getUptime() < MIN_UPTIME) return false; RouterInfo ri = getContext().router().getRouterInfo(); if (ri == null) return false; char bw = ri.getBandwidthTier().charAt(0); // Only if class M, N, O, P, X if (bw != Router.CAPABILITY_BW64 && bw != Router.CAPABILITY_BW128 && bw != Router.CAPABILITY_BW256 && bw != Router.CAPABILITY_BW512 && bw != Router.CAPABILITY_BW_UNLIMITED) return false; // This list will not include ourselves... List<Hash> floodfillPeers = _facade.getFloodfillPeers(); long now = getContext().clock().now(); // We know none at all! Must be our turn... if (floodfillPeers == null || floodfillPeers.isEmpty()) { _lastChanged = now; return true; } // Only change status every so often boolean wasFF = _facade.floodfillEnabled(); if (_lastChanged + MIN_CHANGE_DELAY > now) return wasFF; // This is similar to the qualification we do in FloodOnlySearchJob.runJob(). // Count the "good" ff peers. // // Who's not good? // the unheard-from, unprofiled, failing, unreachable and banlisted ones. // We should hear from floodfills pretty frequently so set a 60m time limit. // If unprofiled we haven't talked to them in a long time. // We aren't contacting the peer directly, so banlist doesn't strictly matter, // but it's a bad sign, and we often banlist a peer before we fail it... // // Future: use Integration calculation // int ffcount = floodfillPeers.size(); int failcount = 0; long before = now - 60 * 60 * 1000; for (Hash peer : floodfillPeers) { PeerProfile profile = getContext().profileOrganizer().getProfile(peer); if (profile == null || profile.getLastHeardFrom() < before || profile.getIsFailing() || getContext().banlist().isBanlisted(peer) || getContext().commSystem().wasUnreachable(peer)) failcount++; } if (wasFF) ffcount++; int good = ffcount - failcount; boolean happy = getContext().router().getRouterInfo().getCapabilities().indexOf('R') >= 0; // TODO - limit may still be too high // For reference, the avg lifetime job lag on my Pi is 6. // Should we consider avg. dropped ff jobs? RateStat lagStat = getContext().statManager().getRate("jobQueue.jobLag"); RateStat queueStat = getContext().statManager().getRate("router.tunnelBacklog"); happy = happy && lagStat.getRate(60 * 60 * 1000L).getAvgOrLifetimeAvg() < 25; happy = happy && queueStat.getRate(60 * 60 * 1000L).getAvgOrLifetimeAvg() < 5; // Only if we're pretty well integrated... happy = happy && _facade.getKnownRouters() >= 400; happy = happy && getContext().commSystem().countActivePeers() >= 50; happy = happy && getContext().tunnelManager().getParticipatingCount() >= 25; happy = happy && Math.abs(getContext().clock().getOffset()) < 10 * 1000; // We need an address and no introducers if (happy) { RouterAddress ra = getContext().router().getRouterInfo().getTargetAddress("SSU"); if (ra == null) happy = false; else { if (ra.getOption("ihost0") != null) happy = false; } } double elG = 0; RateStat stat = getContext().statManager().getRate("crypto.elGamal.decrypt"); if (stat != null) { Rate rate = stat.getRate(60 * 60 * 1000L); if (rate != null) { elG = rate.getAvgOrLifetimeAvg(); happy = happy && elG <= 40.0d; } } if (_log.shouldLog(Log.DEBUG)) { final RouterContext rc = getContext(); final String log = String.format( "FF criteria breakdown: happy=%b, capabilities=%s, maxLag=%d, known=%d, " + "active=%d, participating=%d, offset=%d, ssuAddr=%s ElG=%f", happy, rc.router().getRouterInfo().getCapabilities(), rc.jobQueue().getMaxLag(), _facade.getKnownRouters(), rc.commSystem().countActivePeers(), rc.tunnelManager().getParticipatingCount(), Math.abs(rc.clock().getOffset()), rc.router().getRouterInfo().getTargetAddress("SSU").toString(), elG); _log.debug(log); } // Too few, and we're reachable, let's volunteer if (good < MIN_FF && happy) { if (!wasFF) { _lastChanged = now; _log.logAlways( Log.INFO, "Only " + good + " ff peers and we want " + MIN_FF + " so we are becoming floodfill"); } return true; } // Too many, or we aren't reachable, let's stop if (good > MAX_FF || (good > MIN_FF && !happy)) { if (wasFF) { _lastChanged = now; _log.logAlways( Log.INFO, "Have " + good + " ff peers and we need only " + MIN_FF + " to " + MAX_FF + " so we are disabling floodfill; reachable? " + happy); } return false; } if (_log.shouldLog(Log.INFO)) _log.info( "Have " + good + " ff peers, not changing, enabled? " + wasFF + "; reachable? " + happy); return wasFF; }