Max/sdk stream wrapper (#6320)
* Replace MixnetStream with LP framing
- Replace custom header with LpFrameHeader
- Added sequence number for message ordering
* IPR: support LP Stream-framed client connections
- Detect and route LP Stream frames in mixnet_listener
- Wrap inline responses in LP Stream frames
- Thread stream_id to ConnectedClientHandler for TUN responses
* sdk: add ipr_wrapper module with IpMixStream
- IpMixStream wraps MixnetStream for IPR tunnel over mixnet
- LP Stream framing handled automatically by MixnetStream
- Gateway discovery, connect handshake, IP packet send/receive
* sdk: remove superseded stream_wrapper module
* Trim obvious comments, add architecture.md stub
* sdk: add missing deps and fix warnings
* Cut down architecture diagram until finished with rest of the code, leaving stubs
* sdk: refactor IpMixStream, extract shared helpers
- Extract gateway discovery and connect response parsing
- Add recv() to MixnetStream, remove 64KB read buffer
- Simplify IpMixStream constructor
* Fix SphinxStream renames missed during rebase
* Add IpPacketResponse::from_bytes() for stream-based deserialization
* Clean up ip_packet_client: delete stale connect.rs, take raw bytes not ReconstructedMessage
* Clippy
* Delete unused ip_packet_client modules
- Remove helpers.rs (ICMP utilities moved to example)
- Remove error.rs (errors consolidated into sdk/error.rs)
- Remove README.md
- Update module root to only export discovery + listener
* Simplify listener, IpMixStream, and network_env
- Collapse IprListener struct into standalone handle_ipr_response()
- Move check_ipr_message_version() into listener.rs
- Remove IpMixStream test module (moved to example)
- Remove parse_network() and commented-out Sandbox arms
- Return Result from find_workspace_root() instead of panicking
- Add IprTunnelDisconnected and WorkspaceRootNotFound error variants
* Refactor IPR stream handling and document seq conventions
- Inline stream_id tracking (remove current_stream_id field)
- Re-export encode_stream_frame from clients module
- Document seq=0 reservation for inline control responses
- Document data-path counter starting at 1 with skip-on-wrap
* Add ipr_tunnel example for integration testing
- ICMP ping through IPR with --gateway flag for targeting specific exits
- Move pnet_packet from dependencies to dev-dependencies
* Add message reordering to stream router
- Buffer out-of-order messages per-stream using BTreeMap
- Drain contiguous sequences individually to preserve message boundaries
- Drop duplicate/old sequence numbers with a warning
- Remove dead_code allow on StreamFrame::sequence_num
* Clean up comments and fill architecture.md
- Remove separator line comments
- Update stale comments about ordering not being implemented
- Remove collapsible_if allows, use let-else instead
- Fill in architecture.md data flow and connection lifecycle
* Simplify ipr_tunnel example to minimal smoke test
- Single ping instead of multi-ping loop
- Remove identifier and PING_COUNT
- Collapse ICMP helpers into single build_icmp_ping function
* Add dual-stack IPv6 ping and rename gateway → ipr
- Rename --gateway flag to --ipr and new_with_gateway() to new_with_ipr()
- Add ICMPv6 ping to ipr_tunnel example for dual-stack smoke test
- Tighten echo reply validation (protocol field check, diagnostic output)
- Document IP allocation (subnets, static vs dynamic, client keying) in architecture.md
- Promote LP Stream Open handshake log to INFO
* Tweak subnet comment in docs
* Don't stop IPR listener on decode failure
- Change break to continue so garbage packets can't kill the listener
- Remaining valid packets in the bundle are still processed
* Fix license headers and use workspace dep for pnet_packet
- Switch GPL-3.0 to Apache-2.0 on all SDK library files
- Add missing license headers to 7 files
- Use workspace version for pnet_packet dependency
* Document IP pool isolation from WG/LP dVPN pool
- IPR uses 10.0.0.0/16 on nymtun, WG uses 10.1.0.0/16 on nymwg
- Reference constants.rs as source of truth
* Remove network_env.rs and simplify IpMixStream API
- Default to mainnet via setup_env(None) instead of requiring env param
- Remove NetworkEnvironment enum and workspace root detection
- Remove WorkspaceRootNotFound error variant
- Update ipr_tunnel example to match new signatures
* Use weighted random selection for IPR gateway discovery
- Replace max_by_key with choose_weighted biased by performance score
- Prevents all clients converging on a single highest-performing IPR
* Cap stream reorder buffer to prevent unbounded memory growth
- Add MAX_REORDER_BUFFER (256) to limit per-stream pending messages:
- buffer overflows = skip ahead to lowest buffered seq and drain
- protects against malicious senders that deliberately skip sequence numbers
* Extract shared IPR response helpers into nym-ip-packet-requests
- Add response_helpers module with version check, connect response
parsing, and control response dispatch
- SDK ip_packet_client now delegates to shared module
- Monorepo nym-ip-packet-client uses shared version check and
connect response parsing
- Fix doc comment attributing fork to nym-vpn-client
* Extract ICMP test helpers into nym-ip-packet-requests
- Add icmp_utils module behind test-utils feature flag
- Move build_icmp_ping, build_icmpv6_ping, is_echo_reply_v4/v6 from
example
- Update ipr_tunnel example to use shared helpers
* Add protocol v9 LP-framed transport marker
- Add v9 module (re-exports v8, VERSION=9)
- Accept v9 requests and responses in IPR
- Switch SDK IpMixStream to send v9
* Log protocol version in dynamic connect requests
* Remove KCP from IPR and fix unwrap_or_default in SDK
- Remove all KCP session management from ip-packet-router (replaced by
LP Stream framing)
- Drop nym-kcp dependency and KcpError variant from IPR
- Replace unwrap_or_default with ok_or(Error::NoNymAPIUrl) in
IpMixStream::new()
* Add v9 protocol wrapper constructors and enforce version/transport
consistency
- Add v9::new_connect_request(), new_data_request(),
new_ip_packet_response() to centralise version stamping
- Replace manual protocol.version overrides in SDK and IPR with v9
wrapper calls
- Bump nym-ip-packet-client current re-export from v8 to v9
- Enforce LP Stream frames must carry v9+ payloads, non-stream must be
v8 or lower
* Filter IPR exit nodes by minimum v9-compatible release version
- Define MIN_RELEASE_VERSION (1.30.0) in ip-packet-requests/v9 alongside protocol constants
- Add semver-based filtering in SDK gateway discovery to skip nodes below v9 threshold
- Add semver dependency to ip-packet-requests and nym-sdk
* Use numeric version comparison for transport/version enforcement
- Compare version as u8 instead of enum equality so future v10+ is handled correctly
- Remove unused `use super::*` import left over from KCP test removal
This commit is contained in:
@@ -11,14 +11,10 @@ use tokio::time::sleep;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, error};
|
||||
|
||||
use nym_ip_packet_requests::response_helpers::{self, IprResponseError};
|
||||
|
||||
use crate::{
|
||||
current::{
|
||||
request::IpPacketRequest,
|
||||
response::{
|
||||
ConnectResponse, ConnectResponseReply, ControlResponse, IpPacketResponse,
|
||||
IpPacketResponseData,
|
||||
},
|
||||
},
|
||||
current::{request::IpPacketRequest, response::IpPacketResponse},
|
||||
error::{Error, Result},
|
||||
helpers::check_ipr_message_version,
|
||||
};
|
||||
@@ -101,32 +97,6 @@ impl IprClientConnect {
|
||||
Ok(request_id)
|
||||
}
|
||||
|
||||
async fn handle_connect_response(&self, response: ConnectResponse) -> Result<IpPair> {
|
||||
debug!("Handling dynamic connect response");
|
||||
match response.reply {
|
||||
ConnectResponseReply::Success(r) => Ok(r.ips),
|
||||
ConnectResponseReply::Failure(reason) => Err(Error::ConnectRequestDenied { reason }),
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_ip_packet_router_response(&self, response: IpPacketResponse) -> Result<IpPair> {
|
||||
let control_response = match response.data {
|
||||
IpPacketResponseData::Control(control_response) => control_response,
|
||||
_ => {
|
||||
error!("Received non-control response while waiting for connect response");
|
||||
return Err(Error::UnexpectedConnectResponse);
|
||||
}
|
||||
};
|
||||
|
||||
match *control_response {
|
||||
ControlResponse::Connect(resp) => self.handle_connect_response(resp).await,
|
||||
response => {
|
||||
error!("Unexpected response: {response:?}");
|
||||
Err(Error::UnexpectedConnectResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn listen_for_connect_response(&mut self, request_id: u64) -> Result<IpPair> {
|
||||
// Connecting is basically synchronous from the perspective of the mixnet client, so it's safe
|
||||
// to just grab ahold of the mutex and keep it until we get the response.
|
||||
@@ -173,7 +143,14 @@ impl IprClientConnect {
|
||||
|
||||
if response.id() == Some(request_id) {
|
||||
tracing::debug!("Got response with matching id");
|
||||
return self.handle_ip_packet_router_response(response).await;
|
||||
// Replaces local handle_ip_packet_router_response() + handle_connect_response()
|
||||
return response_helpers::parse_connect_response(response)
|
||||
.map_err(|e| match e {
|
||||
IprResponseError::ConnectDenied(reason) => {
|
||||
Error::ConnectRequestDenied { reason }
|
||||
}
|
||||
_ => Error::UnexpectedConnectResponse,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use nym_ip_packet_requests::response_helpers::IprResponseError;
|
||||
use nym_sdk::mixnet::ReconstructedMessage;
|
||||
|
||||
use crate::{Error, current::VERSION as CURRENT_VERSION, error::Result};
|
||||
use crate::{current::VERSION as CURRENT_VERSION, error::Result};
|
||||
|
||||
pub(crate) fn check_ipr_message_version(message: &ReconstructedMessage) -> Result<()> {
|
||||
// Assuming it's a IPR message, it will have a version as its first byte
|
||||
if let Some(version) = message.message.first() {
|
||||
match version.cmp(&CURRENT_VERSION) {
|
||||
Ordering::Greater => Err(Error::ReceivedResponseWithNewVersion {
|
||||
expected: CURRENT_VERSION,
|
||||
received: *version,
|
||||
}),
|
||||
Ordering::Less => Err(Error::ReceivedResponseWithOldVersion {
|
||||
expected: CURRENT_VERSION,
|
||||
received: *version,
|
||||
}),
|
||||
Ordering::Equal => {
|
||||
// We're good
|
||||
Ok(())
|
||||
}
|
||||
nym_ip_packet_requests::response_helpers::check_ipr_message_version(
|
||||
&message.message,
|
||||
CURRENT_VERSION,
|
||||
)
|
||||
.map_err(|e| match e {
|
||||
IprResponseError::NoVersionByte => crate::Error::NoVersionInMessage,
|
||||
IprResponseError::VersionMismatch { expected, received } if received < expected => {
|
||||
crate::Error::ReceivedResponseWithOldVersion { expected, received }
|
||||
}
|
||||
} else {
|
||||
Err(Error::NoVersionInMessage)
|
||||
}
|
||||
IprResponseError::VersionMismatch { expected, received } => {
|
||||
crate::Error::ReceivedResponseWithNewVersion { expected, received }
|
||||
}
|
||||
_ => crate::Error::NoVersionInMessage,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ pub use error::Error;
|
||||
pub use listener::{IprListener, MixnetMessageOutcome};
|
||||
|
||||
// Re-export the currently used version
|
||||
pub use nym_ip_packet_requests::v8 as current;
|
||||
pub use nym_ip_packet_requests::v9 as current;
|
||||
|
||||
Reference in New Issue
Block a user