- UPnP defaults to on - I didn't do all this for nothing

- Set Status to OK for local public addresses or UPnP port open
- Allow UDP address changes after we transition to firewalled
- Have NTCP start reporting reachability status, this will
  get OK on the console more often and mask UDP problems,
  which might be good or bad...
- Fix UDP port configuration
- Reword and rearrange configuration options again
- Rearrange configuration help
- More right-alignment on config
- Prevent Concurrent modification exception in UPnP
- UPnP HTML output tweaks - remove "plugin" references
- Move UDP message failed log from WARN to INFO
- Short-circuit message history call in UDP
This commit is contained in:
zzz
2009-05-02 18:23:41 +00:00
parent 0e4c846942
commit d41afc0c43
7 changed files with 103 additions and 48 deletions

View File

@ -132,8 +132,11 @@ public class ConfigNetHelper extends HelperBase {
return "";
}
/** default true */
public String getUpnpChecked() {
return getChecked(TransportManager.PROP_ENABLE_UPNP);
if (Boolean.valueOf(_context.getProperty(TransportManager.PROP_ENABLE_UPNP, "true")).booleanValue())
return CHECKED;
return "";
}
public String getRequireIntroductionsChecked() {
@ -229,7 +232,7 @@ public class ConfigNetHelper extends HelperBase {
public String getSharePercentageBox() {
int pct = (int) (100 * _context.router().getSharePercentage());
StringBuffer buf = new StringBuffer(256);
buf.append("<select name=\"sharePercentage\">\n");
buf.append("<select style=\"text-align: right;\" name=\"sharePercentage\">\n");
boolean found = false;
for (int i = 30; i <= 110; i += 10) {
int val = i;
@ -239,7 +242,7 @@ public class ConfigNetHelper extends HelperBase {
else
val = pct;
}
buf.append("<option value=\"").append(val).append("\" ");
buf.append("<option style=\"text-align: right;\" value=\"").append(val).append("\" ");
if (pct == val) {
buf.append("selected=\"true\" ");
found = true;

View File

@ -83,13 +83,13 @@
<b>IP Configuration:</b><br />
Externally reachable hostname or IP address:<br />
<input type="radio" name="udpAutoIP" value="local,upnp,ssu" <%=nethelper.getUdpAutoIPChecked(3) %> />
Use local public address if available, then UPnP detection, then SSU detection<br />
Use all auto-detect methods<br />
<input type="radio" name="udpAutoIP" value="local,ssu" <%=nethelper.getUdpAutoIPChecked(4) %> />
Use local public address if available, then SSU detection<br />
Disable UPnP IP address detection<br />
<input type="radio" name="udpAutoIP" value="upnp,ssu" <%=nethelper.getUdpAutoIPChecked(5) %> />
Use UPnP detection if available, then SSU detection<br />
Ignore local interface IP address<br />
<input type="radio" name="udpAutoIP" value="ssu" <%=nethelper.getUdpAutoIPChecked(0) %> />
Use SSU detection only<br />
Use SSU IP address detection only<br />
<input type="radio" name="udpAutoIP" value="fixed" <%=nethelper.getUdpAutoIPChecked(1) %> />
Specify hostname or IP:
<input name ="udpHost1" type="text" size="16" value="<jsp:getProperty name="nethelper" property="udphostname" />" />
@ -112,34 +112,27 @@
</p><p>
<b>UDP Configuration:</b><br />
Internal UDP port:
<input name ="udpPort" type="text" size="5" maxlength="5" vvalue="<jsp:getProperty name="nethelper" property="configuredUdpPort" />" /><br />
<input name ="udpPort" type="text" size="5" maxlength="5" value="<jsp:getProperty name="nethelper" property="configuredUdpPort" />" /><br />
<input type="checkbox" name="requireIntroductions" value="true" <jsp:getProperty name="nethelper" property="requireIntroductionsChecked" /> />
Require SSU introductions
<i>(Enable if you cannot open your firewall)</i>
</p><p>
Current External UDP address: <i><jsp:getProperty name="nethelper" property="udpAddress" /></i><br />
</p><p>If you can, please poke a hole in your NAT or firewall to allow unsolicited UDP packets to reach
you on your external UDP address. If you can't, I2P now includes supports UDP hole punching
with "SSU introductions" - peers who will relay a request from someone you don't know to your
router for your router so that you can make an outbound connection to them. I2P will use these
introductions automatically if it detects that the port is not forwarded (as shown by
the <i>Reachability: Firewalled</i> line), or you can manually require them here.
Users behind symmetric NATs, such as OpenBSD's pf, are not currently supported.</p>
<input type="submit" name="recheckReachability" value="Check network reachability..." />
</p><p>
<b>Inbound TCP Configuration:</b><br />
Externally reachable hostname or IP address:<br />
<input type="radio" name="ntcpAutoIP" value="true" <%=nethelper.getTcpAutoIPChecked(2) %> />
Use auto-detected IP address
<i>(currently <jsp:getProperty name="nethelper" property="udpIP" />)</i>
if we are not firewalled<br />
<input type="radio" name="ntcpAutoIP" value="always" <%=nethelper.getTcpAutoIPChecked(3) %> />
Always use auto-detected IP address (Not firewalled)<br />
<input type="radio" name="ntcpAutoIP" value="false" <%=nethelper.getTcpAutoIPChecked(0) %> />
Disable (Firewalled)<br />
<input type="radio" name="ntcpAutoIP" value="always" <%=nethelper.getTcpAutoIPChecked(3) %> />
Use IP address detected by SSU (Not firewalled)
<i>(currently <jsp:getProperty name="nethelper" property="udpIP" />)</i><br />
<input type="radio" name="ntcpAutoIP" value="true" <%=nethelper.getTcpAutoIPChecked(2) %> />
Use IP address detected by SSU, only if we do not appear to be firewalled<br />
<input type="radio" name="ntcpAutoIP" value="false" <%=nethelper.getTcpAutoIPChecked(1) %> />
Specify hostname or IP:
<input name ="ntcphost" type="text" size="16" value="<jsp:getProperty name="nethelper" property="ntcphostname" />" />
<i>(dyndns and the like are fine)</i><br />
<br />
</p><p>
Externally reachable TCP port:<br />
<input type="radio" name="ntcpAutoPort" value="2" <%=nethelper.getTcpAutoPortChecked(2) %> />
@ -148,7 +141,21 @@
<input type="radio" name="ntcpAutoPort" value="1" <%=nethelper.getTcpAutoPortChecked(1) %> />
Specify Port:
<input name ="ntcpport" type="text" size="5" maxlength="5" value="<jsp:getProperty name="nethelper" property="ntcpport" />" /><br />
</p><p>Hostnames entered here will be published in the network database.
</p><p><b>Note: changing any of these settings will terminate all of your connections and effectively
restart your router.</b>
</p>
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
<hr />
<b><a name="chelp">Configuration Help:</a></b>
<p>If you can, please poke a hole in your NAT or firewall to allow unsolicited UDP packets to reach
you on your external UDP address. If you can't, I2P now includes supports UDP hole punching
with "SSU introductions" - peers who will relay a request from someone you don't know to your
router for your router so that you can make an outbound connection to them. I2P will use these
introductions automatically if it detects that the port is not forwarded (as shown by
the <i>Reachability: Firewalled</i> line), or you can manually require them here.
Users behind symmetric NATs, such as OpenBSD's pf, are not currently supported.</p>
<input type="submit" name="recheckReachability" value="Check network reachability..." />
<p>Hostnames entered here will be published in the network database.
They are <b>not private</b>.
Also, <b>do not enter a private IP address</b> like 127.0.0.1 or 192.168.1.1.
</p>
@ -157,10 +164,6 @@
in your NAT or firewall for unsolicited TCP connections. If you specify the wrong IP address or
hostname, or do not properly configure your NAT or firewall, your network performance will degrade
substantially. When in doubt, leave the hostname and port number blank.</p>
<p><b>Note: changing any of these settings will terminate all of your connections and effectively
restart your router.</b>
</p>
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
<hr />
<b><a name="help">Reachability Help:</a></b>
<p>

View File

@ -61,7 +61,7 @@ public class MessageHistory {
}
void setDoLog(boolean log) { _doLog = log; }
boolean getDoLog() { return _doLog; }
public boolean getDoLog() { return _doLog; }
void setPauseFlushes(boolean doPause) { _doPause = doPause; }
String getFilename() { return _historyFile; }

View File

@ -44,6 +44,7 @@ public class TransportManager implements TransportEventListener {
private final static String PROP_ENABLE_NTCP = "i2np.ntcp.enable";
private final static String DEFAULT_ENABLE_NTCP = "true";
private final static String DEFAULT_ENABLE_UDP = "true";
/** default true */
public final static String PROP_ENABLE_UPNP = "i2np.upnp.enable";
public TransportManager(RouterContext context) {
@ -56,7 +57,7 @@ public class TransportManager implements TransportEventListener {
_context.statManager().createRateStat("transport.bidFailNoTransports", "Could not attempt to bid on message, as none of the transports could attempt it", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("transport.bidFailAllTransports", "Could not attempt to bid on message, as all of the transports had failed", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_transports = new ArrayList();
if (Boolean.valueOf(_context.getProperty(PROP_ENABLE_UPNP)).booleanValue())
if (Boolean.valueOf(_context.getProperty(PROP_ENABLE_UPNP, "true")).booleanValue())
_upnpManager = new UPnPManager(context, this);
}
@ -227,15 +228,15 @@ public class TransportManager implements TransportEventListener {
return skews;
}
/** @return the best status of any transport */
public short getReachabilityStatus() {
if (_transports.size() <= 0) return CommSystemFacade.STATUS_UNKNOWN;
short status[] = new short[_transports.size()];
for (int i = 0; i < _transports.size(); i++) {
status[i] = ((Transport)_transports.get(i)).getReachabilityStatus();
short rv = CommSystemFacade.STATUS_UNKNOWN;
for (Transport t : _transports) {
short s = t.getReachabilityStatus();
if (s < rv)
rv = s;
}
// the values for the statuses are increasing for their 'badness'
Arrays.sort(status);
return status[0];
return rv;
}
public void recheckReachability() {

View File

@ -232,7 +232,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener {
public void unregisterPortMappings() {
Set ports;
synchronized(lock) {
ports = portsForwarded;
ports = new HashSet(portsForwarded);
}
this.unregisterPorts(ports);
}
@ -406,10 +406,10 @@ public class UPnP extends ControlPoint implements DeviceChangeListener {
sb.append("<a name=\"upnp\"><b>UPnP Status:</b><br />");
if(isDisabled) {
sb.append("The plugin has been disabled; Do you have more than one UPnP Internet Gateway Device on your LAN ?");
sb.append("UPnP has been disabled; Do you have more than one UPnP Internet Gateway Device on your LAN ?");
return sb.toString();
} else if(!isNATPresent()) {
sb.append("The plugin hasn't found any UPnP aware, compatible device on your LAN.");
sb.append("UPnP has not found any UPnP-aware, compatible device on your LAN.");
return sb.toString();
}

View File

@ -20,6 +20,7 @@ import net.i2p.data.Hash;
import net.i2p.data.RouterAddress;
import net.i2p.data.RouterIdentity;
import net.i2p.data.RouterInfo;
import net.i2p.router.CommSystemFacade;
import net.i2p.router.OutNetMessage;
import net.i2p.router.RouterContext;
import net.i2p.router.transport.CommSystemFacadeImpl;
@ -37,7 +38,7 @@ public class NTCPTransport extends TransportImpl {
private SharedBid _slowBid;
private SharedBid _transientFail;
private final Object _conLock;
private Map _conByIdent;
private Map<Hash, NTCPConnection> _conByIdent;
private NTCPAddress _myAddress;
private EventPumper _pumper;
private Reader _reader;
@ -566,6 +567,27 @@ public class NTCPTransport extends TransportImpl {
return _context.getProperty(CommSystemFacadeImpl.PROP_I2NP_NTCP_PORT, -1);
}
/**
* Maybe we should trust UPnP here and report OK if it opened the port, but
* for now we don't. Just go through and if we have one inbound connection,
* we must be good. As we drop idle connections pretty quickly, this will
* be fairly accurate.
*
* We have to be careful here because much of the router console code assumes
* that the reachability status is really just the UDP status.
*/
public short getReachabilityStatus() {
if (isAlive() && _myAddress != null) {
synchronized (_conLock) {
for (NTCPConnection con : _conByIdent.values()) {
if (con.isInbound())
return CommSystemFacade.STATUS_OK;
}
}
}
return CommSystemFacade.STATUS_UNKNOWN;
}
/**
* This doesn't (completely) block, caller should check isAlive()
* before calling startListening() or restartListening()

View File

@ -346,7 +346,28 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
String sources = _context.getProperty(PROP_SOURCES, DEFAULT_SOURCES);
if (!sources.contains(source))
return;
changeAddress(ip, port);
boolean changed = changeAddress(ip, port);
// Assume if we have an interface with a public IP that we aren't firewalled.
// If this is wrong, the peer test will figure it out and change the status.
if (changed && source.equals(Transport.SOURCE_INTERFACE))
setReachabilityStatus(CommSystemFacade.STATUS_OK);
}
/**
* Callback from UPnP.
* If we we have an IP address and UPnP claims success, believe it.
* If this is wrong, the peer test will figure it out and change the status.
* Don't do anything if UPnP claims failure.
*/
public void forwardPortStatus(int port, boolean success, String reason) {
if (_log.shouldLog(Log.WARN)) {
if (success)
_log.warn("UPnP has opened the SSU port: " + port);
else
_log.warn("UPnP has failed to open the SSU port: " + port + " reason: " + reason);
}
if (success && _externalListenHost != null)
setReachabilityStatus(CommSystemFacade.STATUS_OK);
}
/**
@ -397,7 +418,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
/**
* @param ourPort >= 1024 or 0 for no change
*/
private void changeAddress(byte ourIP[], int ourPort) {
private boolean changeAddress(byte ourIP[], int ourPort) {
boolean fixedPort = getIsPortFixed();
boolean updated = false;
boolean fireTest = false;
@ -405,7 +426,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
synchronized (this) {
if ( (_externalListenHost == null) ||
(!eq(_externalListenHost.getAddress(), _externalListenPort, ourIP, ourPort)) ) {
if ( (_reachabilityStatus == CommSystemFacade.STATUS_UNKNOWN) ||
if ( (_reachabilityStatus != CommSystemFacade.STATUS_OK) ||
(_externalListenHost == null) || (_externalListenPort <= 0) ||
(_context.clock().now() - _reachabilityStatusLastUpdated > 2*TEST_FREQUENCY) ) {
// they told us something different and our tests are either old or failing
@ -452,6 +473,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_testEvent.forceRun();
SimpleTimer.getInstance().addEvent(_testEvent, 5*1000);
}
return updated;
}
private static final boolean eq(byte laddr[], int lport, byte raddr[], int rport) {
@ -1199,8 +1221,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// + " lastSentFully: " + sendDelay
// + " expired? " + msg.isExpired());
consecutive = msg.getPeer().incrementConsecutiveFailedSends();
if (_log.shouldLog(Log.WARN))
_log.warn("Consecutive failure #" + consecutive
if (_log.shouldLog(Log.INFO))
_log.info("Consecutive failure #" + consecutive
+ " on " + msg.toString()
+ " to " + msg.getPeer());
if ( (_context.clock().now() - msg.getPeer().getLastSendFullyTime() <= 60*1000) || (consecutive < MAX_CONSECUTIVE_FAILED) ) {
@ -1220,6 +1242,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
}
private void noteSend(OutboundMessageState msg, boolean successful) {
// bail before we do all the work
if (!_context.messageHistory().getDoLog())
return;
int pushCount = msg.getPushCount();
int sends = msg.getMaxSends();
boolean expired = msg.isExpired();
@ -1256,10 +1281,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
public void failed(OutNetMessage msg, String reason) {
if (msg == null) return;
if (_log.shouldLog(Log.WARN))
_log.warn("Sending message failed: " + msg, new Exception("failed from"));
if (_log.shouldLog(Log.INFO))
_log.info("Sending message failed: " + msg, new Exception("failed from"));
_context.messageHistory().sendMessage(msg.getMessageType(), msg.getMessageId(), msg.getExpiration(),
if (!_context.messageHistory().getDoLog())
_context.messageHistory().sendMessage(msg.getMessageType(), msg.getMessageId(), msg.getExpiration(),
msg.getTarget().getIdentity().calculateHash(), false, reason);
super.afterSend(msg, false);
}