diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java index 36d6943a4..6efe33003 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java @@ -105,7 +105,7 @@ class SybilRenderer { /** * A total score and a List of reason Strings */ - private static class Points implements Comparable { + public static class Points implements Comparable { private double points; private final List reasons; @@ -156,6 +156,24 @@ class SybilRenderer { } } + /** + * Merge points1 into points2. + * points1 is unmodified. + */ + private void mergePoints(Map points1, Map points2) { + for (Map.Entry e : points1.entrySet()) { + Hash h = e.getKey(); + Points p1 = e.getValue(); + Points p2 = points2.get(h); + if (p2 != null) { + p2.points += p1.points; + p2.reasons.addAll(p1.reasons); + } else { + points2.put(h, p1); + } + } + } + private void addPoints(Map points, Hash h, double d, String reason) { String rsn = "" + fmt.format(d) + ": " + reason; Points dd = points.get(h); @@ -168,15 +186,12 @@ class SybilRenderer { } /** - * The whole thing - * - * @param routerPrefix ignored + * All the floodfills, not including us + * @since 0.9.38 split out from renderRouterInfoHTML */ - private void renderRouterInfoHTML(Writer out, String routerPrefix) throws IOException { + private List getFloodfills(Hash us) { Set ffs = _context.peerManager().getPeersByCapability('f'); List ris = new ArrayList(ffs.size()); - Hash us = _context.routerHash(); - Hash ourRKey = _context.router().getRouterInfo().getRoutingKey(); for (Hash ff : ffs) { if (ff.equals(us)) continue; @@ -184,6 +199,32 @@ class SybilRenderer { if (ri != null) ris.add(ri); } + return ris; + } + + private double getAvgMinDist(List ris) { + double tot = 0; + int count = 200; + byte[] b = new byte[32]; + for (int i = 0; i < count; i++) { + _context.random().nextBytes(b); + Hash h = new Hash(b); + double d = closestDistance(h, ris); + tot += d; + } + double avgMinDist = tot / count; + return avgMinDist; + } + + /** + * The whole thing + * + * @param routerPrefix ignored + */ + private void renderRouterInfoHTML(Writer out, String routerPrefix) throws IOException { + Hash us = _context.routerHash(); + Hash ourRKey = _context.router().getRouterInfo().getRoutingKey(); + List ris = getFloodfills(us); if (ris.isEmpty()) { out.write("

No known floodfills

"); return; @@ -210,16 +251,7 @@ class SybilRenderer { renderRouterInfo(buf, _context.router().getRouterInfo(), null, true, false); buf.append("

Known Floodfills: ").append(ris.size()).append("

"); - double tot = 0; - int count = 200; - byte[] b = new byte[32]; - for (int i = 0; i < count; i++) { - _context.random().nextBytes(b); - Hash h = new Hash(b); - double d = closestDistance(h, ris); - tot += d; - } - double avgMinDist = tot / count; + double avgMinDist = getAvgMinDist(ris); buf.append("
\n" + "Average closest floodfill distance: ").append(fmt.format(avgMinDist)).append("
\n" + "Routing Data: \"").append(DataHelper.getUTF8(_context.routerKeyGenerator().getModData())) @@ -243,15 +275,15 @@ class SybilRenderer { // Distance to our router analysis buf.append("

Closest Floodfills to Our Routing Key (Where we Store our RI)

"); buf.append("

See all

"); - renderRouterInfoHTML(out, buf, ourRKey, avgMinDist, ris, points); + renderRouterInfoHTML(out, buf, ourRKey, "our rkey", avgMinDist, ris, points); RouterKeyGenerator rkgen = _context.routerKeyGenerator(); Hash nkey = rkgen.getNextRoutingKey(us); buf.append("

Closest Floodfills to Tomorrow's Routing Key (Where we will Store our RI)

"); buf.append("

See all

"); - renderRouterInfoHTML(out, buf, nkey, avgMinDist, ris, points); + renderRouterInfoHTML(out, buf, nkey, "our rkey (tomorrow)", avgMinDist, ris, points); buf.append("

Closest Floodfills to Our Router Hash (DHT Neighbors if we are Floodfill)

"); - renderRouterInfoHTML(out, buf, us, avgMinDist, ris, points); + renderRouterInfoHTML(out, buf, us, "our router", avgMinDist, ris, points); // Distance to our published destinations analysis buf.append("

Floodfills Close to Our Destinations

"); @@ -269,14 +301,14 @@ class SybilRenderer { continue; Hash rkey = ls.getRoutingKey(); TunnelPool in = clientInboundPools.get(client); - String name = (in != null) ? in.getSettings().getDestinationNickname() : client.toBase64().substring(0,4); - buf.append("

Closest floodfills to the Routing Key for " + DataHelper.escapeHTML(name) + " (where we store our LS)

"); + String name = (in != null) ? DataHelper.escapeHTML(in.getSettings().getDestinationNickname()) : client.toBase64().substring(0,4); + buf.append("

Closest floodfills to the Routing Key for " + name + " (where we store our LS)

"); buf.append("

See all

"); - renderRouterInfoHTML(out, buf, rkey, avgMinDist, ris, points); + renderRouterInfoHTML(out, buf, rkey, name, avgMinDist, ris, points); nkey = rkgen.getNextRoutingKey(ls.getHash()); - buf.append("

Closest floodfills to Tomorrow's Routing Key for " + DataHelper.escapeHTML(name) + " (where we will store our LS)

"); + buf.append("

Closest floodfills to Tomorrow's Routing Key for " + name + " (where we will store our LS)

"); buf.append("

See all

"); - renderRouterInfoHTML(out, buf, nkey, avgMinDist, ris, points); + renderRouterInfoHTML(out, buf, nkey, name + " (tomorrow)", avgMinDist, ris, points); } // Profile analysis @@ -311,6 +343,67 @@ class SybilRenderer { buf.setLength(0); } + /** + * Analyze threats. No output. + * Return separate maps for each cause instead? + * @since 0.9.38 + */ + public Map backgroundAnalysis() throws IOException { + Hash us = _context.routerHash(); + List ris = getFloodfills(us); + + double avgMinDist = getAvgMinDist(ris); + Map points = new HashMap(64); + + // IP analysis + renderIPGroupsFamily(null, null, ris, points); + renderIPGroupsUs(null, null, ris, points); + renderIPGroups32(null, null, ris, points); + renderIPGroups24(null, null, ris, points); + renderIPGroups16(null, null, ris, points); + + // Pairwise distance analysis + renderPairDistance(null, null, ris, points); + + // Distance to our router analysis + // closest to our routing key today + Hash ourRKey = _context.router().getRouterInfo().getRoutingKey(); + renderRouterInfoHTML(null, null, ourRKey, "our rkey", avgMinDist, ris, points); + // closest to our routing key tomorrow + RouterKeyGenerator rkgen = _context.routerKeyGenerator(); + Hash nkey = rkgen.getNextRoutingKey(us); + renderRouterInfoHTML(null, null, nkey, "our rkey (tomorrow)", avgMinDist, ris, points); + // closest to us + renderRouterInfoHTML(null, null, us, "our router", avgMinDist, ris, points); + + // Distance to our published destinations analysis + Map clientInboundPools = _context.tunnelManager().getInboundClientPools(); + List destinations = new ArrayList(clientInboundPools.keySet()); + for (Hash client : destinations) { + boolean isLocal = _context.clientManager().isLocal(client); + if (!isLocal) + continue; + if (! _context.clientManager().shouldPublishLeaseSet(client)) + continue; + LeaseSet ls = _context.netDb().lookupLeaseSetLocally(client); + if (ls == null) + continue; + Hash rkey = ls.getRoutingKey(); + TunnelPool in = clientInboundPools.get(client); + String name = (in != null) ? DataHelper.escapeHTML(in.getSettings().getDestinationNickname()) : client.toBase64().substring(0,4); + // closest to routing key today + renderRouterInfoHTML(null, null, rkey, name, avgMinDist, ris, points); + // closest to routing key tomorrow + nkey = rkgen.getNextRoutingKey(ls.getHash()); + renderRouterInfoHTML(null, null, nkey, name + " (tomorrow)", avgMinDist, ris, points); + } + + // Profile analysis + addProfilePoints(ris, points); + addVersionPoints(ris, points); + return points; + } + private static class Pair implements Comparable { public final RouterInfo r1, r2; public final BigInteger dist; @@ -380,7 +473,7 @@ class SybilRenderer { } } - private double closestDistance(Hash h, List ris) throws IOException { + private double closestDistance(Hash h, List ris) { BigInteger min = (new BigInteger("2")).pow(256); for (RouterInfo info : ris) { BigInteger dist = HashDistance.getDistance(h, info.getHash()); @@ -799,8 +892,9 @@ class SybilRenderer { /** * @param out null for background analysis * @param buf null for background analysis + * @param usName HTML escaped */ - private void renderRouterInfoHTML(Writer out, StringBuilder buf, Hash us, double avgMinDist, + private void renderRouterInfoHTML(Writer out, StringBuilder buf, Hash us, String usName, double avgMinDist, List ris, Map points) throws IOException { Collections.sort(ris, new RouterInfoRoutingKeyComparator(us)); double min = 256; @@ -843,7 +937,7 @@ class SybilRenderer { double point = MIN_CLOSE - dist; if (point > 0) { point *= OUR_KEY_FACTOR; - addPoints(points, ri.getHash(), point, "Very close (" + fmt.format(dist) + ") to our key " + us.toBase64()); + addPoints(points, ri.getHash(), point, "Very close (" + fmt.format(dist) + ") to our key " + usName + ": " + us.toBase64()); } if (i >= MAX - 1) break;