From 97195bef06e3b96a867e90de0b28bb683f57fdcd Mon Sep 17 00:00:00 2001 From: shellrow <81893184+shellrow@users.noreply.github.com> Date: Sun, 16 Jan 2022 17:24:48 +0900 Subject: [PATCH] Linux support --- src/gateway/linux.rs | 82 +++++++++++++++++++++++++++++++++++-------- src/gateway/mod.rs | 21 ++++++++++- src/gateway/unix.rs | 23 ++---------- src/interface/mod.rs | 13 +++++++ src/interface/unix.rs | 4 +-- src/lib.rs | 4 +-- 6 files changed, 106 insertions(+), 41 deletions(-) diff --git a/src/gateway/linux.rs b/src/gateway/linux.rs index 27c78ff..793da7e 100644 --- a/src/gateway/linux.rs +++ b/src/gateway/linux.rs @@ -1,24 +1,76 @@ -use std::net::UdpSocket; +use std::net::{IpAddr, Ipv4Addr}; +use std::fs::read_to_string; +use crate::interface::MacAddr; use super::Gateway; -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)), +const PATH_PROC_NET_ROUTE: &str = "/proc/net/route"; +const PATH_PROC_NET_ARP: &str = "/proc/net/arp"; + +fn convert_hex_ipv4(hex_ip: &str) -> Ipv4Addr { + if hex_ip.len() != 8 { + return Ipv4Addr::UNSPECIFIED; } - match socket.send_to(&buf, dst) { - Ok(_) => (), - Err(e) => return Err(format!("Failed to send data {}", e)), + let o1: u8 = u8::from_str_radix(&hex_ip[6..8], 0x10).unwrap_or(0); + let o2: u8 = u8::from_str_radix(&hex_ip[4..6], 0x10).unwrap_or(0); + let o3: u8 = u8::from_str_radix(&hex_ip[2..4], 0x10).unwrap_or(0); + let o4: u8 = u8::from_str_radix(&hex_ip[0..2], 0x10).unwrap_or(0); + Ipv4Addr::new(o1, o2, o3, o4) +} + +#[allow(dead_code)] +fn convert_hex_ipv6(hex_ip: &str) -> Ipv6Addr { + if hex_ip.len() != 32 { + return Ipv6Addr::UNSPECIFIED; } - Ok(()) + let h1: u16 = u16::from_str_radix(&hex_ip[0..4], 0x10).unwrap_or(0); + let h2: u16 = u16::from_str_radix(&hex_ip[4..8], 0x10).unwrap_or(0); + let h3: u16 = u16::from_str_radix(&hex_ip[8..12], 0x10).unwrap_or(0); + let h4: u16 = u16::from_str_radix(&hex_ip[12..16], 0x10).unwrap_or(0); + let h5: u16 = u16::from_str_radix(&hex_ip[16..20], 0x10).unwrap_or(0); + let h6: u16 = u16::from_str_radix(&hex_ip[20..24], 0x10).unwrap_or(0); + let h7: u16 = u16::from_str_radix(&hex_ip[24..28], 0x10).unwrap_or(0); + let h8: u16 = u16::from_str_radix(&hex_ip[28..32], 0x10).unwrap_or(0); + Ipv6Addr::new(h1, h2, h3, h4, h5, h6, h7, h8) } pub fn get_default_gateway(interface_name: String) -> Result { + match super::send_udp_packet() { + Ok(_) => {}, + Err(e) => return Err(format!("Failed to send UDP packet {}", e)), + } + let route_data = read_to_string(PATH_PROC_NET_ROUTE); + let route_text = match route_data { + Ok(content) => content, + Err(_) => String::new(), + }; + let route_table: Vec<&str> = route_text.trim().split("\n").collect(); + let mut gateway_ip: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED); + for row in route_table { + let fields: Vec<&str> = row.split("\t").collect(); + if fields.len() >= 3 { + if fields[0] == interface_name && fields[2] != "00000000" { + gateway_ip = IpAddr::V4(convert_hex_ipv4(fields[2])); + } + } + } + let arp_data = read_to_string(PATH_PROC_NET_ARP); + let arp_text = match arp_data { + Ok(content) => content, + Err(_) => String::new(), + }; + let arp_table: Vec<&str> = arp_text.trim().split("\n").collect(); + for row in arp_table { + let mut fields: Vec<&str> = row.split(" ").collect(); + fields.retain(|value| *value != ""); + if fields.len() >= 6 { + if fields[0] == gateway_ip.to_string() && fields[5] == interface_name { + let gateway: Gateway = Gateway { + mac_addr: MacAddr::from_hex_format(fields[3]), + ip_addr: gateway_ip, + }; + return Ok(gateway); + } + } + } Err(String::new()) } diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index 432b31a..32897c4 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -4,6 +4,7 @@ pub mod unix; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod linux; +use std::net::UdpSocket; use std::net::{IpAddr, Ipv4Addr}; use crate::interface::{self, MacAddr, Interface}; @@ -18,7 +19,7 @@ impl Gateway { pub fn new() -> Gateway { Gateway { mac_addr: MacAddr::zero(), - ip_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), + ip_addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED), } } } @@ -51,6 +52,24 @@ pub fn get_default_gateway() -> Result { Err(String::from("Default Gateway not found")) } +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(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/gateway/unix.rs b/src/gateway/unix.rs index 04357a7..e852e0c 100644 --- a/src/gateway/unix.rs +++ b/src/gateway/unix.rs @@ -1,28 +1,9 @@ -use std::net::UdpSocket; use std::time::{Duration, Instant}; use crate::socket; use super::Gateway; const TIMEOUT: u64 = 3000; -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(()) -} - pub fn get_default_gateway(interface_name: String) -> Result { let timeout = Duration::from_millis(TIMEOUT); let start_time = Instant::now(); @@ -39,7 +20,7 @@ pub fn get_default_gateway(interface_name: String) -> Result { Ok(socket::Channel::Ethernet(tx, rx)) => (tx, rx), Err(e) => panic!("Failed to create channel {}", e), }; - match send_udp_packet() { + match super::send_udp_packet() { Ok(_) => (), Err(e) => return Err(format!("Failed to send UDP packet {}", e)), } @@ -58,7 +39,7 @@ pub fn get_default_gateway(interface_name: String) -> Result { if Instant::now().duration_since(start_time) > timeout { return Err(String::from("Recieve timeout")); }else{ - match send_udp_packet() { + match super::send_udp_packet() { Ok(_) => (), Err(e) => return Err(format!("Failed to send UDP packet {}", e)), } diff --git a/src/interface/mod.rs b/src/interface/mod.rs index aca9521..40f54c2 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -34,6 +34,19 @@ impl MacAddr { pub fn zero() -> MacAddr { MacAddr(0,0,0,0,0,0) } + pub fn from_hex_format(hex_mac_addr: &str) -> MacAddr { + if hex_mac_addr.len() != 17 { + return MacAddr(0,0,0,0,0,0) + } + let fields: Vec<&str> = hex_mac_addr.split(":").collect(); + let o1: u8 = u8::from_str_radix(&fields[0], 0x10).unwrap_or(0); + let o2: u8 = u8::from_str_radix(&fields[1], 0x10).unwrap_or(0); + let o3: u8 = u8::from_str_radix(&fields[2], 0x10).unwrap_or(0); + let o4: u8 = u8::from_str_radix(&fields[3], 0x10).unwrap_or(0); + let o5: u8 = u8::from_str_radix(&fields[4], 0x10).unwrap_or(0); + let o6: u8 = u8::from_str_radix(&fields[5], 0x10).unwrap_or(0); + MacAddr(o1, o2, o3, o4, o5, o6) + } } impl std::fmt::Display for MacAddr { diff --git a/src/interface/unix.rs b/src/interface/unix.rs index 3fd5bfe..e5773b4 100644 --- a/src/interface/unix.rs +++ b/src/interface/unix.rs @@ -55,7 +55,7 @@ pub fn interfaces() -> Vec { match local_ip { IpAddr::V4(local_ipv4) => { if iface.ipv4.contains(&local_ipv4) { - match gateway::unix::get_default_gateway(iface.name.clone()) { + match gateway::linux::get_default_gateway(iface.name.clone()) { Ok(gateway) => { iface.gateway = Some(gateway); }, @@ -65,7 +65,7 @@ pub fn interfaces() -> Vec { }, IpAddr::V6(local_ipv6) => { if iface.ipv6.contains(&local_ipv6) { - match gateway::unix::get_default_gateway(iface.name.clone()) { + match gateway::linux::get_default_gateway(iface.name.clone()) { Ok(gateway) => { iface.gateway = Some(gateway); }, diff --git a/src/lib.rs b/src/lib.rs index db28a1c..9e77641 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ #[cfg(not(target_os="windows"))] mod sys; -#[cfg(not(target_os="windows"))] +#[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "ios"))] mod bpf; -#[cfg(not(target_os="windows"))] +#[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "ios"))] mod socket; pub mod interface;