* ProfileOrganizer, TunnelPoolSettings, ClientPeerSelector:
- Prevent peers with matching IPs from joining same tunnel. Match 0-4 bytes of IP (0=off, 1=most restrictive, 4=least). Default is 2 (disallow routers in same /16). Set with router.defaultPool.IPRestriction=x - Comment out unused RebuildPeriod pool setting - Add random key to pool in preparation for XOR peer ordering
This commit is contained in:
@ -17,7 +17,7 @@ import net.i2p.CoreVersion;
|
||||
public class RouterVersion {
|
||||
public final static String ID = "$Revision: 1.548 $ $Date: 2008-02-10 15:00:00 $";
|
||||
public final static String VERSION = "0.6.1.31";
|
||||
public final static long BUILD = 3202;
|
||||
public final static long BUILD = 3203;
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
||||
System.out.println("Router ID: " + RouterVersion.ID);
|
||||
|
@ -4,6 +4,7 @@ import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
/**
|
||||
* Wrap up the settings for a pool of tunnels (duh)
|
||||
@ -14,7 +15,7 @@ public class TunnelPoolSettings {
|
||||
private String _destinationNickname;
|
||||
private int _quantity;
|
||||
private int _backupQuantity;
|
||||
private int _rebuildPeriod;
|
||||
// private int _rebuildPeriod;
|
||||
private int _duration;
|
||||
private int _length;
|
||||
private int _lengthVariance;
|
||||
@ -22,7 +23,9 @@ public class TunnelPoolSettings {
|
||||
private boolean _isInbound;
|
||||
private boolean _isExploratory;
|
||||
private boolean _allowZeroHop;
|
||||
private int _IPRestriction;
|
||||
private Properties _unknownOptions;
|
||||
private Hash _randomKey;
|
||||
|
||||
/** prefix used to override the router's defaults for clients */
|
||||
public static final String PREFIX_DEFAULT = "router.defaultPool.";
|
||||
@ -34,24 +37,26 @@ public class TunnelPoolSettings {
|
||||
public static final String PROP_NICKNAME = "nickname";
|
||||
public static final String PROP_QUANTITY = "quantity";
|
||||
public static final String PROP_BACKUP_QUANTITY = "backupQuantity";
|
||||
public static final String PROP_REBUILD_PERIOD = "rebuildPeriod";
|
||||
// public static final String PROP_REBUILD_PERIOD = "rebuildPeriod";
|
||||
public static final String PROP_DURATION = "duration";
|
||||
public static final String PROP_LENGTH = "length";
|
||||
public static final String PROP_LENGTH_VARIANCE = "lengthVariance";
|
||||
public static final String PROP_ALLOW_ZERO_HOP = "allowZeroHop";
|
||||
public static final String PROP_IP_RESTRICTION = "IPRestriction";
|
||||
|
||||
public static final int DEFAULT_QUANTITY = 2;
|
||||
public static final int DEFAULT_BACKUP_QUANTITY = 0;
|
||||
public static final int DEFAULT_REBUILD_PERIOD = 60*1000;
|
||||
// public static final int DEFAULT_REBUILD_PERIOD = 60*1000;
|
||||
public static final int DEFAULT_DURATION = 10*60*1000;
|
||||
public static final int DEFAULT_LENGTH = 2;
|
||||
public static final int DEFAULT_LENGTH_VARIANCE = 1;
|
||||
public static final boolean DEFAULT_ALLOW_ZERO_HOP = true;
|
||||
public static final int DEFAULT_IP_RESTRICTION = 2; // class B (/16)
|
||||
|
||||
public TunnelPoolSettings() {
|
||||
_quantity = DEFAULT_QUANTITY;
|
||||
_backupQuantity = DEFAULT_BACKUP_QUANTITY;
|
||||
_rebuildPeriod = DEFAULT_REBUILD_PERIOD;
|
||||
// _rebuildPeriod = DEFAULT_REBUILD_PERIOD;
|
||||
_duration = DEFAULT_DURATION;
|
||||
_length = DEFAULT_LENGTH;
|
||||
_lengthVariance = DEFAULT_LENGTH_VARIANCE;
|
||||
@ -61,7 +66,9 @@ public class TunnelPoolSettings {
|
||||
_isExploratory = false;
|
||||
_destination = null;
|
||||
_destinationNickname = null;
|
||||
_IPRestriction = DEFAULT_IP_RESTRICTION;
|
||||
_unknownOptions = new Properties();
|
||||
_randomKey = generateRandomKey();
|
||||
}
|
||||
|
||||
/** how many tunnels should be available at all times */
|
||||
@ -73,8 +80,8 @@ public class TunnelPoolSettings {
|
||||
public void setBackupQuantity(int quantity) { _backupQuantity = quantity; }
|
||||
|
||||
/** how long before tunnel expiration should new tunnels be built */
|
||||
public int getRebuildPeriod() { return _rebuildPeriod; }
|
||||
public void setRebuildPeriod(int periodMs) { _rebuildPeriod = periodMs; }
|
||||
// public int getRebuildPeriod() { return _rebuildPeriod; }
|
||||
// public void setRebuildPeriod(int periodMs) { _rebuildPeriod = periodMs; }
|
||||
|
||||
/** how many remote hops should be in the tunnel */
|
||||
public int getLength() { return _length; }
|
||||
@ -112,10 +119,22 @@ public class TunnelPoolSettings {
|
||||
public Hash getDestination() { return _destination; }
|
||||
public void setDestination(Hash dest) { _destination = dest; }
|
||||
|
||||
/** random key used for peer ordering */
|
||||
public Hash getRandomKey() { return _randomKey; }
|
||||
|
||||
/** what user supplied name was given to the client connected (can be null) */
|
||||
public String getDestinationNickname() { return _destinationNickname; }
|
||||
public void setDestinationNickname(String name) { _destinationNickname = name; }
|
||||
|
||||
/**
|
||||
* How many bytes to match to determine if a router's IP is too close to another's
|
||||
* to be in the same tunnel
|
||||
* (1-4, 0 to disable)
|
||||
*
|
||||
*/
|
||||
public int getIPRestriction() { int r = _IPRestriction; if (r>4) r=4; else if (r<0) r=0; return r;}
|
||||
public void setIPRestriction(int b) { _IPRestriction = b; }
|
||||
|
||||
public Properties getUnknownOptions() { return _unknownOptions; }
|
||||
|
||||
public void readFromProperties(String prefix, Properties props) {
|
||||
@ -135,10 +154,12 @@ public class TunnelPoolSettings {
|
||||
_lengthVariance = getInt(value, DEFAULT_LENGTH_VARIANCE);
|
||||
else if (name.equalsIgnoreCase(prefix + PROP_QUANTITY))
|
||||
_quantity = getInt(value, DEFAULT_QUANTITY);
|
||||
else if (name.equalsIgnoreCase(prefix + PROP_REBUILD_PERIOD))
|
||||
_rebuildPeriod = getInt(value, DEFAULT_REBUILD_PERIOD);
|
||||
// else if (name.equalsIgnoreCase(prefix + PROP_REBUILD_PERIOD))
|
||||
// _rebuildPeriod = getInt(value, DEFAULT_REBUILD_PERIOD);
|
||||
else if (name.equalsIgnoreCase(prefix + PROP_NICKNAME))
|
||||
_destinationNickname = value;
|
||||
else if (name.equalsIgnoreCase(prefix + PROP_IP_RESTRICTION))
|
||||
_IPRestriction = getInt(value, DEFAULT_IP_RESTRICTION);
|
||||
else
|
||||
_unknownOptions.setProperty(name.substring((prefix != null ? prefix.length() : 0)), value);
|
||||
}
|
||||
@ -155,7 +176,8 @@ public class TunnelPoolSettings {
|
||||
if (_destinationNickname != null)
|
||||
props.setProperty(prefix + PROP_NICKNAME, ""+_destinationNickname);
|
||||
props.setProperty(prefix + PROP_QUANTITY, ""+_quantity);
|
||||
props.setProperty(prefix + PROP_REBUILD_PERIOD, ""+_rebuildPeriod);
|
||||
// props.setProperty(prefix + PROP_REBUILD_PERIOD, ""+_rebuildPeriod);
|
||||
props.setProperty(prefix + PROP_IP_RESTRICTION, ""+_IPRestriction);
|
||||
for (Iterator iter = _unknownOptions.keySet().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
String val = _unknownOptions.getProperty(name);
|
||||
@ -180,8 +202,12 @@ public class TunnelPoolSettings {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
////
|
||||
////
|
||||
// used for strict peer ordering
|
||||
private Hash generateRandomKey() {
|
||||
byte hash[] = new byte[Hash.HASH_LENGTH];
|
||||
RandomSource.getInstance().nextBytes(hash);
|
||||
return new Hash(hash);
|
||||
}
|
||||
|
||||
private static final boolean getBoolean(String str, boolean defaultValue) {
|
||||
if (str == null) return defaultValue;
|
||||
|
@ -3,6 +3,8 @@ package net.i2p.router.peermanager;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.ArrayList;
|
||||
@ -13,11 +15,13 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.NetworkDatabaseFacade;
|
||||
@ -229,13 +233,16 @@ public class ProfileOrganizer {
|
||||
*
|
||||
*/
|
||||
public void selectFastPeers(int howMany, Set exclude, Set matches) {
|
||||
selectFastPeers(howMany, exclude, matches, 0);
|
||||
}
|
||||
public void selectFastPeers(int howMany, Set exclude, Set matches, int mask) {
|
||||
synchronized (_reorganizeLock) {
|
||||
locked_selectPeers(_fastPeers, howMany, exclude, matches);
|
||||
locked_selectPeers(_fastPeers, howMany, exclude, matches, mask);
|
||||
}
|
||||
if (matches.size() < howMany) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("selectFastPeers("+howMany+"), not enough fast (" + matches.size() + ") going on to highCap");
|
||||
selectHighCapacityPeers(howMany, exclude, matches);
|
||||
selectHighCapacityPeers(howMany, exclude, matches, mask);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("selectFastPeers("+howMany+"), found enough fast (" + matches.size() + ")");
|
||||
@ -248,6 +255,9 @@ public class ProfileOrganizer {
|
||||
*
|
||||
*/
|
||||
public void selectHighCapacityPeers(int howMany, Set exclude, Set matches) {
|
||||
selectHighCapacityPeers(howMany, exclude, matches, 0);
|
||||
}
|
||||
public void selectHighCapacityPeers(int howMany, Set exclude, Set matches, int mask) {
|
||||
synchronized (_reorganizeLock) {
|
||||
// we only use selectHighCapacityPeers when we are selecting for PURPOSE_TEST
|
||||
// or we are falling back due to _fastPeers being too small, so we can always
|
||||
@ -258,12 +268,12 @@ public class ProfileOrganizer {
|
||||
else
|
||||
exclude.addAll(_fastPeers.keySet());
|
||||
*/
|
||||
locked_selectPeers(_highCapacityPeers, howMany, exclude, matches);
|
||||
locked_selectPeers(_highCapacityPeers, howMany, exclude, matches, mask);
|
||||
}
|
||||
if (matches.size() < howMany) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("selectHighCap("+howMany+"), not enough fast (" + matches.size() + ") going on to notFailing");
|
||||
selectNotFailingPeers(howMany, exclude, matches);
|
||||
selectNotFailingPeers(howMany, exclude, matches, mask);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("selectHighCap("+howMany+"), found enough highCap (" + matches.size() + ")");
|
||||
@ -275,13 +285,16 @@ public class ProfileOrganizer {
|
||||
*
|
||||
*/
|
||||
public void selectWellIntegratedPeers(int howMany, Set exclude, Set matches) {
|
||||
selectWellIntegratedPeers(howMany, exclude, matches, 0);
|
||||
}
|
||||
public void selectWellIntegratedPeers(int howMany, Set exclude, Set matches, int mask) {
|
||||
synchronized (_reorganizeLock) {
|
||||
locked_selectPeers(_wellIntegratedPeers, howMany, exclude, matches);
|
||||
locked_selectPeers(_wellIntegratedPeers, howMany, exclude, matches, mask);
|
||||
}
|
||||
if (matches.size() < howMany) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("selectWellIntegrated("+howMany+"), not enough integrated (" + matches.size() + ") going on to notFailing");
|
||||
selectNotFailingPeers(howMany, exclude, matches);
|
||||
selectNotFailingPeers(howMany, exclude, matches, mask);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("selectWellIntegrated("+howMany+"), found enough well integrated (" + matches.size() + ")");
|
||||
@ -295,7 +308,13 @@ public class ProfileOrganizer {
|
||||
*
|
||||
*/
|
||||
public void selectNotFailingPeers(int howMany, Set exclude, Set matches) {
|
||||
selectNotFailingPeers(howMany, exclude, matches, false);
|
||||
selectNotFailingPeers(howMany, exclude, matches, false, 0);
|
||||
}
|
||||
public void selectNotFailingPeers(int howMany, Set exclude, Set matches, int mask) {
|
||||
selectNotFailingPeers(howMany, exclude, matches, false, mask);
|
||||
}
|
||||
public void selectNotFailingPeers(int howMany, Set exclude, Set matches, boolean onlyNotFailing) {
|
||||
selectNotFailingPeers(howMany, exclude, matches, onlyNotFailing, 0);
|
||||
}
|
||||
/**
|
||||
* Return a set of Hashes for peers that are not failing, preferring ones that
|
||||
@ -306,9 +325,9 @@ public class ProfileOrganizer {
|
||||
* @param matches set to store the matches in
|
||||
* @param onlyNotFailing if true, don't include any high capacity peers
|
||||
*/
|
||||
public void selectNotFailingPeers(int howMany, Set exclude, Set matches, boolean onlyNotFailing) {
|
||||
public void selectNotFailingPeers(int howMany, Set exclude, Set matches, boolean onlyNotFailing, int mask) {
|
||||
if (matches.size() < howMany)
|
||||
selectAllNotFailingPeers(howMany, exclude, matches, onlyNotFailing);
|
||||
selectAllNotFailingPeers(howMany, exclude, matches, onlyNotFailing, mask);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
@ -347,7 +366,10 @@ public class ProfileOrganizer {
|
||||
* Return a set of Hashes for peers that are not failing.
|
||||
*
|
||||
*/
|
||||
private void selectAllNotFailingPeers(int howMany, Set exclude, Set matches, boolean onlyNotFailing) {
|
||||
public void selectAllNotFailingPeers(int howMany, Set exclude, Set matches, boolean onlyNotFailing) {
|
||||
selectAllNotFailingPeers(howMany, exclude, matches, onlyNotFailing, 0);
|
||||
}
|
||||
private void selectAllNotFailingPeers(int howMany, Set exclude, Set matches, boolean onlyNotFailing, int mask) {
|
||||
if (matches.size() < howMany) {
|
||||
int orig = matches.size();
|
||||
int needed = howMany - orig;
|
||||
@ -676,6 +698,7 @@ public class ProfileOrganizer {
|
||||
_log.info("Our average capacity is doing well [" + meanCapacity
|
||||
+ "], and includes " + numExceedingMean);
|
||||
_thresholdCapacityValue = meanCapacity;
|
||||
// else if mean > median && reordered.size > minHighCapacitiyPeers then threshold = reordered.get(minHighCapacity).getCapacityValue()
|
||||
} else if (reordered.size()/2 >= minHighCapacityPeers) {
|
||||
// ok mean is skewed low, but we still have enough to use the median
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@ -779,6 +802,9 @@ public class ProfileOrganizer {
|
||||
*
|
||||
*/
|
||||
private void locked_selectPeers(Map peers, int howMany, Set toExclude, Set matches) {
|
||||
locked_selectPeers(peers, howMany, toExclude, matches, 0);
|
||||
}
|
||||
private void locked_selectPeers(Map peers, int howMany, Set toExclude, Set matches, int mask) {
|
||||
List all = new ArrayList(peers.keySet());
|
||||
if (toExclude != null)
|
||||
all.removeAll(toExclude);
|
||||
@ -789,6 +815,11 @@ public class ProfileOrganizer {
|
||||
for (int i = 0; (matches.size() < howMany) && (i < all.size()); i++) {
|
||||
Hash peer = (Hash)all.get(i);
|
||||
boolean ok = isSelectable(peer);
|
||||
if (ok) {
|
||||
ok = mask <= 0 || notRestricted(peer, matches, mask);
|
||||
if ((!ok) && _log.shouldLog(Log.WARN))
|
||||
_log.warn("IP restriction prevents " + peer + " from joining " + matches);
|
||||
}
|
||||
if (ok)
|
||||
matches.add(peer);
|
||||
else
|
||||
@ -796,6 +827,82 @@ public class ProfileOrganizer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the peer's IP address NOT match the IP address of any peer already in the set,
|
||||
* on any transport, within a given mask?
|
||||
* mask is 1-4 (number of bytes to match) or 0 to disable
|
||||
* Perhaps rewrite this to just make a set of all the IP addresses rather than loop.
|
||||
*/
|
||||
private boolean notRestricted(Hash peer, Set matches, int mask) {
|
||||
if (mask <= 0) return true;
|
||||
if (matches.size() <= 0) return true;
|
||||
RouterInfo pinfo = _context.netDb().lookupRouterInfoLocally(peer);
|
||||
if (pinfo == null) return false;
|
||||
Set paddr = pinfo.getAddresses();
|
||||
if (paddr == null || paddr.size() == 0)
|
||||
return false;
|
||||
List pladdr = new ArrayList(paddr);
|
||||
List lmatches = new ArrayList(matches);
|
||||
// for each match
|
||||
for (int i = 0; i < matches.size(); i++) {
|
||||
RouterInfo minfo = _context.netDb().lookupRouterInfoLocally((Hash) lmatches.get(i));
|
||||
if (minfo == null) continue;
|
||||
Set maddr = minfo.getAddresses();
|
||||
if (maddr == null || maddr.size() == 0)
|
||||
continue;
|
||||
List mladdr = new ArrayList(maddr);
|
||||
String oldphost = null;
|
||||
// for each peer address
|
||||
for (int j = 0; j < paddr.size(); j++) {
|
||||
RouterAddress pa = (RouterAddress) pladdr.get(j);
|
||||
if (pa == null) continue;
|
||||
Properties pprops = pa.getOptions();
|
||||
if (pprops == null) continue;
|
||||
String phost = pprops.getProperty("host");
|
||||
if (phost == null) continue;
|
||||
if (oldphost != null && oldphost.equals(phost)) continue;
|
||||
oldphost = phost;
|
||||
InetAddress pi;
|
||||
try {
|
||||
pi = InetAddress.getByName(phost);
|
||||
} catch (UnknownHostException uhe) {
|
||||
continue;
|
||||
}
|
||||
if (pi == null) continue;
|
||||
byte[] pib = pi.getAddress();
|
||||
String oldmhost = null;
|
||||
// for each match address
|
||||
for (int k = 0; k < maddr.size(); k++) {
|
||||
RouterAddress ma = (RouterAddress) mladdr.get(k);
|
||||
if (ma == null) continue;
|
||||
Properties mprops = ma.getOptions();
|
||||
if (mprops == null) continue;
|
||||
String mhost = mprops.getProperty("host");
|
||||
if (mhost == null) continue;
|
||||
if (oldmhost != null && oldmhost.equals(mhost)) continue;
|
||||
oldmhost = mhost;
|
||||
InetAddress mi;
|
||||
try {
|
||||
mi = InetAddress.getByName(mhost);
|
||||
} catch (UnknownHostException uhe) {
|
||||
continue;
|
||||
}
|
||||
if (mi == null) continue;
|
||||
byte[] mib = mi.getAddress();
|
||||
// assume ipv4, compare 1 to 4 bytes
|
||||
// log.info("Comparing " + pi + " with " + mi);
|
||||
for (int m = 0; m < mask; m++) {
|
||||
if (pib[m] != mib[m])
|
||||
break;
|
||||
if (m == mask-1)
|
||||
return false; // IP match
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isSelectable(Hash peer) {
|
||||
NetworkDatabaseFacade netDb = _context.netDb();
|
||||
// the CLI shouldn't depend upon the netDb
|
||||
@ -807,7 +914,7 @@ public class ProfileOrganizer {
|
||||
return false; // never select a shitlisted peer
|
||||
}
|
||||
|
||||
RouterInfo info = netDb.lookupRouterInfoLocally(peer);
|
||||
RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer);
|
||||
if (null != info) {
|
||||
if (info.getIdentity().isHidden()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
|
@ -24,10 +24,11 @@ class ClientPeerSelector extends TunnelPeerSelector {
|
||||
}
|
||||
|
||||
Set exclude = getExclude(ctx, settings.isInbound(), settings.isExploratory());
|
||||
ctx.profileOrganizer().selectFastPeers(length, exclude, matches);
|
||||
ctx.profileOrganizer().selectFastPeers(length, exclude, matches, settings.getIPRestriction());
|
||||
|
||||
matches.remove(ctx.routerHash());
|
||||
ArrayList rv = new ArrayList(matches);
|
||||
// Todo - Rather than shuffle, sort using xor distance from settings.getRandomKey()
|
||||
Collections.shuffle(rv, ctx.random());
|
||||
if (settings.isInbound())
|
||||
rv.add(0, ctx.routerHash());
|
||||
|
Reference in New Issue
Block a user