mdns fix: use heuristic to find suitable interface and use IP of that interface to anounce service
This commit is contained in:
Generated
+12
-1
@@ -1948,6 +1948,16 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "if-addrs"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624c5448ba529e74f594c65b7024f31b2de7b64a9b228b8df26796bbb6e32c36"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "if-addrs"
|
||||
version = "0.14.0"
|
||||
@@ -2206,6 +2216,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"eyre",
|
||||
"gethostname",
|
||||
"if-addrs 0.11.1",
|
||||
"lanspread-compat",
|
||||
"lanspread-db",
|
||||
"lanspread-mdns",
|
||||
@@ -2425,7 +2436,7 @@ checksum = "3426fcc57a3b93e136cbc83861d30ccbc6e6eb8788bd09b6eb92565d29841029"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"flume",
|
||||
"if-addrs",
|
||||
"if-addrs 0.14.0",
|
||||
"log",
|
||||
"mio",
|
||||
"socket-pktinfo",
|
||||
|
||||
@@ -39,6 +39,7 @@ tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
uuid = { version = "1", features = ["v7"] }
|
||||
walkdir = "2"
|
||||
if-addrs = "0.11"
|
||||
windows = { version = "0.62", features = [
|
||||
"Win32",
|
||||
"Win32_UI",
|
||||
|
||||
@@ -29,3 +29,4 @@ tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
walkdir = { workspace = true }
|
||||
if-addrs = { workspace = true }
|
||||
|
||||
@@ -5,13 +5,14 @@ mod peer;
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
net::SocketAddr,
|
||||
net::{IpAddr, SocketAddr},
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use if_addrs::{IfAddr, Interface, get_if_addrs};
|
||||
use lanspread_db::db::{Game, GameDB, GameFileDescription};
|
||||
use lanspread_mdns::{LANSPREAD_SERVICE_TYPE, MdnsAdvertiser, discover_service};
|
||||
use lanspread_proto::{Message, Request, Response};
|
||||
@@ -1269,6 +1270,10 @@ async fn run_server_component(
|
||||
let server_addr = server.local_addr()?;
|
||||
log::info!("Peer server listening on {server_addr}");
|
||||
|
||||
let advertise_ip = select_advertise_ip()?;
|
||||
let advertise_addr = SocketAddr::new(advertise_ip, server_addr.port());
|
||||
log::info!("Advertising peer via mDNS from {advertise_addr}");
|
||||
|
||||
// Start mDNS advertising for peer discovery
|
||||
let peer_id = Uuid::now_v7().simple().to_string();
|
||||
let hostname = gethostname::gethostname();
|
||||
@@ -1289,7 +1294,7 @@ async fn run_server_component(
|
||||
};
|
||||
|
||||
let mdns = tokio::task::spawn_blocking(move || {
|
||||
MdnsAdvertiser::new(LANSPREAD_SERVICE_TYPE, &combined_str, server_addr)
|
||||
MdnsAdvertiser::new(LANSPREAD_SERVICE_TYPE, &combined_str, advertise_addr)
|
||||
})
|
||||
.await??;
|
||||
|
||||
@@ -1935,6 +1940,84 @@ async fn ping_peer(peer_addr: SocketAddr) -> eyre::Result<bool> {
|
||||
Ok(is_alive)
|
||||
}
|
||||
|
||||
fn select_advertise_ip() -> eyre::Result<IpAddr> {
|
||||
let mut best_candidate: Option<(u8, IpAddr)> = None;
|
||||
let mut loopback_fallback = None;
|
||||
|
||||
for interface in get_if_addrs()? {
|
||||
if interface.is_loopback() {
|
||||
loopback_fallback.get_or_insert(interface.ip());
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(candidate) = classify_interface(&interface)
|
||||
&& best_candidate
|
||||
.as_ref()
|
||||
.is_none_or(|(rank, _)| candidate.0 < *rank)
|
||||
{
|
||||
best_candidate = Some(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((_, ip)) = best_candidate {
|
||||
return Ok(ip);
|
||||
}
|
||||
|
||||
if let Some(ip) = loopback_fallback {
|
||||
log::warn!(
|
||||
"No non-loopback interface suitable for mDNS advertisement; falling back to {ip}"
|
||||
);
|
||||
return Ok(ip);
|
||||
}
|
||||
|
||||
eyre::bail!("No usable network interface found for mDNS advertisement");
|
||||
}
|
||||
|
||||
fn classify_interface(interface: &Interface) -> Option<(u8, IpAddr)> {
|
||||
match interface.addr {
|
||||
IfAddr::V4(ref v4) => {
|
||||
let ip = v4.ip;
|
||||
|
||||
if ip.is_unspecified() || ip.is_link_local() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut rank = if ip.is_private() { 0 } else { 2 };
|
||||
|
||||
if is_virtual_interface(&interface.name) {
|
||||
rank += 2;
|
||||
}
|
||||
|
||||
Some((rank, IpAddr::V4(ip)))
|
||||
}
|
||||
IfAddr::V6(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_virtual_interface(name: &str) -> bool {
|
||||
const VIRTUAL_HINTS: &[&str] = &[
|
||||
"awdl",
|
||||
"br-",
|
||||
"bridge",
|
||||
"docker",
|
||||
"ham",
|
||||
"llw",
|
||||
"tap",
|
||||
"tailscale",
|
||||
"tun",
|
||||
"utun",
|
||||
"vbox",
|
||||
"veth",
|
||||
"virbr",
|
||||
"vmnet",
|
||||
"wg",
|
||||
"zt",
|
||||
];
|
||||
|
||||
let lower = name.to_ascii_lowercase();
|
||||
VIRTUAL_HINTS.iter().any(|hint| lower.contains(hint))
|
||||
}
|
||||
|
||||
async fn get_game_file_descriptions(
|
||||
game_id: &str,
|
||||
game_dir: &str,
|
||||
|
||||
Reference in New Issue
Block a user