diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
index 7dacfe6bf19b6609e438d025b9fc19d9e895c454..cefb942c8f769eee61f9fdedce81d3a7b99588c5 100644
--- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
@@ -51,7 +51,7 @@ class EstablishmentManager {
     // SSU 2
     private final PacketBuilder2 _builder2;
     private final boolean _enableSSU2;
-    private final Map<Hash, Token> _outboundTokens;
+    private final Map<RemoteHostId, Token> _outboundTokens;
     private final Map<RemoteHostId, Token> _inboundTokens;
 
     /** map of RemoteHostId to InboundEstablishState */
@@ -168,7 +168,7 @@ class EstablishmentManager {
         _outboundByHash = new ConcurrentHashMap<Hash, OutboundEstablishState>();
         if (_enableSSU2) {
             _inboundTokens = new LHMCache<RemoteHostId, Token>(MAX_TOKENS);
-            _outboundTokens = new LHMCache<Hash, Token>(MAX_TOKENS);
+            _outboundTokens = new LHMCache<RemoteHostId, Token>(MAX_TOKENS);
         } else {
             _inboundTokens = null;
             _outboundTokens = null;
@@ -1800,7 +1800,7 @@ class EstablishmentManager {
      *  @param token nonzero
      *  @since 0.9.54
      */
-    public void addOutboundToken(Hash peer, long token, long expires) {
+    public void addOutboundToken(RemoteHostId peer, long token, long expires) {
         if (expires < _context.clock().now())
             return;
         Token tok = new Token(token, expires);
@@ -1815,7 +1815,7 @@ class EstablishmentManager {
      *  @return 0 if none available
      *  @since 0.9.54
      */
-    public long getOutboundToken(Hash peer) {
+    public long getOutboundToken(RemoteHostId peer) {
         Token tok;
         synchronized(_outboundTokens) {
             tok = _outboundTokens.remove(peer);
@@ -1828,17 +1828,54 @@ class EstablishmentManager {
     }
 
     /**
-     *  Remember a token that can be used later for the peer to connect to us
+     *  Remove our outbound tokens for this length
+     *
+     *  @since 0.9.54
+     */
+    public void ipChanged(boolean isIPv6) {
+        if (!_enableSSU2)
+            return;
+        int len = isIPv6 ? 16 : 4;
+        // expire while we're at it
+        long now = _context.clock().now();
+        synchronized(_outboundTokens) {
+            for (Iterator<Map.Entry<RemoteHostId, Token>> iter = _outboundTokens.entrySet().iterator(); iter.hasNext(); ) {
+                Map.Entry<RemoteHostId, Token> e = iter.next();
+                if (e.getKey().getIP().length == len || e.getValue().expires < now)
+                    iter.remove();
+            }
+        }
+    }
+
+    /**
+     *  Remove all outbound tokens
+     *
+     *  @since 0.9.54
+     */
+    public void portChanged() {
+        if (!_enableSSU2)
+            return;
+        synchronized(_outboundTokens) {
+            _outboundTokens.clear();
+        }
+    }
+
+    /**
+     *  Get a token that can be used later for the peer to connect to us
      *
-     *  @param token nonzero
      *  @since 0.9.54
      */
-    public void addInboundToken(RemoteHostId peer, long token) {
+    public Token getInboundToken(RemoteHostId peer) {
+        long token;
+        do {
+            token = _context.random().nextLong();
+        } while (token == 0);
         long expires = _context.clock().now() + IB_TOKEN_EXPIRATION;
         Token tok = new Token(token, expires);
         synchronized(_inboundTokens) {
             _inboundTokens.put(peer, tok);
         }
+        return tok;
     }
 
     /**
@@ -1862,7 +1899,7 @@ class EstablishmentManager {
         return tok.expires >= _context.clock().now();
     }
 
-    private static class Token {
+    public static class Token {
         public final long token, expires;
         public Token(long tok, long exp) {
             token = tok; expires = exp;
diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java
index 1a8b927d04c063dbebc144ee1e400bc5b7617634..c3beba3c86097da8ba552cec94888324109f219f 100644
--- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java
+++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java
@@ -370,7 +370,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
     public void gotToken(long token, long expires) {
         if (_receivedConfirmedIdentity == null)
             throw new IllegalStateException("RI must be first");
-        _transport.getEstablisher().addOutboundToken(_receivedConfirmedIdentity.calculateHash(), token, expires);
+        _transport.getEstablisher().addOutboundToken(_remoteHostId, token, expires);
     }
 
     public void gotI2NP(I2NPMessage msg) {
@@ -429,14 +429,8 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
     public long getSendConnID() { return _sendConnID; }
     public long getRcvConnID() { return _rcvConnID; }
     public long getToken() { return _token; }
-    public long getNextToken() {
-        // generate on the fly, this will only be called once
-        long token;
-        do {
-            token = _context.random().nextLong();
-        } while (token == 0);
-        _transport.getEstablisher().addInboundToken(_remoteHostId, token);
-        return token;
+    public EstablishmentManager.Token getNextToken() {
+        return _transport.getEstablisher().getInboundToken(_remoteHostId);
     }
     public HandshakeState getHandshakeState() { return _handshakeState; }
     public byte[] getSendHeaderEncryptKey1() { return _sendHeaderEncryptKey1; }
@@ -720,6 +714,8 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
         StringBuilder buf = new StringBuilder(128);
         buf.append("IES2 ");
         buf.append(Addresses.toString(_aliceIP, _alicePort));
+        buf.append(" Rcv ID: ").append(_rcvConnID);
+        buf.append(" Send ID: ").append(_sendConnID);
         buf.append(" RelayTag: ").append(_sentRelayTag);
         buf.append(' ').append(_currentState);
         return buf.toString();
diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java
index 27bb6637067b55cf84c369b3a4579651f8c47a76..a430b03858593ccb64c7e6a91b35fd7a3892daee 100644
--- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java
+++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java
@@ -127,7 +127,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
         } while (_sendConnID == rcid);
         _rcvConnID = rcid;
 
-        _token = _transport.getEstablisher().getOutboundToken(_remotePeer.calculateHash());
+        _token = _transport.getEstablisher().getOutboundToken(_remoteHostId);
         _routerAddress = ra;
         if (_token != 0)
             createNewState(ra);
@@ -247,7 +247,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
     }
 
     public void gotToken(long token, long expires) {
-        _transport.getEstablisher().addOutboundToken(_remotePeer.calculateHash(), token, expires);
+        _transport.getEstablisher().addOutboundToken(_remoteHostId, token, expires);
     }
 
     public void gotI2NP(I2NPMessage msg) {
@@ -299,14 +299,8 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
     public long getSendConnID() { return _sendConnID; }
     public long getRcvConnID() { return _rcvConnID; }
     public long getToken() { return _token; }
-    public long getNextToken() {
-        // generate on the fly, this will only be called once
-        long token;
-        do {
-            token = _context.random().nextLong();
-        } while (token == 0);
-        _transport.getEstablisher().addInboundToken(_remoteHostId, token);
-        return token;
+    public EstablishmentManager.Token getNextToken() {
+        return _transport.getEstablisher().getInboundToken(_remoteHostId);
     }
     public HandshakeState getHandshakeState() { return _handshakeState; }
     public byte[] getSendHeaderEncryptKey1() { return _sendHeaderEncryptKey1; }
@@ -567,6 +561,9 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
 
     @Override
     public String toString() {
-        return "OES2 " + _remoteHostId + ' ' + _currentState;
+        return "OES2 " + _remoteHostId +
+               " Rcv ID: " + _rcvConnID +
+               " Send ID: " + _sendConnID +
+               ' ' + _currentState;
     }
 }
diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java
index b8df75bcd6b8226be1d2bdef144c82211ad2fd97..662548b531724c868308c6cb367b5e99c0735ccc 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java
@@ -73,9 +73,6 @@ class PacketBuilder2 {
     /** 80 */
     public static final int MIN_IPV6_DATA_PACKET_OVERHEAD = IPV6_HEADER_SIZE + UDP_HEADER_SIZE + DATA_HEADER_SIZE + MAC_LEN;
 
-/// FIXME
-    private static final int MAX_IDENTITY_FRAGMENT_SIZE = 1280 - (MIN_DATA_PACKET_OVERHEAD + KEY_LEN + MAC_LEN);
-
     private static final int ABSOLUTE_MAX_ACK_RANGES = 512;
 
     /* Higher than all other OutNetMessage priorities, but still droppable,
@@ -788,7 +785,8 @@ class PacketBuilder2 {
      *  @param packet containing only 32 byte header
      */
     private void encryptSessionCreated(UDPPacket packet, HandshakeState state,
-                                       byte[] hdrKey1, byte[] hdrKey2, long relayTag, long token, byte[] ip, int port) {
+                                       byte[] hdrKey1, byte[] hdrKey2, long relayTag,
+                                       EstablishmentManager.Token token, byte[] ip, int port) {
         DatagramPacket pkt = packet.getPacket();
         byte data[] = pkt.getData();
         int off = pkt.getOffset();
@@ -805,8 +803,8 @@ class PacketBuilder2 {
                 len += block.getTotalLength();
                 blocks.add(block);
             }
-            if (token > 0) {
-                block = new SSU2Payload.NewTokenBlock(token, _context.clock().now() + EstablishmentManager.IB_TOKEN_EXPIRATION);
+            if (token != null) {
+                block = new SSU2Payload.NewTokenBlock(token.token, token.expires);
                 len += block.getTotalLength();
                 blocks.add(block);
             }
@@ -938,7 +936,7 @@ class PacketBuilder2 {
      */
     private void encryptSessionConfirmed(UDPPacket packet, HandshakeState state, int mtu,
                                          boolean isIPv6, byte[] hdrKey1, byte[] hdrKey2,
-                                         SSU2Payload.RIBlock riblock, long token) {
+                                         SSU2Payload.RIBlock riblock, EstablishmentManager.Token token) {
         DatagramPacket pkt = packet.getPacket();
         byte data[] = pkt.getData();
         int off = pkt.getOffset();
@@ -949,8 +947,8 @@ class PacketBuilder2 {
             int len = riblock.getTotalLength();
             blocks.add(riblock);
             // only if room
-            if (token > 0 && mtu - (SHORT_HEADER_SIZE + KEY_LEN + MAC_LEN + len + MAC_LEN) >= 15) {
-                Block block = new SSU2Payload.NewTokenBlock(token, _context.clock().now() + EstablishmentManager.IB_TOKEN_EXPIRATION);
+            if (token != null && mtu - (SHORT_HEADER_SIZE + KEY_LEN + MAC_LEN + len + MAC_LEN) >= 15) {
+                Block block = new SSU2Payload.NewTokenBlock(token.token, token.expires);
                 len += block.getTotalLength();
                 blocks.add(block);
             }
diff --git a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
index be750dfdb797c7b2c241dcfe8dfb89fd48544bb2..be1c7ffc8fe585e3b202d320b8051900eefe7ee5 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
@@ -289,8 +289,8 @@ class PacketHandler {
                 if (state.getNextMACKey() != null)
                     isValid = validate(packet, state.getNextMACKey());
                 if (!isValid) {
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info("Failed validation with existing con, trying as new con: " + packet);
+                    if (_log.shouldDebug())
+                        _log.debug("Failed validation with existing con, trying as new con: " + packet);
 
                     isValid = validate(packet, _transport.getIntroKey());
                     if (isValid) {
@@ -472,8 +472,8 @@ class PacketHandler {
             if (state.getMACKey() != null) {
                 isValid = validate(packet, state.getMACKey());
                 if (isValid) {
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info("Valid introduction packet received for inbound con: " + packet);
+                    if (_log.shouldDebug())
+                        _log.debug("Valid introduction packet received for inbound con: " + packet);
 
                     packet.decrypt(state.getCipherKey());
                     handlePacket(reader, packet, null, null, null, AuthType.SESSION);
@@ -513,8 +513,8 @@ class PacketHandler {
                 isValid = validate(packet, state.getMACKey());
                 if (isValid) {
                     // this should be the Session Confirmed packet
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info("Valid introduction packet received for outbound established con: " + packet);
+                    if (_log.shouldDebug())
+                        _log.debug("Valid introduction packet received for outbound established con: " + packet);
                     packet.decrypt(state.getCipherKey());
                     handlePacket(reader, packet, null, state, null, AuthType.SESSION);
                     return;
@@ -524,8 +524,8 @@ class PacketHandler {
             // keys not yet exchanged, lets try it with the peer's intro key
             isValid = validate(packet, state.getIntroKey());
             if (isValid) {
-                if (_log.shouldLog(Log.INFO))
-                    _log.info("Valid packet received for " + state + " with Bob's intro key: " + packet);
+                if (_log.shouldDebug())
+                    _log.debug("Valid packet received for " + state + " with Bob's intro key: " + packet);
                 packet.decrypt(state.getIntroKey());
                 // the only packet we should be getting with Bob's intro key is Session Created
                 handlePacket(reader, packet, null, state, null, AuthType.BOBINTRO);
@@ -721,8 +721,8 @@ class PacketHandler {
                             _log.warn("Dropping type " + type + " auth " + auth + ": " + packet);
                         break;
                     }
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info("Received relay request packet: " + reader + " from " + from);
+                    if (_log.shouldDebug())
+                        _log.debug("Received relay request packet: " + reader + " from " + from);
                     _introManager.receiveRelayRequest(from, reader);
                     break;
                 case UDPPacket.PAYLOAD_TYPE_RELAY_INTRO:
@@ -731,8 +731,8 @@ class PacketHandler {
                             _log.warn("Dropping type " + type + " auth " + auth + ": " + packet);
                         break;
                     }
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info("Received relay intro packet: " + reader + " from " + from);
+                    if (_log.shouldDebug())
+                        _log.debug("Received relay intro packet: " + reader + " from " + from);
                     _introManager.receiveRelayIntro(from, reader);
                     break;
                 case UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE:
@@ -741,8 +741,8 @@ class PacketHandler {
                             _log.warn("Dropping type " + type + " auth " + auth + ": " + packet);
                         break;
                     }
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info("Received relay response packet: " + reader + " from " + from);
+                    if (_log.shouldDebug())
+                        _log.debug("Received relay response packet: " + reader + " from " + from);
                     _establisher.receiveRelayResponse(from, reader);
                     break;
                 case UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY:
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState2.java b/router/java/src/net/i2p/router/transport/udp/PeerState2.java
index 6a467ad51a00547d559e8bbadc7770f6647a84f8..94fc7211f2ecea326f044023fc117a6b3ba1bb6b 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerState2.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerState2.java
@@ -427,7 +427,7 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
     }
 
     public void gotToken(long token, long expires) {
-        _transport.getEstablisher().addOutboundToken(_remotePeer, token, expires);
+        _transport.getEstablisher().addOutboundToken(_remoteHostId, token, expires);
     }
 
     public void gotI2NP(I2NPMessage msg) {
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
index 27d21c4955687143775a857c92341d6060469b71..fd4ad6836c3bb48ebb5e8df2d7e5a6a7d8678f75 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
@@ -184,7 +184,7 @@ class PeerTestManager {
     /**
      *  The next few methods are for when we are Alice
      *
-     *  @param bobIP IPv4 only
+     *  @param bob IPv4 only
      */
     public synchronized void runTest(PeerState bob) {
         if (_currentTest != null) {