This commit is contained in:
Joshua Coles 2025-02-12 10:26:58 +00:00
parent d2909d5c17
commit e07c78b71a

View File

@ -1,10 +1,13 @@
use crate::topology::DeviceCapabilities;
use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig}; use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig};
use serde::{Deserialize, Serialize};
use socket2::{Domain, Protocol, Socket, Type}; use socket2::{Domain, Protocol, Socket, Type};
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::time::Duration; use std::time::Duration;
use serde::{Deserialize, Serialize}; use system_configuration::core_foundation::array::CFArray;
use system_configuration::network_configuration::{get_interfaces, SCNetworkInterface, SCNetworkInterfaceType};
use system_configuration::sys::network_configuration::SCNetworkInterfaceRef;
use tokio::net::UdpSocket; use tokio::net::UdpSocket;
use crate::topology::DeviceCapabilities;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
enum InterfaceType { enum InterfaceType {
@ -45,7 +48,6 @@ impl ToString for InterfaceType {
} }
} }
struct BroadcastCreationInfo { struct BroadcastCreationInfo {
interface_name: String, interface_name: String,
interface_type: InterfaceType, interface_type: InterfaceType,
@ -54,7 +56,84 @@ struct BroadcastCreationInfo {
broadcast_address: Ipv4Addr, broadcast_address: Ipv4Addr,
} }
fn get_broadcast_creation_info() -> Vec<BroadcastCreationInfo> {} fn get_broadcast_creation_info() -> Vec<BroadcastCreationInfo> {
let interfaces = NetworkInterface::show().unwrap();
let mut broadcast_info = Vec::new();
#[cfg(target_os = "macos")]
let sc_interfaces = get_interfaces();
for interface in interfaces {
// Skip interfaces without IPv4 addresses with broadcast
let map = interface.addr.iter().filter_map(|a| {
if let Addr::V4(v4) = a {
v4.broadcast.map(|b| (v4.ip, b))
} else {
None
}
});
for (bind_address, broadcast_address) in map {
let interface_type = if cfg!(target_os = "macos") {
get_sc_interface_type(&interface.name, &sc_interfaces).unwrap_or_else(|| determine_interface_type(&interface.name))
} else {
determine_interface_type(&interface.name)
};
broadcast_info.push(BroadcastCreationInfo {
interface_name: interface.name.clone(),
interface_type,
bind_address,
broadcast_address,
});
}
}
broadcast_info
}
#[cfg(target_os = "macos")]
fn get_sc_interface_type(name: &str, sc_interfaces: &CFArray<SCNetworkInterface>) -> Option<InterfaceType> {
sc_interfaces.iter().find_map(|sc_if| {
sc_if
.bsd_name()
.and_then(|bsd| (bsd.to_string() == name).then(|| sc_if.interface_type()))
.flatten()
.map(|t| match t {
SCNetworkInterfaceType::Ethernet => InterfaceType::Ethernet,
SCNetworkInterfaceType::IEEE80211 => InterfaceType::WiFi,
_ => InterfaceType::Other,
})
})
}
fn determine_interface_type(name: &str) -> InterfaceType {
// Fallback to interface name pattern matching
if name.starts_with("docker") || name.starts_with("br-") || name.starts_with("veth")
|| name.starts_with("cni") || name.starts_with("flannel") || name.starts_with("calico")
|| name.starts_with("weave") || name.contains("bridge")
{
InterfaceType::ContainerVirtual
} else if name.starts_with("lo") {
InterfaceType::Loopback
} else if name.starts_with("tb") || name.starts_with("nx") || name.starts_with("ten") {
InterfaceType::Thunderbolt
} else if (name.starts_with("eth") || name.starts_with("en")) && !matches!(name, "en0" | "en1")
{
InterfaceType::Ethernet
} else if name.starts_with("wlan") || name.starts_with("wifi") || name.starts_with("wl")
|| matches!(name, "en0" | "en1")
{
InterfaceType::WiFi
} else if name.starts_with("tun") || name.starts_with("tap") || name.starts_with("vtun")
|| name.starts_with("utun") || name.starts_with("gif") || name.starts_with("stf")
|| name.starts_with("awdl") || name.starts_with("llw")
{
InterfaceType::ExternalVirtual
} else {
InterfaceType::Other
}
}
struct NodeInfo { struct NodeInfo {
node_id: String, node_id: String,
@ -74,11 +153,13 @@ struct DiscoveryMessage {
interface_type: String, interface_type: String,
} }
async fn listen(broadcast_creation_info: BroadcastCreationInfo, node_info: NodeInfo, broadcast_port: u16, broadcast_interval: Duration) { async fn listen(
let socket_addr = SocketAddr::new( broadcast_creation_info: BroadcastCreationInfo,
IpAddr::V4(broadcast_creation_info.bind_address), node_info: NodeInfo,
0, broadcast_port: u16,
); broadcast_interval: Duration,
) {
let socket_addr = SocketAddr::new(IpAddr::V4(broadcast_creation_info.bind_address), 0);
let socket = bind_to_address(socket_addr); let socket = bind_to_address(socket_addr);
@ -90,13 +171,20 @@ async fn listen(broadcast_creation_info: BroadcastCreationInfo, node_info: NodeI
priority: broadcast_creation_info.interface_type.priority(), priority: broadcast_creation_info.interface_type.priority(),
interface_name: broadcast_creation_info.interface_name, interface_name: broadcast_creation_info.interface_name,
interface_type: broadcast_creation_info.interface_type.to_string(), interface_type: broadcast_creation_info.interface_type.to_string(),
}).unwrap(); })
.unwrap();
loop { loop {
socket.send_to( socket
&message, .send_to(
SocketAddr::new(IpAddr::V4(broadcast_creation_info.broadcast_address), broadcast_port), &message,
).await.unwrap(); SocketAddr::new(
IpAddr::V4(broadcast_creation_info.broadcast_address),
broadcast_port,
),
)
.await
.unwrap();
tokio::time::sleep(broadcast_interval).await; tokio::time::sleep(broadcast_interval).await;
} }
@ -116,17 +204,5 @@ fn bind_to_address(address: SocketAddr) -> UdpSocket {
#[test] #[test]
fn test_interfaces() { fn test_interfaces() {
let raw = NetworkInterface::show().unwrap(); dbg!(get_interfaces());
let names_and_addrs = raw
.iter()
.flat_map(|network_interface| {
let v4_addrs = network_interface
.addr
.iter()
.filter(|addr: &&Addr| matches!(addr, Addr::V4(..)))
.map(|addr: &Addr| (network_interface.name.clone(), *addr));
v4_addrs
})
.collect::<Vec<_>>();
} }