예제 #1
0
  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);
    }
  }
예제 #2
0
 /**
  * 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();
 }
예제 #3
0
 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());
 }
예제 #4
0
  /**
   * 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);
    }
  }
예제 #5
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;
 }
예제 #6
0
 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");
 }
예제 #7
0
 public void coalesceStats() {
   if (_log.shouldLog(Log.DEBUG)) _log.debug("Coallescing stats");
   _failedLookupRate.coalesceStats();
   _invalidReplyRate.coalesceStats();
 }
예제 #8
0
 /** 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();
 }
예제 #9
0
 /** 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();
 }
예제 #10
0
 /**
  * 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();
 }
예제 #11
0
  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;
  }