parent
7ad6493c88
commit
f6159a5ef8
@ -1,12 +0,0 @@ |
|||||||
mod shared; |
|
||||||
pub use self::shared::*; |
|
||||||
|
|
||||||
#[cfg(target_os = "windows")] |
|
||||||
mod windows; |
|
||||||
#[cfg(target_os = "windows")] |
|
||||||
pub use self::windows::*; |
|
||||||
|
|
||||||
#[cfg(not(target_os="windows"))] |
|
||||||
mod unix; |
|
||||||
#[cfg(not(target_os="windows"))] |
|
||||||
pub use self::unix::*; |
|
@ -1,18 +0,0 @@ |
|||||||
use std::net::{IpAddr, UdpSocket}; |
|
||||||
|
|
||||||
pub fn get_local_ipaddr() -> Option<IpAddr> { |
|
||||||
let socket = match UdpSocket::bind("0.0.0.0:0") { |
|
||||||
Ok(s) => s, |
|
||||||
Err(_) => return None, |
|
||||||
}; |
|
||||||
|
|
||||||
match socket.connect("1.1.1.1:80") { |
|
||||||
Ok(()) => (), |
|
||||||
Err(_) => return None, |
|
||||||
}; |
|
||||||
|
|
||||||
match socket.local_addr() { |
|
||||||
Ok(addr) => return Some(addr.ip()), |
|
||||||
Err(_) => return None, |
|
||||||
}; |
|
||||||
} |
|
@ -1,242 +0,0 @@ |
|||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; |
|
||||||
use std::net::UdpSocket; |
|
||||||
use std::time::{Duration, Instant}; |
|
||||||
use pnet_packet::Packet; |
|
||||||
use crate::interface::{MacAddr, Interface}; |
|
||||||
use crate::gateway::Gateway; |
|
||||||
|
|
||||||
const TIMEOUT: u64 = 3000; |
|
||||||
|
|
||||||
fn get_default_gateway(interface_index: u32) -> Result<Gateway, String> { |
|
||||||
let interfaces = pnet_datalink::interfaces(); |
|
||||||
let interface = interfaces.into_iter().filter(|interface: &pnet_datalink::NetworkInterface| interface.index == interface_index).next().expect("Failed to get Interface"); |
|
||||||
let config = pnet_datalink::Config { |
|
||||||
write_buffer_size: 4096, |
|
||||||
read_buffer_size: 4096, |
|
||||||
read_timeout: None, |
|
||||||
write_timeout: None, |
|
||||||
channel_type: pnet_datalink::ChannelType::Layer2, |
|
||||||
bpf_fd_attempts: 1000, |
|
||||||
linux_fanout: None, |
|
||||||
promiscuous: false, |
|
||||||
}; |
|
||||||
let (mut _tx, mut rx) = match pnet_datalink::channel(&interface, config) { |
|
||||||
Ok(pnet_datalink::Channel::Ethernet(tx, rx)) => (tx, rx), |
|
||||||
Ok(_) => panic!("Unknown channel type"), |
|
||||||
Err(e) => panic!("Error happened {}", e), |
|
||||||
}; |
|
||||||
match send_udp_packet() { |
|
||||||
Ok(_) => (), |
|
||||||
Err(e) => return Err(format!("Failed to send UDP packet {}", e)), |
|
||||||
} |
|
||||||
receive_packets(&mut rx) |
|
||||||
} |
|
||||||
|
|
||||||
fn send_udp_packet() -> Result<(), String> { |
|
||||||
let buf = [0u8; 0]; |
|
||||||
let socket = match UdpSocket::bind("0.0.0.0:0") { |
|
||||||
Ok(s) => s, |
|
||||||
Err(e) => return Err(format!("Failed to create UDP socket {}", e)), |
|
||||||
}; |
|
||||||
let dst: &str = "1.1.1.1:80"; |
|
||||||
match socket.set_ttl(1) { |
|
||||||
Ok(_) => (), |
|
||||||
Err(e) => return Err(format!("Failed to set TTL {}", e)), |
|
||||||
} |
|
||||||
match socket.send_to(&buf, dst) { |
|
||||||
Ok(_) => (), |
|
||||||
Err(e) => return Err(format!("Failed to send data {}", e)), |
|
||||||
} |
|
||||||
Ok(()) |
|
||||||
} |
|
||||||
|
|
||||||
fn receive_packets(rx: &mut Box<dyn pnet_datalink::DataLinkReceiver>) -> Result<Gateway, String>{ |
|
||||||
let timeout = Duration::from_millis(TIMEOUT); |
|
||||||
let start_time = Instant::now(); |
|
||||||
loop { |
|
||||||
match rx.next() { |
|
||||||
Ok(frame) => { |
|
||||||
let frame = match pnet_packet::ethernet::EthernetPacket::new(frame) { |
|
||||||
Some(f) => f, |
|
||||||
None => return Err(String::from("Failed to read packet")), |
|
||||||
}; |
|
||||||
match frame.get_ethertype() { |
|
||||||
pnet_packet::ethernet::EtherTypes::Ipv4 => { |
|
||||||
if let Some(ip_addr) = ipv4_handler(&frame) { |
|
||||||
let gateway = Gateway { |
|
||||||
mac_addr: MacAddr::new(frame.get_source().octets()), |
|
||||||
ip_addr: ip_addr, |
|
||||||
}; |
|
||||||
return Ok(gateway); |
|
||||||
} |
|
||||||
}, |
|
||||||
pnet_packet::ethernet::EtherTypes::Ipv6 => { |
|
||||||
if let Some(ip_addr) = ipv6_handler(&frame) { |
|
||||||
let gateway = Gateway { |
|
||||||
mac_addr: MacAddr::new(frame.get_source().octets()), |
|
||||||
ip_addr: ip_addr, |
|
||||||
}; |
|
||||||
return Ok(gateway); |
|
||||||
} |
|
||||||
}, |
|
||||||
_ => {} |
|
||||||
} |
|
||||||
}, |
|
||||||
Err(e) => { |
|
||||||
return Err(format!("An error occurred while reading: {}", e)); |
|
||||||
} |
|
||||||
} |
|
||||||
if Instant::now().duration_since(start_time) > timeout { |
|
||||||
return Err(String::from("Recieve timeout")); |
|
||||||
}else{ |
|
||||||
match send_udp_packet() { |
|
||||||
Ok(_) => (), |
|
||||||
Err(e) => return Err(format!("Failed to send UDP packet {}", e)), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fn ipv4_handler(ethernet: &pnet_packet::ethernet::EthernetPacket) -> Option<IpAddr> { |
|
||||||
if let Some(packet) = pnet_packet::ipv4::Ipv4Packet::new(ethernet.payload()) { |
|
||||||
match packet.get_next_level_protocol() { |
|
||||||
pnet_packet::ip::IpNextHeaderProtocols::Icmp => { |
|
||||||
return icmp_handler(&packet); |
|
||||||
}, |
|
||||||
_ => { |
|
||||||
None |
|
||||||
} |
|
||||||
} |
|
||||||
}else{ |
|
||||||
None |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fn ipv6_handler(ethernet: &pnet_packet::ethernet::EthernetPacket) -> Option<IpAddr> { |
|
||||||
if let Some(packet) = pnet_packet::ipv6::Ipv6Packet::new(ethernet.payload()) { |
|
||||||
match packet.get_next_header() { |
|
||||||
pnet_packet::ip::IpNextHeaderProtocols::Icmpv6 => { |
|
||||||
return icmpv6_handler(&packet); |
|
||||||
}, |
|
||||||
_ => { |
|
||||||
None |
|
||||||
} |
|
||||||
} |
|
||||||
}else{ |
|
||||||
None |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fn icmp_handler(ip_packet: &pnet_packet::ipv4::Ipv4Packet) -> Option<IpAddr> { |
|
||||||
if let Some(packet) = pnet_packet::icmp::IcmpPacket::new(ip_packet.payload()) { |
|
||||||
if packet.get_icmp_type() == pnet_packet::icmp::IcmpTypes::TimeExceeded { |
|
||||||
let ipv4_addr = ip_packet.get_source(); |
|
||||||
return Some(IpAddr::V4(ipv4_addr)) |
|
||||||
}else{ |
|
||||||
None |
|
||||||
} |
|
||||||
}else{ |
|
||||||
None |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fn icmpv6_handler(ip_packet: &pnet_packet::ipv6::Ipv6Packet) -> Option<IpAddr> { |
|
||||||
if let Some(packet) = pnet_packet::icmpv6::Icmpv6Packet::new(ip_packet.payload()) { |
|
||||||
if packet.get_icmpv6_type() == pnet_packet::icmpv6::Icmpv6Types::TimeExceeded { |
|
||||||
let ipv6_addr = ip_packet.get_source(); |
|
||||||
return Some(IpAddr::V6(ipv6_addr)) |
|
||||||
}else{ |
|
||||||
None |
|
||||||
} |
|
||||||
}else{ |
|
||||||
None |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Get network interfaces
|
|
||||||
pub fn interfaces() -> Vec<Interface> { |
|
||||||
let mut result: Vec<Interface> = vec![]; |
|
||||||
let local_ip: IpAddr = match super::get_local_ipaddr(){ |
|
||||||
Some(local_ip) => local_ip, |
|
||||||
None => return result, |
|
||||||
}; |
|
||||||
let interfaces = pnet_datalink::interfaces(); |
|
||||||
for iface in interfaces{ |
|
||||||
let mac_addr: Option<MacAddr> = match iface.mac { |
|
||||||
Some(mac_addr) => Some(MacAddr::new(mac_addr.octets())), |
|
||||||
None => None, |
|
||||||
}; |
|
||||||
let mut ipv4_vec: Vec<Ipv4Addr> = vec![]; |
|
||||||
let mut ipv6_vec: Vec<Ipv6Addr> = vec![]; |
|
||||||
let mut ips: Vec<IpAddr> = vec![]; |
|
||||||
for ip in &iface.ips { |
|
||||||
match ip.ip() { |
|
||||||
IpAddr::V4(ipv4_addr) => { |
|
||||||
ipv4_vec.push(ipv4_addr); |
|
||||||
}, |
|
||||||
IpAddr::V6(ipv6_addr) => { |
|
||||||
ipv6_vec.push(ipv6_addr); |
|
||||||
}, |
|
||||||
} |
|
||||||
ips.push(ip.ip()); |
|
||||||
} |
|
||||||
let default_gateway: Option<Gateway> = if ips.contains(&local_ip) { |
|
||||||
match get_default_gateway(iface.index) { |
|
||||||
Ok(default_gateway) => Some(default_gateway), |
|
||||||
Err(_) => None, |
|
||||||
} |
|
||||||
} else{ |
|
||||||
None |
|
||||||
}; |
|
||||||
let desc: Option<String> = if iface.description.is_empty() { |
|
||||||
None |
|
||||||
} else{ |
|
||||||
Some(iface.description) |
|
||||||
}; |
|
||||||
let interface: Interface = Interface{ |
|
||||||
index: iface.index, |
|
||||||
name: iface.name, |
|
||||||
description: desc, |
|
||||||
mac_addr: mac_addr, |
|
||||||
ipv4: ipv4_vec, |
|
||||||
ipv6: ipv6_vec, |
|
||||||
gateway: default_gateway, |
|
||||||
}; |
|
||||||
result.push(interface); |
|
||||||
} |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
// Get default Interface index
|
|
||||||
pub fn default_interface_index() -> Option<u32> { |
|
||||||
let local_ip: IpAddr = match super::get_local_ipaddr(){ |
|
||||||
Some(local_ip) => local_ip, |
|
||||||
None => return None, |
|
||||||
}; |
|
||||||
let interfaces = pnet_datalink::interfaces(); |
|
||||||
for iface in interfaces { |
|
||||||
for ip in iface.ips { |
|
||||||
if local_ip == ip.ip() { |
|
||||||
return Some(iface.index) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return None; |
|
||||||
} |
|
||||||
|
|
||||||
// Get default Interface name
|
|
||||||
pub fn default_interface_name() -> Option<String> { |
|
||||||
let local_ip: IpAddr = match super::get_local_ipaddr(){ |
|
||||||
Some(local_ip) => local_ip, |
|
||||||
None => return None, |
|
||||||
}; |
|
||||||
let interfaces = pnet_datalink::interfaces(); |
|
||||||
for iface in interfaces { |
|
||||||
for ip in iface.ips { |
|
||||||
if local_ip == ip.ip() { |
|
||||||
return Some(iface.name) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return None; |
|
||||||
} |
|
@ -1,215 +0,0 @@ |
|||||||
use windows::Win32::Foundation::{ERROR_BUFFER_OVERFLOW, NO_ERROR}; |
|
||||||
use windows::Win32::NetworkManagement::IpHelper::{GetAdaptersInfo, IP_ADAPTER_INFO, IP_ADDR_STRING, SendARP}; |
|
||||||
use std::convert::TryInto; |
|
||||||
use std::mem; |
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; |
|
||||||
use std::ffi::CStr; |
|
||||||
use core::ffi::c_void; |
|
||||||
|
|
||||||
use crate::interface::{Interface, MacAddr}; |
|
||||||
use crate::gateway::Gateway; |
|
||||||
|
|
||||||
const EXCEPTION_INTERFACE_INDEX: u32 = 0; |
|
||||||
const EXCEPTION_INTERFACE_COMBOINDEX: u32 = 4928; |
|
||||||
|
|
||||||
// Convert C string to Rust string without trailing null bytes
|
|
||||||
fn bytes_to_string(bytes: &[u8]) -> String { |
|
||||||
let result: String = match CStr::from_bytes_with_nul(bytes) { |
|
||||||
Ok(cstr) => { |
|
||||||
match cstr.to_str() { |
|
||||||
Ok(rstr) => rstr.to_string(), |
|
||||||
Err(_) => cstr.to_string_lossy().replace("\u{0}", "").to_string(), |
|
||||||
}
|
|
||||||
}, |
|
||||||
Err(_) => { |
|
||||||
String::from_utf8_lossy(bytes).replace("\u{0}", "").to_string() |
|
||||||
} |
|
||||||
}; |
|
||||||
result |
|
||||||
} |
|
||||||
|
|
||||||
#[cfg(target_endian = "little")] |
|
||||||
fn htonl(val : u32) -> u32 { |
|
||||||
let o3 = (val >> 24) as u8; |
|
||||||
let o2 = (val >> 16) as u8; |
|
||||||
let o1 = (val >> 8) as u8; |
|
||||||
let o0 = val as u8; |
|
||||||
(o0 as u32) << 24 | (o1 as u32) << 16 | (o2 as u32) << 8 | (o3 as u32) |
|
||||||
} |
|
||||||
|
|
||||||
#[cfg(target_endian = "big")] |
|
||||||
fn htonl(val : u32) -> u32 { |
|
||||||
val |
|
||||||
} |
|
||||||
|
|
||||||
fn get_mac_through_arp(src_ip: Ipv4Addr, dst_ip: Ipv4Addr) -> MacAddr { |
|
||||||
let src_ip_int: u32 = htonl(u32::from(src_ip)); |
|
||||||
let dst_ip_int: u32 = htonl(u32::from(dst_ip)); |
|
||||||
let mut out_buf_len : u32 = 6; |
|
||||||
let mut target_mac_addr: [u8; 6] = [0; 6]; |
|
||||||
let res = unsafe { SendARP(dst_ip_int, src_ip_int, target_mac_addr.as_mut_ptr() as *mut c_void, &mut out_buf_len) }; |
|
||||||
if res == NO_ERROR { |
|
||||||
MacAddr::new(target_mac_addr) |
|
||||||
}else{ |
|
||||||
MacAddr::zero() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Get network interfaces using the IP Helper API
|
|
||||||
// TODO: Make more rusty ...
|
|
||||||
// Reference: https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersinfo
|
|
||||||
pub fn interfaces() -> Vec<Interface> { |
|
||||||
let mut interfaces: Vec<Interface> = vec![]; |
|
||||||
let mut out_buf_len : u32 = mem::size_of::<IP_ADAPTER_INFO>().try_into().unwrap(); |
|
||||||
let mut raw_adaptor_mem: Vec<u8> = Vec::with_capacity(out_buf_len as usize); |
|
||||||
let mut p_adaptor: *mut IP_ADAPTER_INFO; |
|
||||||
let mut res = unsafe { GetAdaptersInfo(raw_adaptor_mem.as_mut_ptr() as *mut IP_ADAPTER_INFO, &mut out_buf_len ) }; |
|
||||||
// Make an initial call to GetAdaptersInfo to get the necessary size into the out_buf_len variable
|
|
||||||
if res == ERROR_BUFFER_OVERFLOW { |
|
||||||
raw_adaptor_mem = Vec::with_capacity(out_buf_len as usize); |
|
||||||
unsafe { |
|
||||||
res = GetAdaptersInfo(raw_adaptor_mem.as_mut_ptr() as *mut IP_ADAPTER_INFO, &mut out_buf_len); |
|
||||||
} |
|
||||||
} |
|
||||||
if res != NO_ERROR { |
|
||||||
return interfaces; |
|
||||||
} |
|
||||||
//Enumerate all adapters
|
|
||||||
p_adaptor = unsafe { mem::transmute(&raw_adaptor_mem) }; |
|
||||||
while p_adaptor as u64 != 0 { |
|
||||||
let adapter: IP_ADAPTER_INFO = unsafe { *p_adaptor }; |
|
||||||
if adapter.Index == EXCEPTION_INTERFACE_INDEX || adapter.ComboIndex == EXCEPTION_INTERFACE_COMBOINDEX{ |
|
||||||
unsafe { p_adaptor = (*p_adaptor).Next; } |
|
||||||
continue; |
|
||||||
} |
|
||||||
let adapter_name: String = bytes_to_string(&adapter.AdapterName); |
|
||||||
let adapter_desc: String = bytes_to_string(&adapter.Description); |
|
||||||
let mac_addr:[u8; 6] = adapter.Address[..6].try_into().unwrap_or([0, 0, 0, 0, 0, 0]); |
|
||||||
//Enumerate all IPs
|
|
||||||
let mut ipv4_vec: Vec<Ipv4Addr> = vec![]; |
|
||||||
let mut ipv6_vec: Vec<Ipv6Addr> = vec![]; |
|
||||||
let mut p_ip_addr: *mut IP_ADDR_STRING; |
|
||||||
p_ip_addr = unsafe { mem::transmute(&(*p_adaptor).IpAddressList) }; |
|
||||||
while p_ip_addr as u64 != 0 { |
|
||||||
let ip_addr_string: IP_ADDR_STRING = unsafe{ *p_ip_addr }; |
|
||||||
let ip_addr: String = bytes_to_string(&ip_addr_string.IpAddress.String); |
|
||||||
match ip_addr.parse::<IpAddr>() { |
|
||||||
Ok(ip_addr) => { |
|
||||||
match ip_addr { |
|
||||||
IpAddr::V4(ipv4_addr) => { |
|
||||||
ipv4_vec.push(ipv4_addr); |
|
||||||
}, |
|
||||||
IpAddr::V6(ipv6_addr) => { |
|
||||||
ipv6_vec.push(ipv6_addr); |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
Err(_) => {}, |
|
||||||
} |
|
||||||
unsafe { p_ip_addr = (*p_ip_addr).Next; } |
|
||||||
} |
|
||||||
//Enumerate all gateways
|
|
||||||
let mut gateway_ips: Vec<IpAddr> = vec![]; |
|
||||||
let mut p_gateway_addr: *mut IP_ADDR_STRING; |
|
||||||
p_gateway_addr = unsafe { mem::transmute(&(*p_adaptor).GatewayList) }; |
|
||||||
while p_gateway_addr as u64 != 0 { |
|
||||||
let gateway_addr_string: IP_ADDR_STRING = unsafe { *p_gateway_addr }; |
|
||||||
let gateway_addr: String = bytes_to_string(&gateway_addr_string.IpAddress.String); |
|
||||||
match gateway_addr.parse::<IpAddr>() { |
|
||||||
Ok(ip_addr) => { |
|
||||||
gateway_ips.push(ip_addr); |
|
||||||
}, |
|
||||||
Err(_) => {}, |
|
||||||
} |
|
||||||
unsafe { p_gateway_addr = (*p_gateway_addr).Next; } |
|
||||||
} |
|
||||||
let default_gateway: Option<Gateway> = match gateway_ips.get(0) { |
|
||||||
Some(gateway_ip) => { |
|
||||||
let gateway_ip: IpAddr = *gateway_ip; |
|
||||||
let default_gateway: Option<Gateway> = if gateway_ip != IpAddr::V4(Ipv4Addr::UNSPECIFIED) { |
|
||||||
match gateway_ip { |
|
||||||
IpAddr::V4(dst_ip) => { |
|
||||||
if let Some(src_ip) = ipv4_vec.get(0) { |
|
||||||
let mac_addr = get_mac_through_arp(*src_ip, dst_ip); |
|
||||||
let gateway = Gateway { |
|
||||||
mac_addr: mac_addr, |
|
||||||
ip_addr: IpAddr::V4(dst_ip), |
|
||||||
}; |
|
||||||
Some(gateway) |
|
||||||
}else{ |
|
||||||
None |
|
||||||
} |
|
||||||
}, |
|
||||||
IpAddr::V6(_dst_ip) => { |
|
||||||
None |
|
||||||
}, |
|
||||||
} |
|
||||||
}else{ |
|
||||||
None |
|
||||||
}; |
|
||||||
default_gateway |
|
||||||
}, |
|
||||||
None => None, |
|
||||||
}; |
|
||||||
let interface: Interface = Interface{ |
|
||||||
index: adapter.Index, |
|
||||||
name: adapter_name, |
|
||||||
description: Some(adapter_desc), |
|
||||||
mac_addr: Some(MacAddr::new(mac_addr)), |
|
||||||
ipv4: ipv4_vec, |
|
||||||
ipv6: ipv6_vec, |
|
||||||
gateway: default_gateway, |
|
||||||
}; |
|
||||||
interfaces.push(interface); |
|
||||||
unsafe { p_adaptor = (*p_adaptor).Next; } |
|
||||||
} |
|
||||||
return interfaces; |
|
||||||
} |
|
||||||
|
|
||||||
// Get default Interface index
|
|
||||||
pub fn default_interface_index() -> Option<u32> { |
|
||||||
let local_ip: IpAddr = match super::get_local_ipaddr(){ |
|
||||||
Some(local_ip) => local_ip, |
|
||||||
None => return None, |
|
||||||
}; |
|
||||||
let interfaces = interfaces(); |
|
||||||
for iface in interfaces { |
|
||||||
match local_ip { |
|
||||||
IpAddr::V4(local_ipv4) => { |
|
||||||
if iface.ipv4.contains(&local_ipv4) { |
|
||||||
return Some(iface.index); |
|
||||||
} |
|
||||||
}, |
|
||||||
IpAddr::V6(local_ipv6) => { |
|
||||||
if iface.ipv6.contains(&local_ipv6) { |
|
||||||
return Some(iface.index); |
|
||||||
} |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
None |
|
||||||
} |
|
||||||
|
|
||||||
// Get default Interface name
|
|
||||||
pub fn default_interface_name() -> Option<String> { |
|
||||||
let local_ip: IpAddr = match super::get_local_ipaddr(){ |
|
||||||
Some(local_ip) => local_ip, |
|
||||||
None => return None, |
|
||||||
}; |
|
||||||
let interfaces = interfaces(); |
|
||||||
for iface in interfaces { |
|
||||||
match local_ip { |
|
||||||
IpAddr::V4(local_ipv4) => { |
|
||||||
if iface.ipv4.contains(&local_ipv4) { |
|
||||||
return Some(iface.name); |
|
||||||
} |
|
||||||
}, |
|
||||||
IpAddr::V6(local_ipv6) => { |
|
||||||
if iface.ipv6.contains(&local_ipv6) { |
|
||||||
return Some(iface.name); |
|
||||||
} |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
None |
|
||||||
} |
|
Loading…
Reference in new issue