* 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
*/
public class RouterAddress extends DataStructureImpl {
private int _cost;
private short _cost;
//private Date _expiration;
private String _transportStyle;
private final Properties _options;
@ -50,19 +50,21 @@ public class RouterAddress extends DataStructureImpl {
public static final String PROP_PORT = "port";
public RouterAddress() {
_cost = -1;
_options = new OrderedProperties();
}
/**
* For efficiency when created by a Transport.
* @param options not copied; do not reuse or modify
* @param cost 0-255
* @since IPv6
*/
public RouterAddress(String style, OrderedProperties options, int cost) {
_transportStyle = style;
_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.
*
* Unused before 0.7.12
* @return 0-255
*/
public int getCost() {
return _cost;
@ -78,12 +81,18 @@ public class RouterAddress extends DataStructureImpl {
/**
* 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
*/
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
*
* @throws IllegalStateException if was already set
* @deprecated unused, use 3-arg constructor
*/
public void setTransportStyle(String transportStyle) {
if (_transportStyle != null)
@ -163,6 +173,7 @@ public class RouterAddress extends DataStructureImpl {
* Makes a copy.
* @param options non-null
* @throws IllegalStateException if was already set
* @deprecated unused, use 3-arg constructor
*/
public void setOptions(Properties options) {
if (!_options.isEmpty())
@ -234,7 +245,7 @@ public class RouterAddress extends DataStructureImpl {
public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_transportStyle != null)
throw new IllegalStateException();
_cost = (int) DataHelper.readLong(in, 1);
_cost = (short) DataHelper.readLong(in, 1);
//_expiration = DataHelper.readDate(in);
DataHelper.readDate(in);
_transportStyle = DataHelper.readString(in);
@ -251,8 +262,8 @@ public class RouterAddress extends DataStructureImpl {
* readin and the signature will fail.
*/
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ((_cost < 0) || (_transportStyle == null))
throw new DataFormatException("Not enough data to write a router address");
if (_transportStyle == null)
throw new DataFormatException("uninitialized");
DataHelper.writeLong(out, 1, _cost);
//DataHelper.writeDate(out, _expiration);
DataHelper.writeDate(out, null);

View File

@ -14,6 +14,7 @@ import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
@ -471,7 +472,8 @@ public abstract class TransportImpl implements Transport {
}
/** 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?
@ -563,6 +565,67 @@ public abstract class TransportImpl implements Transport {
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.
* 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 IPv6Config DEFAULT_IPV6_CONFIG = IPv6Config.IPV6_DISABLED;
static {
for (IPv6Config cfg : IPv6Config.values()) {
@ -68,17 +69,17 @@ public abstract class TransportUtil {
else if (transportStyle.equals("SSU"))
cfg = ctx.getProperty(SSU_IPV6_CONFIG);
else
return IPv6Config.IPV6_DISABLED;
return DEFAULT_IPV6_CONFIG;
return getIPv6Config(cfg);
}
public static IPv6Config getIPv6Config(String cfg) {
if (cfg == null)
return IPv6Config.IPV6_DISABLED;
return DEFAULT_IPV6_CONFIG;
IPv6Config c = BY_NAME.get(cfg);
if (c != null)
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.net.InetSocketAddress;
import java.net.InetAddress;
import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
@ -35,6 +36,8 @@ import net.i2p.router.transport.Transport;
import static net.i2p.router.transport.Transport.AddressSource.*;
import net.i2p.router.transport.TransportBid;
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.udp.UDPTransport;
import net.i2p.util.Addresses;
@ -350,7 +353,7 @@ public class NTCPTransport extends TransportImpl {
* @since 0.9.6
*/
private RouterAddress getTargetAddress(RouterInfo target) {
List<RouterAddress> addrs = target.getTargetAddresses(STYLE);
List<RouterAddress> addrs = getTargetAddresses(target);
for (int i = 0; i < addrs.size(); i++) {
RouterAddress addr = addrs.get(i);
byte[] ip = addr.getIP();
@ -501,7 +504,8 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, ia.getHostAddress());
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);
}
}
@ -604,7 +608,8 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, bindTo);
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 we are already bound to the new address, OR
@ -778,10 +783,23 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, name);
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;
}
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
*
@ -831,7 +849,7 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties newProps = new OrderedProperties();
int cost;
if (oldAddr == null) {
cost = DEFAULT_COST;
cost = getDefaultCost(ip != null && ip.length == 16);
} else {
cost = oldAddr.getCost();
newProps.putAll(oldAddr.getOptionsMap());
@ -930,21 +948,24 @@ public class NTCPTransport extends TransportImpl {
if (!changed) {
if (oldAddr != null) {
// change cost only?
int oldCost = oldAddr.getCost();
int newCost = DEFAULT_COST;
if (TransportImpl.ADJUST_COST && !haveCapacity())
newCost++;
int newCost = getDefaultCost(ohost != null && ohost.contains(":"));
if (ADJUST_COST && !haveCapacity())
newCost += CONGESTION_COST_ADJUSTMENT;
if (newCost != oldCost) {
oldAddr.setCost(newCost);
newAddr.setCost(newCost);
if (_log.shouldLog(Log.WARN))
_log.warn("Changing NTCP cost from " + oldCost + " to " + newCost);
// fall thru and republish
} else {
_log.info("No change to NTCP Address");
return;
}
} else {
_log.info("No change to NTCP Address");
return;
}
return;
}
// 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 net.i2p.router.transport.TransportBid;
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 net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.router.util.RandomIterator;
@ -1544,10 +1546,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @since 0.9.6
*/
RouterAddress getTargetAddress(RouterInfo target) {
List<RouterAddress> addrs = target.getTargetAddresses(STYLE);
// Shuffle so everybody doesn't use the first one
if (addrs.size() > 1)
Collections.shuffle(addrs, _context.random());
List<RouterAddress> addrs = getTargetAddresses(target);
for (int i = 0; i < addrs.size(); i++) {
RouterAddress addr = addrs.get(i);
if (addr.getOption("ihost0") == null) {
@ -1821,7 +1820,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// the whole mechanism is not helpful.
int cost = DEFAULT_COST;
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 current = getCurrentAddress(isIPv6);
@ -2996,7 +3004,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (peerInfo == null)
continue;
ip = null;
List<RouterAddress> addrs = peerInfo.getTargetAddresses(STYLE);
List<RouterAddress> addrs = getTargetAddresses(peerInfo);
for (RouterAddress addr : addrs) {
ip = addr.getIP();
if (ip != null && ip.length == 4)