From ee32763b3e59456b003b16b43e2685d05a83c93c Mon Sep 17 00:00:00 2001 From: Matas Misiunas Date: Thu, 4 Dec 2025 11:24:53 +0200 Subject: [PATCH] Introduce ipv6_enable flag Signed-off-by: Matas Misiunas --- neptun-cli/src/main.rs | 5 ++ neptun/src/device/api.rs | 10 ++-- neptun/src/device/integration_tests/mod.rs | 21 ++++++- neptun/src/device/mod.rs | 69 ++++++++++++++++------ 4 files changed, 82 insertions(+), 23 deletions(-) diff --git a/neptun-cli/src/main.rs b/neptun-cli/src/main.rs index 76ae0562..f7317414 100644 --- a/neptun-cli/src/main.rs +++ b/neptun-cli/src/main.rs @@ -80,6 +80,10 @@ fn main() { .long("socket-buffer-size") .value_parser(value_parser!(u32)) .help("Sets socket buffers to custom size"), + Arg::new("ipv6") + .long("ipv6") + .action(clap::ArgAction::SetTrue) + .help("Enable IPv6"), #[cfg(target_os = "linux")] Arg::new("disable-multi-queue") .long("disable-multi-queue") @@ -160,6 +164,7 @@ fn main() { skt_buffer_size, inter_thread_channel_size: None, max_inter_thread_batched_pkts: None, + enable_ipv6: !matches.get_flag("ipv6"), }; let mut device_handle: DeviceHandle = match DeviceHandle::new(&tun_name, config) { diff --git a/neptun/src/device/api.rs b/neptun/src/device/api.rs index 33a14f0f..e772389d 100644 --- a/neptun/src/device/api.rs +++ b/neptun/src/device/api.rs @@ -253,10 +253,12 @@ fn api_set(reader: &mut BufReader, d: &mut LockReadGuard) -> Err(_) => return EINVAL, }, "listen_port" => match val.parse::() { - Ok(port) => match device.open_listen_socket(port) { - Ok(()) => {} - Err(_) => return EADDRINUSE, - }, + Ok(port) => { + match device.open_listen_socket(port, device.config.enable_ipv6) { + Ok(()) => {} + Err(_) => return EADDRINUSE, + } + } Err(_) => return EINVAL, }, "fwmark" => diff --git a/neptun/src/device/integration_tests/mod.rs b/neptun/src/device/integration_tests/mod.rs index 171eb8c1..6e132526 100644 --- a/neptun/src/device/integration_tests/mod.rs +++ b/neptun/src/device/integration_tests/mod.rs @@ -7,7 +7,7 @@ #[cfg(all(test, not(target_os = "macos")))] mod tests { use crate::device::tun::TunSocket; - use crate::device::{DeviceConfig, DeviceHandle}; + use crate::device::{Device, DeviceConfig, DeviceHandle}; use crate::x25519::{PublicKey, StaticSecret}; use base64::encode as base64encode; use hex::encode; @@ -275,6 +275,7 @@ mod tests { skt_buffer_size: None, inter_thread_channel_size: None, max_inter_thread_batched_pkts: None, + enable_ipv6: true, }, ) } @@ -567,6 +568,7 @@ mod tests { skt_buffer_size: None, inter_thread_channel_size: None, max_inter_thread_batched_pkts: None, + enable_ipv6: true, }, ); @@ -858,6 +860,7 @@ mod tests { skt_buffer_size: None, inter_thread_channel_size: None, max_inter_thread_batched_pkts: None, + enable_ipv6: true, }, ); @@ -938,6 +941,22 @@ mod tests { } } + /// Test ipv6 enabled/disabled + #[test] + #[ignore] + fn test_device_creation_with_ipv6_enabled() { + for ipv6 in [true, false] { + let config = DeviceConfig { + enable_ipv6: ipv6, + ..Default::default() + }; + + let device = Device::new_with_tun(TunSocket::new("test_tun").unwrap(), config); + + assert!(device.is_ok()); + } + } + /// Test many concurrent connections #[test] #[ignore] diff --git a/neptun/src/device/mod.rs b/neptun/src/device/mod.rs index 47933a4a..0a658fb5 100644 --- a/neptun/src/device/mod.rs +++ b/neptun/src/device/mod.rs @@ -150,6 +150,7 @@ pub struct DeviceConfig { pub skt_buffer_size: Option, pub inter_thread_channel_size: Option, pub max_inter_thread_batched_pkts: Option, + pub enable_ipv6: bool, } pub struct Device { @@ -234,8 +235,9 @@ impl DeviceHandle { pub fn new_with_tun(tun: TunSocket, config: DeviceConfig) -> Result { let n_threads = config.n_threads; + let ipv6_enabled = config.enable_ipv6; let mut wg_interface = Device::new_with_tun(tun, config)?; - wg_interface.open_listen_socket(0)?; // Start listening on a random port + wg_interface.open_listen_socket(0, ipv6_enabled)?; // Start listening on a random port let interface_lock = Arc::new(Lock::new(wg_interface)); @@ -649,8 +651,8 @@ impl Device { peers: Default::default(), peers_by_idx: Default::default(), peers_by_ip: AllowedIps::new(), - udp4: Default::default(), - udp6: Default::default(), + udp4: None, + udp6: None, cleanup_paths: Default::default(), mtu: AtomicUsize::new(mtu), rate_limiter: None, @@ -689,7 +691,7 @@ impl Device { Ok(device) } - fn open_listen_socket(&mut self, mut port: u16) -> Result<(), Error> { + fn open_listen_socket(&mut self, mut port: u16, ipv6_enabled: bool) -> Result<(), Error> { // Binds the network facing interfaces // First close any existing open socket, and remove them from the event loop if let Some(s) = self.udp4.take() { @@ -733,25 +735,37 @@ impl Device { } } - let udp_sock6 = socket2::Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?; - udp_sock6.set_reuse_address(true)?; - udp_sock6.bind(&SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, port, 0, 0).into())?; - udp_sock6.set_nonblocking(true)?; - self.config.protect.make_external(udp_sock6.as_raw_fd()); - if let Some(buffer_size) = self.config.skt_buffer_size { - // Modify IPv4 IPv6 snd and recv buffers + // Modify IPv4 snd and recv buffers modify_skt_buffer_size(udp_sock4.as_fd(), buffer_size); - modify_skt_buffer_size(udp_sock6.as_fd(), buffer_size); } self.register_udp_handler(udp_sock4.try_clone()?)?; - self.register_udp_handler(udp_sock6.try_clone()?)?; - let udp4 = Arc::new(udp_sock4); - let udp6 = Arc::new(udp_sock6); + + let udp6 = { + if ipv6_enabled { + let udp_sock6 = + socket2::Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?; + udp_sock6.set_reuse_address(true)?; + udp_sock6.bind(&SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, port, 0, 0).into())?; + udp_sock6.set_nonblocking(true)?; + self.config.protect.make_external(udp_sock6.as_raw_fd()); + + if let Some(buffer_size) = self.config.skt_buffer_size { + // Modify IPv4 snd and recv buffers + modify_skt_buffer_size(udp_sock6.as_fd(), buffer_size); + } + + self.register_udp_handler(udp_sock6.try_clone()?)?; + Some(Arc::new(udp_sock6)) + } else { + None + } + }; + self.udp4 = Some(udp4.clone()); - self.udp6 = Some(udp6.clone()); + self.udp6 = udp6.clone(); // Construct a different closing channel per thread let (close_network_worker_tx, close_network_worker_rx) = @@ -1307,7 +1321,7 @@ fn write_to_socket_worker( tunnel_to_socket_rx: Receiver>, close_chan: Receiver<()>, udp4: Arc, - udp6: Arc, + maybe_udp6: Option>, firewall_process_outbound_callback: Option< Arc bool + Send + Sync>, >, @@ -1366,7 +1380,7 @@ fn write_to_socket_worker( public_key = element.peer.public_key.1 ); } - } else if let Some(addr @ SocketAddr::V6(_)) = endpoint.addr { + } else if let (Some(addr @ SocketAddr::V6(_)), Some(udp6))= (endpoint.addr, maybe_udp6.clone()) { if let Err(err) = udp6.send_to(packet, &addr.into()) { tracing::warn!(message = "Failed to write packet to network v6", error = ?err, dst = ?addr); } else { @@ -1490,6 +1504,25 @@ impl Default for IndexLfsr { mod tests { use super::*; + impl Default for DeviceConfig { + fn default() -> Self { + DeviceConfig { + n_threads: 1, + use_connected_socket: true, + #[cfg(target_os = "linux")] + use_multi_queue: true, + open_uapi_socket: true, + protect: Arc::new(crate::device::MakeExternalNeptunNoop), + firewall_process_inbound_callback: None, + firewall_process_outbound_callback: None, + skt_buffer_size: None, + inter_thread_channel_size: None, + max_inter_thread_batched_pkts: None, + enable_ipv6: true, + } + } + } + #[test] fn test_setting_skt_buffers() { let socket = socket2::Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP)).unwrap();