* RouterAddress:

- Deprecate some setters
  - Add warning about setCost()
  - Change cost storage from int to short
  - Cost range checks
* NTCP:
  - Republish even if only changing cost
* Transports:
  - Sort multiple peer addresses by cost, with adjustment for local IPv6 preference
  - Add default IPv6Config for ease of changing later
This commit is contained in:
zzz
2013-05-19 18:36:29 +00:00
parent 55880844a5
commit c0350702fd
5 changed files with 132 additions and 28 deletions

View File

@ -38,7 +38,7 @@ import net.i2p.util.OrderedProperties;
* @author jrandom * @author jrandom
*/ */
public class RouterAddress extends DataStructureImpl { public class RouterAddress extends DataStructureImpl {
private int _cost; private short _cost;
//private Date _expiration; //private Date _expiration;
private String _transportStyle; private String _transportStyle;
private final Properties _options; private final Properties _options;
@ -50,19 +50,21 @@ public class RouterAddress extends DataStructureImpl {
public static final String PROP_PORT = "port"; public static final String PROP_PORT = "port";
public RouterAddress() { public RouterAddress() {
_cost = -1;
_options = new OrderedProperties(); _options = new OrderedProperties();
} }
/** /**
* For efficiency when created by a Transport. * For efficiency when created by a Transport.
* @param options not copied; do not reuse or modify * @param options not copied; do not reuse or modify
* @param cost 0-255
* @since IPv6 * @since IPv6
*/ */
public RouterAddress(String style, OrderedProperties options, int cost) { public RouterAddress(String style, OrderedProperties options, int cost) {
_transportStyle = style; _transportStyle = style;
_options = options; _options = options;
_cost = cost; if (cost < 0 || cost > 255)
throw new IllegalArgumentException();
_cost = (short) cost;
} }
/** /**
@ -71,6 +73,7 @@ public class RouterAddress extends DataStructureImpl {
* No value above 255 is allowed. * No value above 255 is allowed.
* *
* Unused before 0.7.12 * Unused before 0.7.12
* @return 0-255
*/ */
public int getCost() { public int getCost() {
return _cost; return _cost;
@ -78,12 +81,18 @@ public class RouterAddress extends DataStructureImpl {
/** /**
* Configure the weighted cost of using the address. * Configure the weighted cost of using the address.
* No value above 255 is allowed. * No value negative or above 255 is allowed.
*
* WARNING - do not change cost on a published address or it will break the RI sig.
* There is no check here.
* Rarely used, use 3-arg constructor.
* *
* NTCP is set to 10 and SSU to 5 by default, unused before 0.7.12 * NTCP is set to 10 and SSU to 5 by default, unused before 0.7.12
*/ */
public void setCost(int cost) { public void setCost(int cost) {
_cost = cost; if (cost < 0 || cost > 255)
throw new IllegalArgumentException();
_cost = (short) cost;
} }
/** /**
@ -124,6 +133,7 @@ public class RouterAddress extends DataStructureImpl {
* Configure the type of transport that must be used to communicate on this address * Configure the type of transport that must be used to communicate on this address
* *
* @throws IllegalStateException if was already set * @throws IllegalStateException if was already set
* @deprecated unused, use 3-arg constructor
*/ */
public void setTransportStyle(String transportStyle) { public void setTransportStyle(String transportStyle) {
if (_transportStyle != null) if (_transportStyle != null)
@ -163,6 +173,7 @@ public class RouterAddress extends DataStructureImpl {
* Makes a copy. * Makes a copy.
* @param options non-null * @param options non-null
* @throws IllegalStateException if was already set * @throws IllegalStateException if was already set
* @deprecated unused, use 3-arg constructor
*/ */
public void setOptions(Properties options) { public void setOptions(Properties options) {
if (!_options.isEmpty()) if (!_options.isEmpty())
@ -234,7 +245,7 @@ public class RouterAddress extends DataStructureImpl {
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_transportStyle != null) if (_transportStyle != null)
throw new IllegalStateException(); throw new IllegalStateException();
_cost = (int) DataHelper.readLong(in, 1); _cost = (short) DataHelper.readLong(in, 1);
//_expiration = DataHelper.readDate(in); //_expiration = DataHelper.readDate(in);
DataHelper.readDate(in); DataHelper.readDate(in);
_transportStyle = DataHelper.readString(in); _transportStyle = DataHelper.readString(in);
@ -251,8 +262,8 @@ public class RouterAddress extends DataStructureImpl {
* readin and the signature will fail. * readin and the signature will fail.
*/ */
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ((_cost < 0) || (_transportStyle == null)) if (_transportStyle == null)
throw new DataFormatException("Not enough data to write a router address"); throw new DataFormatException("uninitialized");
DataHelper.writeLong(out, 1, _cost); DataHelper.writeLong(out, 1, _cost);
//DataHelper.writeDate(out, _expiration); //DataHelper.writeDate(out, _expiration);
DataHelper.writeDate(out, null); DataHelper.writeDate(out, null);

View File

@ -14,6 +14,7 @@ import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
@ -471,7 +472,8 @@ public abstract class TransportImpl implements Transport {
} }
/** Do we increase the advertised cost when approaching conn limits? */ /** Do we increase the advertised cost when approaching conn limits? */
public static final boolean ADJUST_COST = true; protected static final boolean ADJUST_COST = true;
protected static final int CONGESTION_COST_ADJUSTMENT = 2;
/** /**
* What addresses are we currently listening to? * What addresses are we currently listening to?
@ -563,6 +565,67 @@ public abstract class TransportImpl implements Transport {
return rv; return rv;
} }
/**
* Get all available address we can use,
* shuffled and then sorted by cost/preference.
* Lowest cost (most preferred) first.
* @return non-null, possibly empty
* @since IPv6
*/
protected List<RouterAddress> getTargetAddresses(RouterInfo target) {
List<RouterAddress> rv = target.getTargetAddresses(getStyle());
// Shuffle so everybody doesn't use the first one
if (rv.size() > 1) {
Collections.shuffle(rv, _context.random());
TransportUtil.IPv6Config config = getIPv6Config();
int adj;
switch (config) {
case IPV6_DISABLED:
adj = 10; break;
case IPV6_NOT_PREFERRED:
adj = 1; break;
default:
case IPV6_ENABLED:
adj = 0; break;
case IPV6_PREFERRED:
adj = -1; break;
case IPV6_ONLY:
adj = -10; break;
}
Collections.sort(rv, new AddrComparator(adj));
}
return rv;
}
/**
* Compare based on published cost, adjusting for our IPv6 preference.
* Lowest cost (most preferred) first.
* @since IPv6
*/
private static class AddrComparator implements Comparator<RouterAddress> {
private final int adj;
public AddrComparator(int ipv6Adjustment) {
adj = ipv6Adjustment;
}
public int compare(RouterAddress l, RouterAddress r) {
int lc = l.getCost();
int rc = r.getCost();
byte[] lip = l.getIP();
byte[] rip = r.getIP();
if (lip != null && lip.length == 16)
lc += adj;
if (rip != null && rip.length == 16)
rc += adj;
if (lc > rc)
return 1;
if (lc < rc)
return -1;
return 0;
}
}
/** /**
* Notify a transport of an external address change. * Notify a transport of an external address change.
* This may be from a local interface, UPnP, a config change, etc. * This may be from a local interface, UPnP, a config change, etc.

View File

@ -52,6 +52,7 @@ public abstract class TransportUtil {
} }
private static final Map<String, IPv6Config> BY_NAME = new HashMap<String, IPv6Config>(); private static final Map<String, IPv6Config> BY_NAME = new HashMap<String, IPv6Config>();
private static final IPv6Config DEFAULT_IPV6_CONFIG = IPv6Config.IPV6_DISABLED;
static { static {
for (IPv6Config cfg : IPv6Config.values()) { for (IPv6Config cfg : IPv6Config.values()) {
@ -68,17 +69,17 @@ public abstract class TransportUtil {
else if (transportStyle.equals("SSU")) else if (transportStyle.equals("SSU"))
cfg = ctx.getProperty(SSU_IPV6_CONFIG); cfg = ctx.getProperty(SSU_IPV6_CONFIG);
else else
return IPv6Config.IPV6_DISABLED; return DEFAULT_IPV6_CONFIG;
return getIPv6Config(cfg); return getIPv6Config(cfg);
} }
public static IPv6Config getIPv6Config(String cfg) { public static IPv6Config getIPv6Config(String cfg) {
if (cfg == null) if (cfg == null)
return IPv6Config.IPV6_DISABLED; return DEFAULT_IPV6_CONFIG;
IPv6Config c = BY_NAME.get(cfg); IPv6Config c = BY_NAME.get(cfg);
if (c != null) if (c != null)
return c; return c;
return IPv6Config.IPV6_DISABLED; return DEFAULT_IPV6_CONFIG;
} }
/** /**

View File

@ -3,6 +3,7 @@ package net.i2p.router.transport.ntcp;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Inet6Address;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.channels.ServerSocketChannel; import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
@ -35,6 +36,8 @@ import net.i2p.router.transport.Transport;
import static net.i2p.router.transport.Transport.AddressSource.*; import static net.i2p.router.transport.Transport.AddressSource.*;
import net.i2p.router.transport.TransportBid; import net.i2p.router.transport.TransportBid;
import net.i2p.router.transport.TransportImpl; import net.i2p.router.transport.TransportImpl;
import net.i2p.router.transport.TransportUtil;
import static net.i2p.router.transport.TransportUtil.IPv6Config.*;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder; import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.router.transport.udp.UDPTransport; import net.i2p.router.transport.udp.UDPTransport;
import net.i2p.util.Addresses; import net.i2p.util.Addresses;
@ -350,7 +353,7 @@ public class NTCPTransport extends TransportImpl {
* @since 0.9.6 * @since 0.9.6
*/ */
private RouterAddress getTargetAddress(RouterInfo target) { private RouterAddress getTargetAddress(RouterInfo target) {
List<RouterAddress> addrs = target.getTargetAddresses(STYLE); List<RouterAddress> addrs = getTargetAddresses(target);
for (int i = 0; i < addrs.size(); i++) { for (int i = 0; i < addrs.size(); i++) {
RouterAddress addr = addrs.get(i); RouterAddress addr = addrs.get(i);
byte[] ip = addr.getIP(); byte[] ip = addr.getIP();
@ -501,7 +504,8 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties(); OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, ia.getHostAddress()); props.setProperty(RouterAddress.PROP_HOST, ia.getHostAddress());
props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port)); props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port));
myAddress = new RouterAddress(STYLE, props, DEFAULT_COST); int cost = getDefaultCost(ia instanceof Inet6Address);
myAddress = new RouterAddress(STYLE, props, cost);
replaceAddress(myAddress); replaceAddress(myAddress);
} }
} }
@ -604,7 +608,8 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties(); OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, bindTo); props.setProperty(RouterAddress.PROP_HOST, bindTo);
props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port)); props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port));
myAddress = new RouterAddress(STYLE, props, DEFAULT_COST); int cost = getDefaultCost(false);
myAddress = new RouterAddress(STYLE, props, cost);
} }
if (!_endpoints.isEmpty()) { if (!_endpoints.isEmpty()) {
// If we are already bound to the new address, OR // If we are already bound to the new address, OR
@ -778,10 +783,23 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties(); OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, name); props.setProperty(RouterAddress.PROP_HOST, name);
props.setProperty(RouterAddress.PROP_PORT, Integer.toString(p)); props.setProperty(RouterAddress.PROP_PORT, Integer.toString(p));
RouterAddress addr = new RouterAddress(STYLE, props, DEFAULT_COST); int cost = getDefaultCost(false);
RouterAddress addr = new RouterAddress(STYLE, props, cost);
return addr; return addr;
} }
private int getDefaultCost(boolean isIPv6) {
int rv = DEFAULT_COST;
if (isIPv6) {
TransportUtil.IPv6Config config = getIPv6Config();
if (config == IPV6_PREFERRED)
rv--;
else if (config == IPV6_NOT_PREFERRED)
rv++;
}
return rv;
}
/** /**
* UDP changed addresses, tell NTCP and (possibly) restart * UDP changed addresses, tell NTCP and (possibly) restart
* *
@ -831,7 +849,7 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties newProps = new OrderedProperties(); OrderedProperties newProps = new OrderedProperties();
int cost; int cost;
if (oldAddr == null) { if (oldAddr == null) {
cost = DEFAULT_COST; cost = getDefaultCost(ip != null && ip.length == 16);
} else { } else {
cost = oldAddr.getCost(); cost = oldAddr.getCost();
newProps.putAll(oldAddr.getOptionsMap()); newProps.putAll(oldAddr.getOptionsMap());
@ -930,21 +948,24 @@ public class NTCPTransport extends TransportImpl {
if (!changed) { if (!changed) {
if (oldAddr != null) { if (oldAddr != null) {
// change cost only?
int oldCost = oldAddr.getCost(); int oldCost = oldAddr.getCost();
int newCost = DEFAULT_COST; int newCost = getDefaultCost(ohost != null && ohost.contains(":"));
if (TransportImpl.ADJUST_COST && !haveCapacity()) if (ADJUST_COST && !haveCapacity())
newCost++; newCost += CONGESTION_COST_ADJUSTMENT;
if (newCost != oldCost) { if (newCost != oldCost) {
oldAddr.setCost(newCost); newAddr.setCost(newCost);
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))
_log.warn("Changing NTCP cost from " + oldCost + " to " + newCost); _log.warn("Changing NTCP cost from " + oldCost + " to " + newCost);
// fall thru and republish
} else { } else {
_log.info("No change to NTCP Address"); _log.info("No change to NTCP Address");
return;
} }
} else { } else {
_log.info("No change to NTCP Address"); _log.info("No change to NTCP Address");
return;
} }
return;
} }
// stopListening stops the pumper, readers, and writers, so required even if // stopListening stops the pumper, readers, and writers, so required even if

View File

@ -38,6 +38,8 @@ import net.i2p.router.transport.Transport;
import static net.i2p.router.transport.Transport.AddressSource.*; import static net.i2p.router.transport.Transport.AddressSource.*;
import net.i2p.router.transport.TransportBid; import net.i2p.router.transport.TransportBid;
import net.i2p.router.transport.TransportImpl; import net.i2p.router.transport.TransportImpl;
import net.i2p.router.transport.TransportUtil;
import static net.i2p.router.transport.TransportUtil.IPv6Config.*;
import static net.i2p.router.transport.udp.PeerTestState.Role.*; import static net.i2p.router.transport.udp.PeerTestState.Role.*;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder; import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.router.util.RandomIterator; import net.i2p.router.util.RandomIterator;
@ -1544,10 +1546,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @since 0.9.6 * @since 0.9.6
*/ */
RouterAddress getTargetAddress(RouterInfo target) { RouterAddress getTargetAddress(RouterInfo target) {
List<RouterAddress> addrs = target.getTargetAddresses(STYLE); List<RouterAddress> addrs = getTargetAddresses(target);
// Shuffle so everybody doesn't use the first one
if (addrs.size() > 1)
Collections.shuffle(addrs, _context.random());
for (int i = 0; i < addrs.size(); i++) { for (int i = 0; i < addrs.size(); i++) {
RouterAddress addr = addrs.get(i); RouterAddress addr = addrs.get(i);
if (addr.getOption("ihost0") == null) { if (addr.getOption("ihost0") == null) {
@ -1821,7 +1820,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// the whole mechanism is not helpful. // the whole mechanism is not helpful.
int cost = DEFAULT_COST; int cost = DEFAULT_COST;
if (ADJUST_COST && !haveCapacity(91)) if (ADJUST_COST && !haveCapacity(91))
cost++; cost += CONGESTION_COST_ADJUSTMENT;
if (introducersIncluded)
cost += 2;
if (isIPv6) {
TransportUtil.IPv6Config config = getIPv6Config();
if (config == IPV6_PREFERRED)
cost--;
else if (config == IPV6_NOT_PREFERRED)
cost++;
}
RouterAddress addr = new RouterAddress(STYLE, options, cost); RouterAddress addr = new RouterAddress(STYLE, options, cost);
RouterAddress current = getCurrentAddress(isIPv6); RouterAddress current = getCurrentAddress(isIPv6);
@ -2996,7 +3004,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (peerInfo == null) if (peerInfo == null)
continue; continue;
ip = null; ip = null;
List<RouterAddress> addrs = peerInfo.getTargetAddresses(STYLE); List<RouterAddress> addrs = getTargetAddresses(peerInfo);
for (RouterAddress addr : addrs) { for (RouterAddress addr : addrs) {
ip = addr.getIP(); ip = addr.getIP();
if (ip != null && ip.length == 4) if (ip != null && ip.length == 4)