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",
|
"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]]
|
[[package]]
|
||||||
name = "if-addrs"
|
name = "if-addrs"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
@@ -2206,6 +2216,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"eyre",
|
"eyre",
|
||||||
"gethostname",
|
"gethostname",
|
||||||
|
"if-addrs 0.11.1",
|
||||||
"lanspread-compat",
|
"lanspread-compat",
|
||||||
"lanspread-db",
|
"lanspread-db",
|
||||||
"lanspread-mdns",
|
"lanspread-mdns",
|
||||||
@@ -2425,7 +2436,7 @@ checksum = "3426fcc57a3b93e136cbc83861d30ccbc6e6eb8788bd09b6eb92565d29841029"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"flume",
|
"flume",
|
||||||
"if-addrs",
|
"if-addrs 0.14.0",
|
||||||
"log",
|
"log",
|
||||||
"mio",
|
"mio",
|
||||||
"socket-pktinfo",
|
"socket-pktinfo",
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ tracing = "0.1"
|
|||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
uuid = { version = "1", features = ["v7"] }
|
uuid = { version = "1", features = ["v7"] }
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
if-addrs = "0.11"
|
||||||
windows = { version = "0.62", features = [
|
windows = { version = "0.62", features = [
|
||||||
"Win32",
|
"Win32",
|
||||||
"Win32_UI",
|
"Win32_UI",
|
||||||
|
|||||||
@@ -29,3 +29,4 @@ tokio = { workspace = true }
|
|||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
walkdir = { workspace = true }
|
walkdir = { workspace = true }
|
||||||
|
if-addrs = { workspace = true }
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ mod peer;
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
net::SocketAddr,
|
net::{IpAddr, SocketAddr},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
|
use if_addrs::{IfAddr, Interface, get_if_addrs};
|
||||||
use lanspread_db::db::{Game, GameDB, GameFileDescription};
|
use lanspread_db::db::{Game, GameDB, GameFileDescription};
|
||||||
use lanspread_mdns::{LANSPREAD_SERVICE_TYPE, MdnsAdvertiser, discover_service};
|
use lanspread_mdns::{LANSPREAD_SERVICE_TYPE, MdnsAdvertiser, discover_service};
|
||||||
use lanspread_proto::{Message, Request, Response};
|
use lanspread_proto::{Message, Request, Response};
|
||||||
@@ -1269,6 +1270,10 @@ async fn run_server_component(
|
|||||||
let server_addr = server.local_addr()?;
|
let server_addr = server.local_addr()?;
|
||||||
log::info!("Peer server listening on {server_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
|
// Start mDNS advertising for peer discovery
|
||||||
let peer_id = Uuid::now_v7().simple().to_string();
|
let peer_id = Uuid::now_v7().simple().to_string();
|
||||||
let hostname = gethostname::gethostname();
|
let hostname = gethostname::gethostname();
|
||||||
@@ -1289,7 +1294,7 @@ async fn run_server_component(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mdns = tokio::task::spawn_blocking(move || {
|
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??;
|
.await??;
|
||||||
|
|
||||||
@@ -1935,6 +1940,84 @@ async fn ping_peer(peer_addr: SocketAddr) -> eyre::Result<bool> {
|
|||||||
Ok(is_alive)
|
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(
|
async fn get_game_file_descriptions(
|
||||||
game_id: &str,
|
game_id: &str,
|
||||||
game_dir: &str,
|
game_dir: &str,
|
||||||
|
|||||||
Reference in New Issue
Block a user