diff --git a/Cargo.lock b/Cargo.lock index 0ecbfad25c..5c0711c668 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6634,6 +6634,7 @@ dependencies = [ "nym-tun", "nym-wireguard", "nym-wireguard-types", + "rand 0.8.5", "reqwest", "serde", "serde_json", diff --git a/service-providers/ip-packet-router/Cargo.toml b/service-providers/ip-packet-router/Cargo.toml index df9822937e..44644623ba 100644 --- a/service-providers/ip-packet-router/Cargo.toml +++ b/service-providers/ip-packet-router/Cargo.toml @@ -27,6 +27,7 @@ nym-task = { path = "../../common/task" } nym-tun = { path = "../../common/tun" } nym-wireguard = { path = "../../common/wireguard" } nym-wireguard-types = { path = "../../common/wireguard-types" } +rand = "0.8.5" reqwest.workspace = true serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/service-providers/ip-packet-router/src/lib.rs b/service-providers/ip-packet-router/src/lib.rs index d85b1a744c..528b575198 100644 --- a/service-providers/ip-packet-router/src/lib.rs +++ b/service-providers/ip-packet-router/src/lib.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(target_os = "linux"), allow(dead_code))] use std::{ + collections::HashMap, net::{IpAddr, SocketAddr}, path::Path, }; @@ -161,6 +162,7 @@ impl IpPacketRouterBuilder { tun_task_response_rx, mixnet_client, task_handle, + connected_clients: Default::default(), }; log::info!("The address of this client is: {self_address}"); @@ -180,7 +182,7 @@ impl IpPacketRouterBuilder { } } -#[allow(unused)] +#[cfg_attr(not(target_os = "linux"), allow(unused))] struct IpPacketRouter { _config: Config, request_filter: request_filter::RequestFilter, @@ -188,6 +190,13 @@ struct IpPacketRouter { tun_task_response_rx: nym_tun::tun_task_channel::TunTaskResponseRx, mixnet_client: nym_sdk::mixnet::MixnetClient, task_handle: TaskHandle, + + connected_clients: HashMap, +} + +struct ConnectedClient { + nym_address: Recipient, + last_activity: std::time::Instant, } #[cfg_attr(not(target_os = "linux"), allow(unused))] @@ -202,11 +211,39 @@ impl IpPacketRouter { ); let request_id = connect_request.request_id; - let _requested_ip = connect_request.ip; + let requested_ip = connect_request.ip; let reply_to = connect_request.reply_to; // TODO: ignoring reply_to_hops and reply_to_avg_mix_delays for now - Ok(Some(IpPacketResponse::new_static_connect_failure( + // Check that the IP is available in the set of connected clients + if self.connected_clients.contains_key(&requested_ip) { + log::info!("Requested IP is not available"); + return Ok(Some(IpPacketResponse::new_static_connect_failure( + request_id, reply_to, + ))); + } + + // Check that the nym address isn't already registered + if self + .connected_clients + .values() + .any(|client| client.nym_address == reply_to) + { + log::info!("Nym address is already registered"); + return Ok(Some(IpPacketResponse::new_static_connect_failure( + request_id, reply_to, + ))); + } + + // Insert the new connected client + let connected_client = ConnectedClient { + nym_address: reply_to, + last_activity: std::time::Instant::now(), + }; + self.connected_clients + .insert(requested_ip, connected_client); + + Ok(Some(IpPacketResponse::new_static_connect_success( request_id, reply_to, ))) } @@ -246,6 +283,15 @@ impl IpPacketRouter { let dst_str = dst.map_or(dst_addr.to_string(), |dst| dst.to_string()); log::info!("Received packet: {packet_type}: {src_addr} -> {dst_str}"); + // Check if there is a connected client for this src_addr. If there is, update the last activity time + // for the client. If there isn't, drop the packet. + if let Some(client) = self.connected_clients.get_mut(&src_addr) { + client.last_activity = std::time::Instant::now(); + } else { + log::info!("Dropping packet: no connected client for {src_addr}"); + return Ok(None); + } + // Filter check if let Some(dst) = dst { if !self.request_filter.check_address(&dst).await { @@ -258,11 +304,11 @@ impl IpPacketRouter { log::warn!("Ignoring filter check for packet without port number! TODO!"); } - // TODO: set the tag correctly. Can we just reuse sender_tag? // TODO: consider changing from Vec to bytes::Bytes? - let peer_tag = 0; + // TODO: consider just removing the tag + let tag = 0; self.tun_task_tx - .try_send((peer_tag, data_request.ip_packet.into())) + .try_send((tag, data_request.ip_packet.into())) .map_err(|err| IpPacketRouterError::FailedToSendPacketToTun { source: err })?; Ok(None) @@ -343,15 +389,18 @@ impl IpPacketRouter { }, packet = self.tun_task_response_rx.recv() => { if let Some((_tag, packet)) = packet { - // Read recipient from env variable NYM_CLIENT_ADDR which is a base58 - // string of the nym-address of the client that the packet should be - // sent back to. - // - // In the near future we will let the client expose it's nym-address - // directly, and after that, provide SURBS - let recipient = std::env::var("NYM_CLIENT_ADDR").ok().and_then(|addr| { - Recipient::try_from_base58_string(addr).ok() - }); + // TODO: skip full parsing we we only need dst_addr + let Ok(ParsedPacket { + packet_type: _, + src_addr: _, + dst_addr, + dst: _, + }) = parse_packet(&packet) else { + log::warn!("Failed to parse packet"); + continue; + }; + + let recipient = self.connected_clients.get(&dst_addr).map(|c| c.nym_address); if let Some(recipient) = recipient { let lane = TransmissionLane::General; @@ -367,7 +416,7 @@ impl IpPacketRouter { log::error!("IpPacketRouter [main loop]: failed to send packet to mixnet: {err}"); }; } else { - log::error!("NYM_CLIENT_ADDR not set or invalid"); + log::error!("IpPacketRouter [main loop]: no nym-address recipient for packet"); } } else { log::trace!("IpPacketRouter [main loop]: stopping since channel closed");