diff --git a/core/java/src/net/i2p/data/RouterAddress.java b/core/java/src/net/i2p/data/RouterAddress.java index adec96f6dd..0b6fde6e0f 100644 --- a/core/java/src/net/i2p/data/RouterAddress.java +++ b/core/java/src/net/i2p/data/RouterAddress.java @@ -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); diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java index c6d49580f7..57bfeeea34 100644 --- a/router/java/src/net/i2p/router/transport/TransportImpl.java +++ b/router/java/src/net/i2p/router/transport/TransportImpl.java @@ -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 getTargetAddresses(RouterInfo target) { + List 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 { + 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. diff --git a/router/java/src/net/i2p/router/transport/TransportUtil.java b/router/java/src/net/i2p/router/transport/TransportUtil.java index 86f081daa4..b41a72e0e5 100644 --- a/router/java/src/net/i2p/router/transport/TransportUtil.java +++ b/router/java/src/net/i2p/router/transport/TransportUtil.java @@ -52,6 +52,7 @@ public abstract class TransportUtil { } private static final Map BY_NAME = new HashMap(); + 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; } /** diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java index 20b00f3999..2d808c5bbc 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -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 addrs = target.getTargetAddresses(STYLE); + List 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 diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index 726c619467..ca36c3d1b6 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -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 addrs = target.getTargetAddresses(STYLE); - // Shuffle so everybody doesn't use the first one - if (addrs.size() > 1) - Collections.shuffle(addrs, _context.random()); + List 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 addrs = peerInfo.getTargetAddresses(STYLE); + List addrs = getTargetAddresses(peerInfo); for (RouterAddress addr : addrs) { ip = addr.getIP(); if (ip != null && ip.length == 4)