SSU2: Add state lookup by conn ID

As fallback for failed lookup by RemoteHostId.
This will eventually be the primary lookup after most traffic is SSU2.
For now, only used to receive traffic from a peer that changed IP/port.
SSU 1/2 send destroy when replacing old session with new.
Prep for full SSU2 connection migration.
This commit is contained in:
zzz
2022-07-31 11:34:55 -04:00
parent b606c2084b
commit 146bbf67f8
4 changed files with 68 additions and 23 deletions

View File

@@ -272,7 +272,7 @@ class PacketHandler {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Packet received IS for an existing peer");
if (state.getVersion() == 2)
receiveSSU2Packet(packet, (PeerState2) state);
((PeerState2) state).receivePacket(rem, packet);
else
receivePacket(reader, packet, state);
}
@@ -771,23 +771,6 @@ class PacketHandler {
//// Begin SSU2 Handling ////
/**
* Hand off to the state for processing.
* Packet is decrypted in-place, no fallback
* processing is possible.
*
* Min packet data size: 40
*
* @param packet any in-session message
* @param state must be version 2, non-null
* @since 0.9.54
*/
private void receiveSSU2Packet(UDPPacket packet, PeerState2 state) {
// header and body decryption is done by PeerState2
// This bypasses InboundMessageStates completely.
// All handling of fragments and acks is done in PeerState2.
state.receivePacket(packet);
}
/**
* Decrypt the header and hand off to the state for processing.
@@ -831,6 +814,20 @@ class PacketHandler {
// in group 4 receive packet
//if (_log.shouldDebug())
// _log.debug("Does not decrypt as Session Request, Token Request, or Peer Test: " + header);
if (header != null) {
// conn ID decryption is the same for short and long header, with k1
// presumably a data packet, either ip/port changed, or a race during establishment?
// lookup peer state by conn ID, pass over for decryption with the proper k2
long id = header.getDestConnID();
PeerState2 ps2 = _transport.getPeerState(id);
if (ps2 != null) {
if (_log.shouldWarn())
_log.warn("Migrated " + packet.getPacket().getLength() + " byte packet from " + from +
" for " + ps2);
ps2.receivePacket(from, packet);
return true;
}
}
return false;
}
type = header.getType();
@@ -925,7 +922,7 @@ class PacketHandler {
_establisher.receiveHolePunch(from, packet);
} else {
if (_log.shouldWarn())
_log.warn("Got unknown message " + header + " on " + state);
_log.warn("Got unknown SSU2 message " + header + " from " + from);
}
return true;
}

View File

@@ -310,6 +310,23 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
* @param packet fully encrypted, header and body decryption will be done here
*/
void receivePacket(UDPPacket packet) {
receivePacket(packet.getRemoteHost(), packet);
}
/**
* From different than expected source IP/port
*
* @param from source address
* @param packet fully encrypted, header and body decryption will be done here
* @since 0.9.56
*/
void receivePacket(RemoteHostId from, UDPPacket packet) {
if (!from.equals(_remoteHostId)) {
if (_log.shouldWarn())
_log.warn("Got packet from " + from + " expected " + _remoteHostId + " on " + this);
// Connection Migration TODO
}
DatagramPacket dpacket = packet.getPacket();
byte[] data = dpacket.getData();
int off = dpacket.getOffset();
@@ -332,6 +349,8 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
// attempting to decrypt with our intro key.
// resend session confirmed in response
checkRetransmitSessionConfirmed(_context.clock().now(), true);
// alternatively, the session closed and we didn't get the termination,
// and this is a new inbound session request? TODO
}
return;
}

View File

@@ -172,6 +172,7 @@ final class SSU2Util {
public static final int REASON_LIMITS = 19;
public static final int REASON_VERSION = 20;
public static final int REASON_NETID = 21;
public static final int REASON_REPLACED = 22;
private SSU2Util() {}

View File

@@ -75,6 +75,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
private final Map<Hash, PeerState> _peersByIdent;
/** RemoteHostId to PeerState */
private final Map<RemoteHostId, PeerState> _peersByRemoteHost;
private final Map<Long, PeerState2> _peersByConnID;
private PacketHandler _handler;
private EstablishmentManager _establisher;
private final MessageQueue _outboundMessages;
@@ -336,6 +337,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_log = ctx.logManager().getLog(UDPTransport.class);
_peersByIdent = new ConcurrentHashMap<Hash, PeerState>(128);
_peersByRemoteHost = new ConcurrentHashMap<RemoteHostId, PeerState>(128);
_peersByConnID = (xdh != null) ? new ConcurrentHashMap<Long, PeerState2>(32) : null;
_dropList = new ConcurrentHashSet<RemoteHostId>(2);
_endpoints = new CopyOnWriteArrayList<UDPEndpoint>();
@@ -852,6 +854,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_testEvent.setIsAlive(false);
_peersByRemoteHost.clear();
_peersByIdent.clear();
if (_peersByConnID != null)
_peersByConnID.clear();
_dropList.clear();
_introManager.reset();
UDPPacket.clearCache();
@@ -1630,15 +1634,23 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
}
return rv;
}
/**
/**
* get the state for the peer with the given ident, or null
* if no state exists
*/
PeerState getPeerState(Hash remotePeer) {
PeerState getPeerState(Hash remotePeer) {
return _peersByIdent.get(remotePeer);
}
/**
* Get the state by SSU2 connection ID
* @since 0.9.56
*/
PeerState2 getPeerState(long rcvConnID) {
return _peersByConnID.get(Long.valueOf(rcvConnID));
}
/**
* For /peers UI only. Not a public API, not for external use.
*
@@ -1769,9 +1781,15 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
oldEstablishedOn = oldPeer.getKeyEstablishedTime();
}
}
if (peer.getVersion() == 2) {
PeerState2 state2 = (PeerState2) peer;
_peersByConnID.put(Long.valueOf(state2.getRcvConnID()), state2);
}
RemoteHostId remoteId = peer.getRemoteHostId();
if (oldPeer != null) {
sendDestroy(oldPeer, SSU2Util.REASON_REPLACED);
oldPeer.dropOutbound();
_introManager.remove(oldPeer);
RemoteHostId oldID = oldPeer.getRemoteHostId();
@@ -1786,6 +1804,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_introManager.remove(oldPeer2);
}
}
if (oldPeer != peer && oldPeer.getVersion() == 2) {
PeerState2 state2 = (PeerState2) oldPeer;
_peersByConnID.remove(Long.valueOf(state2.getRcvConnID()));
}
}
// Should always be direct... except maybe for hidden mode?
@@ -1802,6 +1824,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// transfer over the old state/inbound message fragments/etc
peer.loadFrom(oldPeer2);
oldEstablishedOn = oldPeer2.getKeyEstablishedTime();
sendDestroy(oldPeer2, SSU2Util.REASON_REPLACED);
oldPeer2.dropOutbound();
_introManager.remove(oldPeer2);
}
@@ -1984,6 +2007,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_context.statManager().addRateData("udp.droppedPeer", now - peer.getLastReceiveTime(), now - peer.getKeyEstablishedTime());
altByIdent = _peersByIdent.remove(peer.getRemotePeer());
}
if (peer.getVersion() == 2) {
PeerState2 state2 = (PeerState2) peer;
_peersByConnID.remove(Long.valueOf(state2.getRcvConnID()));
}
RemoteHostId remoteId = peer.getRemoteHostId();
PeerState altByHost = _peersByRemoteHost.remove(remoteId);