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 7fba352139cf5737396ed1dc169ea6e51f38efb4..ae59271efbb074e30fc69a1bc0cffb5b2b6efaa3 100644
--- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
@@ -775,6 +775,9 @@ class EstablishmentManager {
 
     /**
      * Got a SessionDestroy on an established conn
+     *
+     * SSU 1 or 2
+     *
      * @since 0.8.1
      */
     void receiveSessionDestroy(RemoteHostId from, PeerState state) {
@@ -785,6 +788,9 @@ class EstablishmentManager {
 
     /**
      * Got a SessionDestroy during outbound establish
+     *
+     * SSU 1 or 2
+     *
      * @since 0.8.1
      */
     void receiveSessionDestroy(RemoteHostId from, OutboundEstablishState state) {
@@ -800,6 +806,9 @@ class EstablishmentManager {
      * TODO - PacketHandler won't look up inbound establishes
      * As this packet was essentially unauthenticated (i.e. intro key, not session key)
      * we just log it as it could be spoofed.
+     *
+     * SSU 1 or 2
+     *
      * @since 0.8.1
      */
     void receiveSessionDestroy(RemoteHostId from) {
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 d0687eee6402806a31c2e286e33abca4ce7a1969..746d1e94e10419216e0c5fdee03c965b0ec466a8 100644
--- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java
+++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java
@@ -144,6 +144,10 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
             _sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader");
             _currentState = InboundState.IB_STATE_REQUEST_RECEIVED;
         }
+        if (_currentState == InboundState.IB_STATE_FAILED) {
+            // termination block received
+            throw new GeneralSecurityException("Termination block in Session/Token Request");
+        }
         if (_timeReceived == 0)
             throw new GeneralSecurityException("No DateTime block in Session/Token Request");
         long skew = _establishBegin - _timeReceived;
@@ -308,7 +312,11 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
     }
 
     public void gotTermination(int reason, long count) {
-        throw new IllegalStateException("Termination in Handshake");
+        if (_log.shouldWarn())
+            _log.warn("Got TERMINATION block, reason: " + reason + " count: " + count);
+        // this sets the state to FAILED
+        fail();
+        _transport.getEstablisher().receiveSessionDestroy(_remoteHostId);
     }
 
     public void gotUnknown(int type, int len) {
@@ -418,6 +426,11 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
             _log.debug("State after sess req: " + _handshakeState);
         _timeReceived = 0;
         processPayload(data, off + LONG_HEADER_SIZE, len - (LONG_HEADER_SIZE + KEY_LEN + MAC_LEN), true);
+        packetReceived();
+        if (_currentState == InboundState.IB_STATE_FAILED) {
+            // termination block received
+            throw new GeneralSecurityException("Termination block in Session Request");
+        }
         if (_timeReceived == 0)
             throw new GeneralSecurityException("No DateTime block in Session Request");
         long skew = _establishBegin - _timeReceived;
@@ -426,8 +439,6 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
         _sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader");
         _currentState = InboundState.IB_STATE_REQUEST_RECEIVED;
         _rtt = (int) ( _context.clock().now() - _lastSend );
-
-        packetReceived();
     }
 
     /**
@@ -463,6 +474,11 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
         if (_log.shouldDebug())
             _log.debug("State after sess conf: " + _handshakeState);
         processPayload(data, off + SHORT_HEADER_SIZE, len - (SHORT_HEADER_SIZE + KEY_LEN + MAC_LEN + MAC_LEN), false);
+        packetReceived();
+        if (_currentState == InboundState.IB_STATE_FAILED) {
+            // termination block received
+            throw new GeneralSecurityException("Termination block in Session Confirmed");
+        }
         _sessCrForReTX = null;
 
         if (_receivedConfirmedIdentity == null)
@@ -471,7 +487,6 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
         // createPeerState() called from gotRI()
 
         _currentState = InboundState.IB_STATE_CONFIRMED_COMPLETELY;
-        packetReceived();
         return _pstate;
     }
 
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 ea2ecaf48a0925fe4768d2927f1c2550dc0cc97d..48e05f34bc9931b62be5a49e156c929a5c44caf8 100644
--- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java
+++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java
@@ -234,7 +234,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
     }
 
     public void gotTermination(int reason, long count) {
-        throw new IllegalStateException("Termination in Sess Created");
+        if (_log.shouldWarn())
+            _log.warn("Got TERMINATION block, reason: " + reason + " count: " + count);
+        // this sets the state to FAILED
+        fail();
+        _transport.getEstablisher().receiveSessionDestroy(_remoteHostId, this);
     }
 
     public void gotUnknown(int type, int len) {
@@ -323,6 +327,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
                 _log.debug("Retry error", gse);
             throw gse;
         }
+        packetReceived();
+        if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
+            // termination block received
+            return;
+        }
         if (_timeReceived == 0)
             throw new GeneralSecurityException("No DateTime block in Session/Token Request");
         long skew = _establishBegin - _timeReceived;
@@ -330,7 +339,6 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
             throw new GeneralSecurityException("Skew exceeded in Session/Token Request: " + skew);
         createNewState(_routerAddress);
         _currentState = OutboundState.OB_STATE_RETRY_RECEIVED;
-        packetReceived();
     }
 
     public synchronized void receiveSessionCreated(UDPPacket packet) throws GeneralSecurityException {
@@ -371,6 +379,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
             _log.debug("State after sess cr: " + _handshakeState);
         _timeReceived = 0;
         processPayload(data, off + LONG_HEADER_SIZE, len - (LONG_HEADER_SIZE + KEY_LEN + MAC_LEN), true);
+        packetReceived();
+        if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
+            // termination block received
+            return;
+        }
         if (_timeReceived == 0)
             throw new GeneralSecurityException("No DateTime block in Session/Token Request");
         long skew = _establishBegin - _timeReceived;
@@ -384,7 +397,6 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
         if (_requestSentCount == 1) {
             _rtt = (int) (_context.clock().now() - _requestSentTime);
         }
-        packetReceived();
     }
 
     /**
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 4977c9d7d9917692e9eaea55b0597d5623629ec5..189302bbbbcb2a03491384f9149f53c29dbb07a9 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java
@@ -133,6 +133,15 @@ class PacketBuilder2 {
      *
      */
     public UDPPacket buildPacket(List<Fragment> fragments, PeerState2 peer) {
+        return buildPacket(fragments, null, peer);
+    }
+
+    /*
+     *  Multiple fragments and optional other blocks.
+     *
+     *  @param otherBlocks may be null or empty
+     */
+    public UDPPacket buildPacket(List<Fragment> fragments, List<Block> otherBlocks, PeerState2 peer) {
         // calculate data size
         int numFragments = fragments.size();
         int dataSize = 0;
@@ -172,7 +181,10 @@ class PacketBuilder2 {
 
         // ok, now for the body...
         // +2 for acks and padding
-        List<Block> blocks = new ArrayList<Block>(fragments.size() + 2);
+        int bcnt = fragments.size() + 2;
+        if (otherBlocks != null)
+            bcnt += otherBlocks.size();
+        List<Block> blocks = new ArrayList<Block>(bcnt);
         int sizeWritten = 0;
 
         // add the acks
@@ -207,6 +219,17 @@ class PacketBuilder2 {
             off += sz;
             sizeWritten += sz;
         }
+
+        // now the other blocks, if any
+        if (otherBlocks != null) {
+            for (Block block : otherBlocks) {
+                blocks.add(block);
+                int sz = block.getTotalLength();
+                off += sz;
+                sizeWritten += sz;
+            }
+        }
+
         // FIXME
         Block block = getPadding(sizeWritten, currentMTU);
         if (block != null) {
@@ -280,6 +303,15 @@ class PacketBuilder2 {
     public UDPPacket buildACK(PeerState2 peer) {
         return buildPacket(Collections.emptyList(), peer);
     }
+
+    /**
+     *  Build a data packet with a termination block.
+     *  This will also include acks and padding.
+     */
+    public UDPPacket buildSessionDestroyPacket(int reason, PeerState2 peer) {
+        Block block = new SSU2Payload.TerminationBlock(reason, peer.getReceivedMessages().getHighestSet());
+        return buildPacket(Collections.emptyList(), Collections.singletonList(block), peer);
+    }
     
     /**
      * Build a new SessionRequest packet for the given peer, encrypting it 
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 c863aa7b5511d630cda8004ea06de97253e34549..aba46fd9d902f7622e0b5b40b02c6753b7c70d27 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerState2.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerState2.java
@@ -350,8 +350,9 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
     }
 
     public void gotTermination(int reason, long count) {
-        if (_log.shouldDebug())
-            _log.debug("Got TERMINATION block, reason: " + reason + " count: " + count);
+        if (_log.shouldWarn())
+            _log.warn("Got TERMINATION block, reason: " + reason + " count: " + count);
+        _transport.getEstablisher().receiveSessionDestroy(_remoteHostId, this);
     }
 
     public void gotUnknown(int type, int len) {
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 771c3f85d5a0d14c75c814a1fa86723fdda9b48e..da17423947f9b62e9bead2b31bc7ba43a3f8198a 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
@@ -2126,10 +2126,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
      *  @since 0.8.9
      */
     void sendDestroy(PeerState peer) {
-        // peer must be fully established
-        if (peer.getCurrentCipherKey() == null)
-            return;
-        UDPPacket pkt = _packetBuilder.buildSessionDestroyPacket(peer);
+        UDPPacket pkt;
+        if (peer.getVersion() == 1) {
+            // peer must be fully established
+            if (peer.getCurrentCipherKey() == null)
+                return;
+            pkt = _packetBuilder.buildSessionDestroyPacket(peer);
+        } else {
+            // unspecified reason
+            pkt = _packetBuilder2.buildSessionDestroyPacket(0, (PeerState2) peer);
+        }
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Sending destroy to : " + peer);
         send(pkt);