forked from I2P_Developers/i2p.i2p
UPnP: IPv6 part 4
WIP - disabled by default Store local IPv6 address at startup so UPnP can attempt to forward it Request forwarding of ports to IPv6 addresses Update status on successful IPv6 forward Fix IP mismatch test for IPv6 Log tweaks
This commit is contained in:
@@ -58,6 +58,18 @@ public interface Transport {
|
||||
*/
|
||||
public List<RouterAddress> getCurrentAddresses();
|
||||
|
||||
/**
|
||||
* What address are we currently listening to?
|
||||
* Replaces getCurrentAddress()
|
||||
*
|
||||
* Note: An address without a host is considered IPv4.
|
||||
*
|
||||
* @param ipv6 true for IPv6 only; false for IPv4 only
|
||||
* @return first matching address or null
|
||||
* @since 0.9.50 lifted from TransportImpl
|
||||
*/
|
||||
public RouterAddress getCurrentAddress(boolean ipv6);
|
||||
|
||||
/**
|
||||
* Do we have any current address?
|
||||
* @since IPv6
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterIdentity;
|
||||
@@ -85,6 +86,8 @@ public class TransportManager implements TransportEventListener {
|
||||
public final static String PROP_ENABLE_NTCP = "i2np.ntcp.enable";
|
||||
/** default true */
|
||||
public final static String PROP_ENABLE_UPNP = "i2np.upnp.enable";
|
||||
/** default false for now */
|
||||
public final static String PROP_ENABLE_UPNP_IPV6 = "i2np.upnp.ipv6.enable";
|
||||
private static final String PROP_JAVA_PROXY1 = "socksProxyHost";
|
||||
private static final String PROP_JAVA_PROXY2 = "java.net.useSystemProxies";
|
||||
private static final String PROP_JAVA_PROXY3 = "http.proxyHost";
|
||||
@@ -743,25 +746,43 @@ public class TransportManager implements TransportEventListener {
|
||||
static class Port {
|
||||
public final String style;
|
||||
public final int port;
|
||||
public final boolean isIPv6;
|
||||
public final String ip;
|
||||
|
||||
/**
|
||||
* IPv4 only
|
||||
*/
|
||||
public Port(String style, int port) {
|
||||
this.style = style;
|
||||
this.port = port;
|
||||
isIPv6 = false;
|
||||
ip = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* IPv6 only
|
||||
* @since 0.9.50
|
||||
*/
|
||||
public Port(String style, String host, int port) {
|
||||
this.style = style;
|
||||
this.port = port;
|
||||
isIPv6 = true;
|
||||
ip = host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return style.hashCode() ^ port;
|
||||
return style.hashCode() ^ port ^ DataHelper.hashCode(ip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null)
|
||||
return false;
|
||||
if (o == this)
|
||||
return true;
|
||||
if (! (o instanceof Port))
|
||||
return false;
|
||||
Port p = (Port) o;
|
||||
return port == p.port && style.equals(p.style);
|
||||
return port == p.port && style.equals(p.style) && DataHelper.eq(ip, p.ip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -780,8 +801,24 @@ public class TransportManager implements TransportEventListener {
|
||||
if (udp != null)
|
||||
port = udp.getRequestedPort();
|
||||
}
|
||||
if (port > 0)
|
||||
if (port > 0) {
|
||||
// ipv4
|
||||
rv.add(new Port(t.getStyle(), port));
|
||||
// ipv6
|
||||
if (_context.getBooleanProperty(PROP_ENABLE_UPNP_IPV6)) {
|
||||
RouterAddress ra = t.getCurrentAddress(true);
|
||||
if (ra == null) {
|
||||
if (t.getStyle().equals(UDPTransport.STYLE)) {
|
||||
UDPTransport udp = (UDPTransport) t;
|
||||
ra = udp.getCurrentExternalAddress(true);
|
||||
}
|
||||
}
|
||||
if (ra != null) {
|
||||
String host = ra.getHost();
|
||||
rv.add(new Port(t.getStyle(), host, port));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
}
|
||||
}
|
||||
}
|
||||
Set<String> myAddresses = Addresses.getAddresses(true, false); // yes local, no IPv6
|
||||
Set<String> myAddresses = Addresses.getAddresses(true, true); // yes local, yes IPv6
|
||||
if (!ignore && !ALLOW_SAME_HOST && ip != null && myAddresses.contains(ip)) {
|
||||
ignore = true;
|
||||
if (_log.shouldWarn())
|
||||
@@ -282,7 +282,8 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
if (!stringEquals(ip, pktIP)) {
|
||||
ignore = true;
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Ignoring UPnP with IP mismatch: " + name + " UDN: " + udn);
|
||||
_log.warn("Ignoring UPnP with IP mismatch: " + name + " UDN: " + udn +
|
||||
" dev IP " + ip + " pkt IP: " + pktIP);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1452,6 +1453,8 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
} catch (URISyntaxException use) {}
|
||||
}
|
||||
}
|
||||
if (rv != null && rv.startsWith("[") && rv.endsWith("]"))
|
||||
rv = rv.substring(1, rv.length() - 1);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -1556,11 +1559,12 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
portsForwarded.remove(fp);
|
||||
}
|
||||
|
||||
if (!noLog && _log.shouldWarn()) {
|
||||
int level = retval ? Log.INFO : Log.WARN;
|
||||
if (!noLog && _log.shouldLog(level)) {
|
||||
if (retval)
|
||||
_log.warn("UPnP: Removed IPv4 mapping for "+fp.name+" "+port+" / "+protocol);
|
||||
_log.log(level, "UPnP: Removed IPv4 mapping for "+fp.name+" "+port+" / "+protocol);
|
||||
else
|
||||
_log.warn("UPnP: Failed to remove IPv4 mapping for "+fp.name+" "+port+" / "+protocol);
|
||||
_log.log(level, "UPnP: Failed to remove IPv4 mapping for "+fp.name+" "+port+" / "+protocol);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
@@ -1593,12 +1597,23 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
|
||||
synchronized(lock) {
|
||||
portsForwarded.remove(fp);
|
||||
}
|
||||
if (!noLog && _log.shouldWarn()) {
|
||||
|
||||
int level = retval ? Log.INFO : Log.WARN;
|
||||
if (!noLog && _log.shouldLog(level)) {
|
||||
String ip = fp.getIP();
|
||||
if (retval)
|
||||
_log.warn("UPnP: Removed IPv6 mapping for " + fp.name + ' ' + ip + ' ' + port + " / " + protocol);
|
||||
else
|
||||
_log.warn("UPnP: Failed to remove IPv6 mapping for " + fp.name + ' ' + ip + ' ' + port + " / " + protocol);
|
||||
if (retval) {
|
||||
_log.log(level, "UPnP: Removed IPv6 mapping for " + fp.name + ' ' + ip + ' ' + port + " / " + protocol);
|
||||
} 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);
|
||||
UPnPStatus status = remove.getStatus();
|
||||
if (status != null)
|
||||
buf.append(" Status: ").append(status.getCode()).append(' ').append(status.getDescription());
|
||||
status = remove.getControlStatus();
|
||||
if (status != null)
|
||||
buf.append(" ControlStatus: ").append(status.getCode()).append(' ').append(status.getDescription());
|
||||
_log.log(level, buf.toString());
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -287,7 +287,11 @@ class UPnPManager {
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Adding: " + style + " " + port);
|
||||
ForwardPort fp = new ForwardPort(name, false, protocol, port);
|
||||
ForwardPort fp;
|
||||
if (entry.isIPv6)
|
||||
fp = new UPnP.IPv6ForwardPort(name, protocol, port, entry.ip);
|
||||
else
|
||||
fp = new ForwardPort(name, false, protocol, port);
|
||||
forwards.add(fp);
|
||||
}
|
||||
// non-blocking
|
||||
@@ -352,6 +356,7 @@ class UPnPManager {
|
||||
ForwardPortStatus fps = entry.getValue();
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("FPS: " + fp.name + ' ' + fp.protocol + ' ' + fp.portNumber +
|
||||
(fp.isIP6 ? " IPv6" : " IPv4") +
|
||||
" status: " + fps.status + " reason: " + fps.reasonString + " ext port: " + fps.externalPort);
|
||||
String style;
|
||||
if (fp.protocol == ForwardPort.PROTOCOL_UDP_IPV4) {
|
||||
@@ -364,8 +369,18 @@ class UPnPManager {
|
||||
continue;
|
||||
}
|
||||
boolean success = fps.status >= ForwardPortStatus.MAYBE_SUCCESS;
|
||||
byte[] fwdip;
|
||||
if (fp.isIP6) {
|
||||
UPnP.IPv6ForwardPort v6fp = (UPnP.IPv6ForwardPort) fp;
|
||||
String sip = v6fp.getIP();
|
||||
fwdip = Addresses.getIP(sip);
|
||||
if (fwdip == null)
|
||||
continue;
|
||||
} else {
|
||||
fwdip = ipaddr;
|
||||
}
|
||||
// deadlock path 2
|
||||
_manager.forwardPortStatus(style, ipaddr, fp.portNumber, fps.externalPort, success, fps.reasonString);
|
||||
_manager.forwardPortStatus(style, fwdip, fp.portNumber, fps.externalPort, success, fps.reasonString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,6 +633,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
hasv6 = true;
|
||||
if (isIPv6Firewalled() || _context.getBooleanProperty(PROP_IPV6_FIREWALLED)) {
|
||||
setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_FIREWALLED, true);
|
||||
// save the external address but don't publish it
|
||||
// save it where UPnP can get it and try to forward it
|
||||
OrderedProperties localOpts = new OrderedProperties();
|
||||
localOpts.setProperty(UDPAddress.PROP_PORT, String.valueOf(newPort));
|
||||
localOpts.setProperty(UDPAddress.PROP_HOST, newIP);
|
||||
RouterAddress local = new RouterAddress(STYLE, localOpts, DEFAULT_COST);
|
||||
replaceCurrentExternalAddress(local, true);
|
||||
} else {
|
||||
_lastInboundIPv6 = _context.clock().now();
|
||||
setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK, true);
|
||||
@@ -1013,9 +1020,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
else
|
||||
_log.warn("UPnP has failed to open the SSU port: " + port + " reason: " + reason);
|
||||
}
|
||||
if (success && ip != null && getExternalIP() != null) {
|
||||
if (!isIPv4Firewalled())
|
||||
setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
|
||||
if (success && ip != null) {
|
||||
if (ip.length == 4) {
|
||||
if (getExternalIP() != null && !isIPv4Firewalled())
|
||||
setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
|
||||
} else if (ip.length == 16) {
|
||||
if (!isIPv6Firewalled())
|
||||
setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2637,9 +2649,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
* we don't put them in the real, published RouterAddress anymore
|
||||
* if we are firewalled.
|
||||
*
|
||||
* @since 0.9.18, pkg private for PacketBuilder since 0.9.50
|
||||
* @since 0.9.18, public for PacketBuilder and TransportManager since 0.9.50
|
||||
*/
|
||||
RouterAddress getCurrentExternalAddress(boolean isIPv6) {
|
||||
public RouterAddress getCurrentExternalAddress(boolean isIPv6) {
|
||||
// deadlock thru here ticket #1699
|
||||
synchronized (_rebuildLock) {
|
||||
return isIPv6 ? _currentOurV6Address : _currentOurV4Address;
|
||||
|
||||
Reference in New Issue
Block a user