From 4e174df7468bdd83d54c4eb5e4db9a25a73a3627 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 21 Dec 2022 07:00:38 -0500 Subject: [PATCH] Router: Add IPv6 analysis to Sybil checker Add to console sybil page Fix links for IPs close to us Header and reason tweaks Increase verified family credit --- .../i2p/router/web/helpers/SybilRenderer.java | 145 +++++++-- .../src/net/i2p/router/sybil/Analysis.java | 276 ++++++++++++++++-- 2 files changed, 367 insertions(+), 54 deletions(-) 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 8351a728f..d48a415f9 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 @@ -134,8 +134,8 @@ public class SybilRenderer { Hash us = _context.routerHash(); Analysis analysis = Analysis.getInstance(_context); List ris = null; - if (mode != 0 && mode < 12) { - if (mode >= 2 && mode <= 6) { + if (mode != 0 && (mode < 12 || mode == 17 || mode == 18)) { + if (mode >= 2 && (mode <= 6 || mode == 17 || mode == 18)) { // review all routers for family and IP analysis ris = analysis.getAllRouters(us); } else { @@ -159,8 +159,10 @@ public class SybilRenderer { "
  • Same Family" + "
  • IP close to us" + "
  • Same IP" + - "
  • Same /24" + - "
  • Same /16" + + "
  • Same IPv4 /24" + + "
  • Same IPv4 /16" + + "
  • Same IPv6 /64" + + "
  • Same IPv6 /48" + "
  • Pair distance" + "
  • Close to us" + "
  • Close to us tomorrow" + @@ -189,6 +191,10 @@ public class SybilRenderer { renderIP24Summary(out, buf, analysis, ris, points); } else if (mode == 6) { renderIP16Summary(out, buf, analysis, ris, points); + } else if (mode == 17) { + renderIP64Summary(out, buf, analysis, ris, points); + } else if (mode == 18) { + renderIP48Summary(out, buf, analysis, ris, points); } else if (mode == 7) { renderPairSummary(out, buf, analysis, ris, points); } else if (mode == 8) { @@ -385,8 +391,10 @@ public class SybilRenderer { List ri32 = new ArrayList(4); List ri24 = new ArrayList(4); List ri16 = new ArrayList(4); - analysis.calculateIPGroupsUs(ris, points, ri32, ri24, ri16); - renderIPGroupsUs(out, buf, ri32, ri24, ri16); + List ri64 = new ArrayList(4); + List ri48 = new ArrayList(4); + analysis.calculateIPGroupsUs(ris, points, ri32, ri24, ri16, ri64, ri48); + renderIPGroupsUs(out, buf, ri32, ri24, ri16, ri64, ri48); } /** @@ -413,6 +421,22 @@ public class SybilRenderer { renderIPGroups16(out, buf, map); } + /** + * @since 0.9.57 + */ + private void renderIP64Summary(Writer out, StringBuilder buf, Analysis analysis, List ris, Map points) throws IOException { + Map> map = analysis.calculateIPGroups64(ris, points); + renderIPGroups64(out, buf, map); + } + + /** + * @since 0.9.57 + */ + private void renderIP48Summary(Writer out, StringBuilder buf, Analysis analysis, List ris, Map points) throws IOException { + Map> map = analysis.calculateIPGroups48(ris, points); + renderIPGroups48(out, buf, map); + } + /** * @since 0.9.38 split out from renderRouterInfoHTML() */ @@ -565,16 +589,17 @@ public class SybilRenderer { writeBuf(out, buf); } - private static class FooComparator implements Comparator, Serializable { - private final Map> _o; - public FooComparator(Map> o) { _o = o;} - public int compare(Integer l, Integer r) { + private static class FooComparator implements Comparator, Serializable { + private final Map> _o; + public FooComparator(Map> o) { _o = o;} + @SuppressWarnings("unchecked") + public int compare(K l, K r) { // reverse by count int rv = _o.get(r).size() - _o.get(l).size(); if (rv != 0) return rv; - // foward by IP - return l.intValue() - r.intValue(); + // forward by IP + return l.compareTo(r); } } @@ -587,7 +612,7 @@ public class SybilRenderer { int rv = _o.get(r).size() - _o.get(l).size(); if (rv != 0) return rv; - // foward by name + // forward by name return _comp.compare(l, r); } } @@ -596,7 +621,8 @@ public class SybilRenderer { * */ private void renderIPGroupsUs(Writer out, StringBuilder buf, List ri32, - List ri24, List ri16) throws IOException { + List ri24, List ri16, + List ri64, List ri48) throws IOException { buf.append("

    Routers close to Our IP

    "); boolean found = false; for (RouterInfo info : ri32) { @@ -608,14 +634,28 @@ public class SybilRenderer { } for (RouterInfo info : ri24) { buf.append("

    "); - buf.append("Same /24 as us"); + buf.append("Same IPv4 /24 as us"); buf.append(":

    "); renderRouterInfo(buf, info, null, false, false); found = true; } for (RouterInfo info : ri16) { buf.append("

    "); - buf.append("Same /16 as us"); + buf.append("Same IPv4 /16 as us"); + buf.append(":

    "); + renderRouterInfo(buf, info, null, false, false); + found = true; + } + for (RouterInfo info : ri64) { + buf.append("

    "); + buf.append("Same IPv6 /64 as us"); + buf.append(":

    "); + renderRouterInfo(buf, info, null, false, false); + found = true; + } + for (RouterInfo info : ri48) { + buf.append("

    "); + buf.append("Same IPv6 /48 as us"); buf.append(":

    "); renderRouterInfo(buf, info, null, false, false); found = true; @@ -629,9 +669,9 @@ public class SybilRenderer { * */ private void renderIPGroups32(Writer out, StringBuilder buf, Map> map) throws IOException { - buf.append("

    Routers with the Same IP

    "); + buf.append("

    Routers with the same IPv4

    "); List foo = new ArrayList(map.keySet()); - Collections.sort(foo, new FooComparator(map)); + Collections.sort(foo, new FooComparator(map)); boolean found = false; for (Integer ii : foo) { List ris = map.get(ii); @@ -659,9 +699,9 @@ public class SybilRenderer { * */ private void renderIPGroups24(Writer out, StringBuilder buf, Map> map) throws IOException { - buf.append("

    Routers in the Same /24 (2 minimum)

    "); + buf.append("

    Routers in the same IPv4 /24

    "); List foo = new ArrayList(map.keySet()); - Collections.sort(foo, new FooComparator(map)); + Collections.sort(foo, new FooComparator(map)); boolean found = false; for (Integer ii : foo) { List ris = map.get(ii); @@ -688,9 +728,9 @@ public class SybilRenderer { * */ private void renderIPGroups16(Writer out, StringBuilder buf, Map> map) throws IOException { - buf.append("

    Routers in the Same /16 (4 minimum)

    "); + buf.append("

    Routers in the same IPv4 /16 (4 minimum)

    "); List foo = new ArrayList(map.keySet()); - Collections.sort(foo, new FooComparator(map)); + Collections.sort(foo, new FooComparator(map)); boolean found = false; for (Integer ii : foo) { List ris = map.get(ii); @@ -712,6 +752,67 @@ public class SybilRenderer { writeBuf(out, buf); } + /** + * @since 0.9.57 + */ + private void renderIPGroups64(Writer out, StringBuilder buf, Map> map) throws IOException { + buf.append("

    Routers in the same IPv6 /64

    "); + List foo = new ArrayList(map.keySet()); + Collections.sort(foo, new FooComparator(map)); + boolean found = false; + for (Long ii : foo) { + List ris = map.get(ii); + int count = ris.size(); + long i = ii.longValue(); + int i0 = (int) ((i >> 48) & 0xffff); + int i2 = (int) ((i >> 32) & 0xffff); + int i4 = (int) ((i >> 16) & 0xffff); + int i6 = (int) (i & 0xffff); + String sip = Integer.toString(i0, 16) + ":" + Integer.toString(i2, 16) + ':' + + Integer.toString(i4, 16) + ':' + Integer.toString(i6, 16) + "::"; + buf.append("

    ").append(count).append(" routers with IP ").append(sip) + .append("

    "); + for (RouterInfo info : ris) { + found = true; + renderRouterInfo(buf, info, null, false, false); + } + } + if (!found) + buf.append("

    None

    "); + writeBuf(out, buf); + } + + /** + * @since 0.9.57 + */ + private void renderIPGroups48(Writer out, StringBuilder buf, Map> map) throws IOException { + buf.append("

    Routers in the same IPv6 /48 (4 minimum)

    "); + List foo = new ArrayList(map.keySet()); + Collections.sort(foo, new FooComparator(map)); + boolean found = false; + for (Long ii : foo) { + List ris = map.get(ii); + int count = ris.size(); + long i = ii.longValue(); + int i0 = (int) ((i >> 32) & 0xffff); + int i2 = (int) ((i >> 16) & 0xffff); + int i4 = (int) (i & 0xffff); + String sip = Integer.toString(i0, 16) + ":" + Integer.toString(i2, 16) + ':' + + Integer.toString(i4, 16) + "::"; + buf.append("

    ").append(count).append(" routers with IP ").append(sip) + .append("

    "); + for (RouterInfo info : ris) { + found = true; + renderRouterInfo(buf, info, null, false, false); + } + } + if (!found) + buf.append("

    None

    "); + writeBuf(out, buf); + } + /** * */ diff --git a/router/java/src/net/i2p/router/sybil/Analysis.java b/router/java/src/net/i2p/router/sybil/Analysis.java index 9b92b428f..074eae207 100644 --- a/router/java/src/net/i2p/router/sybil/Analysis.java +++ b/router/java/src/net/i2p/router/sybil/Analysis.java @@ -85,8 +85,15 @@ public class Analysis extends JobImpl implements RouterApp { private static final double POINTS_US32 = 25.0; private static final double POINTS_US24 = 20.0; private static final double POINTS_US16 = 10.0; + + // 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 POINTS_FAMILY = -10.0; - private static final double POINTS_FAMILY_VERIFIED = POINTS_FAMILY * 2; + private static final double POINTS_FAMILY_VERIFIED = POINTS_FAMILY * 4; private static final double POINTS_NONFF = -5.0; private static final double POINTS_BAD_FAMILY = 20.0; private static final double POINTS_BAD_OUR_FAMILY = 100.0; @@ -374,13 +381,14 @@ public class Analysis extends JobImpl implements RouterApp { // IP analysis calculateIPGroupsFamily(ris, points); - List ri32 = new ArrayList(4); - List ri24 = new ArrayList(4); - List ri16 = new ArrayList(4); - calculateIPGroupsUs(ris, points, ri32, ri24, ri16); + // unused here, just for the console, so use the same for all of them + List dummy = new DummyList(); + calculateIPGroupsUs(ris, points, dummy, dummy, dummy, dummy, dummy); calculateIPGroups32(ris, points); calculateIPGroups24(ris, points); calculateIPGroups16(ris, points); + calculateIPGroups64(ris, points); + calculateIPGroups48(ris, points); // Pairwise distance analysis List pairs = new ArrayList(PAIRMAX); @@ -427,6 +435,15 @@ public class Analysis extends JobImpl implements RouterApp { return points; } + /** + * @since 0.9.57 + */ + private static class DummyList extends ArrayList { + public DummyList() { super(0); } + @Override + public boolean add(RouterInfo ri) { return true; } + } + /** * Blocklist and Banlist if configured * @since 0.9.41 @@ -545,46 +562,118 @@ public class Analysis extends JobImpl implements RouterApp { return null; } + /** + * v6 only + * @since 0.9.57 + */ + private static byte[] getIPv6(RouterInfo ri) { + for (RouterAddress ra : ri.getAddresses()) { + byte[] rv = ra.getIP(); + if (rv != null && rv.length == 16) + return rv; + } + return null; + } + /** * @param ri32 out parameter * @param ri24 out parameter * @param ri16 out parameter + * @param ri64 out parameter + * @param ri48 out parameter * @since 0.9.38 split out from renderIPGroupsUs() */ public void calculateIPGroupsUs(List ris, Map points, - List ri32, List ri24, List ri16) { + List ri32, List ri24, List ri16, + List ri64, List ri48) { RouterInfo us = _context.router().getRouterInfo(); byte[] ourIP = getIP(us); if (ourIP == null) { String last = _context.getProperty("i2np.lastIP"); - if (last == null) - return; - ourIP = Addresses.getIPOnly(last); - if (ourIP == null) - return; + if (last != null) + ourIP = Addresses.getIPOnly(last); + } + byte[] ourIPv6 = getIPv6(us); + if (ourIPv6 == null) { + String last = _context.getProperty("i2np.lastIPv6"); + if (last != null) + ourIPv6 = Addresses.getIPOnly(last); + } + if (ourIP == null && ourIPv6 == null) + return; + + String reason32; + String reason24; + String reason16; + String reason64; + String reason48; + + if (ourIP != null) { + reason32 = "Same IP as us"; + reason24 = "Same IPv4 /24 as us"; + reason16 = "Same IPv4 /16 as us"; + } else { + reason32 = null; + reason24 = null; + reason16 = null; + } + if (ourIPv6 != null) { + reason64 = "Same IPv6 /64 as us"; + reason48 = "Same IPv6 /48 as us"; + } else { + reason64 = null; + reason48 = null; } - String reason32 = "Same IP as us"; - String reason24 = "Same /24 IP as us"; - String reason16 = "Same /16 IP as us"; for (RouterInfo info : ris) { - byte[] ip = getIP(info); - if (ip == null) - continue; - if (ip[0] == ourIP[0] && ip[1] == ourIP[1]) { - if (ip[2] == ourIP[2]) { - if (ip[3] == ourIP[3]) { - addPoints(points, info.getHash(), POINTS_US32, reason32); - ri32.add(info); + if (ourIP != null) { + byte[] ip = getIP(info); + if (ip == null) + continue; + if (ip[0] == ourIP[0] && ip[1] == ourIP[1]) { + if (ip[2] == ourIP[2]) { + if (ip[3] == ourIP[3]) { + addPoints(points, info.getHash(), POINTS_US32, reason32); + ri32.add(info); + } else { + addPoints(points, info.getHash(), POINTS_US24, reason24); + ri24.add(info); + } } else { - addPoints(points, info.getHash(), POINTS_US24, reason24); - ri24.add(info); + addPoints(points, info.getHash(), POINTS_US16, reason16); + ri16.add(info); + } + } + } + if (ourIPv6 != null) { + byte[] ip = getIPv6(info); + if (ip == null) + continue; + if (DataHelper.eq(ip, 0, ourIPv6, 0, 6)) { + if (ip[6] == ourIPv6[6] && ip[7] == ourIPv6[7]) { + addPoints(points, info.getHash(), POINTS_V6_US64, reason64); + ri64.add(info); + } else { + addPoints(points, info.getHash(), POINTS_V6_US48, reason48); + ri48.add(info); } - } else { - addPoints(points, info.getHash(), POINTS_US16, reason16); - ri16.add(info); } } } @@ -666,7 +755,7 @@ public class Analysis extends JobImpl implements RouterApp { int i0 = i >> 16; int i1 = (i >> 8) & 0xff; int i2 = i & 0xff; - String reason = "Same /24 IP with " + (count - 1) + " other" + (( count > 2) ? "s" : "") + ""; for (RouterInfo info : ris) { @@ -717,7 +806,7 @@ public class Analysis extends JobImpl implements RouterApp { int i = ii.intValue(); int i0 = i >> 8; int i1 = i & 0xff; - String reason = "Same /16 IP with " + (count - 1) + " other" + (( count > 2) ? "s" : "") + ""; for (RouterInfo info : ris) { @@ -735,6 +824,129 @@ public class Analysis extends JobImpl implements RouterApp { return rv; } + /** + * @since 0.9.57 + */ + public Map> calculateIPGroups64(List ris, Map points) { + ObjectCounter oc = new ObjectCounter(); + for (RouterInfo info : ris) { + byte[] ip = getIPv6(info); + if (ip == null) + continue; + Long x = Long.valueOf(DataHelper.fromLong8(ip, 0)); + oc.increment(x); + } + Map> rv = new HashMap>(); + for (Long ii : oc.objects()) { + int count = oc.count(ii); + if (count >= 2) + rv.put(ii, new ArrayList(count)); + } + for (Map.Entry> e : rv.entrySet()) { + Long ii = e.getKey(); + int count = oc.count(ii); + double point = POINTS64 * (count - 1); + long i = ii.longValue(); + int i0 = (int) ((i >> 56) & 0xff); + int i1 = (int) ((i >> 48) & 0xff); + int i2 = (int) ((i >> 40) & 0xff); + int i3 = (int) ((i >> 32) & 0xff); + int i4 = (int) ((i >> 24) & 0xff); + int i5 = (int) ((i >> 16) & 0xff); + int i6 = (int) ((i >> 8) & 0xff); + int i7 = (int) (i & 0xff); + String reason = "Same IPv6 /64 with " + + (count - 1) + " other" + (( count > 2) ? "s" : "") + ""; + for (RouterInfo info : ris) { + byte[] ip = getIPv6(info); + if (ip == null) + continue; + if ((ip[0] & 0xff) != i0) + continue; + if ((ip[1] & 0xff) != i1) + continue; + if ((ip[2] & 0xff) != i2) + continue; + if ((ip[3] & 0xff) != i3) + continue; + if ((ip[4] & 0xff) != i4) + continue; + if ((ip[5] & 0xff) != i5) + continue; + if ((ip[6] & 0xff) != i6) + continue; + if ((ip[7] & 0xff) != i7) + continue; + e.getValue().add(info); + addPoints(points, info.getHash(), point, reason); + } + } + return rv; + } + + /** + * @since 0.9.57 + */ + public Map> calculateIPGroups48(List ris, Map points) { + ObjectCounter oc = new ObjectCounter(); + for (RouterInfo info : ris) { + byte[] ip = getIPv6(info); + if (ip == null) + continue; + Long x = Long.valueOf(DataHelper.fromLong(ip, 0, 6)); + oc.increment(x); + } + Map> rv = new HashMap>(); + for (Long ii : oc.objects()) { + int count = oc.count(ii); + if (count >= 4) + rv.put(ii, new ArrayList(count)); + } + for (Map.Entry> e : rv.entrySet()) { + Long ii = e.getKey(); + int count = oc.count(ii); + double point = POINTS48 * (count - 1); + long i = ii.longValue(); + int i0 = (int) ((i >> 40) & 0xff); + int i1 = (int) ((i >> 32) & 0xff); + int i2 = (int) ((i >> 24) & 0xff); + int i3 = (int) ((i >> 16) & 0xff); + int i4 = (int) ((i >> 8) & 0xff); + int i5 = (int) (i & 0xff); + String reason = "Same IPv6 /48 with " + + (count - 1) + " other" + (( count > 2) ? "s" : "") + ""; + for (RouterInfo info : ris) { + byte[] ip = getIPv6(info); + if (ip == null) + continue; + if ((ip[0] & 0xff) != i0) + continue; + if ((ip[1] & 0xff) != i1) + continue; + if ((ip[2] & 0xff) != i2) + continue; + if ((ip[3] & 0xff) != i3) + continue; + if ((ip[4] & 0xff) != i4) + continue; + if ((ip[5] & 0xff) != i5) + continue; + e.getValue().add(info); + addPoints(points, info.getHash(), point, reason); + } + } + return rv; + } + /** * @return map of family name to list of routers in that family * @since 0.9.38 split out from renderIPGroupsFamily()