sparse-v2/nl-sys/src/netlink.rs
2025-01-20 14:40:29 -05:00

203 lines
5.0 KiB
Rust

// Copyright (C) 2023 Andrew Rioux
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use std::{marker::PhantomData, ptr};
use libc::{AF_INET, AF_UNSPEC};
use crate::{
error,
nl_ffi::*,
route::{Link, Neigh, Route, RtAddr},
};
/// A netlink socket used to communicate with the kernel
pub struct Socket {
pub(crate) sock: *mut nl_sock,
}
impl Socket {
/// Establish a new connection with the Linux kernel
pub fn new() -> error::Result<Self> {
unsafe {
let sock = Socket {
sock: nl_socket_alloc(),
};
let ret = nl_connect(sock.sock, crate::netlink_route());
if ret < 0 {
return Err(error::Error::new(ret));
}
Ok(sock)
}
}
pub fn get_links(&self) -> error::Result<Cache<Link>> {
unsafe {
let mut link_cache = ptr::null_mut::<nl_cache>();
let ret = rtnl_link_alloc_cache(self.sock, AF_UNSPEC, &mut link_cache as *mut _);
if ret < 0 {
return Err(error::Error::new(ret));
}
Ok(Cache {
cache: link_cache,
dt: PhantomData,
})
}
}
pub fn get_neigh(&self) -> error::Result<Cache<Neigh>> {
unsafe {
let mut neigh_cache = ptr::null_mut::<nl_cache>();
let ret = rtnl_neigh_alloc_cache(self.sock, &mut neigh_cache as *mut _);
if ret < 0 {
return Err(error::Error::new(ret));
}
Ok(Cache {
cache: neigh_cache,
dt: PhantomData,
})
}
}
pub fn get_routes(&self) -> error::Result<Cache<Route>> {
unsafe {
let mut route_cache = ptr::null_mut::<nl_cache>();
let ret = rtnl_route_alloc_cache(self.sock, AF_INET, 0, &mut route_cache as *mut _);
if ret < 0 {
return Err(error::Error::new(ret));
}
Ok(Cache {
cache: route_cache,
dt: PhantomData,
})
}
}
pub fn get_addrs(&self) -> error::Result<Cache<RtAddr>> {
unsafe {
let mut addr_cache = ptr::null_mut::<nl_cache>();
let ret = rtnl_addr_alloc_cache(self.sock, &mut addr_cache as *mut _);
if ret < 0 {
return Err(error::Error::new(ret));
}
Ok(Cache {
cache: addr_cache,
dt: PhantomData,
})
}
}
}
impl Drop for Socket {
fn drop(&mut self) {
unsafe {
nl_close(self.sock);
}
}
}
/// Tries to get a link by the specified ifindex
pub fn get_link_by_index(cache: &Cache<Link>, index: i32) -> Option<Link> {
unsafe {
let link = rtnl_link_get(cache.cache, index);
if link.is_null() {
return None;
}
Some(Link { link })
}
}
/// Represents the nl_cache in the libnl library, which is itself a general
/// collection of nl_objects
pub struct Cache<T>
where
T: From<*mut nl_object>,
{
pub(crate) cache: *mut nl_cache,
dt: PhantomData<T>,
}
impl<T: From<*mut nl_object>> Cache<T> {
pub fn iter(&self) -> CacheIter<'_, T> {
let cache_size = unsafe { nl_cache_nitems(self.cache) } as usize;
CacheIter {
obj: unsafe { nl_cache_get_first(self.cache) },
cache_size,
index: 0,
item_type: PhantomData {},
}
}
}
impl<T: From<*mut nl_object>> Drop for Cache<T> {
fn drop(&mut self) {
unsafe {
nl_cache_put(self.cache);
}
}
}
/// Iterates over caches and provides an easy way to work with them
pub struct CacheIter<'a, T> {
obj: *mut nl_object,
cache_size: usize,
index: usize,
item_type: PhantomData<&'a T>,
}
impl<T: From<*mut nl_object>> Iterator for CacheIter<'_, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.index >= self.cache_size {
return None;
}
self.index += 1;
let obj = self.obj;
self.obj = unsafe { nl_cache_get_next(obj) };
if obj.is_null() {
continue;
}
break Some(T::from(obj));
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.cache_size, Some(self.cache_size))
}
}