From 95b5f3ee82e2a8c54c64a13bfed883b7aca8e63b Mon Sep 17 00:00:00 2001 From: Andrew Rioux Date: Mon, 1 May 2023 05:56:04 -0400 Subject: [PATCH] feat: added basic wrapper around libnl --- .gitignore | 4 +- core | Bin 286720 -> 0 bytes nl-sys/src/bridge.c | 3 + nl-sys/src/error.rs | 2 +- nl-sys/src/lib.rs | 98 ++++++++++++++ nl-sys/src/main.rs | 36 ------ nl-sys/src/netlink.rs | 79 +++++++++++- nl-sys/src/nl_ffi.rs | 40 +++++- nl-sys/src/route.rs | 292 +++++++++++++++++++++++++++++++++++++++++- 9 files changed, 504 insertions(+), 50 deletions(-) delete mode 100644 core create mode 100644 nl-sys/src/lib.rs delete mode 100644 nl-sys/src/main.rs diff --git a/.gitignore b/.gitignore index 2f4d92b..56c0441 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ target examples/bind-shell/key-generator/pubkey -examples/bind-shell/key-generator/privkey \ No newline at end of file +examples/bind-shell/key-generator/privkey +core +**/core \ No newline at end of file diff --git a/core b/core deleted file mode 100644 index ba7a4fe8446a7b984ab8d1b2b4b8741ead78dac6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286720 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 F005fxMsxrG diff --git a/nl-sys/src/bridge.c b/nl-sys/src/bridge.c index c03b2f5..7cabed6 100644 --- a/nl-sys/src/bridge.c +++ b/nl-sys/src/bridge.c @@ -19,4 +19,7 @@ int netlink_route() { return NETLINK_ROUTE; +} +int netlink_fib_lookup() { + return NETLINK_FIB_LOOKUP; } \ No newline at end of file diff --git a/nl-sys/src/error.rs b/nl-sys/src/error.rs index c0ede71..3215f7c 100644 --- a/nl-sys/src/error.rs +++ b/nl-sys/src/error.rs @@ -39,7 +39,7 @@ impl Display for Error { 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}") } } diff --git a/nl-sys/src/lib.rs b/nl-sys/src/lib.rs new file mode 100644 index 0000000..9335930 --- /dev/null +++ b/nl-sys/src/lib.rs @@ -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 . + +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::().unwrap(); + let target_ip_num = u32::from(target_ip); + + let mut routes2 = routes.iter().collect::>(); + 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 = 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::().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(()) +} */ \ No newline at end of file diff --git a/nl-sys/src/main.rs b/nl-sys/src/main.rs deleted file mode 100644 index 291e85b..0000000 --- a/nl-sys/src/main.rs +++ /dev/null @@ -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 . - -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(()) -} \ No newline at end of file diff --git a/nl-sys/src/netlink.rs b/nl-sys/src/netlink.rs index 7291edd..dc8817d 100644 --- a/nl-sys/src/netlink.rs +++ b/nl-sys/src/netlink.rs @@ -15,16 +15,30 @@ 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 { - 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 { - pub fn new() -> error::Result { + pub fn new(stype: SocketType) -> error::Result { unsafe { let sock = Socket { sock: nl_socket_alloc() }; @@ -37,7 +51,7 @@ impl Socket { } } - pub fn get_links(&self) -> error::Result> { + pub fn get_links(&self) -> error::Result> { unsafe { let mut link_cache = ptr::null_mut::(); @@ -53,17 +67,70 @@ impl Socket { }) } } + + pub fn get_neigh(&self) -> error::Result> { + unsafe { + let mut neigh_cache = ptr::null_mut::(); + + 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> { + unsafe { + let mut route_cache = ptr::null_mut::(); + + 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, index: i32) -> Option { + unsafe { + let link = rtnl_link_get(cache.cache, index); + + if link.is_null() { + return None; + } + + Some(Link { link }) + } } pub struct Cache where T: From<*mut nl_object> { - cache: *mut nl_cache, + pub(crate) cache: *mut nl_cache, dt: PhantomData } impl> Cache { + pub(crate) fn new(cache: *mut nl_cache) -> Cache { + Cache { + cache, + dt: PhantomData + } + } + pub fn iter(&self) -> CacheIter<'_, T> { let cache_size = unsafe { nl_cache_nitems(self.cache) diff --git a/nl-sys/src/nl_ffi.rs b/nl-sys/src/nl_ffi.rs index e9df0a9..330a97d 100644 --- a/nl-sys/src/nl_ffi.rs +++ b/nl-sys/src/nl_ffi.rs @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use libc::{c_int, c_void, c_char}; +use libc::{c_int, c_void, c_char, c_uint}; macro_rules! nl_obj { ($name:ident) => { @@ -28,9 +28,14 @@ macro_rules! nl_obj { nl_obj!(nl_sock); nl_obj!(nl_cache); -nl_obj!(rtnl_link); nl_obj!(nl_addr); 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 extern "C" { @@ -38,17 +43,44 @@ extern "C" { pub fn nl_socket_free(sock: *mut nl_sock); 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_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_put(cache: *mut nl_cache) -> c_void; 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_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_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_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; } diff --git a/nl-sys/src/route.rs b/nl-sys/src/route.rs index c20d5bf..29575c2 100644 --- a/nl-sys/src/route.rs +++ b/nl-sys/src/route.rs @@ -13,12 +13,16 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -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::*; pub struct Link { - link: *mut rtnl_link + pub(crate) link: *mut rtnl_link } impl Link { @@ -29,6 +33,41 @@ impl Link { 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 { + 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, addr: &Addr) -> Option { + 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 { @@ -37,4 +76,253 @@ impl From<*mut nl_object> for Link { link: value as *mut _ } } +} + +pub fn get_neigh_for_addr(neighs: &Cache, links: &Cache, 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 { + 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 for Addr { + fn from(value: Ipv4Addr) -> Self { + unsafe { + let mut addr = std::ptr::null_mut::(); + 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 for Ipv4Addr { + type Error = error::Error; + + fn try_from(value: Addr) -> Result { + 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 { + unsafe { + let addr = rtnl_route_get_src(self.route); + + if addr.is_null() { + return None; + } + + Some(Addr { addr }) + } + } + + pub fn dst(&self) -> Option { + 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 { + 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 { + 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 { + let next = self.route.nexthop(self.index); + + if next.is_none() { + return None; + } + + self.index += 1; + + next + } + + fn size_hint(&self) -> (usize, Option) { + (self.route.nexthop_len() as usize, Some(self.route.nexthop_len() as usize)) + } +} + +pub fn get_route_for_ip(routes: &Cache, ip: Ipv4Addr) -> Option { + let mut sorted_routes = routes.iter().collect::>(); + + 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 = 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() } \ No newline at end of file