From ad4bf965874b3480cddcd1b2fe8d0558994e6975 Mon Sep 17 00:00:00 2001 From: shellrow <81893184+shellrow@users.noreply.github.com> Date: Sat, 29 Jan 2022 16:10:14 +0900 Subject: [PATCH] Add Ipv4Net and Ipv6Net struct (#2) Fields: addr, prefix_len, and netmask --- src/gateway/mod.rs | 4 +- src/interface/mod.rs | 23 ++++++----- src/interface/unix.rs | 58 ++++++++++++++++++++++---- src/ip.rs | 95 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 5 files changed, 160 insertions(+), 21 deletions(-) create mode 100644 src/ip.rs diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index d8c90ad..989e402 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -33,14 +33,14 @@ pub fn get_default_gateway() -> Result { for iface in interfaces { match local_ip { IpAddr::V4(local_ipv4) => { - if iface.ipv4.contains(&local_ipv4) { + if iface.ipv4.iter().any(|x| x.addr == local_ipv4) { if let Some(gateway) = iface.gateway { return Ok(gateway); } } }, IpAddr::V6(local_ipv6) => { - if iface.ipv6.contains(&local_ipv6) { + if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { if let Some(gateway) = iface.gateway { return Ok(gateway); } diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 1e176e8..5ac4bfe 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -11,7 +11,8 @@ mod windows; #[cfg(target_os = "windows")] use self::windows::*; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::net::IpAddr; +use crate::ip::{Ipv4Net, Ipv6Net}; use crate::gateway::{Gateway}; /// Structure of MAC address @@ -71,10 +72,10 @@ pub struct Interface { pub description: Option, /// MAC address of network interface pub mac_addr: Option, - /// List of IPv4 addresses for the network interface - pub ipv4: Vec, - /// List of IPv6 addresses for the network interface - pub ipv6: Vec, + /// List of Ipv4Net for the network interface + pub ipv4: Vec, + /// List of Ipv6Net for the network interface + pub ipv6: Vec, /// Default gateway for the network interface pub gateway: Option, } @@ -89,12 +90,12 @@ pub fn get_default_interface() -> Result { for iface in interfaces { match local_ip { IpAddr::V4(local_ipv4) => { - if iface.ipv4.contains(&local_ipv4) { + if iface.ipv4.iter().any(|x| x.addr == local_ipv4) { return Ok(iface); } }, IpAddr::V6(local_ipv6) => { - if iface.ipv6.contains(&local_ipv6) { + if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { return Ok(iface); } }, @@ -113,12 +114,12 @@ pub fn get_default_interface_index() -> Option { for iface in interfaces { match local_ip { IpAddr::V4(local_ipv4) => { - if iface.ipv4.contains(&local_ipv4) { + if iface.ipv4.iter().any(|x| x.addr == local_ipv4) { return Some(iface.index); } }, IpAddr::V6(local_ipv6) => { - if iface.ipv6.contains(&local_ipv6) { + if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { return Some(iface.index); } }, @@ -137,12 +138,12 @@ pub fn get_default_interface_name() -> Option { for iface in interfaces { match local_ip { IpAddr::V4(local_ipv4) => { - if iface.ipv4.contains(&local_ipv4) { + if iface.ipv4.iter().any(|x| x.addr == local_ipv4) { return Some(iface.name); } }, IpAddr::V6(local_ipv6) => { - if iface.ipv6.contains(&local_ipv6) { + if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { return Some(iface.name); } }, diff --git a/src/interface/unix.rs b/src/interface/unix.rs index e5773b4..b7ec31b 100644 --- a/src/interface/unix.rs +++ b/src/interface/unix.rs @@ -2,6 +2,7 @@ use super::Interface; use super::MacAddr; use crate::sys; use crate::gateway; +use crate::ip::{Ipv4Net, Ipv6Net}; use libc; use std::ffi::{CStr, CString}; @@ -20,7 +21,7 @@ pub fn interfaces() -> Vec { for iface in &mut interfaces { match local_ip { IpAddr::V4(local_ipv4) => { - if iface.ipv4.contains(&local_ipv4) { + if iface.ipv4.iter().any(|x| x.addr == local_ipv4) { match gateway::unix::get_default_gateway(iface.name.clone()) { Ok(gateway) => { iface.gateway = Some(gateway); @@ -30,7 +31,7 @@ pub fn interfaces() -> Vec { } }, IpAddr::V6(local_ipv6) => { - if iface.ipv6.contains(&local_ipv6) { + if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { match gateway::unix::get_default_gateway(iface.name.clone()) { Ok(gateway) => { iface.gateway = Some(gateway); @@ -92,15 +93,36 @@ pub fn unix_interfaces() -> Vec { let bytes = unsafe { CStr::from_ptr(c_str).to_bytes() }; let name = unsafe {from_utf8_unchecked(bytes).to_owned() }; let (mac, ip) = sockaddr_to_network_addr(addr_ref.ifa_addr as *const libc::sockaddr); - let mut ini_ipv4: Vec = vec![]; - let mut ini_ipv6: Vec = vec![]; + let (_, netmask) = sockaddr_to_network_addr(addr_ref.ifa_netmask as *const libc::sockaddr); + let mut ini_ipv4: Vec = vec![]; + let mut ini_ipv6: Vec = vec![]; if let Some(ip) = ip { match ip { IpAddr::V4(ipv4) => { - ini_ipv4.push(ipv4); + let netmask: Ipv4Addr = match netmask { + Some(netmask) => { + match netmask { + IpAddr::V4(netmask) => netmask, + IpAddr::V6(_) => Ipv4Addr::UNSPECIFIED, + } + }, + None => Ipv4Addr::UNSPECIFIED, + }; + let ipv4_net: Ipv4Net = Ipv4Net::new_with_netmask(ipv4, netmask); + ini_ipv4.push(ipv4_net); }, IpAddr::V6(ipv6) => { - ini_ipv6.push(ipv6); + let netmask: Ipv6Addr = match netmask { + Some(netmask) => { + match netmask { + IpAddr::V4(_) => Ipv6Addr::UNSPECIFIED, + IpAddr::V6(netmask) => netmask, + } + }, + None => Ipv6Addr::UNSPECIFIED, + }; + let ipv6_net: Ipv6Net = Ipv6Net::new_with_netmask(ipv6, netmask); + ini_ipv6.push(ipv6_net); }, } } @@ -122,10 +144,30 @@ pub fn unix_interfaces() -> Vec { if let Some(ip) = ip { match ip { IpAddr::V4(ipv4) => { - iface.ipv4.push(ipv4); + let netmask: Ipv4Addr = match netmask { + Some(netmask) => { + match netmask { + IpAddr::V4(netmask) => netmask, + IpAddr::V6(_) => Ipv4Addr::UNSPECIFIED, + } + }, + None => Ipv4Addr::UNSPECIFIED, + }; + let ipv4_net: Ipv4Net = Ipv4Net::new_with_netmask(ipv4, netmask); + iface.ipv4.push(ipv4_net); }, IpAddr::V6(ipv6) => { - iface.ipv6.push(ipv6); + let netmask: Ipv6Addr = match netmask { + Some(netmask) => { + match netmask { + IpAddr::V4(_) => Ipv6Addr::UNSPECIFIED, + IpAddr::V6(netmask) => netmask, + } + }, + None => Ipv6Addr::UNSPECIFIED, + }; + let ipv6_net: Ipv6Net = Ipv6Net::new_with_netmask(ipv6, netmask); + iface.ipv6.push(ipv6_net); }, } } diff --git a/src/ip.rs b/src/ip.rs new file mode 100644 index 0000000..566cf19 --- /dev/null +++ b/src/ip.rs @@ -0,0 +1,95 @@ +use std::net::{Ipv4Addr, Ipv6Addr}; + +#[derive(Clone, Debug)] +pub struct Ipv4Net { + pub addr: Ipv4Addr, + pub prefix_len: u8, + pub netmask: Ipv4Addr, +} + +impl Ipv4Net { + pub fn new(ipv4_addr: Ipv4Addr, prefix_len: u8) -> Ipv4Net { + Ipv4Net { + addr: ipv4_addr, + prefix_len: prefix_len, + netmask: prefix_to_ipv4_netmask(prefix_len), + } + } + pub fn new_with_netmask(ipv4_addr: Ipv4Addr, netmask: Ipv4Addr) -> Ipv4Net { + Ipv4Net { + addr: ipv4_addr, + prefix_len: ipv4_netmask_to_prefix(netmask), + netmask: netmask, + } + } +} + +#[derive(Clone, Debug)] +pub struct Ipv6Net { + pub addr: Ipv6Addr, + pub prefix_len: u8, + pub netmask: Ipv6Addr, +} + +impl Ipv6Net { + pub fn new(ipv6_addr: Ipv6Addr, prefix_len: u8) -> Ipv6Net { + Ipv6Net { + addr: ipv6_addr, + prefix_len: prefix_len, + netmask: prefix_to_ipv6_netmask(prefix_len), + } + } + pub fn new_with_netmask(ipv6_addr: Ipv6Addr, netmask: Ipv6Addr) -> Ipv6Net { + Ipv6Net { + addr: ipv6_addr, + prefix_len: ipv6_netmask_to_prefix(netmask), + netmask: netmask, + } + } +} + +fn ipv4_netmask_to_prefix(netmask: Ipv4Addr) -> u8 { + let netmask = u32::from(netmask); + let prefix = (!netmask).leading_zeros() as u8; + if (u64::from(netmask) << prefix) & 0xffff_ffff != 0 { + 0 + } else { + prefix + } +} + +fn ipv6_netmask_to_prefix(netmask: Ipv6Addr) -> u8 { + let netmask = netmask.segments(); + let mut mask_iter = netmask.iter(); + let mut prefix = 0; + for &segment in &mut mask_iter { + if segment == 0xffff { + prefix += 16; + } else if segment == 0 { + break; + } else { + let prefix_bits = (!segment).leading_zeros() as u8; + if segment << prefix_bits != 0 { + return 0; + } + prefix += prefix_bits; + break; + } + } + for &segment in mask_iter { + if segment != 0 { + return 0; + } + } + prefix +} + +fn prefix_to_ipv4_netmask(prefix_len: u8) -> Ipv4Addr { + let netmask_u32: u32 = u32::max_value().checked_shl(32 - prefix_len as u32).unwrap_or(0); + Ipv4Addr::from(netmask_u32) +} + +fn prefix_to_ipv6_netmask(prefix_len: u8) -> Ipv6Addr { + let netmask_u128: u128 = u128::max_value().checked_shl((128 - prefix_len) as u32).unwrap_or(u128::min_value()); + Ipv6Addr::from(netmask_u128) +} diff --git a/src/lib.rs b/src/lib.rs index 9e77641..0000396 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ mod bpf; #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "ios"))] mod socket; +pub mod ip; pub mod interface; pub mod gateway;