rename mixtcp -> smolmix

This commit is contained in:
mfahampshire
2026-02-27 16:34:36 +00:00
parent 6caccd9e4c
commit 583627f27a
12 changed files with 24 additions and 57 deletions
-34
View File
@@ -1,34 +0,0 @@
# MixTCP
**TODO change name to smolmix**
This is an initial proof of concept of a SmolTCP `device` that uses the Mixnet for transport. It relies on the `IpMixStream` module from the Rust SDK to set up a connection with an Exit Gateway's Ip-Packet-Router, meaning that this is the IP that is seen by the receiver of the request.
This can be used as the basis for building more generic transport crates on top of the Mixnet (e.g. trying to mirror the interface of a common HTTPS crate) whilst abstracting away the complexities of using the Mixnet for transport.
More to come in the future.
`examples/` contains examples for:
- `cloudflare_ping` - HTTPS request to Cloudflare through the mixnet
- `https_client` - `reqwest`-like HTTPS `GET` client with timed clearnet comparison
- `tls` - TLS handshake diagnostics with state logging
- `dns_udp` - DNS A-record lookup over UDP with timed clearnet comparison
## Component Interaction
```sh
create_device()
|
+----------+-------+-------+-----------+
| | | |
v v v v
NymIprDevice NymIprBridge ShutdownHandle IpPair
| | | (10.0.x.x)
| | |
+- channels + shutdown signal
|
v
IpMixStream
|
v
Mixnet
```
+1
View File
@@ -4,5 +4,6 @@ This repo contains several components:
- `mixnet`: exposes Nym Client builders and methods. This is useful if you want to interact directly with the Client, or build transport abstractions.
- `tcp_proxy`: exposes functionality to set up client/server instances that expose a localhost TcpSocket to read/write to like a 'normal' socket connection. `tcp_proxy/bin/` contains standalone `nym-proxy-client` and `nym-proxy-server` binaries.
- `clientpool`: a configurable pool of ephemeral Nym Clients which can be created as a background process and quickly grabbed.
- `stream_wrapper`: made up of two parts: a TCP-Socket-like abstraction (`mixnet_stream_wrapper.rs`) for a Nym Client, and an abstraction built on top of this (`mixnet_stream_wrapper_ipr`) which allows for client-side integrations to send IP packets through Exit Gateways' IpPacketRouter, and use the Mixnet as a proxy. For an example of where this is used, see the `smolmix` crate.
Documentation can be found [here](https://nym.com/docs/developers/rust).
+1 -1
View File
@@ -1,5 +1,5 @@
[package]
name = "mixtcp"
name = "smolmix"
version = "0.0.1"
edition = "2021"
license.workspace = true
@@ -9,8 +9,8 @@
mod support;
use mixtcp::create_device;
use nym_sdk::stream_wrapper::{IpMixStream, NetworkEnvironment};
use smolmix::create_device;
use smoltcp::{
iface::{Config, Interface, SocketSet},
socket::tcp,
@@ -119,7 +119,7 @@ async fn main() -> Result<(), BoxError> {
// Send simple HTTP request
request_start = tokio::time::Instant::now();
let request = b"GET /cdn-cgi/trace HTTP/1.1\r\nHost: cloudflare.com\r\nUser-Agent: mixtcp-test/1.0\r\nAccept: */*\r\nConnection: close\r\n\r\n";
let request = b"GET /cdn-cgi/trace HTTP/1.1\r\nHost: cloudflare.com\r\nUser-Agent: smolmix-test/1.0\r\nAccept: */*\r\nConnection: close\r\n\r\n";
match tls_conn.send(request, socket) {
Ok(_) => {
info!("HTTPS request sent");
@@ -15,8 +15,8 @@ use std::time::Duration;
use hickory_proto::op::{Message, MessageType, OpCode, Query};
use hickory_proto::rr::{Name, RData, RecordType};
use hickory_resolver::TokioResolver;
use mixtcp::create_device;
use nym_sdk::stream_wrapper::{IpMixStream, NetworkEnvironment};
use smolmix::create_device;
use smoltcp::{
iface::{Config, Interface, SocketSet},
socket::udp,
@@ -9,9 +9,9 @@
mod support;
use mixtcp::{create_device, NymIprDevice};
use nym_sdk::stream_wrapper::{IpMixStream, NetworkEnvironment};
use reqwest::StatusCode;
use smolmix::{create_device, NymIprDevice};
use smoltcp::{
iface::{Config, Interface, SocketSet},
socket::tcp,
@@ -25,14 +25,14 @@ use support::{BoxError, TlsOverTcp};
use tracing::info;
/// Reqwest-ish client right now, just a handrolled GET request for the example
pub struct MixtcpReqwestClient {
pub struct SmolmixReqwestClient {
device: Arc<tokio::sync::Mutex<(Interface, NymIprDevice)>>,
bridge_handle: tokio::task::JoinHandle<()>,
shutdown_handle: Option<mixtcp::BridgeShutdownHandle>,
shutdown_handle: Option<smolmix::BridgeShutdownHandle>,
_allocated_ip: Ipv4Address,
}
impl MixtcpReqwestClient {
impl SmolmixReqwestClient {
pub async fn new() -> Result<Self, BoxError> {
let ipr_stream = IpMixStream::new(NetworkEnvironment::Mainnet).await?;
let (mut device, bridge, shutdown_handle, allocated_ips) =
@@ -74,7 +74,7 @@ impl MixtcpReqwestClient {
let _ = self.bridge_handle.await;
}
pub async fn get(&self, url: &str) -> Result<MixtcpResponse, BoxError> {
pub async fn get(&self, url: &str) -> Result<SmolmixResponse, BoxError> {
let parsed_url = reqwest::Url::parse(url)?;
let host = parsed_url.host_str().ok_or("URL has no host")?.to_string();
let path = parsed_url.path().to_string();
@@ -83,7 +83,7 @@ impl MixtcpReqwestClient {
self.simple_get_request(&host, &path).await?;
let (status, body) = Self::parse_simple_response(&response_bytes)?;
Ok(MixtcpResponse {
Ok(SmolmixResponse {
status,
body,
handshake_duration,
@@ -159,7 +159,7 @@ impl MixtcpReqwestClient {
request_start = tokio::time::Instant::now();
let request = format!(
"GET {} HTTP/1.1\r\nHost: {}\r\nUser-Agent: mixtcp/1.0\r\nAccept: */*\r\nConnection: close\r\n\r\n",
"GET {} HTTP/1.1\r\nHost: {}\r\nUser-Agent: smolmix/1.0\r\nAccept: */*\r\nConnection: close\r\n\r\n",
path, domain
);
tls_conn.send(request.as_bytes(), socket)?;
@@ -222,14 +222,14 @@ impl MixtcpReqwestClient {
}
}
pub struct MixtcpResponse {
pub struct SmolmixResponse {
status: u16,
body: String,
handshake_duration: Duration,
request_duration: Duration,
}
impl MixtcpResponse {
impl SmolmixResponse {
pub fn status(&self) -> StatusCode {
StatusCode::from_u16(self.status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
}
@@ -258,7 +258,7 @@ async fn main() -> Result<(), BoxError> {
);
info!("Setting up mixnet client...");
let client = MixtcpReqwestClient::new().await?;
let client = SmolmixReqwestClient::new().await?;
let mixnet_response = client.get(test_url).await?;
let mixnet_status = mixnet_response.status();
let handshake_duration = mixnet_response.handshake_duration;
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-2.0-only
//! Shared helpers for the TCP/TLS mixtcp examples.
//! Shared helpers for the TCP/TLS smolmix examples.
//!
//! Provides a [`TlsOverTcp`] adapter that bridges rustls with smoltcp TCP
//! sockets, plus common utilities like [`init_logging`].
@@ -10,8 +10,8 @@
mod support;
use mixtcp::create_device;
use nym_sdk::stream_wrapper::{IpMixStream, NetworkEnvironment};
use smolmix::create_device;
use smoltcp::{
iface::{Config, Interface, SocketSet},
socket::tcp,
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-2.0-only
use crate::error::MixtcpError;
use crate::error::SmolmixError;
use nym_ip_packet_requests::codec::MultiIpPacketCodec;
use nym_sdk::stream_wrapper::IpMixStream;
use tokio::sync::{mpsc, oneshot};
@@ -75,7 +75,7 @@ impl NymIprBridge {
///
/// The loop exits when a shutdown signal is received, channels are closed,
/// or an error occurs. On exit the mixnet client is disconnected gracefully.
pub async fn run(mut self) -> Result<(), MixtcpError> {
pub async fn run(mut self) -> Result<(), SmolmixError> {
info!("Starting Nym IPR bridge");
let mut packets_sent = 0;
let mut packets_received = 0;
@@ -115,7 +115,7 @@ impl NymIprBridge {
// Forward to device via channel
if self.rx_sender.send(packet.to_vec()).is_err() {
error!("Failed to send packet to device - receiver dropped");
return Err(MixtcpError::ChannelClosed);
return Err(SmolmixError::ChannelClosed);
}
packets_received += 1;
debug!("Total packets received: {}", packets_received);
+1 -1
View File
@@ -4,7 +4,7 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum MixtcpError {
pub enum SmolmixError {
#[error("Channel closed")]
ChannelClosed,
+3 -3
View File
@@ -7,7 +7,7 @@ mod error;
pub use bridge::{BridgeShutdownHandle, NymIprBridge};
pub use device::NymIprDevice;
pub use error::MixtcpError;
pub use error::SmolmixError;
use nym_ip_packet_requests::IpPair;
use nym_sdk::stream_wrapper::IpMixStream;
@@ -27,7 +27,7 @@ use tokio::sync::mpsc;
/// to disconnect from the mixnet cleanly.
pub async fn create_device(
mut ipr_stream: IpMixStream,
) -> Result<(NymIprDevice, NymIprBridge, BridgeShutdownHandle, IpPair), MixtcpError> {
) -> Result<(NymIprDevice, NymIprBridge, BridgeShutdownHandle, IpPair), SmolmixError> {
// Ensure the stream is connected
if !ipr_stream.is_connected() {
ipr_stream.connect_tunnel().await?;
@@ -37,7 +37,7 @@ pub async fn create_device(
// further 'up' the flow in the code calling this fn.
let allocated_ips = *ipr_stream
.allocated_ips()
.ok_or(MixtcpError::NotConnected)?;
.ok_or(SmolmixError::NotConnected)?;
// Create channels for device <-> bridge communication
let (tx_to_bridge, tx_from_device) = mpsc::unbounded_channel();