feat: added basic wrapper around libnl
This commit is contained in:
parent
da9aa2178c
commit
95b5f3ee82
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
target
|
target
|
||||||
examples/bind-shell/key-generator/pubkey
|
examples/bind-shell/key-generator/pubkey
|
||||||
examples/bind-shell/key-generator/privkey
|
examples/bind-shell/key-generator/privkey
|
||||||
|
core
|
||||||
|
**/core
|
||||||
@ -19,4 +19,7 @@
|
|||||||
|
|
||||||
int netlink_route() {
|
int netlink_route() {
|
||||||
return NETLINK_ROUTE;
|
return NETLINK_ROUTE;
|
||||||
|
}
|
||||||
|
int netlink_fib_lookup() {
|
||||||
|
return NETLINK_FIB_LOOKUP;
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ impl Display for Error {
|
|||||||
std::str::from_utf8(error_msg_ptr.to_bytes()).unwrap()
|
std::str::from_utf8(error_msg_ptr.to_bytes()).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, "nternal libnl error: {error_msg_utf8}")
|
write!(f, "internal libnl error: {error_msg_utf8}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
98
nl-sys/src/lib.rs
Normal file
98
nl-sys/src/lib.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// 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::net::Ipv4Addr;
|
||||||
|
|
||||||
|
pub mod nl_ffi;
|
||||||
|
pub mod netlink;
|
||||||
|
pub mod route;
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
|
// from bridge.c
|
||||||
|
extern "C" {
|
||||||
|
pub(crate) fn netlink_route() -> libc::c_int;
|
||||||
|
pub(crate) fn netlink_fib_lookup() -> libc::c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fn main() -> error::Result<()> {
|
||||||
|
let sock = netlink::Socket::new(netlink::SocketType::Routing)?;
|
||||||
|
let lookup_sock = netlink::Socket::new(netlink::SocketType::Lookup)?;
|
||||||
|
|
||||||
|
let links = sock.get_links()?;
|
||||||
|
let routes = sock.get_routes()?;
|
||||||
|
|
||||||
|
let target_ip = "1.1.1.1".parse::<Ipv4Addr>().unwrap();
|
||||||
|
let target_ip_num = u32::from(target_ip);
|
||||||
|
|
||||||
|
let mut routes2 = routes.iter().collect::<Vec<_>>();
|
||||||
|
println!("Routes: {}", routes2.len());
|
||||||
|
routes2.sort_by(|r1, r2| {
|
||||||
|
r2.dst().map(|a| a.cidrlen())
|
||||||
|
.partial_cmp(&r1.dst().map(|a| a.cidrlen()))
|
||||||
|
.unwrap_or(std::cmp::Ordering::Equal)
|
||||||
|
});
|
||||||
|
|
||||||
|
let routes3 = routes2.iter().filter(|route| {
|
||||||
|
let Some(dst) = route.dst() else { return false };
|
||||||
|
|
||||||
|
let mask = if dst.cidrlen() != 0 {
|
||||||
|
(0xFFFFFFFFu32.overflowing_shr(32 - dst.cidrlen())).0.overflowing_shl(32 - dst.cidrlen()).0
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(dst_addr): Result<Ipv4Addr, _> = dst.try_into() else { return false };
|
||||||
|
let dst_addr = u32::from(dst_addr);
|
||||||
|
|
||||||
|
(mask & dst_addr) == (mask & target_ip_num)
|
||||||
|
});
|
||||||
|
|
||||||
|
for route in routes3 {
|
||||||
|
let link = netlink::get_link_by_index(&links, route.ifindex());
|
||||||
|
println!(
|
||||||
|
"route: src: {:?}, dst: {:?}, link: {}, {:?}",
|
||||||
|
route.src().map(|s| (s.hw_address(), s.cidrlen())),
|
||||||
|
route.dst().map(|s| (s.hw_address(), s.cidrlen())),
|
||||||
|
route.ifindex(),
|
||||||
|
link.map(|l| l.name())
|
||||||
|
);
|
||||||
|
|
||||||
|
for hops in route.hop_iter() {
|
||||||
|
println!(
|
||||||
|
"\tgateway: {:?}, ifindex: {}",
|
||||||
|
hops.gateway().map(|g| g.hw_address()),
|
||||||
|
hops.ifindex()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let src_ip = route::Addr::from("172.17.0.1".parse::<Ipv4Addr>().unwrap());
|
||||||
|
|
||||||
|
let neighs = sock.get_neigh()?;
|
||||||
|
|
||||||
|
let target_neigh = route::get_neigh_for_addr(&neighs, &links, &src_ip);
|
||||||
|
|
||||||
|
if let Some((link, neigh)) = target_neigh {
|
||||||
|
println!(
|
||||||
|
"link: {}; src mac: {:?}; src ip: {:?}; dst mac: {:?}",
|
||||||
|
link.name(),
|
||||||
|
link.addr().hw_address(),
|
||||||
|
neigh.dst().hw_address(),
|
||||||
|
neigh.lladdr().hw_address()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} */
|
||||||
@ -1,36 +0,0 @@
|
|||||||
// 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/>.
|
|
||||||
|
|
||||||
mod nl_ffi;
|
|
||||||
mod netlink;
|
|
||||||
mod route;
|
|
||||||
mod error;
|
|
||||||
|
|
||||||
// from bridge.c
|
|
||||||
extern "C" {
|
|
||||||
pub(crate) fn netlink_route() -> libc::c_int;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> error::Result<()> {
|
|
||||||
let sock = netlink::Socket::new()?;
|
|
||||||
|
|
||||||
let links = sock.get_links()?;
|
|
||||||
|
|
||||||
for link in links.iter() {
|
|
||||||
println!("Link: {}", link.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@ -15,16 +15,30 @@
|
|||||||
|
|
||||||
use std::{ptr, marker::PhantomData};
|
use std::{ptr, marker::PhantomData};
|
||||||
|
|
||||||
use libc::AF_UNSPEC;
|
use libc::{AF_UNSPEC, AF_INET};
|
||||||
|
|
||||||
use crate::{nl_ffi::*, error};
|
use crate::{nl_ffi::*, error, route::{Link, Neigh, Route}};
|
||||||
|
|
||||||
pub struct Socket {
|
pub struct Socket {
|
||||||
sock: *mut nl_sock
|
pub(crate) sock: *mut nl_sock
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SocketType {
|
||||||
|
Routing,
|
||||||
|
Lookup
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SocketType {
|
||||||
|
pub fn to_proto(&self) -> i32 {
|
||||||
|
unsafe { match self {
|
||||||
|
Self::Routing => crate::netlink_route(),
|
||||||
|
Self::Lookup => crate::netlink_fib_lookup()
|
||||||
|
} }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Socket {
|
impl Socket {
|
||||||
pub fn new() -> error::Result<Self> {
|
pub fn new(stype: SocketType) -> error::Result<Self> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let sock = Socket { sock: nl_socket_alloc() };
|
let sock = Socket { sock: nl_socket_alloc() };
|
||||||
|
|
||||||
@ -37,7 +51,7 @@ impl Socket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_links(&self) -> error::Result<Cache<crate::route::Link>> {
|
pub fn get_links(&self) -> error::Result<Cache<Link>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut link_cache = ptr::null_mut::<nl_cache>();
|
let mut link_cache = ptr::null_mut::<nl_cache>();
|
||||||
|
|
||||||
@ -53,17 +67,70 @@ impl Socket {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_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 })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Cache<T>
|
pub struct Cache<T>
|
||||||
where
|
where
|
||||||
T: From<*mut nl_object>
|
T: From<*mut nl_object>
|
||||||
{
|
{
|
||||||
cache: *mut nl_cache,
|
pub(crate) cache: *mut nl_cache,
|
||||||
dt: PhantomData<T>
|
dt: PhantomData<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: From<*mut nl_object>> Cache<T> {
|
impl<T: From<*mut nl_object>> Cache<T> {
|
||||||
|
pub(crate) fn new(cache: *mut nl_cache) -> Cache<T> {
|
||||||
|
Cache {
|
||||||
|
cache,
|
||||||
|
dt: PhantomData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> CacheIter<'_, T> {
|
pub fn iter(&self) -> CacheIter<'_, T> {
|
||||||
let cache_size = unsafe {
|
let cache_size = unsafe {
|
||||||
nl_cache_nitems(self.cache)
|
nl_cache_nitems(self.cache)
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use libc::{c_int, c_void, c_char};
|
use libc::{c_int, c_void, c_char, c_uint};
|
||||||
|
|
||||||
macro_rules! nl_obj {
|
macro_rules! nl_obj {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
@ -28,9 +28,14 @@ macro_rules! nl_obj {
|
|||||||
|
|
||||||
nl_obj!(nl_sock);
|
nl_obj!(nl_sock);
|
||||||
nl_obj!(nl_cache);
|
nl_obj!(nl_cache);
|
||||||
nl_obj!(rtnl_link);
|
|
||||||
nl_obj!(nl_addr);
|
nl_obj!(nl_addr);
|
||||||
nl_obj!(nl_object);
|
nl_obj!(nl_object);
|
||||||
|
nl_obj!(nl_list_head);
|
||||||
|
nl_obj!(rtnl_link);
|
||||||
|
nl_obj!(rtnl_neigh);
|
||||||
|
nl_obj!(rtnl_route);
|
||||||
|
nl_obj!(rtnl_nexthop);
|
||||||
|
nl_obj!(flnl_request);
|
||||||
|
|
||||||
// from libnl and libnl-route
|
// from libnl and libnl-route
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -38,17 +43,44 @@ extern "C" {
|
|||||||
pub fn nl_socket_free(sock: *mut nl_sock);
|
pub fn nl_socket_free(sock: *mut nl_sock);
|
||||||
pub fn nl_socket_get_local_port(sock: *const nl_sock) -> u32;
|
pub fn nl_socket_get_local_port(sock: *const nl_sock) -> u32;
|
||||||
pub fn nl_connect(sock: *mut nl_sock, protocol: c_int) -> c_int;
|
pub fn nl_connect(sock: *mut nl_sock, protocol: c_int) -> c_int;
|
||||||
|
|
||||||
pub fn nl_geterror(error: c_int) -> *const c_char;
|
pub fn nl_geterror(error: c_int) -> *const c_char;
|
||||||
|
|
||||||
|
pub fn nl_object_put(obj: *mut nl_object) -> c_void;
|
||||||
|
|
||||||
|
pub fn nl_addr_get_len(addr: *mut nl_addr) -> c_uint;
|
||||||
|
pub fn nl_addr_get_binary_addr(addr: *mut nl_addr) -> *mut c_void;
|
||||||
|
pub fn nl_addr_parse(addrstr: *const i8, hint: c_int, result: *mut *mut nl_addr) -> c_int;
|
||||||
|
pub fn nl_addr_put(addr: *mut nl_addr) -> c_void;
|
||||||
|
pub fn nl_addr_get_family(addr: *mut nl_addr) -> c_int;
|
||||||
|
pub fn nl_addr_get_prefixlen(addr: *mut nl_addr) -> c_uint;
|
||||||
|
|
||||||
pub fn nl_cache_foreach(cache: *mut nl_cache, cb: extern "C" fn(*mut nl_object, *mut c_void), arg: *mut c_void) -> c_void;
|
pub fn nl_cache_foreach(cache: *mut nl_cache, cb: extern "C" fn(*mut nl_object, *mut c_void), arg: *mut c_void) -> c_void;
|
||||||
pub fn nl_cache_put(cache: *mut nl_cache) -> c_void;
|
pub fn nl_cache_put(cache: *mut nl_cache) -> c_void;
|
||||||
pub fn nl_cache_nitems(cache: *mut nl_cache) -> c_int;
|
pub fn nl_cache_nitems(cache: *mut nl_cache) -> c_int;
|
||||||
pub fn nl_cache_get_first(cache: *mut nl_cache) -> *mut nl_object;
|
pub fn nl_cache_get_first(cache: *mut nl_cache) -> *mut nl_object;
|
||||||
pub fn nl_cache_get_next(obj: *mut nl_object) -> *mut nl_object;
|
pub fn nl_cache_get_next(obj: *mut nl_object) -> *mut nl_object;
|
||||||
|
pub fn nl_cache_destroy_and_free(obj: *mut nl_cache) -> c_void;
|
||||||
|
|
||||||
|
pub fn rtnl_neigh_alloc_cache(sock: *mut nl_sock, result: *mut *mut nl_cache) -> c_int;
|
||||||
|
pub fn rtnl_neigh_get(cache: *mut nl_cache, ifindex: c_int, dst: *mut nl_addr) -> *mut rtnl_neigh;
|
||||||
|
pub fn rtnl_neigh_get_dst(neigh: *mut rtnl_neigh) -> *mut nl_addr;
|
||||||
|
pub fn rtnl_neigh_get_lladdr(neigh: *mut rtnl_neigh) -> *mut nl_addr;
|
||||||
|
|
||||||
|
pub fn rtnl_link_get(cache: *mut nl_cache, index: c_int) -> *mut rtnl_link;
|
||||||
pub fn rtnl_link_alloc_cache(sock: *mut nl_sock, family: c_int, result: *mut *mut nl_cache) -> c_int;
|
pub fn rtnl_link_alloc_cache(sock: *mut nl_sock, family: c_int, result: *mut *mut nl_cache) -> c_int;
|
||||||
pub fn rtnl_link_get_by_name(cache: *mut nl_cache, name: *const c_char) -> *mut rtnl_link;
|
|
||||||
pub fn rtnl_link_get_addr(link: *mut rtnl_link) -> *mut nl_addr;
|
pub fn rtnl_link_get_addr(link: *mut rtnl_link) -> *mut nl_addr;
|
||||||
pub fn rtnl_link_get_name(link: *mut rtnl_link) -> *const c_char;
|
pub fn rtnl_link_get_name(link: *mut rtnl_link) -> *const c_char;
|
||||||
|
pub fn rtnl_link_get_ifindex(link: *mut rtnl_link) -> c_int;
|
||||||
|
pub fn rtnl_link_get_type(link: *mut rtnl_link) -> *const c_char;
|
||||||
|
|
||||||
|
pub fn rtnl_route_alloc_cache(sock: *mut nl_sock, family: c_int, flags: c_int, result: *mut *mut nl_cache) -> c_int;
|
||||||
|
pub fn rtnl_route_get_src(route: *mut rtnl_route) -> *mut nl_addr;
|
||||||
|
pub fn rtnl_route_get_dst(route: *mut rtnl_route) -> *mut nl_addr;
|
||||||
|
pub fn rtnl_route_get_iif(route: *mut rtnl_route) -> c_int;
|
||||||
|
pub fn rtnl_route_get_pref_src(route: *mut rtnl_route) -> *mut nl_addr;
|
||||||
|
pub fn rtnl_route_get_nnexthops(route: *mut rtnl_route) -> c_int;
|
||||||
|
pub fn rtnl_route_nexthop_n(route: *mut rtnl_route, ind: c_int) -> *mut rtnl_nexthop;
|
||||||
|
|
||||||
|
pub fn rtnl_route_nh_get_gateway(hop: *mut rtnl_nexthop) -> *mut nl_addr;
|
||||||
|
pub fn rtnl_route_nh_get_ifindex(hop: *mut rtnl_nexthop) -> c_int;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,12 +13,16 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::ffi::CStr;
|
use std::{ffi::{CStr, CString}, net::Ipv4Addr};
|
||||||
|
|
||||||
|
use libc::{c_int, AF_INET, AF_UNIX, AF_UNSPEC, c_uint};
|
||||||
|
|
||||||
|
use crate::{error, netlink::{Cache, Socket}};
|
||||||
|
|
||||||
use super::nl_ffi::*;
|
use super::nl_ffi::*;
|
||||||
|
|
||||||
pub struct Link {
|
pub struct Link {
|
||||||
link: *mut rtnl_link
|
pub(crate) link: *mut rtnl_link
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Link {
|
impl Link {
|
||||||
@ -29,6 +33,41 @@ impl Link {
|
|||||||
std::str::from_utf8(name_rs.to_bytes()).unwrap().to_owned()
|
std::str::from_utf8(name_rs.to_bytes()).unwrap().to_owned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn addr(&self) -> Addr {
|
||||||
|
unsafe {
|
||||||
|
Addr { addr: rtnl_link_get_addr(self.link) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ltype(&self) -> Option<String> {
|
||||||
|
unsafe {
|
||||||
|
let ltype = rtnl_link_get_type(self.link);
|
||||||
|
if ltype.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let ltype_rs = CStr::from_ptr(ltype);
|
||||||
|
Some(std::str::from_utf8(ltype_rs.to_bytes()).unwrap().to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ifindex(&self) -> c_int {
|
||||||
|
unsafe {
|
||||||
|
rtnl_link_get_ifindex(self.link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_neigh(&self, neigh_table: &Cache<Neigh>, addr: &Addr) -> Option<Neigh> {
|
||||||
|
unsafe {
|
||||||
|
let neigh = rtnl_neigh_get(neigh_table.cache, self.ifindex(), addr.addr);
|
||||||
|
|
||||||
|
if neigh.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Neigh { neigh })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<*mut nl_object> for Link {
|
impl From<*mut nl_object> for Link {
|
||||||
@ -37,4 +76,253 @@ impl From<*mut nl_object> for Link {
|
|||||||
link: value as *mut _
|
link: value as *mut _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_neigh_for_addr(neighs: &Cache<Neigh>, links: &Cache<Link>, addr: &Addr) -> Option<(Link, Neigh)> {
|
||||||
|
for link in links.iter() {
|
||||||
|
let Some(neigh) = link.get_neigh(&neighs, addr) else { continue; };
|
||||||
|
return Some((link, neigh));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Neigh {
|
||||||
|
neigh: *mut rtnl_neigh
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neigh {
|
||||||
|
pub fn dst(&self) -> Addr {
|
||||||
|
unsafe {
|
||||||
|
let addr = rtnl_neigh_get_dst(self.neigh);
|
||||||
|
Addr { addr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lladdr(&self) -> Addr {
|
||||||
|
unsafe {
|
||||||
|
let addr = rtnl_neigh_get_lladdr(self.neigh);
|
||||||
|
Addr { addr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<*mut nl_object> for Neigh {
|
||||||
|
fn from(value: *mut nl_object) -> Self {
|
||||||
|
Self {
|
||||||
|
neigh: value as *mut _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Addr {
|
||||||
|
addr: *mut nl_addr
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Addr {
|
||||||
|
pub fn len(&self) -> u32 {
|
||||||
|
unsafe {
|
||||||
|
nl_addr_get_len(self.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hw_address(&self) -> Vec<u8> {
|
||||||
|
unsafe {
|
||||||
|
let hw_address_ptr = nl_addr_get_binary_addr(self.addr) as *const u8;
|
||||||
|
let hw_address_slice = std::slice::from_raw_parts(hw_address_ptr, self.len() as usize);
|
||||||
|
|
||||||
|
hw_address_slice.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn atype(&self) -> c_int {
|
||||||
|
unsafe {
|
||||||
|
nl_addr_get_family(self.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cidrlen(&self) -> c_uint {
|
||||||
|
unsafe {
|
||||||
|
nl_addr_get_prefixlen(self.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Ipv4Addr> for Addr {
|
||||||
|
fn from(value: Ipv4Addr) -> Self {
|
||||||
|
unsafe {
|
||||||
|
let mut addr = std::ptr::null_mut::<nl_addr>();
|
||||||
|
let value = CString::new(format!("{value}")).unwrap();
|
||||||
|
|
||||||
|
// we can ignore the return code because it is guaranteed to not be invalid
|
||||||
|
nl_addr_parse(value.as_ptr(), AF_INET, &mut addr as *mut _);
|
||||||
|
|
||||||
|
Addr { addr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Addr> for Ipv4Addr {
|
||||||
|
type Error = error::Error;
|
||||||
|
|
||||||
|
fn try_from(value: Addr) -> Result<Self, Self::Error> {
|
||||||
|
if value.len() != 4 {
|
||||||
|
return Err(error::Error::new(15 /* NL_AF_MISMATCH */));
|
||||||
|
}
|
||||||
|
|
||||||
|
let addr = value.hw_address();
|
||||||
|
Ok(Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Route {
|
||||||
|
route: *mut rtnl_route
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Route {
|
||||||
|
pub fn src(&self) -> Option<Addr> {
|
||||||
|
unsafe {
|
||||||
|
let addr = rtnl_route_get_src(self.route);
|
||||||
|
|
||||||
|
if addr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Addr { addr })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dst(&self) -> Option<Addr> {
|
||||||
|
unsafe {
|
||||||
|
let addr = rtnl_route_get_dst(self.route);
|
||||||
|
|
||||||
|
if addr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Addr { addr })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ifindex(&self) -> c_int {
|
||||||
|
unsafe {
|
||||||
|
rtnl_route_get_iif(self.route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nexthop_len(&self) -> c_int {
|
||||||
|
unsafe {
|
||||||
|
rtnl_route_get_nnexthops(self.route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nexthop(&self, ind: i32) -> Option<Nexthop> {
|
||||||
|
unsafe {
|
||||||
|
let nexthop = rtnl_route_nexthop_n(self.route, ind);
|
||||||
|
if nexthop.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Nexthop { nexthop })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hop_iter(&self) -> NexthopIter<'_> {
|
||||||
|
NexthopIter { route: &self, index: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<*mut nl_object> for Route {
|
||||||
|
fn from(value: *mut nl_object) -> Self {
|
||||||
|
Route {
|
||||||
|
route: value as *mut _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Nexthop {
|
||||||
|
nexthop: *mut rtnl_nexthop
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Nexthop {
|
||||||
|
pub fn gateway(&self) -> Option<Addr> {
|
||||||
|
unsafe {
|
||||||
|
let addr = rtnl_route_nh_get_gateway(self.nexthop);
|
||||||
|
|
||||||
|
if addr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Addr { addr })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ifindex(&self) -> i32 {
|
||||||
|
unsafe {
|
||||||
|
rtnl_route_nh_get_ifindex(self.nexthop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NexthopIter<'a> {
|
||||||
|
route: &'a Route,
|
||||||
|
index: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for NexthopIter<'_> {
|
||||||
|
type Item = Nexthop;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let next = self.route.nexthop(self.index);
|
||||||
|
|
||||||
|
if next.is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.index += 1;
|
||||||
|
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
(self.route.nexthop_len() as usize, Some(self.route.nexthop_len() as usize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_route_for_ip(routes: &Cache<Route>, ip: Ipv4Addr) -> Option<Ipv4Addr> {
|
||||||
|
let mut sorted_routes = routes.iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
sorted_routes.sort_by(|r1, r2| {
|
||||||
|
r2.dst().map(|a| a.cidrlen())
|
||||||
|
.partial_cmp(&r1.dst().map(|a| a.cidrlen()))
|
||||||
|
.unwrap_or(std::cmp::Ordering::Equal)
|
||||||
|
});
|
||||||
|
|
||||||
|
let ip_int = u32::from(ip);
|
||||||
|
|
||||||
|
sorted_routes
|
||||||
|
.iter()
|
||||||
|
.find(|route| {
|
||||||
|
let Some(dst) = route.dst() else { return false };
|
||||||
|
|
||||||
|
let mask = if dst.cidrlen() != 0 {
|
||||||
|
(0xFFFFFFFFu32.overflowing_shr(32 - dst.cidrlen())).0.overflowing_shl(32 - dst.cidrlen()).0
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(dst_addr): Result<Ipv4Addr, _> = dst.try_into() else { return false };
|
||||||
|
let dst_addr: u32 = dst_addr.into();
|
||||||
|
|
||||||
|
(mask & dst_addr) == (mask & ip_int)
|
||||||
|
})
|
||||||
|
.map(|route| {
|
||||||
|
route
|
||||||
|
.hop_iter()
|
||||||
|
.next()
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.map(|hop| hop.gateway())
|
||||||
|
.flatten()
|
||||||
|
.map(|gateway| gateway.try_into().ok())
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user