diff --git a/Cargo.lock b/Cargo.lock index dd82c3a..9c20d4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1044,6 +1044,15 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] + [[package]] name = "is-terminal" version = "0.4.12" @@ -1254,6 +1263,7 @@ dependencies = [ "netdev", "nix 0.29.0", "openssl", + "pnet", "quinn", "rand", "rcgen", @@ -1435,6 +1445,12 @@ dependencies = [ "memoffset", ] +[[package]] +name = "no-std-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" + [[package]] name = "nom" version = "7.1.3" @@ -1668,6 +1684,97 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "pnet" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "682396b533413cc2e009fbb48aadf93619a149d3e57defba19ff50ce0201bd0d" +dependencies = [ + "ipnetwork", + "pnet_base", + "pnet_datalink", + "pnet_packet", + "pnet_sys", + "pnet_transport", +] + +[[package]] +name = "pnet_base" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7" +dependencies = [ + "no-std-net", +] + +[[package]] +name = "pnet_datalink" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79e70ec0be163102a332e1d2d5586d362ad76b01cec86f830241f2b6452a7b7" +dependencies = [ + "ipnetwork", + "libc", + "pnet_base", + "pnet_sys", + "winapi", +] + +[[package]] +name = "pnet_macros" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13325ac86ee1a80a480b0bc8e3d30c25d133616112bb16e86f712dcf8a71c863" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.48", +] + +[[package]] +name = "pnet_macros_support" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed67a952585d509dd0003049b1fc56b982ac665c8299b124b90ea2bdb3134ab" +dependencies = [ + "pnet_base", +] + +[[package]] +name = "pnet_packet" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c96ebadfab635fcc23036ba30a7d33a80c39e8461b8bd7dc7bb186acb96560f" +dependencies = [ + "glob", + "pnet_base", + "pnet_macros", + "pnet_macros_support", +] + +[[package]] +name = "pnet_sys" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d4643d3d4db6b08741050c2f3afa9a892c4244c085a72fcda93c9c2c9a00f4b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "pnet_transport" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f604d98bc2a6591cf719b58d3203fd882bdd6bf1db696c4ac97978e9f4776bf" +dependencies = [ + "libc", + "pnet_base", + "pnet_packet", + "pnet_sys", +] + [[package]] name = "polyval" version = "0.6.1" diff --git a/mycelium/Cargo.toml b/mycelium/Cargo.toml index 5bac72c..ba17678 100644 --- a/mycelium/Cargo.toml +++ b/mycelium/Cargo.toml @@ -46,6 +46,7 @@ netdev = "0.30.0" openssl = { version = "0.10.66", optional = true } tokio-openssl = { version = "0.6.4", optional = true } arc-swap = "1.7.1" +pnet = "0.35.0" [target.'cfg(target_os = "linux")'.dependencies] rtnetlink = "0.14.1" diff --git a/mycelium/src/tun/darwin.rs b/mycelium/src/tun/darwin.rs index 770d098..5a57b4e 100644 --- a/mycelium/src/tun/darwin.rs +++ b/mycelium/src/tun/darwin.rs @@ -81,13 +81,9 @@ pub async fn new( ), Box, > { - if !validate_utun_name(&tun_config.name) { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "TUN device name must be of the form 'utunXXX...' where X is a digit", - ))?; - } - let mut tun = match create_tun_interface(&tun_config.name) { + let tun_name = find_available_utun_name(&tun_config.name)?; + + let mut tun = match create_tun_interface(&tun_name) { Ok(tun) => tun, Err(e) => { error!( @@ -97,7 +93,7 @@ pub async fn new( return Err(e); } }; - let iface = Iface::by_name(&tun_config.name)?; + let iface = Iface::by_name(&tun_name)?; iface.add_address(tun_config.node_subnet, tun_config.route_subnet)?; let (tun_sink, mut sink_receiver) = mpsc::channel::(1000); @@ -174,6 +170,40 @@ fn validate_utun_name(input: &str) -> bool { .is_ok() } +/// Find an available utun interface name +fn find_available_utun_name(preferred_name: &str) -> Result> { + let interfaces = datalink::interfaces(); + let utun_interfaces: Vec<_> = interfaces + .iter() + .filter(|iface| iface.name.starts_with("utun")) + .collect(); + + if !preferred_name.is_empty() && validate_utun_name(preferred_name) { + if !utun_interfaces + .iter() + .any(|iface| iface.name == preferred_name) + { + return Ok(preferred_name.to_string()); + } + error!("Preferred utun name '{}' is already in use", preferred_name); + } + + let max_utun_number = utun_interfaces + .iter() + .filter_map(|iface| iface.name[4..].parse::().ok()) + .max() + .unwrap_or(0); + + let new_utun_number = max_utun_number + 1; + let new_utun_name = format!("utun{}", new_utun_number); + + if validate_utun_name(&new_utun_name) { + Ok(new_utun_name) + } else { + Err("Could not find an available utun interface name".into()) + } +} + /// Create a new TUN interface fn create_tun_interface(name: &str) -> Result> { let mut config = tun::Configuration::default();