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:
zzz
2021-03-01 16:39:17 -05:00
parent ca974a85c5
commit ea736d71ef
2 changed files with 111 additions and 15 deletions

View File

@@ -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()) {

View File

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