]> code.octet-stream.net Git - netwatcher/blob - src/watch_linux.rs
efc3842482207998e40f36e868e259af4bc44c72
[netwatcher] / src / watch_linux.rs
1 use std::os::fd::AsRawFd;
2 use std::os::fd::OwnedFd;
3
4 use nix::libc::RTMGRP_IPV4_IFADDR;
5 use nix::libc::RTMGRP_IPV6_IFADDR;
6 use nix::libc::RTMGRP_LINK;
7 use nix::sys::socket::bind;
8 use nix::sys::socket::recv;
9 use nix::sys::socket::socket;
10 use nix::sys::socket::AddressFamily;
11 use nix::sys::socket::MsgFlags;
12 use nix::sys::socket::NetlinkAddr;
13 use nix::sys::socket::SockFlag;
14 use nix::sys::socket::SockProtocol;
15 use nix::sys::socket::SockType;
16
17 use crate::Error;
18 use crate::List;
19 use crate::Update;
20
21 pub(crate) struct WatchHandle {
22 // PROBLEM: close() doesn't cancel recv() for a netlink socket
23 // SOLUTION: open a pipe() and use poll() inside the thread to watch for cancellation too
24 sockfd: OwnedFd,
25 }
26
27 pub(crate) fn watch_interfaces<F: FnMut(Update) + Send + 'static>(
28 callback: F,
29 ) -> Result<WatchHandle, Error> {
30 let sockfd = start_watcher_thread(callback)?;
31 Ok(WatchHandle { sockfd })
32 }
33
34 fn start_watcher_thread<F: FnMut(Update) + Send + 'static>(mut callback: F) -> Result<OwnedFd, Error> {
35 let sockfd = socket(AddressFamily::Netlink, SockType::Raw, SockFlag::empty(), Some(SockProtocol::NetlinkRoute))
36 .map_err(|_| Error::Internal)?; // TODO: proper errors
37 let sa_nl = NetlinkAddr::new(0, (RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR) as u32);
38 bind(sockfd.as_raw_fd(), &sa_nl).map_err(|_| Error::Internal)?; // TODO: proper errors
39 let fd = sockfd.as_raw_fd();
40 println!("netlink socket on fd {}", fd);
41
42 std::thread::spawn(move || {
43 println!("watch thread running");
44 let mut prev_list = List::default();
45 let mut buf = [0u8; 4096];
46 let mut handle_update = move |new_list: List| {
47 if new_list == prev_list {
48 return;
49 }
50 let update = Update {
51 interfaces: new_list.0.clone(),
52 diff: new_list.diff_from(&prev_list),
53 };
54 (callback)(update);
55 prev_list = new_list;
56 };
57
58 if let Ok(initial) = crate::list::list_interfaces() {
59 handle_update(initial);
60 };
61
62 while let Ok(n) = recv(fd, &mut buf, MsgFlags::empty()) {
63 println!("something on the netlink socket: {} bytes", n);
64 let Ok(new_list) = crate::list::list_interfaces() else {
65 continue;
66 };
67 handle_update(new_list);
68 }
69 println!("netlink recv thread terminating");
70 });
71
72 Ok(sockfd)
73 }