netwatcher 0.6.0

List network interfaces and watch for changes efficiently
Documentation
use std::os::fd::{AsRawFd, BorrowedFd, OwnedFd};

use nix::errno::Errno;
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;

pub(crate) use crate::watch_fd::{AsyncWatch, BlockingWatch, WatchHandle};
use crate::Error;
use crate::Update;

const EVENT_SOCKET_OPS: crate::watch_fd::EventSocketOps = crate::watch_fd::EventSocketOps {
    open: open_event_socket,
    drain: drain_event_socket,
};

const RTMGRP_IPV4_IFADDR: u32 = 0x10;
const RTMGRP_IPV6_IFADDR: u32 = 0x20;
const RTMGRP_LINK: u32 = 0x01;

pub(crate) fn watch_interfaces_with_callback<F: FnMut(Update) + Send + 'static>(
    callback: F,
) -> Result<WatchHandle, Error> {
    crate::watch_fd::watch_interfaces_with_callback(callback, EVENT_SOCKET_OPS)
}

pub(crate) fn watch_interfaces_async<A: crate::async_adapter::AsyncFdAdapter>(
) -> Result<AsyncWatch, Error> {
    crate::watch_fd::watch_interfaces_async::<A>(EVENT_SOCKET_OPS)
}

pub(crate) fn watch_interfaces_blocking() -> Result<BlockingWatch, Error> {
    crate::watch_fd::watch_interfaces_blocking(EVENT_SOCKET_OPS)
}

pub(crate) fn open_event_socket() -> Result<OwnedFd, Error> {
    let sockfd = socket(
        AddressFamily::Netlink,
        SockType::Raw,
        SockFlag::SOCK_NONBLOCK,
        Some(SockProtocol::NetlinkRoute),
    )
    .map_err(|e| Error::CreateSocket(e.to_string()))?;
    let sa_nl = NetlinkAddr::new(0, RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR);
    bind(sockfd.as_raw_fd(), &sa_nl).map_err(|e| Error::Bind(e.to_string()))?;
    Ok(sockfd)
}

pub(crate) fn drain_event_socket(fd: BorrowedFd<'_>) {
    let mut buf = [0u8; 4096];
    loop {
        match recv(fd.as_raw_fd(), &mut buf, MsgFlags::empty()) {
            Ok(0) => break,
            Ok(_) => continue,
            Err(Errno::EAGAIN) => break,
            Err(_) => break,
        }
    }
}