use std::{
io,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
};
use hardware_address::xtoi2;
use rustix::net::AddressFamily;
use smallvec_wrapper::{SmallVec, TinyVec};
use smol_str::SmolStr;
use super::{
IfAddr, IfNet, Ifv4Addr, Ifv4Net, Ifv6Addr, Ifv6Net, Interface, MacAddr, Net, MAC_ADDRESS_SIZE,
};
pub(super) use local_addr::*;
#[path = "linux/netlink.rs"]
mod netlink;
#[path = "linux/local_addr.rs"]
mod local_addr;
use netlink::{netlink_addr, netlink_interface};
macro_rules! rt_generic_mod {
($($name:ident($rta:expr, $rtn:expr)), +$(,)?) => {
$(
paste::paste! {
pub(super) use [< rt_ $name >]::*;
mod [< rt_ $name >] {
use rustix::net::AddressFamily;
use smallvec_wrapper::SmallVec;
use std::{
io,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
};
use crate::{ipv4_filter_to_ip_filter, ipv6_filter_to_ip_filter};
use super::{
super::{IfAddr, Ifv4Addr, Ifv6Addr},
netlink::rt_generic_addrs,
};
pub(crate) fn [< $name _addrs >]() -> io::Result<SmallVec<IfAddr>> {
rt_generic_addrs(AddressFamily::UNSPEC, $rta, $rtn, |_| true)
}
pub(crate) fn [< $name _ipv4_addrs >]() -> io::Result<SmallVec<Ifv4Addr>> {
rt_generic_addrs(AddressFamily::INET, $rta, $rtn, |_| true)
}
pub(crate) fn [< $name _ipv6_addrs >]() -> io::Result<SmallVec<Ifv6Addr>> {
rt_generic_addrs(AddressFamily::INET6, $rta, $rtn, |_| true)
}
pub(crate) fn [< $name _addrs_by_filter >]<F>(f: F) -> io::Result<SmallVec<IfAddr>>
where
F: FnMut(&IpAddr) -> bool,
{
rt_generic_addrs(AddressFamily::UNSPEC, $rta, $rtn, f)
}
pub(crate) fn [< $name _ipv4_addrs_by_filter >]<F>(f: F) -> io::Result<SmallVec<Ifv4Addr>>
where
F: FnMut(&Ipv4Addr) -> bool,
{
rt_generic_addrs(AddressFamily::INET, $rta, $rtn, ipv4_filter_to_ip_filter(f))
}
pub(crate) fn [< $name _ipv6_addrs_by_filter >]<F>(f: F) -> io::Result<SmallVec<Ifv6Addr>>
where
F: FnMut(&Ipv6Addr) -> bool,
{
rt_generic_addrs(AddressFamily::INET6, $rta, $rtn, ipv6_filter_to_ip_filter(f))
}
}
}
)*
};
}
rt_generic_mod!(gateway(
linux_raw_sys::netlink::rtattr_type_t::RTA_GATEWAY as u16,
None
),);
impl Interface {
#[inline]
fn new(index: u32, flags: Flags) -> Self {
Self {
index,
mtu: 0,
name: SmolStr::default(),
mac_addr: None,
flags,
}
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Flags: u32 {
const UP = 0x1;
const BROADCAST = 0x2;
const DEBUG = 0x4;
const LOOPBACK = 0x8;
const POINTOPOINT = 0x10;
const NOTRAILERS = 0x20;
const RUNNING = 0x40;
const NOARP = 0x80;
const PROMISC = 0x100;
const ALLMULTI = 0x200;
const MASTER = 0x400;
const SLAVE = 0x800;
const MULTICAST = 0x1000;
const PORTSEL = 0x2000;
const AUTOMEDIA = 0x4000;
const DYNAMIC = 0x8000;
}
}
pub(super) fn interface_table(index: u32) -> io::Result<TinyVec<Interface>> {
netlink_interface(AddressFamily::UNSPEC, index)
}
pub(super) fn interface_ipv4_addresses<F>(index: u32, f: F) -> io::Result<SmallVec<Ifv4Net>>
where
F: FnMut(&IpAddr) -> bool,
{
netlink_addr(AddressFamily::INET, index, f)
}
pub(super) fn interface_ipv6_addresses<F>(index: u32, f: F) -> io::Result<SmallVec<Ifv6Net>>
where
F: FnMut(&IpAddr) -> bool,
{
netlink_addr(AddressFamily::INET6, index, f)
}
pub(super) fn interface_addresses<F>(index: u32, f: F) -> io::Result<SmallVec<IfNet>>
where
F: FnMut(&IpAddr) -> bool,
{
netlink_addr(AddressFamily::UNSPEC, index, f)
}
const IGMP_PATH: &str = "/proc/net/igmp";
const IGMP6_PATH: &str = "/proc/net/igmp6";
pub(super) fn interface_multicast_ipv4_addresses<F>(
ifi: u32,
f: F,
) -> io::Result<SmallVec<Ifv4Addr>>
where
F: FnMut(&Ipv4Addr) -> bool,
{
parse_proc_net_igmp(IGMP_PATH, ifi, f)
}
pub(super) fn interface_multicast_ipv6_addresses<F>(
ifi: u32,
f: F,
) -> io::Result<SmallVec<Ifv6Addr>>
where
F: FnMut(&Ipv6Addr) -> bool,
{
parse_proc_net_igmp6(IGMP6_PATH, ifi, f)
}
pub(super) fn interface_multicast_addresses<F>(ifi: u32, mut f: F) -> io::Result<SmallVec<IfAddr>>
where
F: FnMut(&IpAddr) -> bool,
{
let ifmat4 = parse_proc_net_igmp("/proc/net/igmp", ifi, |addr| f(&(*addr).into()))?;
let ifmat6 = parse_proc_net_igmp6("/proc/net/igmp6", ifi, |addr| f(&(*addr).into()))?;
Ok(
ifmat4
.into_iter()
.map(From::from)
.chain(ifmat6.into_iter().map(From::from))
.collect(),
)
}
fn parse_proc_net_igmp<F>(path: &str, ifi: u32, mut f: F) -> std::io::Result<SmallVec<Ifv4Addr>>
where
F: FnMut(&Ipv4Addr) -> bool,
{
use std::io::BufRead;
let file = std::fs::File::open(path)?;
let reader = std::io::BufReader::new(file);
let mut ifmat = SmallVec::new();
let mut idx = 0;
let mut lines = reader.lines();
lines.next();
for line in lines {
let line = line?;
let mut it = line.split_ascii_whitespace();
let field0 = match it.next() {
Some(s) => s,
None => continue,
};
if it.nth(2).is_none() {
continue;
}
if !line.starts_with(' ') && !line.starts_with('\t') {
match field0.parse() {
Ok(res) => idx = res,
Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)),
}
} else if field0.len() == 8 {
if ifi == 0 || ifi == idx {
let src = field0.as_bytes();
let mut b = [0u8; 4];
for i in (0..src.len()).step_by(2) {
b[i / 2] = xtoi2(&src[i..i + 2], 0).unwrap_or(0);
}
b.reverse();
let ip = b.into();
if f(&ip) {
ifmat.push(Ifv4Addr::new(idx, ip));
}
}
}
}
Ok(ifmat)
}
fn parse_proc_net_igmp6<F>(path: &str, ifi: u32, mut f: F) -> io::Result<SmallVec<Ifv6Addr>>
where
F: FnMut(&Ipv6Addr) -> bool,
{
use std::io::BufRead;
let file = std::fs::File::open(path)?;
let reader = std::io::BufReader::new(file);
let mut ifmat = SmallVec::new();
for line in reader.lines() {
let line = line?;
let mut it = line.split_ascii_whitespace();
let field0 = match it.next() {
Some(s) => s,
None => continue,
};
if it.next().is_none() {
continue;
}
let field2 = match it.next() {
Some(s) => s,
None => continue,
};
if it.nth(2).is_none() {
continue;
}
let idx = match field0.parse() {
Ok(res) => res,
Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)),
};
if ifi == 0 || ifi == idx {
let mut i = 0;
let src = field2.as_bytes();
let mut data = [0u8; 16];
while i + 1 < src.len() {
data[i / 2] = xtoi2(&src[i..i + 2], 0).unwrap_or(0);
i += 2;
}
let ip = data.into();
if f(&ip) {
ifmat.push(Ifv6Addr::new(idx, ip));
}
}
}
Ok(ifmat)
}