windows default route

This commit is contained in:
asxalex 2026-04-22 23:27:56 +08:00
parent 7e7a9ca2e6
commit 6874bc5291
6 changed files with 370 additions and 72 deletions

131
Cargo.lock generated
View File

@ -524,6 +524,16 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation"
version = "0.10.1"
@ -698,6 +708,23 @@ dependencies = [
"parking_lot_core",
]
[[package]]
name = "default-net"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c5a6569a908354d49b10db3c516d69aca1eccd97562fd31c98b13f00b73ca66"
dependencies = [
"dlopen2",
"libc",
"memalloc",
"netlink-packet-core",
"netlink-packet-route",
"netlink-sys",
"once_cell",
"system-configuration",
"windows 0.48.0",
]
[[package]]
name = "der"
version = "0.7.10"
@ -772,6 +799,17 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "dlopen2"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa"
dependencies = [
"libc",
"once_cell",
"winapi",
]
[[package]]
name = "dns-lookup"
version = "2.1.1"
@ -1694,6 +1732,12 @@ dependencies = [
"digest",
]
[[package]]
name = "memalloc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1"
[[package]]
name = "memchr"
version = "2.8.0"
@ -1761,6 +1805,54 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "netlink-packet-core"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4"
dependencies = [
"anyhow",
"byteorder",
"netlink-packet-utils",
]
[[package]]
name = "netlink-packet-route"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66"
dependencies = [
"anyhow",
"bitflags 1.3.2",
"byteorder",
"libc",
"netlink-packet-core",
"netlink-packet-utils",
]
[[package]]
name = "netlink-packet-utils"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34"
dependencies = [
"anyhow",
"byteorder",
"paste",
"thiserror 1.0.69",
]
[[package]]
name = "netlink-sys"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae"
dependencies = [
"bytes",
"libc",
"log",
]
[[package]]
name = "nom"
version = "7.1.3"
@ -2153,6 +2245,7 @@ dependencies = [
"crc32fast",
"daemonize",
"dashmap 6.1.0",
"default-net",
"dns-lookup",
"etherparse",
"futures-util",
@ -2179,11 +2272,13 @@ dependencies = [
"serde",
"serde_json",
"simple-dns",
"socket2",
"structopt",
"tokio",
"tokio-util",
"tracing",
"winapi",
"windows-sys 0.61.2",
"wintun",
]
@ -2575,7 +2670,7 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
dependencies = [
"core-foundation",
"core-foundation 0.10.1",
"core-foundation-sys",
"jni",
"log",
@ -2676,7 +2771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [
"bitflags 2.11.0",
"core-foundation",
"core-foundation 0.10.1",
"core-foundation-sys",
"libc",
"security-framework-sys",
@ -3193,6 +3288,27 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "system-configuration"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "tar"
version = "0.4.45"
@ -3922,6 +4038,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows"
version = "0.52.0"
@ -4322,7 +4447,7 @@ dependencies = [
"libloading",
"log",
"thiserror 1.0.69",
"windows",
"windows 0.52.0",
]
[[package]]

View File

@ -44,6 +44,8 @@ ipnet = "2.12.0"
arc-swap = "1.9.0"
rustls-native-certs = "0.8.3"
simple-dns = "0.11.2"
default-net = "0.22.0"
socket2 = "0.6.3"
# rolling-file = { path = "../rolling-file" }
[target.'cfg(unix)'.dependencies]
@ -54,6 +56,7 @@ daemonize = "0.5.0"
[target.'cfg(windows)'.dependencies]
wintun = "0.4.0"
winapi = "0.3.9"
windows-sys = "0.61.2"
[features]
tun = []

View File

@ -1,10 +1,10 @@
use arc_swap::ArcSwap;
use dashmap::DashMap;
use default_net::Interface;
use prost::Message;
use quinn::Endpoint;
use rsa::RsaPrivateKey;
use sdlan_sn_rs::config::{AF_INET, AF_INET6};
use tokio::net::UdpSocket;
use std::any::Any;
use std::future::Future;
use std::net::SocketAddr;
@ -12,17 +12,22 @@ use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicU8, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::time::Duration;
use tokio::io::AsyncReadExt;
use tokio::net::UdpSocket;
use tokio::sync::mpsc::Sender;
use tracing::{debug, error, warn};
use crate::network::{ArpTable, RouteTable2};
use crate::quic::quic_init;
use crate::{CommandLine, ConnectionInfo, DNSMatcher, MyEncryptor, RuleCache, get_base_dir};
use crate::pb::{
SdlArpRequest, SdlEmpty, SdlStunProbe, SdlStunProbeReply, encode_to_tcp_message, encode_to_udp_message
encode_to_tcp_message, encode_to_udp_message, SdlArpRequest, SdlEmpty, SdlStunProbe,
SdlStunProbeReply,
};
use crate::quic::quic_init;
use crate::tcp::{get_quic_write_conn, NatType, PacketType, StunProbeAttr};
use crate::utils::Socket;
use crate::{
get_base_dir, get_default_interface, CommandLine, ConnectionInfo, DNSMatcher, MyEncryptor,
RuleCache,
};
use crate::tcp::{NatType, PacketType, StunProbeAttr, get_quic_write_conn};
use crate::utils::{Socket};
use sdlan_sn_rs::peer::{IpSubnet, V6Info};
@ -32,7 +37,7 @@ use super::device::{DeviceConfig, Mode};
use super::tun::{new_iface, Iface};
use tokio::fs::File;
use sdlan_sn_rs::utils::{Mac, gen_rsa_keys, load_private_key_file, save_to_file};
use sdlan_sn_rs::utils::{gen_rsa_keys, load_private_key_file, save_to_file, Mac};
use sdlan_sn_rs::utils::{Result, SDLanError};
static EDGE: OnceCell<Node> = OnceCell::new();
@ -51,7 +56,7 @@ pub async fn init_edge(
udp_sock_for_global_dns: Arc<UdpSocket>,
hostname: String,
server_ip: String,
install_channel: String
install_channel: String,
) -> Result<()> {
// gen public key
let rsa_path = format!("{}/.client", get_base_dir());
@ -69,13 +74,33 @@ pub async fn init_edge(
// let edge_uuid = create_or_load_uuid("")?;
//let node_conf = parse_config(edge_uuid, &args).await?;
let Ok(sock_v4) = Socket::build(node_conf._local_port, true, false, args.tos).await else {
let default_interface = match get_default_interface() {
Ok(interface) => {
println!("get default interface: {:?}", interface);
Some(interface)
}
Err(e) => {
println!("failed to get default net interface: {}", e.as_str());
None
}
};
let Ok(sock_v4) = Socket::build(
node_conf._local_port,
true,
false,
args.tos,
&default_interface,
)
.await
else {
panic!("failed to build sock for sock v4");
};
let mut sock_multicast = None;
if !node_conf._drop_multicast {
sock_multicast = Some(Socket::build(MULTICAST_PORT, true, true, 0).await?);
sock_multicast =
Some(Socket::build(MULTICAST_PORT, true, true, 0, &default_interface).await?);
}
// let sock_multicast = Socket::build(MULTICAST_PORT, true, true, 0).await?;
// allow multicast
@ -88,6 +113,7 @@ pub async fn init_edge(
pubkey,
node_conf,
sock_v4,
&default_interface,
sock_multicast,
// token,
// network_code,
@ -103,7 +129,8 @@ pub async fn init_edge(
install_channel,
);
edge.route_table.parse_and_add_route(&args.route_file, &args.route_str);
edge.route_table
.parse_and_add_route(&args.route_file, &args.route_str);
let matcher = Arc::clone(&edge.dns_matcher);
do_init_edge(edge)?;
@ -214,7 +241,6 @@ pub struct Node {
// user token info
// pub _token: Mutex<String>,
// pub network_code: Mutex<String>,
pub device_config: DeviceConfig,
pub device: Iface,
@ -222,7 +248,6 @@ pub struct Node {
pub authorized: AtomicBool,
// pub header_key: RwLock<Arc<Vec<u8>>>,
// pub encrypt_key: RwLock<Arc<Vec<u8>>>,
pub rsa_pubkey: String,
pub rsa_private: RsaPrivateKey,
@ -262,7 +287,6 @@ pub struct Node {
//cookie_match: DashMap<u32, oneshot::Sender<SdlStunProbeReply>>,
pub cookie_match: Queryer,
// packet_id_match: DashMap<u32, oneshot::Sender<RegisterSuperFeedback>>,
}
@ -285,7 +309,7 @@ impl Node {
ip_net: u32,
ip_net_bit_len: u8,
identity_id: u32,
hostname: Option<String>
hostname: Option<String>,
) -> Result<()> {
if let Some(host) = hostname {
let idfile = format!("{}/.host", get_base_dir());
@ -355,7 +379,9 @@ impl Node {
// *self.network_code.lock().unwrap() = network_code;
let id = self.get_next_packet_id();
let res = self.cookie_match.do_action_and_wait_for(
let res = self
.cookie_match
.do_action_and_wait_for(
0,
|| async {
let _ = self
@ -367,13 +393,16 @@ impl Node {
.await;
debug!("start with feedback");
},
timeout
).await?;
timeout,
)
.await?;
if let Ok(res) = res.downcast() {
Ok(*res)
} else {
Err(SDLanError::ConvertError("failed to convert feedback to RSFeedback".to_owned()))
Err(SDLanError::ConvertError(
"failed to convert feedback to RSFeedback".to_owned(),
))
}
}
@ -394,6 +423,7 @@ impl Node {
pubkey: String,
config: NodeConfig,
sock: Socket,
iface: &Option<Interface>,
multicast_sock: Option<Socket>,
// tcpsock: TCPSocket,
// token: &str,
@ -416,14 +446,12 @@ impl Node {
};
Self {
#[cfg(any(feature = "tun", target_os = "windows"))]
arp_table: ArpTable::new(),
packet_id: AtomicU32::new(1),
encryptor: ArcSwap::from(Arc::new(MyEncryptor::new())),
// encryptor: RwLock::new(MyEncryptor::new()),
network_id: AtomicU32::new(0),
hostname: RwLock::new(hostname),
@ -437,7 +465,7 @@ impl Node {
udp_sock_for_global_dns: udp_sock_for_global_dns,
dns_matcher: Arc::new(DNSMatcher::new()),
quic_endpoint: quic_init(),
quic_endpoint: quic_init(iface),
identity_id: IdentityID::new(0),
access_token: StringToken::new(String::new()),
@ -445,7 +473,6 @@ impl Node {
// _token: Mutex::new(token.to_owned()),
// network_code: Mutex::new(network_code.to_owned()),
start_stop_sender: start_stop,
connection_chan: connecting_chan,
@ -666,12 +693,24 @@ impl Node {
step: 0,
};
let result = self.cookie_match.send_message_to_udp_and_wait_for(&self.udp_sock_v4, cookie, probe, PacketType::StunProbe as u8, to_server, Duration::from_secs(3)).await?;
let result = self
.cookie_match
.send_message_to_udp_and_wait_for(
&self.udp_sock_v4,
cookie,
probe,
PacketType::StunProbe as u8,
to_server,
Duration::from_secs(3),
)
.await?;
if let Ok(res) = result.downcast() {
return Ok(*res);
}
return Err(SDLanError::ConvertError("failed to convert to StunprobeReply".to_owned()))
return Err(SDLanError::ConvertError(
"failed to convert to StunprobeReply".to_owned(),
));
// println!("==> sending probe request: {:?}", probe);
}
}
@ -877,7 +916,15 @@ impl Queryer {
}
}
pub async fn send_message_to_udp_and_wait_for<T: Message>(&self, sock: &Socket, id: u32, message: T, packet_type: u8, to_server: &SocketAddr, timeout: Duration) -> Result<BoxedProstMessage> {
pub async fn send_message_to_udp_and_wait_for<T: Message>(
&self,
sock: &Socket,
id: u32,
message: T,
packet_type: u8,
to_server: &SocketAddr,
timeout: Duration,
) -> Result<BoxedProstMessage> {
let (tx, rx) = tokio::sync::oneshot::channel();
self.mailbox.insert(id, tx);
@ -903,10 +950,14 @@ impl Queryer {
Err(SDLanError::IOError("timed out".to_string()))
}
}
}
pub async fn do_action_and_wait_for<T, F>(&self, id: u32, action: T, timeout: Duration) -> Result<BoxedProstMessage>
pub async fn do_action_and_wait_for<T, F>(
&self,
id: u32,
action: T,
timeout: Duration,
) -> Result<BoxedProstMessage>
where
F: Future<Output = ()>,
T: Fn() -> F,
@ -933,7 +984,13 @@ impl Queryer {
}
}
pub async fn send_message_to_quic_and_wait_for<T: Message>(&self, id: u32, message: T, packet_type: u8, timeout: Duration) -> Result<BoxedProstMessage> {
pub async fn send_message_to_quic_and_wait_for<T: Message>(
&self,
id: u32,
message: T,
packet_type: u8,
timeout: Duration,
) -> Result<BoxedProstMessage> {
let (tx, rx) = tokio::sync::oneshot::channel();
self.mailbox.insert(id, tx);

View File

@ -1,19 +1,30 @@
use std::fs::File;
use std::io::BufReader;
use std::net::SocketAddr;
use std::net::UdpSocket;
use std::os::windows::io::FromRawSocket;
use std::os::windows::io::IntoRawSocket;
use std::path::Path;
use std::sync::Arc;
use quinn::Endpoint;
use default_net::Interface;
use quinn::crypto::rustls::QuicClientConfig;
use rustls::crypto::CryptoProvider;
use quinn::Endpoint;
use quinn::TokioRuntime;
use rustls::crypto::ring;
use rustls::crypto::CryptoProvider;
use rustls::pki_types::CertificateDer;
use rustls::pki_types::PrivateKeyDer;
use rustls::pki_types::ServerName;
use rustls_pemfile::{certs, private_key};
use socket2::Domain;
use socket2::Protocol;
pub fn quic_init() -> Endpoint {
use crate::get_default_interface;
use crate::set_unicast_if_v4;
pub fn quic_init(iface: &Option<Interface>) -> Endpoint {
let default_provider = ring::default_provider();
CryptoProvider::install_default(default_provider).unwrap();
@ -26,7 +37,10 @@ pub fn quic_init() -> Endpoint {
*/
let mut root_store = rustls::RootCertStore::empty();
rustls_native_certs::load_native_certs().expect("could not load platform certs").into_iter().for_each(|cert| {
rustls_native_certs::load_native_certs()
.expect("could not load platform certs")
.into_iter()
.for_each(|cert| {
root_store.add(cert).unwrap();
});
@ -43,11 +57,34 @@ pub fn quic_init() -> Endpoint {
.with_no_client_auth();
*/
let socket2 =
socket2::Socket::new(Domain::IPV4, socket2::Type::DGRAM, Some(Protocol::UDP)).unwrap();
socket2
.bind(&"0.0.0.0:0".parse::<SocketAddr>().unwrap().into())
.unwrap();
if let Some(iface) = iface {
set_unicast_if_v4(&socket2, iface);
}
let std_socket = unsafe { UdpSocket::from_raw_socket(socket2.into_raw_socket()) };
std_socket.set_nonblocking(true);
rustls_config.alpn_protocols = vec![b"punchnet/1.0".to_vec()];
let quinn_client_config = quinn::ClientConfig::new(Arc::new(QuicClientConfig::try_from(rustls_config).unwrap()));
let quinn_client_config =
quinn::ClientConfig::new(Arc::new(QuicClientConfig::try_from(rustls_config).unwrap()));
let quinn_endpoint_config = quinn::EndpointConfig::default();
let mut endpoint = Endpoint::new(
quinn_endpoint_config,
None,
std_socket,
Arc::new(TokioRuntime),
)
.unwrap();
let mut endpoint = Endpoint::client("0.0.0.0:0".parse().unwrap()).unwrap();
endpoint.set_default_client_config(quinn_client_config);
endpoint
@ -56,21 +93,42 @@ pub fn quic_init() -> Endpoint {
#[derive(Debug)]
struct SkipServerVerification;
impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
fn verify_server_cert(&self, _: &CertificateDer<'_>, _: &[CertificateDer<'_>], _: &ServerName<'_>, _: &[u8], _: rustls::pki_types::UnixTime) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
fn verify_server_cert(
&self,
_: &CertificateDer<'_>,
_: &[CertificateDer<'_>],
_: &ServerName<'_>,
_: &[u8],
_: rustls::pki_types::UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
fn verify_tls12_signature(&self, _: &[u8], _: &CertificateDer<'_>, _: &rustls::DigitallySignedStruct) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
fn verify_tls12_signature(
&self,
_: &[u8],
_: &CertificateDer<'_>,
_: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(&self, _: &[u8], _: &CertificateDer<'_>, _: &rustls::DigitallySignedStruct) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
fn verify_tls13_signature(
&self,
_: &[u8],
_: &CertificateDer<'_>,
_: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
rustls::crypto::ring::default_provider().signature_verification_algorithms.supported_schemes()
rustls::crypto::ring::default_provider()
.signature_verification_algorithms
.supported_schemes()
}
}
pub fn load_certs_from_pem(path: impl AsRef<Path>) -> Result<Vec<CertificateDer<'static>>, Box<dyn std::error::Error>> {
pub fn load_certs_from_pem(
path: impl AsRef<Path>,
) -> Result<Vec<CertificateDer<'static>>, Box<dyn std::error::Error>> {
let file = File::open(path.as_ref())?;
let mut reader = BufReader::new(file);
let certs = certs(&mut reader)

View File

@ -1,8 +1,10 @@
use default_net::Interface;
use sdlan_sn_rs::{
config::{AF_INET, AF_INET6},
utils::{Result, SDLanError},
};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use socket2::{Domain, Protocol, SockAddr};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4};
use tokio::net::ToSocketAddrs;
use tracing::{debug, error};
@ -61,13 +63,34 @@ impl Socket {
Ok(Self { udp })
}
pub async fn build(port: u16, bind_any: bool, join_multicast: bool, tos: u32) -> Result<Self> {
pub async fn build(
port: u16,
bind_any: bool,
join_multicast: bool,
tos: u32,
default_interface: &Option<Interface>,
) -> Result<Self> {
let addr = match bind_any {
true => "0.0.0.0",
false => "127.0.0.1",
};
let addr = format!("{}:{}", addr, port);
let udp = UdpSocket::bind(&addr).await?;
let socket2 =
socket2::Socket::new(Domain::IPV4, socket2::Type::DGRAM, Some(Protocol::UDP))?;
socket2.bind(&addr.parse::<SocketAddr>()?.into());
if let Some(iface) = default_interface {
set_unicast_if_v4(&socket2, iface);
}
let std_socket: std::net::UdpSocket = socket2.into();
std_socket.set_nonblocking(true);
let udp = UdpSocket::from_std(std_socket)?;
// let udp = UdpSocket::bind(&addr).await?;
if join_multicast {
if let Err(e) =
udp.join_multicast_v4(Ipv4Addr::new(224, 0, 0, 69), Ipv4Addr::new(0, 0, 0, 0))
@ -86,6 +109,29 @@ impl Socket {
}
}
pub fn set_unicast_if_v4(sock: &socket2::Socket, interface: &Interface) -> Result<()> {
use std::os::windows::io::AsRawSocket;
use windows_sys::Win32::Networking::WinSock::{setsockopt, IPPROTO_IP, IP_UNICAST_IF, SOCKET};
let idx = interface.index.to_be();
let ret = unsafe {
setsockopt(
sock.as_raw_socket() as SOCKET,
IPPROTO_IP as i32,
IP_UNICAST_IF as i32,
&idx as *const _ as *const _,
std::mem::size_of_val(&idx) as i32,
)
};
if ret == 0 {
Ok(())
} else {
Err(SDLanError::IOError(
std::io::Error::last_os_error().to_string(),
))
}
}
/*
pub async fn send_to_sock_v4_and_v6(
// sk: &Socket,

View File

@ -1,6 +1,8 @@
use std::{net::Ipv4Addr, sync::Arc};
use arc_swap::ArcSwap;
use default_net::Interface;
use sdlan_sn_rs::utils::{Result, SDLanError};
#[derive(Default, Clone)]
pub struct TrieNode {
@ -63,6 +65,13 @@ impl IpTrie {
}
}
pub fn get_default_interface() -> Result<Interface> {
match default_net::get_default_interface() {
Ok(interface) => Ok(interface),
Err(e) => Err(SDLanError::IOError(e)),
}
}
#[cfg(test)]
mod test {
use std::net::Ipv4Addr;