forked from I2P_Developers/i2p.i2p
UPnP: IPv6 part 8
Retain old UID when refreshing Continue to forward deprecated addresses for a while Refresh mappings before lease expiration Log tweaks
This commit is contained in:
@@ -74,6 +74,7 @@ public class TransportManager implements TransportEventListener {
|
||||
private final Map<String, Transport> _pluggableTransports;
|
||||
private final RouterContext _context;
|
||||
private final UPnPManager _upnpManager;
|
||||
private final SimpleTimer2.TimedEvent _upnpRefresher;
|
||||
private final DHSessionKeyBuilder.PrecalcRunner _dhThread;
|
||||
private final X25519KeyFactory _xdhThread;
|
||||
private final boolean _enableUDP;
|
||||
@@ -104,6 +105,8 @@ public class TransportManager implements TransportEventListener {
|
||||
/** not forever, since they may update */
|
||||
private static final long SIGTYPE_BANLIST_DURATION = 36*60*60*1000L;
|
||||
|
||||
private static final long UPNP_REFRESH_TIME = UPnP.LEASE_TIME_SECONDS * 1000L / 3;
|
||||
|
||||
public TransportManager(RouterContext context) {
|
||||
_context = context;
|
||||
_log = _context.logManager().getLog(TransportManager.class);
|
||||
@@ -120,6 +123,7 @@ public class TransportManager implements TransportEventListener {
|
||||
boolean isProxied = isProxied();
|
||||
boolean enableUPnP = !isProxied && _context.getBooleanPropertyDefaultTrue(PROP_ENABLE_UPNP);
|
||||
_upnpManager = enableUPnP ? new UPnPManager(context, this) : null;
|
||||
_upnpRefresher = enableUPnP ? new UPnPRefresher() : null;
|
||||
_enableUDP = _context.getBooleanPropertyDefaultTrue(PROP_ENABLE_UDP);
|
||||
_enableNTCP1 = isNTCPEnabled(context) &&
|
||||
context.getProperty(PROP_NTCP1_ENABLE, DEFAULT_NTCP1_ENABLE);
|
||||
@@ -453,8 +457,10 @@ public class TransportManager implements TransportEventListener {
|
||||
// Always start on Android, as we may have a cellular IPv4 address but
|
||||
// are routing all traffic through WiFi.
|
||||
// Also, conditions may change rapidly.
|
||||
if (_upnpManager != null && (SystemVersion.isAndroid() || Addresses.getAnyAddress() == null))
|
||||
if (_upnpManager != null && (SystemVersion.isAndroid() || Addresses.getAnyAddress() == null)) {
|
||||
_upnpManager.start();
|
||||
_upnpRefresher.schedule(UPNP_REFRESH_TIME);
|
||||
}
|
||||
configTransports();
|
||||
_log.debug("Starting up the transport manager");
|
||||
// Let's do this in a predictable order to make testing easier
|
||||
@@ -491,8 +497,10 @@ public class TransportManager implements TransportEventListener {
|
||||
* Can be restarted.
|
||||
*/
|
||||
synchronized void stopListening() {
|
||||
if (_upnpManager != null)
|
||||
if (_upnpManager != null) {
|
||||
_upnpRefresher.cancel();
|
||||
_upnpManager.stop();
|
||||
}
|
||||
for (Transport t : _transports.values()) {
|
||||
t.stopListening();
|
||||
}
|
||||
@@ -989,6 +997,23 @@ public class TransportManager implements TransportEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Periodic refresh of UPnP ports.
|
||||
* This is required because UPnP leases expire.
|
||||
* UPnPManager.Rescanner finds new devices but does not refresh the ports.
|
||||
* Caller must schedule.
|
||||
*
|
||||
* @since 0.9.50
|
||||
*/
|
||||
private class UPnPRefresher extends SimpleTimer2.TimedEvent {
|
||||
public UPnPRefresher() { super(_context.simpleTimer2()); }
|
||||
|
||||
public void timeReached() {
|
||||
transportAddressChanged();
|
||||
reschedule(UPNP_REFRESH_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> getMostRecentErrorMessages() {
|
||||
List<String> rv = new ArrayList<String>(16);
|
||||
for (Transport t : _transports.values()) {
|
||||
|
||||
@@ -5,6 +5,7 @@ package net.i2p.router.transport;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.UnknownHostException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
@@ -101,6 +102,8 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
private static final String WAN_IP_CONNECTION_2 = "urn:schemas-upnp-org:service:WANIPConnection:2";
|
||||
private static final String WAN_IPV6_CONNECTION = "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1";
|
||||
|
||||
public static final int LEASE_TIME_SECONDS = 3*60*60;
|
||||
|
||||
private Device _router;
|
||||
private Service _service;
|
||||
private Service _service6;
|
||||
@@ -1201,7 +1204,14 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
synchronized(lock) {
|
||||
for(ForwardPort port : portsToForward) {
|
||||
sb.append("<br>");
|
||||
sb.append(port.isIP6 ? "IPv6: " : "IPv4: ");
|
||||
if (port.isIP6) {
|
||||
sb.append("IPv6 ");
|
||||
sb.append(((IPv6ForwardPort) port).getIP()).append(' ');
|
||||
} else {
|
||||
sb.append("IPv4 ");
|
||||
if (addr != null)
|
||||
sb.append(DataHelper.escapeHTML(addr)).append(' ');
|
||||
}
|
||||
if(portsForwarded.contains(port))
|
||||
// {0} is TCP or UDP
|
||||
// {1,number,#####} prevents 12345 from being output as 12,345 in the English locale.
|
||||
@@ -1295,7 +1305,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
add.setArgumentValue("NewEnabled","1");
|
||||
// 3 hours
|
||||
// MUST be longer than max RI republish which is 52 minutes
|
||||
int leaseTime = _permanentLeasesOnly ? 0 : 3*60*60;
|
||||
int leaseTime = _permanentLeasesOnly ? 0 : LEASE_TIME_SECONDS;
|
||||
add.setArgumentValue("NewLeaseDuration", leaseTime);
|
||||
|
||||
boolean rv = add.postControlAction();
|
||||
@@ -1370,8 +1380,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
add.setArgumentValue("InternalPort", port);
|
||||
add.setArgumentValue("Protocol", fp.protocol);
|
||||
// permanent leases aren't supported by miniupnpd anyway
|
||||
int leaseTime = 3*60*60;
|
||||
add.setArgumentValue("LeaseTime", leaseTime);
|
||||
add.setArgumentValue("LeaseTime", LEASE_TIME_SECONDS);
|
||||
int uid = fp.getUID();
|
||||
if (uid < 0) {
|
||||
uid = getNewUID();
|
||||
@@ -1398,7 +1407,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
int newuid = Integer.parseInt(a.getValue());
|
||||
if (newuid != uid) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Updated UID from " + uid + " to " + newuid);
|
||||
_log.warn("Updating UID from " + uid + " to " + newuid + " for " + fp);
|
||||
fp.setUID(newuid);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {}
|
||||
@@ -1410,7 +1419,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
int level = rv ? Log.INFO : Log.WARN;
|
||||
if (_log.shouldLog(level)) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("AddPinhole result for ").append(ip).append(' ').append(fp.protocol).append(" port ").append(port);
|
||||
buf.append("AddPinhole result for ").append(fp.toString());
|
||||
UPnPStatus status = add.getStatus();
|
||||
if (status != null)
|
||||
buf.append(" Status: ").append(status.getCode()).append(' ').append(status.getDescription());
|
||||
@@ -1621,10 +1630,10 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
if (!noLog && _log.shouldLog(level)) {
|
||||
String ip = fp.getIP();
|
||||
if (retval) {
|
||||
_log.log(level, "UPnP: Removed IPv6 mapping for " + fp.name + ' ' + ip + ' ' + port + " / " + protocol);
|
||||
_log.log(level, "UPnP: Removed IPv6 mapping for " + fp);
|
||||
} else {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("UPnP: Failed to remove IPv6 mapping for ").append(fp.getIP()).append(" port ").append(fp.portNumber).append(" / ").append(protocol);
|
||||
buf.append("UPnP: Failed to remove IPv6 mapping for ").append(fp.toString());
|
||||
UPnPStatus status = remove.getStatus();
|
||||
if (status != null)
|
||||
buf.append(" Status: ").append(status.getCode()).append(' ').append(status.getDescription());
|
||||
@@ -1662,6 +1671,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
portsToForward.clear();
|
||||
portsToForwardNow = null;
|
||||
} else {
|
||||
portsToForwardNow = new HashSet<ForwardPort>();
|
||||
// Some ports to keep, some ports to dump
|
||||
// Ports in ports but not in portsToForwardNow we must forward
|
||||
// Ports in portsToForwardNow but not in ports we must dump
|
||||
@@ -1676,19 +1686,55 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
// Do we need to re-forward anyway? or poll the router?
|
||||
//} else {
|
||||
// Needs forwarding
|
||||
if(portsToForwardNow == null) portsToForwardNow = new HashSet<ForwardPort>();
|
||||
portsToForwardNow.add(port);
|
||||
//}
|
||||
}
|
||||
for(ForwardPort port : portsToForward) {
|
||||
if(ports.contains(port)) {
|
||||
// Should be forwarded, has been forwarded, cool.
|
||||
if (port.isIP6) {
|
||||
// copy over uid and expiration from existing
|
||||
ports.remove(port);
|
||||
ports.add(port);
|
||||
portsToForwardNow.remove(port);
|
||||
portsToForwardNow.add(port);
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Retaining: " + port);
|
||||
}
|
||||
} else {
|
||||
// TODO don't dump old ipv6 immediately if temporary
|
||||
boolean keep = false;
|
||||
if (port.isIP6) {
|
||||
// Don't dump old ipv6 immediately if deprecated
|
||||
IPv6ForwardPort v6port = (IPv6ForwardPort) port;
|
||||
long now = _context.clock().now();
|
||||
long exp = v6port.getExpiration();
|
||||
if (exp > 0) {
|
||||
keep = exp < now;
|
||||
} else {
|
||||
try {
|
||||
Inet6Address v6addr = (Inet6Address) InetAddress.getByName(v6port.getIP());
|
||||
// Addresses caches the result, so don't use isDeprecated(), it may not be current
|
||||
if (Addresses.isTemporary(v6addr)) {
|
||||
v6port.setExpiration(now + 24*60*60*1000L);
|
||||
keep = true;
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Address now deprecated, continue forwarding for 24h: " + v6port);
|
||||
}
|
||||
} catch (UnknownHostException uhe) {}
|
||||
}
|
||||
if (keep) {
|
||||
// copy over uid and expiration from existing
|
||||
ports.add(port);
|
||||
portsToForwardNow.remove(port);
|
||||
portsToForwardNow.add(port);
|
||||
}
|
||||
}
|
||||
|
||||
// Needs dropping
|
||||
if(portsToDumpNow == null) portsToDumpNow = new HashSet<ForwardPort>();
|
||||
portsToDumpNow.add(port);
|
||||
if (!keep) {
|
||||
// Needs dropping
|
||||
if (portsToDumpNow == null) portsToDumpNow = new HashSet<ForwardPort>();
|
||||
portsToDumpNow.add(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
portsToForward.clear();
|
||||
@@ -1764,6 +1810,15 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
} else {
|
||||
fps = new ForwardPortStatus(ForwardPortStatus.PROBABLE_FAILURE, "UPnP port forwarding apparently failed", port.portNumber);
|
||||
}
|
||||
if (port.isIP6) {
|
||||
// Don't report result if deprecated
|
||||
IPv6ForwardPort v6port = (IPv6ForwardPort) port;
|
||||
if (v6port.getExpiration() > 0) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Not reporting result for deprecated " + v6port + " - " + fps.reasonString);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
map.put(port, fps);
|
||||
}
|
||||
forwardCallback.portForwardStatus(map);
|
||||
@@ -1809,6 +1864,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
static class IPv6ForwardPort extends ForwardPort {
|
||||
private final String _ip;
|
||||
private int _uid = -1;
|
||||
private long _expires;
|
||||
|
||||
/**
|
||||
* @param ip the IPv6 address being forwarded
|
||||
@@ -1829,6 +1885,16 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
* @param uid 0-65535
|
||||
*/
|
||||
public synchronized void setUID(int uid) { _uid = uid; }
|
||||
|
||||
/**
|
||||
* @return absolute time or 0 if unset
|
||||
*/
|
||||
public synchronized long getExpiration() { return _expires; }
|
||||
|
||||
/**
|
||||
* @param expires absolute time
|
||||
*/
|
||||
public synchronized void setExpiration(long expires) { _expires = expires; }
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
@@ -1845,6 +1911,11 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
IPv6ForwardPort f = (IPv6ForwardPort) o;
|
||||
return _ip.equals(f.getIP()) && super.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IPv6FP " + name + ' ' + protocol + ' ' + _ip + ' ' + portNumber + ' ' + _uid + ' ' + _expires;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user