forked from I2P_Developers/i2p.i2p
Router: Improve tunnel peer selection of closest hop
for routers that are hidden, IPv6-only, or have disabled transports. Don't try to build tunnel if adjacent peers don't have compatible transports. Don't select IPv6-only routers for IBGW or OBEP. Remove old version check in peer selectors Peer selector cleanups Extend peer selection startup time for Android
This commit is contained in:
21
history.txt
21
history.txt
@@ -1,4 +1,25 @@
|
||||
2018-03-08 zzz
|
||||
* Crypto: Generate non-CA cert for family
|
||||
* Router:
|
||||
- Improved tunnel peer selection of closest hop for routers
|
||||
that are hidden, IPv6-only, or have disabled transports
|
||||
- Don't select IPv6-only routers for IBGW or OBEP
|
||||
- Don't try to build tunnel if adjacent peers aren't compatible
|
||||
- Remove ancient version check in peer selectors
|
||||
|
||||
2018-03-07 zzz
|
||||
* Router:
|
||||
- Validate tunnel ID in requests
|
||||
- Remove ancient version check in BuildRequestor
|
||||
- Disable floodfill for IPv6-only and disabled transports
|
||||
|
||||
2018-03-05 zzz
|
||||
* Util: Consolidate FileFilters
|
||||
|
||||
2018-03-04 zzz
|
||||
* Console:
|
||||
- Show SAM cert on /certs
|
||||
- Fix more forms inside tables
|
||||
* i2ptunnel: Fix multipart config for /register
|
||||
* KeyStore: Log expiration of self-signed certs
|
||||
|
||||
|
@@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 12;
|
||||
public final static long BUILD = 13;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
@@ -5,6 +5,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
@@ -21,6 +22,17 @@ class ClientPeerSelector extends TunnelPeerSelector {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ENDPOINT FIRST, GATEWAY LAST!!!!
|
||||
* In: us .. closest .. middle .. IBGW
|
||||
* Out: OBGW .. middle .. closest .. us
|
||||
*
|
||||
* @return ordered list of Hash objects (one per peer) specifying what order
|
||||
* they should appear in a tunnel (ENDPOINT FIRST). This includes
|
||||
* the local router in the list. If there are no tunnels or peers
|
||||
* to build through, and the settings reject 0 hop tunnels, this will
|
||||
* return null.
|
||||
*/
|
||||
public List<Hash> selectPeers(TunnelPoolSettings settings) {
|
||||
int length = getLength(settings);
|
||||
if (length < 0)
|
||||
@@ -29,19 +41,36 @@ class ClientPeerSelector extends TunnelPeerSelector {
|
||||
return null;
|
||||
|
||||
List<Hash> rv;
|
||||
boolean isInbound = settings.isInbound();
|
||||
|
||||
if (length > 0) {
|
||||
// special cases
|
||||
boolean v6Only = isIPv6Only();
|
||||
boolean ntcpDisabled = isNTCPDisabled();
|
||||
boolean ssuDisabled = isSSUDisabled();
|
||||
boolean checkClosestHop = v6Only || ntcpDisabled || ssuDisabled;
|
||||
boolean hidden = ctx.router().isHidden() ||
|
||||
ctx.router().getRouterInfo().getAddressCount() <= 0;
|
||||
boolean hiddenInbound = hidden && isInbound;
|
||||
|
||||
if (shouldSelectExplicit(settings))
|
||||
return selectExplicit(settings, length);
|
||||
|
||||
Set<Hash> exclude = getExclude(settings.isInbound(), false);
|
||||
Set<Hash> exclude = getExclude(isInbound, false);
|
||||
Set<Hash> matches = new HashSet<Hash>(length);
|
||||
if (length == 1) {
|
||||
// closest-hop restrictions
|
||||
Set<Hash> moreExclude = getClosestHopExclude(settings.isInbound());
|
||||
if (moreExclude != null)
|
||||
exclude.addAll(moreExclude);
|
||||
ctx.profileOrganizer().selectFastPeers(length, exclude, matches, 0);
|
||||
if (checkClosestHop) {
|
||||
Set<Hash> moreExclude = getClosestHopExclude(isInbound);
|
||||
if (moreExclude != null)
|
||||
exclude.addAll(moreExclude);
|
||||
}
|
||||
if (hiddenInbound)
|
||||
ctx.profileOrganizer().selectActiveNotFailingPeers(1, exclude, matches);
|
||||
if (matches.isEmpty()) {
|
||||
// ANFP does not fall back to non-connected
|
||||
ctx.profileOrganizer().selectFastPeers(length, exclude, matches, 0);
|
||||
}
|
||||
matches.remove(ctx.routerHash());
|
||||
rv = new ArrayList<Hash>(matches);
|
||||
} else {
|
||||
@@ -52,21 +81,33 @@ class ClientPeerSelector extends TunnelPeerSelector {
|
||||
// OBEP or IB last hop
|
||||
// group 0 or 1 if two hops, otherwise group 0
|
||||
Set<Hash> firstHopExclude;
|
||||
if (!settings.isInbound()) {
|
||||
if (isInbound) {
|
||||
// exclude existing OBEPs to get some diversity ?
|
||||
|
||||
// closest-hop restrictions
|
||||
Set<Hash> moreExclude = getClosestHopExclude(false);
|
||||
if (moreExclude != null) {
|
||||
moreExclude.addAll(exclude);
|
||||
firstHopExclude = moreExclude;
|
||||
if (checkClosestHop) {
|
||||
Set<Hash> moreExclude = getClosestHopExclude(false);
|
||||
if (moreExclude != null) {
|
||||
moreExclude.addAll(exclude);
|
||||
firstHopExclude = moreExclude;
|
||||
} else {
|
||||
firstHopExclude = exclude;
|
||||
}
|
||||
} else {
|
||||
firstHopExclude = exclude;
|
||||
firstHopExclude = exclude;
|
||||
}
|
||||
} else {
|
||||
firstHopExclude = exclude;
|
||||
}
|
||||
ctx.profileOrganizer().selectFastPeers(1, firstHopExclude, matches, settings.getRandomKey(), length == 2 ? SLICE_0_1 : SLICE_0);
|
||||
if (hiddenInbound) {
|
||||
ctx.profileOrganizer().selectActiveNotFailingPeers(1, exclude, matches);
|
||||
if (matches.isEmpty()) {
|
||||
// ANFP does not fall back to non-connected
|
||||
ctx.profileOrganizer().selectFastPeers(1, firstHopExclude, matches, settings.getRandomKey(), length == 2 ? SLICE_0_1 : SLICE_0);
|
||||
}
|
||||
} else {
|
||||
// TODO exclude IPv6-only at OBEP? Caught in checkTunnel() below
|
||||
ctx.profileOrganizer().selectFastPeers(1, firstHopExclude, matches, settings.getRandomKey(), length == 2 ? SLICE_0_1 : SLICE_0);
|
||||
}
|
||||
matches.remove(ctx.routerHash());
|
||||
exclude.addAll(matches);
|
||||
rv.addAll(matches);
|
||||
@@ -89,14 +130,16 @@ class ClientPeerSelector extends TunnelPeerSelector {
|
||||
}
|
||||
// IBGW or OB first hop
|
||||
// group 2 or 3 if two hops, otherwise group 1
|
||||
if (settings.isInbound()) {
|
||||
if (!isInbound) {
|
||||
// exclude existing IBGWs to get some diversity ?
|
||||
|
||||
// closest-hop restrictions
|
||||
Set<Hash> moreExclude = getClosestHopExclude(true);
|
||||
if (moreExclude != null)
|
||||
exclude.addAll(moreExclude);
|
||||
if (checkClosestHop) {
|
||||
Set<Hash> moreExclude = getClosestHopExclude(true);
|
||||
if (moreExclude != null)
|
||||
exclude.addAll(moreExclude);
|
||||
}
|
||||
}
|
||||
// TODO exclude IPv6-only at IBGW? Caught in checkTunnel() below
|
||||
ctx.profileOrganizer().selectFastPeers(1, exclude, matches, settings.getRandomKey(), length == 2 ? SLICE_2_3 : SLICE_1);
|
||||
matches.remove(ctx.routerHash());
|
||||
rv.addAll(matches);
|
||||
@@ -105,10 +148,18 @@ class ClientPeerSelector extends TunnelPeerSelector {
|
||||
rv = new ArrayList<Hash>(1);
|
||||
}
|
||||
|
||||
if (settings.isInbound())
|
||||
//if (length != rv.size() && log.shouldWarn())
|
||||
// log.warn("CPS requested " + length + " got " + rv.size() + ": " + DataHelper.toString(rv));
|
||||
//else if (log.shouldDebug())
|
||||
// log.debug("EPS result: " + DataHelper.toString(rv));
|
||||
if (isInbound)
|
||||
rv.add(0, ctx.routerHash());
|
||||
else
|
||||
rv.add(ctx.routerHash());
|
||||
if (rv.size() > 1) {
|
||||
if (!checkTunnel(isInbound, rv))
|
||||
rv = null;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* Pick peers randomly out of the not-failing pool, and put them into a tunnel
|
||||
@@ -24,12 +25,22 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ENDPOINT FIRST, GATEWAY LAST!!!!
|
||||
* In: us .. closest .. middle .. IBGW
|
||||
* Out: OBGW .. middle .. closest .. us
|
||||
*
|
||||
* @return ordered list of Hash objects (one per peer) specifying what order
|
||||
* they should appear in a tunnel (ENDPOINT FIRST). This includes
|
||||
* the local router in the list. If there are no tunnels or peers
|
||||
* to build through, and the settings reject 0 hop tunnels, this will
|
||||
* return null.
|
||||
*/
|
||||
public List<Hash> selectPeers(TunnelPoolSettings settings) {
|
||||
Log l = ctx.logManager().getLog(getClass());
|
||||
int length = getLength(settings);
|
||||
if (length < 0) {
|
||||
if (l.shouldLog(Log.DEBUG))
|
||||
l.debug("Length requested is zero: " + settings);
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Length requested is zero: " + settings);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -40,14 +51,70 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
// return rv;
|
||||
//}
|
||||
|
||||
Set<Hash> exclude = getExclude(settings.isInbound(), true);
|
||||
boolean isInbound = settings.isInbound();
|
||||
Set<Hash> exclude = getExclude(isInbound, true);
|
||||
exclude.add(ctx.routerHash());
|
||||
|
||||
// special cases
|
||||
boolean nonzero = length > 0;
|
||||
boolean exploreHighCap = nonzero && shouldPickHighCap();
|
||||
boolean v6Only = nonzero && isIPv6Only();
|
||||
boolean ntcpDisabled = nonzero && isNTCPDisabled();
|
||||
boolean ssuDisabled = nonzero && isSSUDisabled();
|
||||
boolean checkClosestHop = v6Only || ntcpDisabled || ssuDisabled;
|
||||
boolean hidden = nonzero && (ctx.router().isHidden() ||
|
||||
ctx.router().getRouterInfo().getAddressCount() <= 0);
|
||||
boolean hiddenInbound = hidden && isInbound;
|
||||
boolean lowOutbound = nonzero && !isInbound && !ctx.commSystem().haveHighOutboundCapacity();
|
||||
|
||||
|
||||
// closest-hop restrictions
|
||||
// Since we're applying orderPeers() later, we don't know
|
||||
// which will be the closest hop, so just appply to all peers for now.
|
||||
Set<Hash> moreExclude = getClosestHopExclude(settings.isInbound());
|
||||
if (moreExclude != null)
|
||||
exclude.addAll(moreExclude);
|
||||
// which will be the closest hop, so select the closest one here if necessary.
|
||||
|
||||
Hash closestHop = null;
|
||||
if (v6Only || hiddenInbound || lowOutbound) {
|
||||
Set<Hash> closestExclude;
|
||||
if (checkClosestHop) {
|
||||
closestExclude = getClosestHopExclude(isInbound);
|
||||
if (closestExclude != null)
|
||||
closestExclude.addAll(exclude);
|
||||
else
|
||||
closestExclude = exclude;
|
||||
} else {
|
||||
closestExclude = exclude;
|
||||
}
|
||||
|
||||
Set<Hash> closest = new HashSet<Hash>(1);
|
||||
if (hiddenInbound || lowOutbound) {
|
||||
// If hidden and inbound, use fast peers - that we probably have recently
|
||||
// connected to and so they have our real RI - to maximize the chance
|
||||
// that the adjacent hop can connect to us.
|
||||
// use only connected peers so we don't make more connections
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("EPS SANFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size());
|
||||
ctx.profileOrganizer().selectActiveNotFailingPeers(1, closestExclude, closest);
|
||||
if (closest.isEmpty()) {
|
||||
// ANFP does not fall back to non-connected
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("EPS SFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size());
|
||||
ctx.profileOrganizer().selectFastPeers(1, closestExclude, closest);
|
||||
}
|
||||
} else if (exploreHighCap) {
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("EPS SHCP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size());
|
||||
ctx.profileOrganizer().selectHighCapacityPeers(1, closestExclude, closest);
|
||||
} else {
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("EPS SNFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size());
|
||||
ctx.profileOrganizer().selectNotFailingPeers(1, closestExclude, closest, false);
|
||||
}
|
||||
if (!closest.isEmpty()) {
|
||||
closestHop = closest.iterator().next();
|
||||
exclude.add(closestHop);
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't use ff peers for exploratory tunnels to lessen exposure to netDb searches and stores
|
||||
// Hmm if they don't get explored they don't get a speed/capacity rating
|
||||
@@ -57,52 +124,48 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
HashSet<Hash> matches = new HashSet<Hash>(length);
|
||||
|
||||
if (length > 0) {
|
||||
boolean exploreHighCap = shouldPickHighCap();
|
||||
|
||||
//
|
||||
// We don't honor IP Restriction here, to be fixed
|
||||
//
|
||||
|
||||
// If hidden and inbound, use fast peers - that we probably have recently
|
||||
// connected to and so they have our real RI - to maximize the chance
|
||||
// that the adjacent hop can connect to us.
|
||||
if (settings.isInbound() &&
|
||||
(ctx.router().isHidden() ||
|
||||
ctx.router().getRouterInfo().getAddressCount() <= 0)) {
|
||||
if (l.shouldLog(Log.INFO))
|
||||
l.info("EPS SFP " + length + (settings.isInbound() ? " IB" : " OB") + " exclude " + exclude.size());
|
||||
ctx.profileOrganizer().selectFastPeers(length, exclude, matches);
|
||||
} else if (exploreHighCap) {
|
||||
if (l.shouldLog(Log.INFO))
|
||||
l.info("EPS SHCP " + length + (settings.isInbound() ? " IB" : " OB") + " exclude " + exclude.size());
|
||||
if (exploreHighCap) {
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("EPS SHCP " + length + (isInbound ? " IB" : " OB") + " exclude " + exclude.size());
|
||||
ctx.profileOrganizer().selectHighCapacityPeers(length, exclude, matches);
|
||||
} else if (ctx.commSystem().haveHighOutboundCapacity()) {
|
||||
if (l.shouldLog(Log.INFO))
|
||||
l.info("EPS SNFP " + length + (settings.isInbound() ? " IB" : " OB") + " exclude " + exclude.size());
|
||||
} else {
|
||||
// As of 0.9.23, we include a max of 2 not failing peers,
|
||||
// to improve build success on 3-hop tunnels.
|
||||
// Peer org credits existing items in matches
|
||||
if (length > 2)
|
||||
ctx.profileOrganizer().selectHighCapacityPeers(length - 2, exclude, matches);
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("EPS SNFP " + length + (isInbound ? " IB" : " OB") + " exclude " + exclude.size());
|
||||
ctx.profileOrganizer().selectNotFailingPeers(length, exclude, matches, false);
|
||||
} else { // use only connected peers so we don't make more connections
|
||||
if (l.shouldLog(Log.INFO))
|
||||
l.info("EPS SANFP " + length + (settings.isInbound() ? " IB" : " OB") + " exclude " + exclude.size());
|
||||
ctx.profileOrganizer().selectActiveNotFailingPeers(length, exclude, matches);
|
||||
}
|
||||
|
||||
matches.remove(ctx.routerHash());
|
||||
}
|
||||
|
||||
ArrayList<Hash> rv = new ArrayList<Hash>(matches);
|
||||
if (rv.size() > 1)
|
||||
orderPeers(rv, settings.getRandomKey());
|
||||
if (l.shouldLog(Log.DEBUG))
|
||||
l.debug("EPS got " + rv.size() + ": " + DataHelper.toString(rv));
|
||||
if (settings.isInbound())
|
||||
if (closestHop != null) {
|
||||
if (isInbound)
|
||||
rv.add(0, closestHop);
|
||||
else
|
||||
rv.add(closestHop);
|
||||
length++;
|
||||
}
|
||||
//if (length != rv.size() && log.shouldWarn())
|
||||
// log.warn("EPS requested " + length + " got " + rv.size() + ": " + DataHelper.toString(rv));
|
||||
//else if (log.shouldDebug())
|
||||
// log.debug("EPS result: " + DataHelper.toString(rv));
|
||||
if (isInbound)
|
||||
rv.add(0, ctx.routerHash());
|
||||
else
|
||||
rv.add(ctx.routerHash());
|
||||
if (rv.size() > 1) {
|
||||
if (!checkTunnel(isInbound, rv))
|
||||
rv = null;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -131,7 +194,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
return false;
|
||||
|
||||
// no need to explore too wildly at first (if we have enough connected peers)
|
||||
if (ctx.router().getUptime() <= 5*60*1000)
|
||||
if (ctx.router().getUptime() <= (SystemVersion.isAndroid() ? 15*60*1000 : 5*60*1000))
|
||||
return true;
|
||||
// or at the end
|
||||
if (ctx.router().gracefulShutdownInProgress())
|
||||
|
@@ -16,12 +16,17 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.CommSystemFacade.Status;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.transport.TransportManager;
|
||||
import net.i2p.router.transport.TransportUtil;
|
||||
import net.i2p.router.util.HashDistance;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.VersionComparator;
|
||||
@@ -34,9 +39,19 @@ import net.i2p.util.VersionComparator;
|
||||
*/
|
||||
public abstract class TunnelPeerSelector {
|
||||
protected final RouterContext ctx;
|
||||
protected final Log log;
|
||||
|
||||
private static final int NTCP_V4 = 0x01;
|
||||
private static final int SSU_V4 = 0x02;
|
||||
private static final int ANY_V4 = NTCP_V4 | SSU_V4;
|
||||
private static final int NTCP_V6 = 0x04;
|
||||
private static final int SSU_V6 = 0x08;
|
||||
private static final int ANY_V6 = NTCP_V6 | SSU_V6;
|
||||
|
||||
|
||||
protected TunnelPeerSelector(RouterContext context) {
|
||||
ctx = context;
|
||||
log = ctx.logManager().getLog(getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +141,6 @@ public abstract class TunnelPeerSelector {
|
||||
if (peers == null)
|
||||
peers = I2PAppContext.getGlobalContext().getProperty("explicitPeers");
|
||||
|
||||
Log log = ctx.logManager().getLog(ClientPeerSelector.class);
|
||||
List<Hash> rv = new ArrayList<Hash>();
|
||||
StringTokenizer tok = new StringTokenizer(peers, ",");
|
||||
while (tok.hasMoreTokens()) {
|
||||
@@ -200,7 +214,7 @@ public abstract class TunnelPeerSelector {
|
||||
//
|
||||
// Defaults changed to true for inbound only in filterUnreachable below.
|
||||
|
||||
Set<Hash> peers = new HashSet<Hash>(1);
|
||||
Set<Hash> peers = new HashSet<Hash>(8);
|
||||
peers.addAll(ctx.profileOrganizer().selectPeersRecentlyRejecting());
|
||||
peers.addAll(ctx.tunnelManager().selectPeersInTooManyTunnels());
|
||||
// if (false && filterUnreachable(ctx, isInbound, isExploratory)) {
|
||||
@@ -216,7 +230,6 @@ public abstract class TunnelPeerSelector {
|
||||
}
|
||||
if (filterSlow(isInbound, isExploratory)) {
|
||||
// NOTE: filterSlow always returns true
|
||||
Log log = ctx.logManager().getLog(TunnelPeerSelector.class);
|
||||
char excl[] = getExcludeCaps(ctx);
|
||||
if (excl != null) {
|
||||
FloodfillNetworkDatabaseFacade fac = (FloodfillNetworkDatabaseFacade)ctx.netDb();
|
||||
@@ -224,7 +237,7 @@ public abstract class TunnelPeerSelector {
|
||||
if (known != null) {
|
||||
for (int i = 0; i < known.size(); i++) {
|
||||
RouterInfo peer = known.get(i);
|
||||
boolean shouldExclude = shouldExclude(ctx, log, peer, excl);
|
||||
boolean shouldExclude = shouldExclude(peer, excl);
|
||||
if (shouldExclude) {
|
||||
peers.add(peer.getIdentity().calculateHash());
|
||||
continue;
|
||||
@@ -327,12 +340,91 @@ public abstract class TunnelPeerSelector {
|
||||
}
|
||||
return peers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we IPv6 only?
|
||||
* @since 0.9.34
|
||||
*/
|
||||
protected boolean isIPv6Only() {
|
||||
// The setting is the same for both SSU and NTCP, so just take the SSU one
|
||||
return TransportUtil.getIPv6Config(ctx, "SSU") == TransportUtil.IPv6Config.IPV6_ONLY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is NTCP disabled?
|
||||
* @since 0.9.34
|
||||
*/
|
||||
protected boolean isNTCPDisabled() {
|
||||
return !TransportManager.isNTCPEnabled(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is SSU disabled?
|
||||
* @since 0.9.34
|
||||
*/
|
||||
protected boolean isSSUDisabled() {
|
||||
return !ctx.getBooleanPropertyDefaultTrue(TransportManager.PROP_ENABLE_UDP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we allow as OBEP?
|
||||
* This just checks for IPv4 support.
|
||||
* Will return false for IPv6-only.
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
* Default true.
|
||||
*
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private boolean allowAsOBEP(Hash h) {
|
||||
RouterInfo ri = ctx.netDb().lookupRouterInfoLocally(h);
|
||||
if (ri == null)
|
||||
return true;
|
||||
return canConnect(ri, ANY_V4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we allow as IBGW?
|
||||
* This just checks for IPv4 support.
|
||||
* Will return false for hidden or IPv6-only.
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
* Default true.
|
||||
*
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private boolean allowAsIBGW(Hash h) {
|
||||
RouterInfo ri = ctx.netDb().lookupRouterInfoLocally(h);
|
||||
if (ri == null)
|
||||
return true;
|
||||
return canConnect(ANY_V4, ri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick peers that we want to avoid for the first OB hop or last IB hop.
|
||||
* This is only filled in if our router sig type is not DSA.
|
||||
* There's several cases of importance:
|
||||
* <ol><li>Inbound and we are hidden -
|
||||
* Exclude all unless connected.
|
||||
* This is taken care of in ClientPeerSelector and TunnelPeerSelector selectPeers(), not here.
|
||||
*
|
||||
* @param isInbound unused
|
||||
* <li>We are IPv6-only.
|
||||
* Exclude all v4-only peers, unless connected
|
||||
* This is taken care of here.
|
||||
*
|
||||
* <li>We have NTCP or SSU disabled.
|
||||
* Exclude all incompatible peers, unless connected
|
||||
* This is taken care of here.
|
||||
*
|
||||
* <li>Minimum version check, if we are some brand-new sig type,
|
||||
* or are using some new tunnel build method.
|
||||
* Not currently used, but this is where to implement the checks if needed.
|
||||
* Make sure that ClientPeerSelector and TunnelPeerSelector selectPeers() call this when needed.
|
||||
* </ol>
|
||||
*
|
||||
* Don't call this unless you need to.
|
||||
* See ClientPeerSelector and TunnelPeerSelector selectPeers().
|
||||
*
|
||||
* @param isInbound
|
||||
* @return null if none
|
||||
* @since 0.9.17
|
||||
*/
|
||||
@@ -340,21 +432,34 @@ public abstract class TunnelPeerSelector {
|
||||
RouterInfo ri = ctx.router().getRouterInfo();
|
||||
if (ri == null)
|
||||
return null;
|
||||
SigType type = ri.getIdentity().getSigType();
|
||||
if (type == SigType.DSA_SHA1)
|
||||
return null;
|
||||
Set<Hash> rv = new HashSet<Hash>(1024);
|
||||
|
||||
// we can skip this check now, uncomment if we have some new sigtype
|
||||
//SigType type = ri.getIdentity().getSigType();
|
||||
//if (type == SigType.DSA_SHA1)
|
||||
// return null;
|
||||
|
||||
int ourMask = isInbound ? getInboundMask(ri) : getOutboundMask(ri);
|
||||
Set<Hash> connected = ctx.commSystem().getEstablished();
|
||||
Set<Hash> rv = new HashSet<Hash>(256);
|
||||
FloodfillNetworkDatabaseFacade fac = (FloodfillNetworkDatabaseFacade)ctx.netDb();
|
||||
List<RouterInfo> known = fac.getKnownRouterData();
|
||||
if (known != null) {
|
||||
for (int i = 0; i < known.size(); i++) {
|
||||
RouterInfo peer = known.get(i);
|
||||
String v = peer.getVersion();
|
||||
// we can skip this check now, uncomment if we have some breaking change
|
||||
//String v = peer.getVersion();
|
||||
// RI sigtypes added in 0.9.16
|
||||
// SSU inbound connection bug fixed in 0.9.17, but it won't bid, so NTCP only,
|
||||
// no need to check
|
||||
if (VersionComparator.comp(v, "0.9.16") < 0)
|
||||
rv.add(peer.getIdentity().calculateHash());
|
||||
//if (VersionComparator.comp(v, "0.9.16") < 0)
|
||||
// rv.add(peer.getIdentity().calculateHash());
|
||||
|
||||
Hash h = peer.getIdentity().calculateHash();
|
||||
if (connected.contains(h))
|
||||
continue;
|
||||
boolean canConnect = isInbound ? canConnect(peer, ourMask) : canConnect(ourMask, peer);
|
||||
if (!canConnect)
|
||||
rv.add(h);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
@@ -362,8 +467,7 @@ public abstract class TunnelPeerSelector {
|
||||
|
||||
/** warning, this is also called by ProfileOrganizer.isSelectable() */
|
||||
public static boolean shouldExclude(RouterContext ctx, RouterInfo peer) {
|
||||
Log log = ctx.logManager().getLog(TunnelPeerSelector.class);
|
||||
return shouldExclude(ctx, log, peer, getExcludeCaps(ctx));
|
||||
return shouldExclude(peer, getExcludeCaps(ctx));
|
||||
}
|
||||
|
||||
private static char[] getExcludeCaps(RouterContext ctx) {
|
||||
@@ -378,9 +482,9 @@ public abstract class TunnelPeerSelector {
|
||||
}
|
||||
|
||||
/** 0.7.8 and earlier had major message corruption bugs */
|
||||
private static final String MIN_VERSION = "0.7.9";
|
||||
//private static final String MIN_VERSION = "0.7.9";
|
||||
|
||||
private static boolean shouldExclude(RouterContext ctx, Log log, RouterInfo peer, char excl[]) {
|
||||
private static boolean shouldExclude(RouterInfo peer, char excl[]) {
|
||||
String cap = peer.getCapabilities();
|
||||
for (int j = 0; j < excl.length; j++) {
|
||||
if (cap.indexOf(excl[j]) >= 0) {
|
||||
@@ -400,9 +504,10 @@ public abstract class TunnelPeerSelector {
|
||||
// so don't exclude it based on published capacity
|
||||
|
||||
// minimum version check
|
||||
String v = peer.getVersion();
|
||||
if (VersionComparator.comp(v, MIN_VERSION) < 0)
|
||||
return true;
|
||||
// we can skip this check now
|
||||
//String v = peer.getVersion();
|
||||
//if (VersionComparator.comp(v, MIN_VERSION) < 0)
|
||||
// return true;
|
||||
|
||||
// uptime is always spoofed to 90m, so just remove all this
|
||||
/******
|
||||
@@ -583,4 +688,352 @@ public abstract class TunnelPeerSelector {
|
||||
return ll.compareTo(rr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connectivity check.
|
||||
* Check that each hop can connect to the next, including us.
|
||||
* Check that the OBEP is not IPv6-only, and the IBGW is
|
||||
* not hidden or IPv6-only.
|
||||
* Tells the profile manager to blame the hop, and returns false on failure.
|
||||
*
|
||||
* @param tunnel ENDPOINT FIRST, GATEWAY LAST!!!!, length 2 or greater
|
||||
* @return ok
|
||||
* @since 0.9.34
|
||||
*/
|
||||
protected boolean checkTunnel(boolean isInbound, List<Hash> tunnel) {
|
||||
if (!checkTunnel(tunnel))
|
||||
return false;
|
||||
if (isInbound) {
|
||||
Hash h = tunnel.get(tunnel.size() - 1);
|
||||
if (!allowAsIBGW(h)) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Picked IPv6-only or hidden peer for IBGW: " + h);
|
||||
// treat as a timeout in the profile
|
||||
// tunnelRejected() would set the last heard from time
|
||||
ctx.profileManager().tunnelTimedOut(h);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Hash h = tunnel.get(0);
|
||||
if (!allowAsOBEP(h)) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Picked IPv6-only peer for OBEP: " + h);
|
||||
// treat as a timeout in the profile
|
||||
// tunnelRejected() would set the last heard from time
|
||||
ctx.profileManager().tunnelTimedOut(h);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connectivity check.
|
||||
* Check that each hop can connect to the next, including us.
|
||||
*
|
||||
* @param tunnel ENDPOINT FIRST, GATEWAY LAST!!!!
|
||||
* @return ok
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private boolean checkTunnel(List<Hash> tunnel) {
|
||||
boolean rv = true;
|
||||
for (int i = 0; i < tunnel.size() - 1; i++) {
|
||||
// order is backwards!
|
||||
Hash hf = tunnel.get(i+1);
|
||||
Hash ht = tunnel.get(i);
|
||||
if (!canConnect(hf, ht)) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Connect check fail hop " + (i+1) + " to " + i +
|
||||
" in tunnel (EP<-GW): " + DataHelper.toString(tunnel));
|
||||
// Blame them both
|
||||
// treat as a timeout in the profile
|
||||
// tunnelRejected() would set the last heard from time
|
||||
Hash us = ctx.routerHash();
|
||||
if (!hf.equals(us))
|
||||
ctx.profileManager().tunnelTimedOut(hf);
|
||||
if (!ht.equals(us))
|
||||
ctx.profileManager().tunnelTimedOut(ht);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can "from" connect to "to" based on published addresses?
|
||||
*
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
* Either from or to may be us.
|
||||
*
|
||||
* This is best effort, as we can't know for sure.
|
||||
* Published addresses or introducers may have changed.
|
||||
* Even if a can't connect to b, they may already be connected
|
||||
* as b connected to a.
|
||||
*
|
||||
* @return true if we don't have either RI
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private boolean canConnect(Hash from, Hash to) {
|
||||
Hash us = ctx.routerHash();
|
||||
if (us == null)
|
||||
return true;
|
||||
boolean usf = from.equals(us);
|
||||
if (usf && ctx.commSystem().isEstablished(to))
|
||||
return true;
|
||||
boolean ust = to.equals(us);
|
||||
if (ust && ctx.commSystem().isEstablished(from))
|
||||
return true;
|
||||
RouterInfo rt = ctx.netDb().lookupRouterInfoLocally(to);
|
||||
if (rt == null)
|
||||
return true;
|
||||
RouterInfo rf = ctx.netDb().lookupRouterInfoLocally(from);
|
||||
if (rf == null)
|
||||
return true;
|
||||
int ct;
|
||||
if (ust) {
|
||||
// to us
|
||||
ct = getInboundMask(rt);
|
||||
} else {
|
||||
Collection<RouterAddress> at = rt.getAddresses();
|
||||
// assume nothing if hidden
|
||||
if (at.isEmpty())
|
||||
return false;
|
||||
ct = getConnectMask(at);
|
||||
}
|
||||
|
||||
int cf;
|
||||
if (usf) {
|
||||
// from us
|
||||
cf = getOutboundMask(rf);
|
||||
} else {
|
||||
Collection<RouterAddress> a = rf.getAddresses();
|
||||
if (a.isEmpty()) {
|
||||
// assume IPv4 if hidden
|
||||
cf = NTCP_V4 | SSU_V4;
|
||||
} else {
|
||||
cf = getConnectMask(a);
|
||||
}
|
||||
}
|
||||
|
||||
boolean rv = (ct & cf) != 0;
|
||||
if (!rv && log.shouldWarn()) {
|
||||
log.warn("Cannot connect: " +
|
||||
(usf ? "us" : from.toString()) + " with mask " + cf + "\nto " +
|
||||
(ust ? "us" : to.toString()) + " with mask " + ct);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we connect to "to" based on published addresses?
|
||||
*
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
*
|
||||
* This is best effort, as we can't know for sure.
|
||||
* Does not check isEstablished(); do that first.
|
||||
*
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private boolean canConnect(int ourMask, RouterInfo to) {
|
||||
Collection<RouterAddress> ra = to.getAddresses();
|
||||
// assume nothing if hidden
|
||||
if (ra.isEmpty())
|
||||
return false;
|
||||
int ct = getConnectMask(ra);
|
||||
boolean rv = (ourMask & ct) != 0;
|
||||
//if (!rv && log.shouldWarn())
|
||||
// log.warn("Cannot connect: us with mask " + ourMask + " to " + to + " with mask " + ct);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can "from" connect to us based on published addresses?
|
||||
*
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
*
|
||||
* This is best effort, as we can't know for sure.
|
||||
* Does not check isEstablished(); do that first.
|
||||
*
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private boolean canConnect(RouterInfo from, int ourMask) {
|
||||
if (ourMask == 0)
|
||||
return false;
|
||||
Collection<RouterAddress> ra = from.getAddresses();
|
||||
int cf;
|
||||
// assume v4 if hidden
|
||||
if (ra.isEmpty())
|
||||
cf = NTCP_V4 | SSU_V4;
|
||||
else
|
||||
cf = getConnectMask(ra);
|
||||
boolean rv = (cf & ourMask) != 0;
|
||||
//if (!rv && log.shouldWarn())
|
||||
// log.warn("Cannot connect: " + from + " with mask " + cf + " to us with mask " + ourMask);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our inbound mask.
|
||||
* For most cases, we use what we published, i.e. getConnectMask()
|
||||
*
|
||||
* @return bitmask for accepting connections
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private int getInboundMask(RouterInfo us) {
|
||||
// to us
|
||||
int ct = 0;
|
||||
Status status = ctx.commSystem().getStatus();
|
||||
switch (status) {
|
||||
case OK:
|
||||
case IPV4_UNKNOWN_IPV6_OK:
|
||||
case IPV4_FIREWALLED_IPV6_OK:
|
||||
case IPV4_SNAT_IPV6_OK:
|
||||
case IPV4_SNAT_IPV6_UNKNOWN:
|
||||
case IPV4_FIREWALLED_IPV6_UNKNOWN:
|
||||
case IPV4_UNKNOWN_IPV6_FIREWALLED:
|
||||
case IPV4_OK_IPV6_FIREWALLED:
|
||||
case DIFFERENT:
|
||||
case REJECT_UNSOLICITED:
|
||||
// use what we published
|
||||
Collection<RouterAddress> at = us.getAddresses();
|
||||
if (at.isEmpty())
|
||||
return 0;
|
||||
ct = getConnectMask(at);
|
||||
break;
|
||||
|
||||
case IPV4_DISABLED_IPV6_OK:
|
||||
case IPV4_DISABLED_IPV6_UNKNOWN:
|
||||
// maybe should return zero for this one?
|
||||
case IPV4_DISABLED_IPV6_FIREWALLED:
|
||||
// TODO look at force-firewalled settings per-transport
|
||||
if (!isNTCPDisabled())
|
||||
ct |= NTCP_V6;
|
||||
if (!isSSUDisabled())
|
||||
ct |= SSU_V6;
|
||||
break;
|
||||
|
||||
case IPV4_OK_IPV6_UNKNOWN:
|
||||
case DISCONNECTED:
|
||||
case HOSED:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
if (!isNTCPDisabled())
|
||||
ct |= NTCP_V4;
|
||||
if (!isSSUDisabled())
|
||||
ct |= SSU_V4;
|
||||
break;
|
||||
}
|
||||
return ct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our outbound mask.
|
||||
* For most cases, we use our comm system status.
|
||||
*
|
||||
* @return bitmask for initiating connections
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private int getOutboundMask(RouterInfo us) {
|
||||
// from us
|
||||
int cf = 0;
|
||||
Status status = ctx.commSystem().getStatus();
|
||||
switch (status) {
|
||||
case OK:
|
||||
// use what we published, as the OK state doesn't tell us about IPv6
|
||||
// Addresses.isConnectedIPv6() is too slow
|
||||
Collection<RouterAddress> a = us.getAddresses();
|
||||
if (a.isEmpty()) {
|
||||
// we are hidden
|
||||
// TODO ipv6
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V4;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V4;
|
||||
} else {
|
||||
cf = getConnectMask(a);
|
||||
}
|
||||
break;
|
||||
|
||||
case IPV4_OK_IPV6_FIREWALLED:
|
||||
case IPV4_UNKNOWN_IPV6_OK:
|
||||
case IPV4_FIREWALLED_IPV6_OK:
|
||||
case IPV4_SNAT_IPV6_OK:
|
||||
case IPV4_UNKNOWN_IPV6_FIREWALLED:
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V4 | NTCP_V6;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V4 | SSU_V6;
|
||||
break;
|
||||
|
||||
case IPV4_DISABLED_IPV6_OK:
|
||||
case IPV4_DISABLED_IPV6_UNKNOWN:
|
||||
case IPV4_DISABLED_IPV6_FIREWALLED:
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V6;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V6;
|
||||
break;
|
||||
|
||||
case DIFFERENT:
|
||||
case IPV4_SNAT_IPV6_UNKNOWN:
|
||||
case IPV4_FIREWALLED_IPV6_UNKNOWN:
|
||||
case REJECT_UNSOLICITED:
|
||||
case IPV4_OK_IPV6_UNKNOWN:
|
||||
case DISCONNECTED:
|
||||
case HOSED:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V4;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V4;
|
||||
break;
|
||||
}
|
||||
return cf;
|
||||
}
|
||||
|
||||
/** prevent object churn */
|
||||
private static final String IHOST[] = { "ihost0", "ihost1", "ihost2" };
|
||||
|
||||
/**
|
||||
* @param addrs non-empty, set your own default if empty
|
||||
* @return bitmask of v4/v6 NTCP/SSU
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private static int getConnectMask(Collection<RouterAddress> addrs) {
|
||||
int rv = 0;
|
||||
for (RouterAddress ra : addrs) {
|
||||
String style = ra.getTransportStyle();
|
||||
String host = ra.getHost();
|
||||
if ("NTCP".equals(style)) {
|
||||
if (host != null) {
|
||||
if (host.contains(":"))
|
||||
rv |= NTCP_V6;
|
||||
else
|
||||
rv |= NTCP_V4;
|
||||
}
|
||||
} else if ("SSU".equals(style)) {
|
||||
if (host == null) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
String ihost = ra.getOption(IHOST[i]);
|
||||
if (ihost == null)
|
||||
break;
|
||||
if (ihost.contains(":"))
|
||||
rv |= SSU_V6;
|
||||
else
|
||||
rv |= SSU_V4;
|
||||
}
|
||||
} else if (host.contains(":")) {
|
||||
rv |= SSU_V6;
|
||||
} else {
|
||||
rv |= SSU_V4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user