forked from I2P_Developers/i2p.i2p
Transport: Fixes for NTCP when SSU disabled (ticket #1417)
Delay port forwarding until after UPnP rescan complete WIP
This commit is contained in:
@ -1179,6 +1179,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
synchronized(_configFileLock) {
|
||||
removeConfigSetting(UDPTransport.PROP_INTERNAL_PORT);
|
||||
removeConfigSetting(UDPTransport.PROP_EXTERNAL_PORT);
|
||||
removeConfigSetting(NTCPTransport.PROP_I2NP_NTCP_PORT);
|
||||
removeConfigSetting(NTCPTransport.PROP_NTCP2_SP);
|
||||
removeConfigSetting(NTCPTransport.PROP_NTCP2_IV);
|
||||
removeConfigSetting(PROP_IB_RANDOM_KEY);
|
||||
|
@ -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 = 7;
|
||||
public final static long BUILD = 8;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
@ -42,6 +42,8 @@ import net.i2p.router.transport.ntcp.NTCPTransport;
|
||||
import net.i2p.router.transport.udp.UDPTransport;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
import net.i2p.util.SystemVersion;
|
||||
import net.i2p.util.Translate;
|
||||
import net.i2p.util.VersionComparator;
|
||||
@ -72,6 +74,7 @@ public class TransportManager implements TransportEventListener {
|
||||
private final X25519KeyFactory _xdhThread;
|
||||
private final boolean _enableUDP;
|
||||
private final boolean _enableNTCP1;
|
||||
private boolean _upnpUpdateQueued;
|
||||
|
||||
/** default true */
|
||||
public final static String PROP_ENABLE_UDP = "i2np.udp.enable";
|
||||
@ -198,6 +201,11 @@ public class TransportManager implements TransportEventListener {
|
||||
int port = udp.getRequestedPort();
|
||||
if (port > 0)
|
||||
ntcp.externalAddressReceived(SOURCE_CONFIG, (byte[]) null, port);
|
||||
} else {
|
||||
// SSU disabled
|
||||
int port = ntcp.getRequestedPort();
|
||||
if (port > 0)
|
||||
ntcp.externalAddressReceived(SOURCE_CONFIG, (byte[]) null, port);
|
||||
}
|
||||
}
|
||||
if (_transports.isEmpty())
|
||||
@ -661,7 +669,7 @@ public class TransportManager implements TransportEventListener {
|
||||
_context.getBooleanProperty(NTCPTransport.PROP_I2NP_NTCP_AUTO_PORT)) {
|
||||
Transport udp = getTransport(UDPTransport.STYLE);
|
||||
if (udp != null)
|
||||
port = t.getRequestedPort();
|
||||
port = udp.getRequestedPort();
|
||||
}
|
||||
if (port > 0)
|
||||
rv.add(new Port(t.getStyle(), port));
|
||||
@ -806,9 +814,32 @@ public class TransportManager implements TransportEventListener {
|
||||
*/
|
||||
public void transportAddressChanged() {
|
||||
if (_upnpManager != null) {
|
||||
_upnpManager.rescan();
|
||||
// should really delay the following by 5 seconds?
|
||||
_upnpManager.update(getPorts());
|
||||
synchronized (_upnpManager) {
|
||||
if (!_upnpUpdateQueued) {
|
||||
boolean shouldWait = _upnpManager.rescan();
|
||||
if (shouldWait) {
|
||||
// Delay until the rescan finishes, MX time + 250
|
||||
_upnpUpdateQueued = true;
|
||||
_context.simpleTimer2().addEvent(new UpdatePorts(), 3250);
|
||||
} else {
|
||||
_upnpManager.update(getPorts());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delayed update of UPnP ports
|
||||
*
|
||||
* @since 0.9.39
|
||||
*/
|
||||
private class UpdatePorts implements SimpleTimer.TimedEvent {
|
||||
public void timeReached() {
|
||||
synchronized (_upnpManager) {
|
||||
_upnpUpdateQueued = false;
|
||||
_upnpManager.update(getPorts());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,12 @@ package net.i2p.router.transport;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
/**
|
||||
@ -27,6 +29,15 @@ public abstract class TransportUtil {
|
||||
public static final String PROP_IPV4_FIREWALLED = "i2np.ipv4.firewalled";
|
||||
/** @since 0.9.28 */
|
||||
public static final String PROP_IPV6_FIREWALLED = "i2np.ipv6.firewalled";
|
||||
private static final String PROP_PORT_PFX = "i2np.";
|
||||
private static final String PROP_MIN_PORT_SFX = ".minPort";
|
||||
private static final String PROP_MAX_PORT_SFX = ".maxPort";
|
||||
/**
|
||||
* 8998 is monotone, and 31000 is the wrapper outbound, so let's stay between those
|
||||
* Was 9111, increase to skip Tor browser at 9050
|
||||
*/
|
||||
private static final int MIN_RANDOM_PORT = 9151;
|
||||
private static final int MAX_RANDOM_PORT = 30777;
|
||||
|
||||
public enum IPv6Config {
|
||||
/** IPv6 disabled */
|
||||
@ -250,4 +261,29 @@ public abstract class TransportUtil {
|
||||
port != 31000 && // Wrapper
|
||||
port != 32000; // Wrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* log an error
|
||||
* @since 0.9.39 pulled out of UDPEndpoint
|
||||
*/
|
||||
public static void logInvalidPort(Log log, String transportStyle, int port) {
|
||||
log.error("Specified " + transportStyle + " port " + port + " is not valid, selecting a new port");
|
||||
log.error("Invalid ports are: 0-1023, 1900, 2049, 2827, 3659, 4045, 4444, 4445, 6000, 6665-6669, 6697, 7650-7668, 8998, 9001, 9030, 9050, 9100, 9150, 31000, 32000, 65536+");
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a random port between the configured boundaries
|
||||
* @since IPv6, moved from UDPEndpoint in 0.9.39 to support NTCP also
|
||||
*/
|
||||
public static int selectRandomPort(RouterContext ctx, String transportStyle) {
|
||||
if (transportStyle.equals("SSU"))
|
||||
transportStyle = "udp";
|
||||
else
|
||||
transportStyle = transportStyle.toLowerCase(Locale.US);
|
||||
String minprop = PROP_PORT_PFX + transportStyle + PROP_MIN_PORT_SFX;
|
||||
String maxprop = PROP_PORT_PFX + transportStyle + PROP_MAX_PORT_SFX;
|
||||
int minPort = Math.min(65535, Math.max(1, ctx.getProperty(minprop, MIN_RANDOM_PORT)));
|
||||
int maxPort = Math.min(65535, Math.max(minPort, ctx.getProperty(maxprop, MAX_RANDOM_PORT)));
|
||||
return minPort + ctx.random().nextInt(1 + maxPort - minPort);
|
||||
}
|
||||
}
|
||||
|
@ -143,16 +143,17 @@ class UPnPManager {
|
||||
* Should be fast. This only starts the search, the responses
|
||||
* will come in over the MX time (3 seconds).
|
||||
*
|
||||
* @return true if a rescan was actually fired off
|
||||
* @since 0.9.18
|
||||
*/
|
||||
public synchronized void rescan() {
|
||||
public synchronized boolean rescan() {
|
||||
if (!_shouldBeRunning)
|
||||
return;
|
||||
return false;
|
||||
if (_context.router().gracefulShutdownInProgress())
|
||||
return;
|
||||
return false;
|
||||
long now = System.currentTimeMillis();
|
||||
if (_lastRescan + RESCAN_MIN_DELAY > now)
|
||||
return;
|
||||
return false;
|
||||
_lastRescan = now;
|
||||
if (_isRunning) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -165,6 +166,7 @@ class UPnPManager {
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -195,7 +197,7 @@ class UPnPManager {
|
||||
*/
|
||||
public void update(Set<TransportManager.Port> ports) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("UPnP Update with " + ports.size() + " ports");
|
||||
_log.debug("UPnP Update with " + ports.size() + " ports", new Exception("I did it"));
|
||||
|
||||
//synchronized(this) {
|
||||
// TODO
|
||||
@ -240,7 +242,7 @@ class UPnPManager {
|
||||
/** Called to indicate status on one or more forwarded ports. */
|
||||
public void portForwardStatus(Map<ForwardPort,ForwardPortStatus> statuses) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("UPnP Callback:");
|
||||
_log.debug("UPnP Callback: with " + statuses.size() + " statuses");
|
||||
// Let's not have two of these running at once.
|
||||
// Deadlock reported in ticket #1699
|
||||
// and the locking isn't foolproof in UDPTransport.
|
||||
@ -254,7 +256,7 @@ class UPnPManager {
|
||||
private void locked_PFS(Map<ForwardPort,ForwardPortStatus> statuses) {
|
||||
byte[] ipaddr = null;
|
||||
DetectedIP[] ips = _upnp.getAddress();
|
||||
if (ips != null) {
|
||||
if (ips != null && ips.length > 0) {
|
||||
for (DetectedIP ip : ips) {
|
||||
// store the first public one and tell the transport manager if it changed
|
||||
// Note that getAddress() will actually return a max of one address.
|
||||
@ -268,6 +270,9 @@ class UPnPManager {
|
||||
}
|
||||
ipaddr = ip.publicAddress.getAddress();
|
||||
break;
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Unusable external address: " + ip.publicAddress + " type: " + ip.natType);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -275,19 +280,28 @@ class UPnPManager {
|
||||
_log.debug("No external address returned");
|
||||
}
|
||||
|
||||
if (statuses.isEmpty()) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("No statuses returned");
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<ForwardPort, ForwardPortStatus> entry : statuses.entrySet()) {
|
||||
ForwardPort fp = entry.getKey();
|
||||
ForwardPortStatus fps = entry.getValue();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(fp.name + " " + fp.protocol + " " + fp.portNumber +
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("FPS: " + fp.name + ' ' + fp.protocol + ' ' + fp.portNumber +
|
||||
" status: " + fps.status + " reason: " + fps.reasonString + " ext port: " + fps.externalPort);
|
||||
String style;
|
||||
if (fp.protocol == ForwardPort.PROTOCOL_UDP_IPV4)
|
||||
if (fp.protocol == ForwardPort.PROTOCOL_UDP_IPV4) {
|
||||
style = "SSU";
|
||||
else if (fp.protocol == ForwardPort.PROTOCOL_TCP_IPV4)
|
||||
} else if (fp.protocol == ForwardPort.PROTOCOL_TCP_IPV4) {
|
||||
style = "NTCP";
|
||||
else
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.debug("Unknown protocol " + fp.protocol);
|
||||
continue;
|
||||
}
|
||||
boolean success = fps.status >= ForwardPortStatus.MAYBE_SUCCESS;
|
||||
// deadlock path 2
|
||||
_manager.forwardPortStatus(style, ipaddr, fp.portNumber, fps.externalPort, success, fps.reasonString);
|
||||
|
@ -47,6 +47,7 @@ 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.TransportManager;
|
||||
import net.i2p.router.transport.TransportUtil;
|
||||
import static net.i2p.router.transport.TransportUtil.IPv6Config.*;
|
||||
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
|
||||
@ -240,6 +241,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
_nearCapacityCostBid = new SharedBid(105);
|
||||
_transientFail = new SharedBid(TransportBid.TRANSIENT_FAIL);
|
||||
|
||||
setupPort();
|
||||
_enableNTCP1 = dh != null;
|
||||
_enableNTCP2 = xdh != null;
|
||||
if (!_enableNTCP1 && !_enableNTCP2)
|
||||
@ -305,6 +307,28 @@ public class NTCPTransport extends TransportImpl {
|
||||
_b64Ntcp2StaticIV = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a port if not previously configured.
|
||||
* Only if UDP is disabled.
|
||||
*
|
||||
* @since 0.9.39
|
||||
*/
|
||||
private void setupPort() {
|
||||
if (_context.getBooleanPropertyDefaultTrue(TransportManager.PROP_ENABLE_UDP))
|
||||
return;
|
||||
int port = getRequestedPort();
|
||||
if (port > 0 && !TransportUtil.isValidPort(port)) {
|
||||
TransportUtil.logInvalidPort(_log, STYLE, port);
|
||||
}
|
||||
if (port <= 0) {
|
||||
port = TransportUtil.selectRandomPort(_context, STYLE);
|
||||
Map<String, String> changes = new HashMap<String, String>(2);
|
||||
changes.put(PROP_I2NP_NTCP_PORT, Integer.toString(port));
|
||||
_context.router().saveConfig(changes, null);
|
||||
_log.logAlways(Log.INFO, "NTCP selected random port " + port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param con that is established
|
||||
@ -999,6 +1023,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
_log.error("Specified NTCP port is " + port + ", ports lower than 1024 not recommended");
|
||||
ServerSocketChannel chan = ServerSocketChannel.open();
|
||||
chan.configureBlocking(false);
|
||||
// TODO retry
|
||||
chan.socket().bind(addr);
|
||||
_endpoints.add(addr);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@ -1437,8 +1462,9 @@ public class NTCPTransport extends TransportImpl {
|
||||
}
|
||||
return;
|
||||
}
|
||||
// ignore UPnP for now, get everything from SSU
|
||||
if (source != SOURCE_SSU)
|
||||
// ignore UPnP for now, get everything from SSU if it's enabled
|
||||
if (source != SOURCE_SSU &&
|
||||
_context.getBooleanPropertyDefaultTrue(TransportManager.PROP_ENABLE_UDP))
|
||||
return;
|
||||
boolean isIPv6 = ip != null && ip.length == 16;
|
||||
externalAddressReceived(ip, isIPv6, port);
|
||||
@ -1464,8 +1490,9 @@ public class NTCPTransport extends TransportImpl {
|
||||
public void externalAddressRemoved(AddressSource source, boolean ipv6) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Removing address, ipv6? " + ipv6 + " from: " + source, new Exception());
|
||||
// ignore UPnP for now, get everything from SSU
|
||||
if (source != SOURCE_SSU)
|
||||
// ignore UPnP for now, get everything from SSU if it's enabled
|
||||
if (source != SOURCE_SSU &&
|
||||
_context.getBooleanPropertyDefaultTrue(TransportManager.PROP_ENABLE_UDP))
|
||||
return;
|
||||
externalAddressReceived(null, ipv6, 0);
|
||||
}
|
||||
@ -1659,6 +1686,14 @@ public class NTCPTransport extends TransportImpl {
|
||||
else
|
||||
_log.warn("UPnP has failed to open the NTCP port: " + port + " reason: " + reason);
|
||||
}
|
||||
// ignore UPnP for now, get everything from SSU if it's enabled
|
||||
if (!_context.getBooleanPropertyDefaultTrue(TransportManager.PROP_ENABLE_UDP)) {
|
||||
// TODO
|
||||
//if (success && ip != null && getExternalIP() != null) {
|
||||
// if (!isIPv4Firewalled())
|
||||
// setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,12 +97,6 @@ class UDPEndpoint implements SocketListener {
|
||||
}
|
||||
********/
|
||||
|
||||
/** 8998 is monotone, and 31000 is the wrapper outbound, so let's stay between those */
|
||||
public static final String PROP_MIN_PORT = "i2np.udp.minPort";
|
||||
public static final String PROP_MAX_PORT = "i2np.udp.maxPort";
|
||||
/** Was 9111, increase to skip Tor browser at 9050 */
|
||||
private static final int MIN_RANDOM_PORT = 9151;
|
||||
private static final int MAX_RANDOM_PORT = 30777;
|
||||
private static final int MAX_PORT_RETRIES = 20;
|
||||
|
||||
/**
|
||||
@ -115,9 +109,7 @@ class UDPEndpoint implements SocketListener {
|
||||
DatagramSocket socket = null;
|
||||
int port = _listenPort;
|
||||
if (port > 0 && !TransportUtil.isValidPort(port)) {
|
||||
_log.error("Specified UDP port " + port + " is not valid, selecting a new port");
|
||||
// See isValidPort() for list
|
||||
_log.error("Invalid ports are: 0-1023, 1900, 2049, 2827, 3659, 4045, 4444, 4445, 6000, 6665-6669, 6697, 7650-7668, 8998, 9001, 9030, 9050, 9100, 9150, 31000, 32000, 65536+");
|
||||
TransportUtil.logInvalidPort(_log, "UDP", port);
|
||||
port = -1;
|
||||
}
|
||||
|
||||
@ -125,7 +117,7 @@ class UDPEndpoint implements SocketListener {
|
||||
if (port <= 0) {
|
||||
// try random ports rather than just do new DatagramSocket()
|
||||
// so we stay out of the way of other I2P stuff
|
||||
port = selectRandomPort(_context);
|
||||
port = TransportUtil.selectRandomPort(_context, UDPTransport.STYLE);
|
||||
}
|
||||
try {
|
||||
if (_bindAddress == null)
|
||||
@ -151,16 +143,6 @@ class UDPEndpoint implements SocketListener {
|
||||
return socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a random port between the configured boundaries
|
||||
* @since IPv6
|
||||
*/
|
||||
public static int selectRandomPort(RouterContext ctx) {
|
||||
int minPort = Math.min(65535, Math.max(1, ctx.getProperty(PROP_MIN_PORT, MIN_RANDOM_PORT)));
|
||||
int maxPort = Math.min(65535, Math.max(minPort, ctx.getProperty(PROP_MAX_PORT, MAX_RANDOM_PORT)));
|
||||
return minPort + ctx.random().nextInt(1 + maxPort - minPort);
|
||||
}
|
||||
|
||||
|
||||
/** call after startup() to get actual port or -1 on startup failure */
|
||||
public int getListenPort() { return _listenPort; }
|
||||
|
@ -308,8 +308,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
*/
|
||||
private void setupPort() {
|
||||
int port = getRequestedPort();
|
||||
if (port < 0) {
|
||||
port = UDPEndpoint.selectRandomPort(_context);
|
||||
if (port <= 0) {
|
||||
port = TransportUtil.selectRandomPort(_context, STYLE);
|
||||
Map<String, String> changes = new HashMap<String, String>(2);
|
||||
changes.put(PROP_INTERNAL_PORT, Integer.toString(port));
|
||||
changes.put(PROP_EXTERNAL_PORT, Integer.toString(port));
|
||||
|
Reference in New Issue
Block a user