From: Thomas Karpiniec Date: Sat, 8 Jun 2024 15:59:24 +0000 (+1000) Subject: Groundwork for watching interface changes on Linux X-Git-Tag: v0.1.0~17 X-Git-Url: https://code.octet-stream.net/netwatcher/commitdiff_plain/7cbddf0b21f7cf98b46c66a430eb8221f3622d55?ds=sidebyside;hp=244781f32194c2498ef0e3b8225a9ff9791624af Groundwork for watching interface changes on Linux --- diff --git a/src/list_unix.rs b/src/list_unix.rs index a6b2bd3..45ffc63 100644 --- a/src/list_unix.rs +++ b/src/list_unix.rs @@ -1,11 +1,9 @@ use std::fmt::Write; use std::{collections::HashMap, net::IpAddr}; -use block2::Block; -use nix::libc::c_long; use nix::{ifaddrs::getifaddrs, net::if_::if_nametoindex}; -use crate::{Error, IfIndex, Interface}; +use crate::{Error, Interface, List}; struct CandidateInterface { name: String, @@ -14,7 +12,7 @@ struct CandidateInterface { ips: Vec, } -pub(crate) fn list_interfaces() -> Result, Error> { +pub(crate) fn list_interfaces() -> Result { let addrs = getifaddrs().map_err(|_| Error::Internal)?; let mut candidates = HashMap::new(); @@ -59,7 +57,7 @@ pub(crate) fn list_interfaces() -> Result, Error> { }) }) .collect(); - Ok(ifs) + Ok(List(ifs)) } fn format_mac(bytes: &[u8]) -> Result { diff --git a/src/watch_linux.rs b/src/watch_linux.rs index a987919..4e4049b 100644 --- a/src/watch_linux.rs +++ b/src/watch_linux.rs @@ -1,13 +1,57 @@ +use std::os::fd::AsRawFd; +use std::os::fd::OwnedFd; + +use nix::libc::nlmsghdr; +use nix::libc::RTMGRP_IPV4_IFADDR; +use nix::libc::RTMGRP_IPV6_IFADDR; +use nix::libc::RTMGRP_LINK; +use nix::sys::socket::bind; +use nix::sys::socket::recv; +use nix::sys::socket::socket; +use nix::sys::socket::AddressFamily; +use nix::sys::socket::MsgFlags; +use nix::sys::socket::NetlinkAddr; +use nix::sys::socket::SockFlag; +use nix::sys::socket::SockProtocol; +use nix::sys::socket::SockType; + use crate::Error; use crate::Update; -pub(crate) struct WatchHandle; +pub(crate) struct WatchHandle { + // PROBLEM: close() doesn't cancel recv() for a netlink socket + sockfd: OwnedFd, +} pub(crate) fn watch_interfaces( callback: F, ) -> Result { - // stop current worker thread - // post this into a thread that will use it - drop(callback); - Ok(WatchHandle) + let sockfd = start_watcher_thread(callback)?; + Ok(WatchHandle { sockfd }) +} + +fn start_watcher_thread(callback: F) -> Result { + let sockfd = socket(AddressFamily::Netlink, SockType::Raw, SockFlag::empty(), Some(SockProtocol::NetlinkRoute)) + .map_err(|_| Error::Internal)?; // TODO: proper errors + let sa_nl = NetlinkAddr::new(0, (RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR) as u32); + bind(sockfd.as_raw_fd(), &sa_nl).map_err(|_| Error::Internal)?; // TODO: proper errors + let fd = sockfd.as_raw_fd(); + println!("netlink socket on fd {}", fd); + + std::thread::spawn(move || { + println!("watch thread running"); + let mut buf = [0u8; 4096]; + // recvmsg? + while let Ok(n) = recv(fd, &mut buf, MsgFlags::empty()) { + println!("something on the netlink socket: {} bytes", n); + let nlmsg_ptr = &buf as *const _ as *const nlmsghdr; + let nlmsg = unsafe { &*nlmsg_ptr }; + // Right conventionally there's some trick here involving macros NLMSG_OK + // I can presumably do this using NetlinkGeneric too + // It's unclear whether this is worse or not - need to know what those macros do + } + println!("netlink recv thread terminating"); + }); + + Ok(sockfd) }