mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-07 02:09:06 +00:00
use workspace, prepare for config server and gui (#48)
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use crate::rpc::peer::GetIpListResponse;
|
||||
use pnet::datalink::NetworkInterface;
|
||||
use tokio::{
|
||||
sync::{Mutex, RwLock},
|
||||
task::JoinSet,
|
||||
};
|
||||
|
||||
use super::netns::NetNS;
|
||||
|
||||
pub const CACHED_IP_LIST_TIMEOUT_SEC: u64 = 60;
|
||||
|
||||
struct InterfaceFilter {
|
||||
iface: NetworkInterface,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl InterfaceFilter {
|
||||
async fn is_tun_tap_device(&self) -> bool {
|
||||
let path = format!("/sys/class/net/{}/tun_flags", self.iface.name);
|
||||
tokio::fs::metadata(&path).await.is_ok()
|
||||
}
|
||||
|
||||
async fn has_valid_ip(&self) -> bool {
|
||||
self.iface
|
||||
.ips
|
||||
.iter()
|
||||
.map(|ip| ip.ip())
|
||||
.any(|ip| !ip.is_loopback() && !ip.is_unspecified() && !ip.is_multicast())
|
||||
}
|
||||
|
||||
async fn filter_iface(&self) -> bool {
|
||||
tracing::trace!(
|
||||
"filter linux iface: {:?}, is_point_to_point: {}, is_loopback: {}, is_up: {}, is_lower_up: {}, is_tun: {}, has_valid_ip: {}",
|
||||
self.iface,
|
||||
self.iface.is_point_to_point(),
|
||||
self.iface.is_loopback(),
|
||||
self.iface.is_up(),
|
||||
self.iface.is_lower_up(),
|
||||
self.is_tun_tap_device().await,
|
||||
self.has_valid_ip().await
|
||||
);
|
||||
|
||||
!self.iface.is_point_to_point()
|
||||
&& !self.iface.is_loopback()
|
||||
&& self.iface.is_up()
|
||||
&& self.iface.is_lower_up()
|
||||
&& !self.is_tun_tap_device().await
|
||||
&& self.has_valid_ip().await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl InterfaceFilter {
|
||||
async fn is_interface_physical(interface_name: &str) -> bool {
|
||||
let output = tokio::process::Command::new("networksetup")
|
||||
.args(&["-listallhardwareports"])
|
||||
.output()
|
||||
.await
|
||||
.expect("Failed to execute command");
|
||||
|
||||
let stdout = std::str::from_utf8(&output.stdout).expect("Invalid UTF-8");
|
||||
|
||||
let lines: Vec<&str> = stdout.lines().collect();
|
||||
|
||||
for i in 0..lines.len() {
|
||||
let line = lines[i];
|
||||
|
||||
if line.contains("Device:") && line.contains(interface_name) {
|
||||
let next_line = lines[i + 1];
|
||||
if next_line.contains("Virtual Interface") {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
async fn filter_iface(&self) -> bool {
|
||||
!self.iface.is_point_to_point()
|
||||
&& !self.iface.is_loopback()
|
||||
&& self.iface.is_up()
|
||||
&& Self::is_interface_physical(&self.iface.name).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
impl InterfaceFilter {
|
||||
async fn filter_iface(&self) -> bool {
|
||||
tracing::debug!(
|
||||
"iface_name: {:?}, p2p: {:?}, is_up: {:?}, iface: {:?}",
|
||||
self.iface.name,
|
||||
self.iface.is_point_to_point(),
|
||||
self.iface.is_up(),
|
||||
self.iface
|
||||
);
|
||||
!self.iface.is_point_to_point()
|
||||
&& !self.iface.is_loopback()
|
||||
&& self
|
||||
.iface
|
||||
.ips
|
||||
.iter()
|
||||
.map(|ip| ip.ip())
|
||||
.any(|ip| !ip.is_loopback() && !ip.is_unspecified() && !ip.is_multicast())
|
||||
&& self.iface.mac.map(|mac| !mac.is_zero()).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn local_ipv4() -> std::io::Result<std::net::Ipv4Addr> {
|
||||
let socket = tokio::net::UdpSocket::bind("0.0.0.0:0").await?;
|
||||
socket.connect("8.8.8.8:80").await?;
|
||||
let addr = socket.local_addr()?;
|
||||
match addr.ip() {
|
||||
std::net::IpAddr::V4(ip) => Ok(ip),
|
||||
std::net::IpAddr::V6(_) => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::AddrNotAvailable,
|
||||
"no ipv4 address",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn local_ipv6() -> std::io::Result<std::net::Ipv6Addr> {
|
||||
let socket = tokio::net::UdpSocket::bind("[::]:0").await?;
|
||||
socket
|
||||
.connect("[2001:4860:4860:0000:0000:0000:0000:8888]:80")
|
||||
.await?;
|
||||
let addr = socket.local_addr()?;
|
||||
match addr.ip() {
|
||||
std::net::IpAddr::V6(ip) => Ok(ip),
|
||||
std::net::IpAddr::V4(_) => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::AddrNotAvailable,
|
||||
"no ipv4 address",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IPCollector {
|
||||
cached_ip_list: Arc<RwLock<GetIpListResponse>>,
|
||||
collect_ip_task: Mutex<JoinSet<()>>,
|
||||
net_ns: NetNS,
|
||||
}
|
||||
|
||||
impl IPCollector {
|
||||
pub fn new(net_ns: NetNS) -> Self {
|
||||
Self {
|
||||
cached_ip_list: Arc::new(RwLock::new(GetIpListResponse::new())),
|
||||
collect_ip_task: Mutex::new(JoinSet::new()),
|
||||
net_ns,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn collect_ip_addrs(&self) -> GetIpListResponse {
|
||||
let mut task = self.collect_ip_task.lock().await;
|
||||
if task.is_empty() {
|
||||
let cached_ip_list = self.cached_ip_list.clone();
|
||||
*cached_ip_list.write().await =
|
||||
Self::do_collect_ip_addrs(false, self.net_ns.clone()).await;
|
||||
let net_ns = self.net_ns.clone();
|
||||
task.spawn(async move {
|
||||
loop {
|
||||
let ip_addrs = Self::do_collect_ip_addrs(true, net_ns.clone()).await;
|
||||
*cached_ip_list.write().await = ip_addrs;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(CACHED_IP_LIST_TIMEOUT_SEC))
|
||||
.await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return self.cached_ip_list.read().await.deref().clone();
|
||||
}
|
||||
|
||||
pub async fn collect_interfaces(net_ns: NetNS) -> Vec<NetworkInterface> {
|
||||
let _g = net_ns.guard();
|
||||
let ifaces = pnet::datalink::interfaces();
|
||||
let mut ret = vec![];
|
||||
for iface in ifaces {
|
||||
let f = InterfaceFilter {
|
||||
iface: iface.clone(),
|
||||
};
|
||||
|
||||
if !f.filter_iface().await {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.push(iface);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(net_ns))]
|
||||
async fn do_collect_ip_addrs(with_public: bool, net_ns: NetNS) -> GetIpListResponse {
|
||||
let mut ret = crate::rpc::peer::GetIpListResponse::new();
|
||||
|
||||
if with_public {
|
||||
if let Some(v4_addr) =
|
||||
public_ip::addr_with(public_ip::http::ALL, public_ip::Version::V4).await
|
||||
{
|
||||
ret.public_ipv4 = v4_addr.to_string();
|
||||
}
|
||||
|
||||
if let Some(v6_addr) = public_ip::addr_v6().await {
|
||||
ret.public_ipv6 = v6_addr.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
let ifaces = Self::collect_interfaces(net_ns.clone()).await;
|
||||
let _g = net_ns.guard();
|
||||
for iface in ifaces {
|
||||
for ip in iface.ips {
|
||||
let ip: std::net::IpAddr = ip.ip();
|
||||
if ip.is_loopback() || ip.is_multicast() {
|
||||
continue;
|
||||
}
|
||||
if ip.is_ipv4() {
|
||||
ret.interface_ipv4s.push(ip.to_string());
|
||||
} else if ip.is_ipv6() {
|
||||
ret.interface_ipv6s.push(ip.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(v4_addr) = local_ipv4().await {
|
||||
tracing::trace!("got local ipv4: {}", v4_addr);
|
||||
if !ret.interface_ipv4s.contains(&v4_addr.to_string()) {
|
||||
ret.interface_ipv4s.push(v4_addr.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(v6_addr) = local_ipv6().await {
|
||||
tracing::trace!("got local ipv6: {}", v6_addr);
|
||||
if !ret.interface_ipv6s.contains(&v6_addr.to_string()) {
|
||||
ret.interface_ipv6s.push(v6_addr.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user