mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
Merge maint into master (cross-init NAT-traversal tie-breaker)
This commit is contained in:
@@ -133,6 +133,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- NAT-traversal cross-init adoption is now deterministic under
|
||||
simultaneous dual-initiation. Previously, when two peers'
|
||||
Nostr-mediated UDP punches completed within the same scheduling
|
||||
window, each side's bootstrap-completion event arrived with an
|
||||
in-flight handshake already recorded against the other peer (each
|
||||
side had received an inbound msg1 from the other's pre-punch
|
||||
outbound attempt). The deduplication skip then fired on both
|
||||
sides, neither installed the fresh traversal socket as canonical,
|
||||
and the 45-second peer-adoption budget expired with both nodes
|
||||
stuck waiting for an adoption that never happened. The handler now
|
||||
applies the same deterministic NodeAddr tie-breaker the codebase
|
||||
already uses for rekey dual-initiation and cross-connection
|
||||
resolution: the smaller NodeAddr wins as adopter, tears down its
|
||||
in-flight handshake state, and proceeds with adoption; the larger
|
||||
NodeAddr keeps the skip semantics, and its in-flight outbound is
|
||||
reconciled by the cross-connection logic when the winner's fresh
|
||||
msg1 arrives over the adopted socket. The dual cross-init stall is
|
||||
eliminated; cross-init NAT-traversal completes in well under a
|
||||
second even under host CPU contention.
|
||||
- FSP session rekey is now hitless under packet loss and reordering.
|
||||
Previously, a rekey could leave the two endpoints holding different
|
||||
key sets for a brief window — if a handshake message was lost in
|
||||
|
||||
@@ -62,7 +62,7 @@ impl Node {
|
||||
/// Frees the session index, removes pending_outbound entry, and cleans up
|
||||
/// the link and address mapping. Does not log — callers provide context-appropriate
|
||||
/// log messages.
|
||||
fn cleanup_stale_connection(&mut self, link_id: LinkId, _now_ms: u64) {
|
||||
pub(in crate::node) fn cleanup_stale_connection(&mut self, link_id: LinkId, _now_ms: u64) {
|
||||
let conn = match self.connections.remove(&link_id) {
|
||||
Some(c) => c,
|
||||
None => return,
|
||||
|
||||
+35
-2
@@ -423,11 +423,44 @@ impl Node {
|
||||
continue;
|
||||
}
|
||||
if self.is_connecting_to_peer(&peer_addr) {
|
||||
// Dual cross-init: both nodes' Nostr-mediated punches
|
||||
// completed simultaneously, and each side already
|
||||
// holds in-flight handshake state for the other.
|
||||
// Apply the deterministic NodeAddr tie-breaker —
|
||||
// smaller NodeAddr wins as adopter (same convention
|
||||
// as cross_connection_winner and the rekey dual-
|
||||
// init resolution at handshake.rs:269). The winner
|
||||
// tears down its in-flight state and adopts the
|
||||
// fresh traversal socket; the loser keeps continue
|
||||
// semantics, and its existing cross-connection
|
||||
// logic in handle_msg1 reconciles when the winner's
|
||||
// fresh msg1 arrives over the adopted socket.
|
||||
let our_addr = self.identity.node_addr();
|
||||
if our_addr >= &peer_addr {
|
||||
debug!(
|
||||
peer_npub = %peer_npub,
|
||||
"Dual cross-init NAT traversal: we lose (larger addr), keeping in-flight handshake"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
debug!(
|
||||
peer_npub = %peer_npub,
|
||||
"Ignoring established NAT traversal while peer handshake is already in progress"
|
||||
"Dual cross-init NAT traversal: we win (smaller addr), tearing down in-flight handshake to adopt fresh socket"
|
||||
);
|
||||
continue;
|
||||
let now_ms = Self::now_ms();
|
||||
let stale: Vec<LinkId> = self
|
||||
.connections
|
||||
.iter()
|
||||
.filter(|(_, conn)| {
|
||||
conn.expected_identity()
|
||||
.map(|id| id.node_addr() == &peer_addr)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.map(|(link_id, _)| *link_id)
|
||||
.collect();
|
||||
for link_id in stale {
|
||||
self.cleanup_stale_connection(link_id, now_ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
match self.adopt_established_traversal(traversal).await {
|
||||
|
||||
Reference in New Issue
Block a user