diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java
index dcefa84e323319783db9d209c83741b7b77e38e6..657039400329a4d37d34b51f221bc1e2f26be19a 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java
@@ -33,7 +33,8 @@ public class ConfigRestartBean {
                 ctx.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_HARD));
                 //ctx.router().shutdown(Router.EXIT_HARD); // never returns
                 ctx.router().shutdownGracefully(Router.EXIT_HARD); // give the UI time to respond
-            } else if ("cancelShutdown".equals(action) || _("Cancel shutdown", ctx).equals(action)) {
+            } else if ("cancelShutdown".equals(action) || _("Cancel shutdown", ctx).equals(action) ||
+                       _("Cancel restart", ctx).equals(action)) {
                 ctx.router().cancelGracefulShutdown();
             } else if ("restartImmediate".equals(action) || _("Restart immediately", ctx).equals(action)) {
                 ctx.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_HARD_RESTART));
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUIHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUIHelper.java
index bac8d0009f21c0a6a3b93e6fce6c42c7b47f825b..fffdcad6a14dcfacf1e41e4ecd2cd988c1458328 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUIHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUIHelper.java
@@ -37,6 +37,7 @@ public class ConfigUIHelper extends HelperBase {
     }
 
     private static final String langs[] = {"de", "en", "fr", "nl", "se", "zh"};
+    private static final String flags[] = {"de", "us", "fr", "nl", "se", "cn"};
     private static final String xlangs[] = {_x("German"), _x("English"), _x("French"),
                                             _x("Dutch"), _x("Swedish"), _x("Chinese")};
 
@@ -49,7 +50,9 @@ public class ConfigUIHelper extends HelperBase {
             buf.append("<input type=\"radio\" class=\"optbox\" name=\"lang\" ");
             if (langs[i].equals(current))
                 buf.append("checked=\"true\" ");
-            buf.append("value=\"").append(langs[i]).append("\">").append(_(xlangs[i])).append("<br>\n");
+            buf.append("value=\"").append(langs[i]).append("\">")
+               .append("<img height=\"11\" width=\"16\" alt=\"\" src=\"/flags.jsp?c=").append(flags[i]).append("\"> ")
+               .append(_(xlangs[i])).append("<br>\n");
         }
         return buf.toString();
     }
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java
index abd20b770ef3e9728368328f751e4cf8bf6a7664..e551b15f03222b57d79396da5dceffc32b486b0d 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java
@@ -84,8 +84,8 @@ public class NetDbRenderer {
     public void renderLeaseSetHTML(Writer out) throws IOException {
         StringBuilder buf = new StringBuilder(4*1024);
         buf.append("<h2>" + _("Network Database Contents") + "</h2>\n");
-        buf.append("<a href=\"netdb.jsp\">" + _("View") + " RouterInfo</a>");
-        buf.append("<h3>LeaseSets</h3>\n");
+        buf.append("<a href=\"netdb.jsp\">" + _("View RouterInfo") + "</a>");
+        buf.append("<h3>").append(_("LeaseSets")).append("</h3>\n");
         Set leases = new TreeSet(new LeaseSetComparator());
         leases.addAll(_context.netDb().getLeases());
         long now = _context.clock().now();
@@ -132,7 +132,7 @@ public class NetDbRenderer {
     }
 
     public void renderStatusHTML(Writer out, boolean full) throws IOException {
-        out.write("<h2>" + _("Network Database Contents") + " (<a href=\"netdb.jsp?l=1\">" + _("View") + " LeaseSets</a>)</h2>\n");
+        out.write("<h2>" + _("Network Database Contents") + " (<a href=\"netdb.jsp?l=1\">" + _("View LeaseSets") + "</a>)</h2>\n");
         if (!_context.netDb().isInitialized()) {
             out.write(_("Not initialized"));
             out.flush();
@@ -244,7 +244,7 @@ public class NetDbRenderer {
         }
         for (Iterator iter = info.getAddresses().iterator(); iter.hasNext(); ) {
             RouterAddress addr = (RouterAddress)iter.next();
-            buf.append(DataHelper.stripHTML(addr.getTransportStyle())).append(": ");
+            buf.append("<b>").append(DataHelper.stripHTML(addr.getTransportStyle())).append("</b>: ");
             for (Iterator optIter = addr.getOptions().keySet().iterator(); optIter.hasNext(); ) {
                 String name = (String)optIter.next();
                 String val = addr.getOptions().getProperty(name);
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ProfileOrganizerRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/ProfileOrganizerRenderer.java
index 090219d5049a05b5716fd98ed3ed6be357909db8..f7cb64a0a10173c96c72d51cd85f3f6f06943a10 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ProfileOrganizerRenderer.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ProfileOrganizerRenderer.java
@@ -105,6 +105,9 @@ class ProfileOrganizerRenderer {
             
             buf.append("<tr><td align=\"center\" nowrap>");
             buf.append(_context.commSystem().renderPeerHTML(peer));
+            // debug
+            //if(prof.getIsExpandedDB())
+            //   buf.append(" ** ");
             buf.append("</td><td align=\"center\">");
             
             switch (tier) {
@@ -145,18 +148,19 @@ class ProfileOrganizerRenderer {
             buf.append("</td><td align=\"right\">").append(num(prof.getIntegrationValue()));
             buf.append("</td><td align=\"center\">");
             if (_context.shitlist().isShitlisted(peer)) buf.append(_("Banned"));
-            if (prof.getIsFailing()) buf.append(" ").append(_("Failing"));
-            if (_context.commSystem().wasUnreachable(peer)) buf.append(" ").append(_("Unreachable"));
+            if (prof.getIsFailing()) buf.append(' ').append(_("Failing"));
+            if (_context.commSystem().wasUnreachable(peer)) buf.append(' ').append(_("Unreachable"));
             Rate failed = prof.getTunnelHistory().getFailedRate().getRate(30*60*1000);
             long fails = failed.getCurrentEventCount() + failed.getLastEventCount();
             if (fails > 0) {
                 Rate accepted = prof.getTunnelCreateResponseTime().getRate(30*60*1000);
                 long total = fails + accepted.getCurrentEventCount() + accepted.getLastEventCount();
                 if (total / fails <= 10)   // hide if < 10%
-                    buf.append(' ').append(fails).append('/').append(total).append(" ").append(_("Test Fails"));
+                    buf.append(' ').append(fails).append('/').append(total).append(' ').append(_("Test Fails"));
             }
             buf.append("&nbsp;</td>");
-            buf.append("<td nowrap align=\"center\"><a target=\"_blank\" href=\"dumpprofile.jsp?peer=").append(peer.toBase64().substring(0,6)).append("\">profile</a>");
+            buf.append("<td nowrap align=\"center\"><a target=\"_blank\" href=\"dumpprofile.jsp?peer=")
+               .append(peer.toBase64().substring(0,6)).append("\">").append(_("profile")).append("</a>");
             buf.append("&nbsp;<a href=\"configpeer.jsp?peer=").append(peer.toBase64()).append("\">+-</a></td>\n");
             buf.append("</tr>");
             // let's not build the whole page in memory (~500 bytes per peer)
@@ -223,14 +227,20 @@ class ProfileOrganizerRenderer {
                 buf.append("<td align=\"right\">").append(dbh.getUnpromptedDbStoreOld()).append("</td>");
                 buf.append("<td align=\"right\">").append(davg(dbh, 60*60*1000l)).append("</td>");
                 buf.append("<td align=\"right\">").append(davg(dbh, 24*60*60*1000l)).append("</td>");
+            } else {
+                for (int i = 0; i < 6; i++)
+                    buf.append("<td align=\"right\">").append(_(NA));
             }
         }
         buf.append("</table>");
 
         buf.append("<h3>").append(_("Thresholds:")).append("</h3>");
-        buf.append("<p><b>").append(_("Speed")).append(":</b> ").append(num(_organizer.getSpeedThreshold())).append(" (").append(fast).append(" fast peers)<br>");
-        buf.append("<b>").append(_("Capacity")).append(":</b> ").append(num(_organizer.getCapacityThreshold())).append(" (").append(reliable).append(" high capacity peers)<br>");
-        buf.append("<b>").append(_("Integration")).append(":</b> ").append(num(_organizer.getIntegrationThreshold())).append(" (").append(integrated).append(" well integrated peers)</p>");
+        buf.append("<p><b>").append(_("Speed")).append(":</b> ").append(num(_organizer.getSpeedThreshold()))
+           .append(" (").append(fast).append(' ').append(_("fast peers")).append(")<br>");
+        buf.append("<b>").append(_("Capacity")).append(":</b> ").append(num(_organizer.getCapacityThreshold()))
+           .append(" (").append(reliable).append(' ').append(_("high capacity peers")).append(")<br>");
+        buf.append("<b>").append(_("Integration")).append(":</b> ").append(num(_organizer.getIntegrationThreshold()))
+           .append(" (").append(integrated).append(' ').append(_(" well integrated peers")).append(")</p>");
         buf.append("<h3>").append(_("Definitions")).append(":</h3><ul>");
                    buf.append("<li><b>").append(_("groups")).append("</b>: ").append(_("as determined by the profile organizer")).append("</li>");
                    buf.append("<li><b>").append(_("caps")).append("</b>: ").append(_("capabilities in the netDb, not used to determine profiles")).append("</li>");
@@ -295,29 +305,29 @@ class ProfileOrganizerRenderer {
     
     private final static DecimalFormat _fmt = new DecimalFormat("###,##0.00");
     private final static String num(double num) { synchronized (_fmt) { return _fmt.format(num); } }
-    private final static String na = "n/a";
+    private final static String NA = HelperBase._x("n/a");
 
-    private static String avg (PeerProfile prof, long rate) {
+    private String avg (PeerProfile prof, long rate) {
             RateStat rs = prof.getDbResponseTime();
             if (rs == null)
-                return na;
+                return _(NA);
             Rate r = rs.getRate(rate);
             if (r == null)
-                return na;
+                return _(NA);
             long c = r.getCurrentEventCount() + r.getLastEventCount();
             if (c == 0)
-                return na;
+                return _(NA);
             double d = r.getCurrentTotalValue() + r.getLastTotalValue();
             return Math.round(d/c) + "ms";
     }
 
-    private static String davg (DBHistory dbh, long rate) {
+    private String davg (DBHistory dbh, long rate) {
             RateStat rs = dbh.getFailedLookupRate();
             if (rs == null)
-                return na;
+                return _(NA);
             Rate r = rs.getRate(rate);
             if (r == null)
-                return na;
+                return _(NA);
             long c = r.getCurrentEventCount() + r.getLastEventCount();
             return "" + c;
     }
diff --git a/apps/routerconsole/jsp/config.jsp b/apps/routerconsole/jsp/config.jsp
index 92975a02e2e48d050ebb44c9ee7c3537bfdc6b93..1013653c1010b481c0f0c8c689efba04e2c36483 100644
--- a/apps/routerconsole/jsp/config.jsp
+++ b/apps/routerconsole/jsp/config.jsp
@@ -104,7 +104,7 @@
     <input name ="udpHost1" type="text" size="16" value="<jsp:getProperty name="nethelper" property="udphostname" />" />
     <% String[] ips = nethelper.getAddresses();
        if (ips.length > 0) {
-           out.print(" " + intl._("or") + " <select name=\"udpHost2\"><option value=\"\" selected=\"true\">"+intl._("Select Interface")+"</option>\n");
+           out.print(intl._("or") + " <select name=\"udpHost2\"><option value=\"\" selected=\"true\">"+intl._("Select Interface")+"</option>\n");
            for (int i = 0; i < ips.length; i++) {
                out.print("<option value=\"");
                out.print(ips[i]);
@@ -156,7 +156,7 @@
 <hr><div class="formaction">
  <input type="submit" name="save" value="<%=intl._("Save changes")%>" /> <input type="reset" value="<%=intl._("Cancel")%>" />
 </div><h3><a name="chelp"><%=intl._("Configuration Help")%>:</a></h3><div align="justify"><p>
- <%=intl._("While I2P will work fine behind most firewalls, your speeds and network integration will generally improve if the I2P port (generally 8887) is forwarded for both UDP and TCP.")%>
+ <%=intl._("While I2P will work fine behind most firewalls, your speeds and network integration will generally improve if the I2P port is forwarded for both UDP and TCP.")%>
  </p><p>
  <%=intl._("If you can, please poke a hole in your firewall to allow unsolicited UDP and TCP packets to reach you.")%>
    <%=intl._("If you can't, I2P supports UPnP (Universal Plug and Play) and UDP hole punching with \"SSU introductions\" to relay traffic.")%>
@@ -187,7 +187,7 @@
     <%=intl._("When in doubt, leave the settings at the defaults.")%>
 </p>
 <h3><a name="help"><%=intl._("Reachability Help")%>:</a></h3><p>
- <%=intl._("While I2P will work fine behind most firewalls, your speeds and network integration will generally improve if the I2P port (generally 8887) is forwarded for both UDP and TCP.")%>
+ <%=intl._("While I2P will work fine behind most firewalls, your speeds and network integration will generally improve if the I2P port is forwarded for both UDP and TCP.")%>
  <%=intl._("If you think you have opened up your firewall and I2P still thinks you are firewalled, remember that you may have multiple firewalls, for example both software packages and external hardware routers.")%>
  <%=intl._("If there is an error, the <a href=\"logs.jsp\">logs</a> may also help diagnose the problem.")%>
  <ul>
@@ -196,7 +196,7 @@
 <li class="tidylist"><b><%=intl._("Firewalled")%></b> - 
      <%=intl._("Your UDP port appears to be firewalled.")%>
      <%=intl._("As the firewall detection methods are not 100% reliable, this may occasionally be displayed in error.")%>
-     <%=intl._("However, if it appears consistently, you should check whether both your external and internal firewalls are open on port 8887.")%> 
+     <%=intl._("However, if it appears consistently, you should check whether both your external and internal firewalls are open for your port.")%> 
      <%=intl._("I2P will work fine when firewalled, there is no reason for concern. When firewalled, the router uses \"introducers\" to relay inbound connections.")%>
      <%=intl._("However, you will get more participating traffic and help the network more if you can open your firewall(s).")%>
      <%=intl._("If you think you have already done so, remember that you may have both a hardware and a software firewall, or be behind an additional, institutional firewall you cannot control.")%>
@@ -230,7 +230,7 @@
      <%=intl._("I2P does not work well behind this type of firewall. You will probably not be able to accept inbound connections, which will limit your participation in the network.")%>
 <li class="tidylist"><b><%=intl._("ERR - UDP Port In Use - Set i2np.udp.internalPort=xxxx in advanced config and restart")%></b> -
      <%=intl._("I2P was unable to bind to port 8887 or other configured port.")%>
-     <%=intl._("Check to see if another program is using port 8887. If so, stop that program or configure I2P to use a different port.")%> 
+     <%=intl._("Check to see if another program is using the configured port. If so, stop that program or configure I2P to use a different port.")%> 
      <%=intl._("This may be a transient error, if the other program is no longer using the port.")%> 
      <%=intl._("However, a restart is always required after this error.")%>
 <li class="tidylist"><b><%=intl._("ERR - UDP Disabled and Inbound TCP host/port not set")%></b> -
diff --git a/build.xml b/build.xml
index 9d7f55c1b4a6ab719babadaba3a8aa45752bc401..d065d065520ff78651a90d5644be0bf02096d30f 100644
--- a/build.xml
+++ b/build.xml
@@ -291,7 +291,8 @@
         <!-- polecat: please put your modified toolbar.html in installer/resources/toolbar.html 
              and uncomment the following -->
         <!-- <copy file="installer/resources/toolbar.html" todir="pkg-temp/docs/" /> -->
-        <copy file="initialNews.xml" tofile="pkg-temp/docs/news.xml" />
+        <!-- overwrite the news put in by the updater -->
+        <copy file="installer/resources/initialNews.xml" tofile="pkg-temp/docs/news.xml" overwrite="true" />
         <copy file="installer/resources/startconsole.html" todir="pkg-temp/docs/" />
         <copy file="installer/resources/start.ico" todir="pkg-temp/docs/" />
         <copy file="installer/resources/console.ico" todir="pkg-temp/docs/" />
@@ -351,7 +352,7 @@
             <fileset  dir="installer/resources/themes/console/images/" />
         </copy>          
         <copy todir="pkg-temp/docs/" >
-          <fileset dir="." includes="readme*.html" />
+          <fileset dir="installer/resources/readme/" includes="readme*.html" />
         </copy>
     </target>
 
@@ -398,7 +399,8 @@
         <copy file="build/susidns.war" todir="pkg-temp/webapps/" />
         <copy file="build/i2psnark.war" todir="pkg-temp/webapps/" />
         <copy file="history.txt" todir="pkg-temp/" />
-        <copy file="news.xml" todir="pkg-temp/docs/" />
+        <!-- may be pointless now, people with split directories will never see this -->
+        <copy file="installer/resources/news.xml" todir="pkg-temp/docs/" />
     </target>
     <target name="prepupdateSmall" depends="buildSmall, prepupdateRouter, prepthemeupdates">
         <copy file="build/i2ptunnel.jar" todir="pkg-temp/lib/" />
@@ -574,9 +576,9 @@
         <exec executable="ls">
             <arg value="-l" />
             <arg value="history.txt" />
-            <arg value="initialNews.xml" />
+            <arg value="installer/resources/initialNews.xml" />
             <arg value="installer/install.xml" />
-            <arg value="news.xml" />
+            <arg value="installer/resources/news.xml" />
 	    <arg value="core/java/src/net/i2p/CoreVersion.java" />
 	    <arg value="router/java/src/net/i2p/router/RouterVersion.java" />
         </exec>
diff --git a/initialNews.xml b/installer/resources/initialNews.xml
similarity index 78%
rename from initialNews.xml
rename to installer/resources/initialNews.xml
index 40892c28dbefe5fa0f3514ad45db786c05809e7a..35800d7d537b2d1d072f83d217a0411f2be4e6d6 100644
--- a/initialNews.xml
+++ b/installer/resources/initialNews.xml
@@ -11,10 +11,6 @@ While you are waiting, please <b>adjust your bandwidth settings</b> on the
 <a href="config.jsp">configuration page</a>.
 </li>
 <li>
-If you can, open up <b>port 8887</b> on your firewall, then <b>enable inbound TCP</b> on the
-<a href="config.jsp">configuration page</a>.
-</li>
-<li>
 Once you have a "shared clients" destination listed on the left,
 please <b>check out</b> our
 <a href="http://www.i2p2.i2p/faq.html">FAQ</a>.
@@ -35,9 +31,6 @@ Passe bitte In der Wartezeit <b>deine Einstellungen zur Bandbreite</b> auf der
 <a href="config.jsp">Einstellungsseite</a> an.
 </li>
 <li>
-Bitte &ouml;ffne sobald m&ouml;glich  den <b>Port 8887</b> in deiner Firewall, aktiviere danach den <b>eingehenden TCP Verkehr</b> auf der <a href="config.jsp">Einstellungsseite</a>.
-</li>
-<li>
 Sobald auf der linken Seite eine "shared clients" Verbindung aufgelistet ist <b>besuche bitte</b> unsere <a href="http://www.i2p2.i2p/faq_de.html">FAQ</a>.
 </li>
 <li>
diff --git a/news.xml b/installer/resources/news.xml
similarity index 100%
rename from news.xml
rename to installer/resources/news.xml
diff --git a/readme.html b/installer/resources/readme/readme.html
similarity index 100%
rename from readme.html
rename to installer/resources/readme/readme.html
diff --git a/readme_de.html b/installer/resources/readme/readme_de.html
similarity index 100%
rename from readme_de.html
rename to installer/resources/readme/readme_de.html
diff --git a/readme_fr.html b/installer/resources/readme/readme_fr.html
similarity index 100%
rename from readme_fr.html
rename to installer/resources/readme/readme_fr.html
diff --git a/readme_nl.html b/installer/resources/readme/readme_nl.html
similarity index 100%
rename from readme_nl.html
rename to installer/resources/readme/readme_nl.html
diff --git a/readme_sv.html b/installer/resources/readme/readme_sv.html
similarity index 100%
rename from readme_sv.html
rename to installer/resources/readme/readme_sv.html
diff --git a/readme_zh.html b/installer/resources/readme/readme_zh.html
similarity index 100%
rename from readme_zh.html
rename to installer/resources/readme/readme_zh.html
diff --git a/router/java/src/net/i2p/router/RouterThrottleImpl.java b/router/java/src/net/i2p/router/RouterThrottleImpl.java
index 228c216f1c4af38870e111b8389a58272561bf48..00adcda785a44a9ac9b151b5556b42811d1ad7c9 100644
--- a/router/java/src/net/i2p/router/RouterThrottleImpl.java
+++ b/router/java/src/net/i2p/router/RouterThrottleImpl.java
@@ -34,7 +34,7 @@ class RouterThrottleImpl implements RouterThrottle {
     private static final int DEFAULT_MAX_TUNNELS = 2500;
     private static final String PROP_DEFAULT_KBPS_THROTTLE = "router.defaultKBpsThrottle";
     private static final String PROP_MAX_PROCESSINGTIME = "router.defaultProcessingTimeThrottle";
-    private static final int DEFAULT_MAX_PROCESSINGTIME = 1500;
+    private static final int DEFAULT_MAX_PROCESSINGTIME = 1250;
 
     /** tunnel acceptance */
     public static final int TUNNEL_ACCEPT = 0;
@@ -137,7 +137,7 @@ class RouterThrottleImpl implements RouterThrottle {
                     _log.warn("Refusing tunnel request due to sendProcessingTime of " + avgSendProcessingTime
                             + " ms over the last two minutes, which is too much.");
                 }
-                setTunnelStatus("Rejecting tunnels: congestion");
+                setTunnelStatus("Rejecting tunnels: High message delay");
                 return TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
             }
         }
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
index 6c55e1f994faed9a3f9c6beac4f2f7f268415d7c..f7ecf82929e99d234d56b3e1548f4d8ebd243be0 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
@@ -659,7 +659,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
     String validate(Hash key, RouterInfo routerInfo) throws IllegalArgumentException {
         long now = _context.clock().now();
         boolean upLongEnough = _context.router().getUptime() > 60*60*1000;
-        // Once we're over 150 routers, reduce the expiration time down from the default,
+        // Once we're over 120 routers, reduce the expiration time down from the default,
         // as a crude way of limiting memory usage.
         // i.e. at 300 routers the expiration time will be about half the default, etc.
         // And if we're floodfill, we can keep the expiration really short, since
@@ -673,7 +673,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
             // _kb.size() includes leasesets but that's ok
             adjustedExpiration = Math.min(ROUTER_INFO_EXPIRATION,
                                           ROUTER_INFO_EXPIRATION_MIN +
-                                          ((ROUTER_INFO_EXPIRATION - ROUTER_INFO_EXPIRATION_MIN) * 150 / (_kb.size() + 1)));
+                                          ((ROUTER_INFO_EXPIRATION - ROUTER_INFO_EXPIRATION_MIN) * 120 / (_kb.size() + 1)));
 
         if (!key.equals(routerInfo.getIdentity().getHash())) {
             if (_log.shouldLog(Log.WARN))
@@ -891,12 +891,13 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
     public int getPeerTimeout(Hash peer) {
         PeerProfile prof = _context.profileOrganizer().getProfile(peer);
         double responseTime = MAX_PER_PEER_TIMEOUT;
-        if (prof != null)
+        if (prof != null && prof.getIsExpandedDB()) {
             responseTime = prof.getDbResponseTime().getLifetimeAverageValue();
-        if (responseTime < MIN_PER_PEER_TIMEOUT)
-            responseTime = MIN_PER_PEER_TIMEOUT;
-        else if (responseTime > MAX_PER_PEER_TIMEOUT)
-            responseTime = MAX_PER_PEER_TIMEOUT;
+            if (responseTime < MIN_PER_PEER_TIMEOUT)
+                responseTime = MIN_PER_PEER_TIMEOUT;
+            else if (responseTime > MAX_PER_PEER_TIMEOUT)
+                responseTime = MAX_PER_PEER_TIMEOUT;
+        }
         return 4 * (int)responseTime;  // give it up to 4x the average response time
     }
 
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
index af3ba2b277624a51c4e7a8cfcbb8aace6b51439a..ce8dee19310b02ab1d2f0b9304fe372ba46ec159 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
@@ -174,11 +174,13 @@ class StoreJob extends JobImpl {
                     _state.addSkipped(peer);
                 } else {
                     int peerTimeout = _facade.getPeerTimeout(peer);
-                    PeerProfile prof = getContext().profileOrganizer().getProfile(peer);
-                    if (prof != null) {
-                        RateStat failing = prof.getDBHistory().getFailedLookupRate();
-                        Rate failed = failing.getRate(60*60*1000);
-                    }
+
+                    //PeerProfile prof = getContext().profileOrganizer().getProfile(peer);
+                    //if (prof != null && prof.getIsExpandedDB()) {
+                    //    RateStat failing = prof.getDBHistory().getFailedLookupRate();
+                    //    Rate failed = failing.getRate(60*60*1000);
+                    //}
+
                     //long failedCount = failed.getCurrentEventCount()+failed.getLastEventCount();
                     //if (failedCount > 10) {
                     //    _state.addSkipped(peer);
diff --git a/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java b/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java
index 7fdc5fbce21aed41be4a5181fb74f0221aabba66..f306b199573ceead4a7c635d9953cc35d1dba7cf 100644
--- a/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java
+++ b/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java
@@ -19,12 +19,15 @@ public class IntegrationCalculator extends Calculator {
     
     @Override
     public double calc(PeerProfile profile) {
-        // give more weight to recent counts
-        long val = profile.getDbIntroduction().getRate(24*60*60*1000l).getCurrentEventCount();
-        val += 2 * 4 * profile.getDbIntroduction().getRate(6*60*60*1000l).getLastEventCount();
-        val += 3 * 4 * profile.getDbIntroduction().getRate(6*60*60*1000l).getCurrentEventCount();
-        val += 4 * 24 * profile.getDbIntroduction().getRate(60*60*1000l).getCurrentEventCount();
-        val /= 10;
+        long val = 0;
+        if (profile.getIsExpandedDB()) {
+            // give more weight to recent counts
+            val = profile.getDbIntroduction().getRate(24*60*60*1000l).getCurrentEventCount();
+            val += 2 * 4 * profile.getDbIntroduction().getRate(6*60*60*1000l).getLastEventCount();
+            val += 3 * 4 * profile.getDbIntroduction().getRate(6*60*60*1000l).getCurrentEventCount();
+            val += 4 * 24 * profile.getDbIntroduction().getRate(60*60*1000l).getCurrentEventCount();
+            val /= 10;
+        }
         val += profile.getIntegrationBonus();
         return val;
     }
diff --git a/router/java/src/net/i2p/router/peermanager/PeerProfile.java b/router/java/src/net/i2p/router/peermanager/PeerProfile.java
index 5a5d51eedd266b9f2e2ff01032cc50c22acf0b9d..a0e4f76a05dfcaadeae54aaa4970af16469c9ee5 100644
--- a/router/java/src/net/i2p/router/peermanager/PeerProfile.java
+++ b/router/java/src/net/i2p/router/peermanager/PeerProfile.java
@@ -36,8 +36,8 @@ public class PeerProfile {
     private long _lastHeardFrom;
     private double _tunnelTestResponseTimeAvg;
     // periodic rates
-    private RateStat _sendSuccessSize = null;
-    private RateStat _receiveSize = null;
+    //private RateStat _sendSuccessSize = null;
+    //private RateStat _receiveSize = null;
     private RateStat _dbResponseTime = null;
     private RateStat _tunnelCreateResponseTime = null;
     private RateStat _tunnelTestResponseTime = null;
@@ -56,6 +56,7 @@ public class PeerProfile {
     private DBHistory _dbHistory;
     // does this peer profile contain expanded data, or just the basics?
     private boolean _expanded;
+    private boolean _expandedDB;
     private int _consecutiveShitlists;
     
     public PeerProfile(RouterContext context, Hash peer) {
@@ -72,6 +73,8 @@ public class PeerProfile {
         _consecutiveShitlists = 0;
         _tunnelTestResponseTimeAvg = 0.0d;
         _peer = peer;
+        // this is always true, and there are several places in the router that will NPE
+        // if it is false, so all need to be fixed before we can have non-expanded profiles
         if (expand)
             expandProfile();
     }
@@ -87,6 +90,7 @@ public class PeerProfile {
      *
      */
     public boolean getIsExpanded() { return _expanded; }
+    public boolean getIsExpandedDB() { return _expandedDB; }
     
     public int incrementShitlists() { return _consecutiveShitlists++; }
     public void unshitlist() { _consecutiveShitlists = 0; }
@@ -107,18 +111,25 @@ public class PeerProfile {
      *
      * Note: this appears to be the only use for these two RateStats.
      *
+     * Update: Rewritten so we can get rid of the two RateStats.
+     *         This also helps by not having it depend on coalesce boundaries.
+     *
      * @param period must be one of the periods in the RateStat constructors below
      *        (5*60*1000 or 60*60*1000)
      */
     public boolean getIsActive(long period) {
-        if ( (getSendSuccessSize().getRate(period).getCurrentEventCount() > 0) ||
-             (getSendSuccessSize().getRate(period).getLastEventCount() > 0) ||
-             (getReceiveSize().getRate(period).getCurrentEventCount() > 0) ||
-             (getReceiveSize().getRate(period).getLastEventCount() > 0) ||
-             _context.commSystem().isEstablished(_peer) )
-            return true;
-        else
-            return false;
+        //if ( (getSendSuccessSize().getRate(period).getCurrentEventCount() > 0) ||
+        //     (getSendSuccessSize().getRate(period).getLastEventCount() > 0) ||
+        //     (getReceiveSize().getRate(period).getCurrentEventCount() > 0) ||
+        //     (getReceiveSize().getRate(period).getLastEventCount() > 0) ||
+        //     _context.commSystem().isEstablished(_peer) )
+        //    return true;
+        //else
+        //    return false;
+        long before = _context.clock().now() - period;
+        return getLastHeardFrom() < before ||
+               getLastSendSuccessful() < before ||
+             _context.commSystem().isEstablished(_peer);
     }
     
     
@@ -142,25 +153,31 @@ public class PeerProfile {
     public long getLastHeardFrom() { return _lastHeardFrom; }
     public void setLastHeardFrom(long when) { _lastHeardFrom = when; }
     
-    /** history of tunnel activity with the peer */
+    /** history of tunnel activity with the peer
+        Warning - may return null if !getIsExpanded() */
     public TunnelHistory getTunnelHistory() { return _tunnelHistory; }
     public void setTunnelHistory(TunnelHistory history) { _tunnelHistory = history; }
     
-    /** history of db activity with the peer */
+    /** history of db activity with the peer
+        Warning - may return null if !getIsExpandedDB() */
     public DBHistory getDBHistory() { return _dbHistory; }
     public void setDBHistory(DBHistory hist) { _dbHistory = hist; }
     
     /** how large successfully sent messages are, calculated over a 1 minute, 1 hour, and 1 day period */
-    public RateStat getSendSuccessSize() { return _sendSuccessSize; }
+    //public RateStat getSendSuccessSize() { return _sendSuccessSize; }
     /** how large received messages are, calculated over a 1 minute, 1 hour, and 1 day period */
-    public RateStat getReceiveSize() { return _receiveSize; }
-    /** how long it takes to get a db response from the peer (in milliseconds), calculated over a 1 minute, 1 hour, and 1 day period */
+    //public RateStat getReceiveSize() { return _receiveSize; }
+    /** how long it takes to get a db response from the peer (in milliseconds), calculated over a 1 minute, 1 hour, and 1 day period
+        Warning - may return null if !getIsExpandedDB() */
     public RateStat getDbResponseTime() { return _dbResponseTime; }
-    /** how long it takes to get a tunnel create response from the peer (in milliseconds), calculated over a 1 minute, 1 hour, and 1 day period */
+    /** how long it takes to get a tunnel create response from the peer (in milliseconds), calculated over a 1 minute, 1 hour, and 1 day period
+        Warning - may return null if !getIsExpanded() */
     public RateStat getTunnelCreateResponseTime() { return _tunnelCreateResponseTime; }
-    /** how long it takes to successfully test a tunnel this peer participates in (in milliseconds), calculated over a 10 minute, 1 hour, and 1 day period */
+    /** how long it takes to successfully test a tunnel this peer participates in (in milliseconds), calculated over a 10 minute, 1 hour, and 1 day period
+        Warning - may return null if !getIsExpanded() */
     public RateStat getTunnelTestResponseTime() { return _tunnelTestResponseTime; }
-    /** how many new peers we get from dbSearchReplyMessages or dbStore messages, calculated over a 1 hour, 1 day, and 1 week period */
+    /** how many new peers we get from dbSearchReplyMessages or dbStore messages, calculated over a 1 hour, 1 day, and 1 week period
+        Warning - may return null if !getIsExpandedDB() */
     public RateStat getDbIntroduction() { return _dbIntroduction; }
     
     /**
@@ -327,10 +344,12 @@ public class PeerProfile {
      * extensive stats on them, call this to discard excess data points.  Specifically,
      * this drops the rates, the tunnelHistory, and the dbHistory.
      *
+     * UNUSED for now, will cause NPEs elsewhere
      */
+/*****
     public void shrinkProfile() {
-        _sendSuccessSize = null;
-        _receiveSize = null;
+        //_sendSuccessSize = null;
+        //_receiveSize = null;
         _dbResponseTime = null;
         _tunnelCreateResponseTime = null;
         _tunnelTestResponseTime = null;
@@ -339,7 +358,9 @@ public class PeerProfile {
         _dbHistory = null;
         
         _expanded = false;
+        _expandedDB = false;
     }
+******/
     
     /**
      * When the given peer is performing well enough that we want to keep detailed
@@ -350,32 +371,43 @@ public class PeerProfile {
      */
     public void expandProfile() {
         String group = (null == _peer ? "profileUnknown" : _peer.toBase64().substring(0,6));
-        if (_sendSuccessSize == null)
-            _sendSuccessSize = new RateStat("sendSuccessSize", "How large successfully sent messages are", group, new long[] { 5*60*1000l, 60*60*1000l });
-        if (_receiveSize == null)
-            _receiveSize = new RateStat("receiveSize", "How large received messages are", group, new long[] { 5*60*1000l, 60*60*1000l } );
-        if (_dbResponseTime == null)
-            _dbResponseTime = new RateStat("dbResponseTime", "how long it takes to get a db response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
+        //if (_sendSuccessSize == null)
+        //    _sendSuccessSize = new RateStat("sendSuccessSize", "How large successfully sent messages are", group, new long[] { 5*60*1000l, 60*60*1000l });
+        //if (_receiveSize == null)
+        //    _receiveSize = new RateStat("receiveSize", "How large received messages are", group, new long[] { 5*60*1000l, 60*60*1000l } );
         if (_tunnelCreateResponseTime == null)
             _tunnelCreateResponseTime = new RateStat("tunnelCreateResponseTime", "how long it takes to get a tunnel create response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000 } );
         if (_tunnelTestResponseTime == null)
             _tunnelTestResponseTime = new RateStat("tunnelTestResponseTime", "how long it takes to successfully test a tunnel this peer participates in (in milliseconds)", group, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 3*60*60*1000l, 24*60*60*1000 } );
-        if (_dbIntroduction == null)
-            _dbIntroduction = new RateStat("dbIntroduction", "how many new peers we get from dbSearchReplyMessages or dbStore messages", group, new long[] { 60*60*1000l, 6*60*60*1000l, 24*60*60*1000l });
 
         if (_tunnelHistory == null)
             _tunnelHistory = new TunnelHistory(_context, group);
+
+        //_sendSuccessSize.setStatLog(_context.statManager().getStatLog());
+        //_receiveSize.setStatLog(_context.statManager().getStatLog());
+        _tunnelCreateResponseTime.setStatLog(_context.statManager().getStatLog());
+        _tunnelTestResponseTime.setStatLog(_context.statManager().getStatLog());
+        _expanded = true;
+    }
+
+    /**
+     * For floodfills
+     */
+    public synchronized void expandDBProfile() {
+        String group = (null == _peer ? "profileUnknown" : _peer.toBase64().substring(0,6));
+        if (_dbResponseTime == null)
+            _dbResponseTime = new RateStat("dbResponseTime", "how long it takes to get a db response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
+        if (_dbIntroduction == null)
+            _dbIntroduction = new RateStat("dbIntroduction", "how many new peers we get from dbSearchReplyMessages or dbStore messages", group, new long[] { 60*60*1000l, 6*60*60*1000l, 24*60*60*1000l });
+
         if (_dbHistory == null)
             _dbHistory = new DBHistory(_context, group);
 
-        _sendSuccessSize.setStatLog(_context.statManager().getStatLog());
-        _receiveSize.setStatLog(_context.statManager().getStatLog());
         _dbResponseTime.setStatLog(_context.statManager().getStatLog());
-        _tunnelCreateResponseTime.setStatLog(_context.statManager().getStatLog());
-        _tunnelTestResponseTime.setStatLog(_context.statManager().getStatLog());
         _dbIntroduction.setStatLog(_context.statManager().getStatLog());
-        _expanded = true;
+        _expandedDB = true;
     }
+
     /** once a day, on average, cut the measured throughtput values in half */
     /** let's try once an hour times 3/4 */
     private static final int DROP_PERIOD_MINUTES = 60;
@@ -419,14 +451,16 @@ public class PeerProfile {
     /** update the stats and rates (this should be called once a minute) */
     public void coalesceStats() {
         if (!_expanded) return;
-        _dbIntroduction.coalesceStats();
-        _dbResponseTime.coalesceStats();
-        _receiveSize.coalesceStats();
-        _sendSuccessSize.coalesceStats();
+        //_receiveSize.coalesceStats();
+        //_sendSuccessSize.coalesceStats();
         _tunnelCreateResponseTime.coalesceStats();
         _tunnelTestResponseTime.coalesceStats();
-        _dbHistory.coalesceStats();
         _tunnelHistory.coalesceStats();
+        if (_expandedDB) {
+            _dbIntroduction.coalesceStats();
+            _dbResponseTime.coalesceStats();
+            _dbHistory.coalesceStats();
+        }
         
         coalesceThroughput();
         
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java b/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java
index 1c40b003dbf4436ce5d93ffebf65bacde1c88255..5da853c6190484f731228d185444b76897219d46 100644
--- a/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java
+++ b/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java
@@ -39,7 +39,7 @@ public class ProfileManagerImpl implements ProfileManager {
         PeerProfile data = getProfile(peer);
         if (data == null) return;
         data.setLastSendSuccessful(_context.clock().now());
-        data.getSendSuccessSize().addData(bytesSent, msToSend);
+        //data.getSendSuccessSize().addData(bytesSent, msToSend);
     }
     
     /**
@@ -169,11 +169,14 @@ public class ProfileManagerImpl implements ProfileManager {
     /**
      * Note that the peer was able to return the valid data for a db lookup
      *
+     * This will force creation of DB stats
      */
     public void dbLookupSuccessful(Hash peer, long responseTimeMs) {
         PeerProfile data = getProfile(peer);
         if (data == null) return;
         data.setLastHeardFrom(_context.clock().now());
+        if (!data.getIsExpandedDB())
+            data.expandDBProfile();
         data.getDbResponseTime().addData(responseTimeMs, responseTimeMs);
         DBHistory hist = data.getDBHistory();
         hist.lookupSuccessful();
@@ -183,10 +186,13 @@ public class ProfileManagerImpl implements ProfileManager {
      * Note that the peer was unable to reply to a db lookup - either with data or with
      * a lookupReply redirecting the user elsewhere
      *
+     * This will force creation of DB stats
      */
     public void dbLookupFailed(Hash peer) {
         PeerProfile data = getProfile(peer);
         if (data == null) return;
+        if (!data.getIsExpandedDB())
+            data.expandDBProfile();
         DBHistory hist = data.getDBHistory();
         hist.lookupFailed();
     }
@@ -203,6 +209,8 @@ public class ProfileManagerImpl implements ProfileManager {
         PeerProfile data = getProfile(peer);
         if (data == null) return;
         data.setLastHeardFrom(_context.clock().now());
+        if (!data.getIsExpandedDB())
+            return;
         data.getDbResponseTime().addData(responseTimeMs, responseTimeMs);
         data.getDbIntroduction().addData(newPeers, responseTimeMs);
         DBHistory hist = data.getDBHistory();
@@ -217,6 +225,8 @@ public class ProfileManagerImpl implements ProfileManager {
         PeerProfile data = getProfile(peer);
         if (data == null) return;
         data.setLastHeardFrom(_context.clock().now());
+        if (!data.getIsExpandedDB())
+            return;
         DBHistory hist = data.getDBHistory();
         hist.lookupReceived();
     }
@@ -229,6 +239,8 @@ public class ProfileManagerImpl implements ProfileManager {
         PeerProfile data = getProfile(peer);
         if (data == null) return;
         data.setLastHeardFrom(_context.clock().now());
+        if (!data.getIsExpandedDB())
+            return;
         DBHistory hist = data.getDBHistory();
         hist.unpromptedStoreReceived(wasNewKey);
     }
@@ -242,8 +254,10 @@ public class ProfileManagerImpl implements ProfileManager {
         PeerProfile data = getProfile(peer);
         if (data == null) return;
         long now = _context.clock().now();
-        data.setLastSendSuccessful(now);
         data.setLastHeardFrom(now);
+        if (!data.getIsExpandedDB())
+            return;
+        data.setLastSendSuccessful(now);
         // we could do things like update some sort of "how many successful stores we've sent them"...
         // naah.. dont really care now
     }
@@ -279,7 +293,7 @@ public class ProfileManagerImpl implements ProfileManager {
         PeerProfile data = getProfile(peer);
         if (data == null) return;
         data.setLastHeardFrom(_context.clock().now());
-        data.getReceiveSize().addData(bytesRead, msToReceive);
+        //data.getReceiveSize().addData(bytesRead, msToReceive);
     }
     
     private PeerProfile getProfile(Hash peer) {
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
index 4fd812efa99075c121850e1caee5ea3dc7821e02..20f0fba3d4b5977c2247efab62056070b3f98a9c 100644
--- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
+++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
@@ -241,7 +241,7 @@ public class ProfileOrganizer {
      */
     public boolean peerSendsBadReplies(Hash peer) {
         PeerProfile profile = getProfile(peer);
-        if (profile != null) {
+        if (profile != null && profile.getIsExpandedDB()) {
             RateStat invalidReplyRateStat = profile.getDBHistory().getInvalidReplyRate();
             Rate invalidReplyRate = invalidReplyRateStat.getRate(30*60*1000l);
             if ( (invalidReplyRate.getCurrentTotalValue() > MAX_BAD_REPLIES_PER_HOUR) ||
diff --git a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java
index e7151314e1985ff9e73f95823e11c836cc32f1c5..606b4883cf288f4e7a905aa67df81a3fd7bf2456 100644
--- a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java
+++ b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java
@@ -128,18 +128,20 @@ class ProfilePersistenceHelper {
         
         out.write(buf.toString().getBytes());
         
-        profile.getTunnelHistory().store(out);
-        profile.getDBHistory().store(out);
-        
         if (profile.getIsExpanded()) {
             // only write out expanded data if, uh, we've got it
-            profile.getDbIntroduction().store(out, "dbIntroduction");
-            profile.getDbResponseTime().store(out, "dbResponseTime");
-            profile.getReceiveSize().store(out, "receiveSize");
-            profile.getSendSuccessSize().store(out, "sendSuccessSize");
+            profile.getTunnelHistory().store(out);
+            //profile.getReceiveSize().store(out, "receiveSize");
+            //profile.getSendSuccessSize().store(out, "sendSuccessSize");
             profile.getTunnelCreateResponseTime().store(out, "tunnelCreateResponseTime");
             profile.getTunnelTestResponseTime().store(out, "tunnelTestResponseTime");
         }
+
+        if (profile.getIsExpandedDB()) {
+            profile.getDBHistory().store(out);
+            profile.getDbIntroduction().store(out, "dbIntroduction");
+            profile.getDbResponseTime().store(out, "dbResponseTime");
+        }
     }
     
     public Set readProfiles() {
@@ -211,12 +213,22 @@ class ProfilePersistenceHelper {
             profile.setPeakTunnel1mThroughputKBps(getDouble(props, "tunnelPeakTunnel1mThroughput"));
             
             profile.getTunnelHistory().load(props);
-            profile.getDBHistory().load(props);
-            
-            profile.getDbIntroduction().load(props, "dbIntroduction", true);
-            profile.getDbResponseTime().load(props, "dbResponseTime", true);
-            profile.getReceiveSize().load(props, "receiveSize", true);
-            profile.getSendSuccessSize().load(props, "sendSuccessSize", true);
+
+            // In the interest of keeping the in-memory profiles small,
+            // don't load the DB info at all unless there is something interesting there
+            // (i.e. floodfills)
+            // It seems like we do one or two lookups as a part of handshaking?
+            // Not sure, to be researched.
+            if (getLong(props, "dbHistory.successfulLookups") > 1 ||
+                getLong(props, "dbHistory.failedlLokups") > 1) {
+                profile.expandDBProfile();
+                profile.getDBHistory().load(props);
+                profile.getDbIntroduction().load(props, "dbIntroduction", true);
+                profile.getDbResponseTime().load(props, "dbResponseTime", true);
+            }
+
+            //profile.getReceiveSize().load(props, "receiveSize", true);
+            //profile.getSendSuccessSize().load(props, "sendSuccessSize", true);
             profile.getTunnelCreateResponseTime().load(props, "tunnelCreateResponseTime", true);
             profile.getTunnelTestResponseTime().load(props, "tunnelTestResponseTime", true);
             
diff --git a/router/java/src/net/i2p/router/transport/UPnP.java b/router/java/src/net/i2p/router/transport/UPnP.java
index 6ed11cdc2a62239540212ef94abe558b45e4e6da..0fc538a3f3503ddbe3510650426b308f24dc82d5 100644
--- a/router/java/src/net/i2p/router/transport/UPnP.java
+++ b/router/java/src/net/i2p/router/transport/UPnP.java
@@ -636,25 +636,20 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
 		}
 
 		public void run() {
+			HashMap<ForwardPort, ForwardPortStatus> map = new HashMap(1);
 			for(ForwardPort port : portsToForwardNow) {
 				String proto = protoToString(port.protocol);
+				map.clear();
+				ForwardPortStatus fps;
 				if (proto.length() <= 1) {
-					HashMap<ForwardPort, ForwardPortStatus> map = new HashMap<ForwardPort, ForwardPortStatus>();
-					map.put(port, new ForwardPortStatus(ForwardPortStatus.DEFINITE_FAILURE, "Protocol not supported", port.portNumber));
-					forwardCallback.portForwardStatus(map);
-					continue;
-				}
-				if(tryAddMapping(proto, port.portNumber, port.name, port)) {
-					HashMap<ForwardPort, ForwardPortStatus> map = new HashMap<ForwardPort, ForwardPortStatus>();
-					map.put(port, new ForwardPortStatus(ForwardPortStatus.MAYBE_SUCCESS, "Port apparently forwarded by UPnP", port.portNumber));
-					forwardCallback.portForwardStatus(map);
-					continue;
+					fps = new ForwardPortStatus(ForwardPortStatus.DEFINITE_FAILURE, "Protocol not supported", port.portNumber);
+				} else if(tryAddMapping(proto, port.portNumber, port.name, port)) {
+					fps = new ForwardPortStatus(ForwardPortStatus.MAYBE_SUCCESS, "Port apparently forwarded by UPnP", port.portNumber);
 				} else {
-					HashMap<ForwardPort, ForwardPortStatus> map = new HashMap<ForwardPort, ForwardPortStatus>();
-					map.put(port, new ForwardPortStatus(ForwardPortStatus.PROBABLE_FAILURE, "UPnP port forwarding apparently failed", port.portNumber));
-					forwardCallback.portForwardStatus(map);
-					continue;
+					fps = new ForwardPortStatus(ForwardPortStatus.PROBABLE_FAILURE, "UPnP port forwarding apparently failed", port.portNumber);
 				}
+				map.put(port, fps);
+				forwardCallback.portForwardStatus(map);
 			}
 		}
 	}
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java
index f828361e2669d7cbc511facea094bf883a8f526b..cffb8fd8bdb931bec3733689fb0c8d22b6240b19 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java
@@ -22,7 +22,11 @@ public class UDPEndpoint {
     private DatagramSocket _socket;
     private InetAddress _bindAddress;
     
-    public UDPEndpoint(RouterContext ctx, UDPTransport transport, int listenPort, InetAddress bindAddress) throws SocketException {
+    /**
+     *  @param listenPort -1 or the requested port, may not be honored
+     *  @param bindAddress null ok
+     */
+    public UDPEndpoint(RouterContext ctx, UDPTransport transport, int listenPort, InetAddress bindAddress) {
         _context = ctx;
         _log = ctx.logManager().getLog(UDPEndpoint.class);
         _transport = transport;
@@ -30,23 +34,20 @@ public class UDPEndpoint {
         _listenPort = listenPort;
     }
     
+    /** caller should call getListenPort() after this to get the actual bound port and determine success */
     public void startup() {
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Starting up the UDP endpoint");
         shutdown();
-        try {
-            if (_bindAddress == null)
-                _socket = new DatagramSocket(_listenPort);
-            else
-                _socket = new DatagramSocket(_listenPort, _bindAddress);
-            _sender = new UDPSender(_context, _socket, "UDPSender");
-            _receiver = new UDPReceiver(_context, _transport, _socket, "UDPReceiver");
-            _sender.startup();
-            _receiver.startup();
-        } catch (SocketException se) {
-            _transport.setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
-            _log.log(Log.CRIT, "Unable to bind on port " + _listenPort, se);
+        _socket = getSocket();
+        if (_socket == null) {
+            _log.log(Log.CRIT, "UDP Unable to open a port");
+            return;
         }
+        _sender = new UDPSender(_context, _socket, "UDPSender");
+        _receiver = new UDPReceiver(_context, _transport, _socket, "UDPReceiver");
+        _sender.startup();
+        _receiver.startup();
     }
     
     public void shutdown() {
@@ -60,6 +61,8 @@ public class UDPEndpoint {
     }
     
     public void setListenPort(int newPort) { _listenPort = newPort; }
+
+/*******
     public void updateListenPort(int newPort) {
         if (newPort == _listenPort) return;
         try {
@@ -76,7 +79,54 @@ public class UDPEndpoint {
                 _log.error("Unable to bind on " + _listenPort);
         }
     }
+********/
     
+    /** 8998 is monotone, and 32000 is the wrapper, so let's stay between those */
+    private static final int MIN_RANDOM_PORT = 9111;
+    private static final int MAX_RANDOM_PORT = 31777;
+    private static final int MAX_PORT_RETRIES = 20;
+
+    /**
+     *  Open socket using requested port in _listenPort and  bind host in _bindAddress.
+     *  If _listenPort <= 0, or requested port is busy, repeatedly try a new random port.
+     *  @return null on failure
+     *  Sets _listenPort to actual port or -1 on failure
+     */
+    private DatagramSocket getSocket() {
+        DatagramSocket socket = null;
+        int port = _listenPort;
+
+        for (int i = 0; i < MAX_PORT_RETRIES; i++) {
+             if (port <= 0) {
+                 // try random ports rather than just do new DatagramSocket()
+                 // so we stay out of the way of other I2P stuff
+                 port = MIN_RANDOM_PORT + _context.random().nextInt(MAX_RANDOM_PORT - MIN_RANDOM_PORT);
+             }
+             try {
+                 if (_bindAddress == null)
+                     socket = new DatagramSocket(port);
+                 else
+                     socket = new DatagramSocket(port, _bindAddress);
+                 break;
+             } catch (SocketException se) {
+                 if (_log.shouldLog(Log.WARN))
+                     _log.warn("Binding to port " + port + " failed: " + se);
+             }
+             port = -1;
+        }
+        if (socket == null) {
+            _log.log(Log.CRIT, "SSU Unable to bind to a port on " + _bindAddress);
+        } else if (port != _listenPort) {
+            if (_listenPort > 0)
+                _log.error("SSU Unable to bind to requested port " + _listenPort + ", using random port " + port);
+            else
+                _log.error("SSU selected random port " + port);
+        }
+        _listenPort = port;
+        return socket;
+    }
+
+    /** call after startup() to get actual port or -1 on startup failure */
     public int getListenPort() { return _listenPort; }
     public UDPSender getSender() { return _sender; }
     
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
index 2bdf8a466f82fc781ce8754b21ae3802330f4b5e..2556bdb7be8490b562fbbdb9dd3837152caa547c 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
@@ -100,6 +100,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     
     public static final String STYLE = "SSU";
     public static final String PROP_INTERNAL_PORT = "i2np.udp.internalPort";
+    /** now unused, we pick a random port */
     public static final int DEFAULT_INTERNAL_PORT = 8887;
     /** since fixed port defaults to true, this doesnt do anything at the moment.
      *  We should have an exception if it matches the existing low port. */
@@ -137,6 +138,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     public static final String PROP_FORCE_INTRODUCERS = "i2np.udp.forceIntroducers";
     /** do we allow direct SSU connections, sans introducers?  */
     public static final String PROP_ALLOW_DIRECT = "i2np.udp.allowDirect";
+    /** this is rarely if ever used, default is to bind to wildcard address */
     public static final String PROP_BIND_INTERFACE = "i2np.udp.bindInterface";
         
     /** how many relays offered to us will we use at a time? */
@@ -226,40 +228,41 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         System.arraycopy(_context.routerHash().getData(), 0, _introKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
         
         rebuildExternalAddress();
+
+        // bind host
+        String bindTo = _context.getProperty(PROP_BIND_INTERFACE);
+        InetAddress bindToAddr = null;
+        if (bindTo != null) {
+            try {
+                bindToAddr = InetAddress.getByName(bindTo);
+            } catch (UnknownHostException uhe) {
+                _log.log(Log.CRIT, "Invalid SSU bind interface specified [" + bindTo + "]", uhe);
+                setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
+                return;
+            }
+        }
         
-        int port = -1;
+        // Requested bind port
+        // This may be -1 or may not be honored if busy,
+        // we will check below after starting up the endpoint.
+        int port;
+        int oldIPort = _context.getProperty(PROP_INTERNAL_PORT, -1);
+        int oldEPort = _context.getProperty(PROP_EXTERNAL_PORT, -1);
         if (_externalListenPort <= 0) {
             // no explicit external port, so lets try an internal one
-            port = _context.getProperty(PROP_INTERNAL_PORT, DEFAULT_INTERNAL_PORT);
-            // attempt to use it as our external port - this will be overridden by
-            // externalAddressReceived(...)
-            _context.router().setConfigSetting(PROP_EXTERNAL_PORT, port+"");
-            _context.router().saveConfig();
+            if (oldIPort > 0)
+                port = oldIPort;
+            else
+                port = oldEPort;
         } else {
             port = _externalListenPort;
-            if (_log.shouldLog(Log.INFO))
-                _log.info("Binding to the explicitly specified external port: " + port);
         }
+        if (_log.shouldLog(Log.INFO))
+            _log.info("Binding to the port: " + port);
         if (_endpoint == null) {
-            String bindTo = _context.getProperty(PROP_BIND_INTERFACE);
-            InetAddress bindToAddr = null;
-            if (bindTo != null) {
-                try {
-                    bindToAddr = InetAddress.getByName(bindTo);
-                } catch (UnknownHostException uhe) {
-                    if (_log.shouldLog(Log.ERROR))
-                        _log.error("Invalid SSU bind interface specified [" + bindTo + "]", uhe);
-                    bindToAddr = null;
-                }
-            }
-            try {
-                _endpoint = new UDPEndpoint(_context, this, port, bindToAddr);
-            } catch (SocketException se) {
-                if (_log.shouldLog(Log.CRIT))
-                    _log.log(Log.CRIT, "Unable to listen on the UDP port (" + port + ")", se);
-                return;
-            }
+            _endpoint = new UDPEndpoint(_context, this, port, bindToAddr);
         } else {
+            // todo, set bind address too
             _endpoint.setListenPort(port);
         }
         
@@ -278,7 +281,24 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         if (_flooder == null)
             _flooder = new UDPFlooder(_context, this);
         
+        // Startup the endpoint with the requested port, check the actual port, and
+        // take action if it failed or was different than requested or it needs to be saved
         _endpoint.startup();
+        int newPort = _endpoint.getListenPort();
+        _externalListenPort = newPort;
+        if (newPort <= 0) {
+            _log.log(Log.CRIT, "Unable to open UDP port");
+            setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
+            return;
+        }
+        if (newPort != port || newPort != oldIPort || newPort != oldEPort) {
+            // attempt to use it as our external port - this will be overridden by
+            // externalAddressReceived(...)
+            _context.router().setConfigSetting(PROP_INTERNAL_PORT, newPort+"");
+            _context.router().setConfigSetting(PROP_EXTERNAL_PORT, newPort+"");
+            _context.router().saveConfig();
+        }
+
         _establisher.startup();
         _handler.startup();
         _fragments.startup();
@@ -321,11 +341,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     public int getLocalPort() { return _externalListenPort; }
     public InetAddress getLocalAddress() { return _externalListenHost; }
     public int getExternalPort() { return _externalListenPort; }
+
+    /**
+     *  _externalListenPort should always be set (by startup()) before this is called,
+     *  so the returned value should be > 0
+     */
     @Override
     public int getRequestedPort() {
         if (_externalListenPort > 0)
             return _externalListenPort;
-        return _context.getProperty(PROP_INTERNAL_PORT, DEFAULT_INTERNAL_PORT);
+        return _context.getProperty(PROP_INTERNAL_PORT, -1);
     }
 
     /**
@@ -2003,6 +2028,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         buf.append("      <td align=\"center\"><b>").append(resentTotal);
         buf.append("</b></td> <td align=\"center\"><b>").append(dupRecvTotal).append("</b></td>\n");
         buf.append(" </tr></table></div>\n");
+
+      /*****
         long bytesTransmitted = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes();
         // NPE here early
         double averagePacketSize = _context.statManager().getRate("udp.sendPacketSize").getLifetimeAverageValue();
@@ -2012,6 +2039,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         double bwResent = (nondupSent <= 0 ? 0d : ((((double)resentTotal)*averagePacketSize) / nondupSent));
         buf.append("<h3>Percentage of bytes retransmitted (lifetime): ").append(formatPct(bwResent));
         buf.append("</h3><i>(Includes retransmission required by packet loss)</i>\n");
+      *****/
+
         out.write(buf.toString());
         buf.setLength(0);
         out.write(KEY);