Transport: Fixes for NTCP when SSU disabled (ticket #1417)

Delay port forwarding until after UPnP rescan complete
WIP
This commit is contained in:
zzz
2019-02-18 17:55:17 +00:00
parent d244d17363
commit 2d67d11537
9 changed files with 153 additions and 43 deletions

View File

@ -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);

View File

@ -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 = "";

View File

@ -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());
}
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
//}
}
}
/**

View File

@ -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; }

View File

@ -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));