fix: fixed weird issues with querying routes

this just involved a better understanding of the data
types provided by libnl and some refactoring to make querying
as a user of the libnl library easier
This commit is contained in:
Andrew Rioux
2023-05-01 09:15:15 -04:00
parent be5772fa23
commit c16bf366b7
10 changed files with 285 additions and 28 deletions

View File

@@ -66,6 +66,7 @@ extern "C" {
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_neigh_get_ifindex(neigh: *mut rtnl_neigh) -> c_int;
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;

View File

@@ -13,11 +13,11 @@
// 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::{ffi::{CStr, CString}, net::Ipv4Addr};
use std::{ffi::{CStr, CString}, net::Ipv4Addr, fmt::Debug};
use libc::{c_int, AF_INET, c_uint};
use crate::{error, netlink::Cache};
use crate::{error, netlink::{Cache, self}};
use super::nl_ffi::*;
@@ -66,7 +66,7 @@ impl Link {
/// Tries to get the neighbor for this link, which can provide the destination address and the
/// link layer address (lladdr)
pub fn get_neigh(&self, neigh_table: &Cache<Neigh>, addr: &Addr) -> Option<Neigh> {
pub fn get_neigh(&self, neigh_table: &Cache<Neigh>, addr: &Addr) -> Option<[u8; 6]> {
unsafe {
let neigh = rtnl_neigh_get(neigh_table.cache, self.ifindex(), addr.addr);
@@ -74,11 +74,20 @@ impl Link {
return None;
}
Some(Neigh { neigh })
Neigh { neigh }.lladdr().hw_address().try_into().ok()
}
}
}
impl Debug for Link {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f
.debug_struct("Link")
.field("name", &self.name())
.finish()
}
}
impl From<*mut nl_object> for Link {
fn from(value: *mut nl_object) -> Self {
Self {
@@ -87,16 +96,50 @@ impl From<*mut nl_object> for Link {
}
}
/// Gets the neighbor record for the source IP specified
pub fn get_neigh_for_addr(neighs: &Cache<Neigh>, links: &Cache<Link>, addr: &Addr) -> Option<(Link, Neigh)> {
/// Gets the neighbor record for the source IP specified, or get the default address
pub fn get_neigh_for_addr(routes: &Cache<Route>, neighs: &Cache<Neigh>, links: &Cache<Link>, addr: &Addr) -> Option<(Link, [u8; 6])> {
for link in links.iter() {
let Some(neigh) = link.get_neigh(&neighs, addr) else { continue; };
return Some((link, neigh));
}
// No good neighbors were found above, try to use the default address
println!("here");
if let Some(def_neigh) = get_default_route(routes) {
println!("Found default route, trying to get link for it");
if let Some((link, neigh)) = neighs
.iter()
.filter_map(|n| {
let Some(link) = netlink::get_link_by_index(links, n.ifindex()) else {
return None;
};
if Some(n.ifindex()) != def_neigh.hop_iter().next().map(|h| h.ifindex()) {
return None;
}
Some((link, n.lladdr()))
})
.next() {
return Some((link, neigh.hw_address().try_into().ok()?))
}
}
None
}
/// Given the routes cache, returns the default route among them
pub fn get_default_route(routes: &Cache<Route>) -> Option<Route> {
routes
.iter()
.find(|r|
r
.dst()
.map(|a| a.cidrlen())
.unwrap_or(33) == 0
)
}
/// A struct representing the neighbor of a link
pub struct Neigh {
neigh: *mut rtnl_neigh
@@ -118,6 +161,12 @@ impl Neigh {
Addr { addr }
}
}
pub fn ifindex(&self) -> i32 {
unsafe {
rtnl_neigh_get_ifindex(self.neigh)
}
}
}
impl From<*mut nl_object> for Neigh {
@@ -167,6 +216,26 @@ impl Addr {
}
}
impl Debug for Addr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.atype() {
AF_INET => {
let octets = self.hw_address();
f
.debug_struct("Addr")
.field("addr", &format!("{}.{}.{}.{}/{}", octets[0], octets[1], octets[2], octets[3], self.cidrlen()))
.finish()
},
_ => {
f
.debug_struct("Addr")
.field("addr", &self.hw_address())
.finish()
}
}
}
}
impl From<Ipv4Addr> for Addr {
fn from(value: Ipv4Addr) -> Self {
unsafe {
@@ -200,6 +269,19 @@ pub struct Route {
}
impl Route {
/// Represents the destination of the 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 })
}
}
/// Represents the destination of the route
pub fn dst(&self) -> Option<Addr> {
unsafe {
@@ -319,7 +401,7 @@ pub fn get_srcip_for_dstip(routes: &Cache<Route>, ip: Ipv4Addr) -> Option<Ipv4Ad
sorted_routes
.iter()
.find(|route| {
.filter(|route| {
let Some(dst) = route.dst() else { return false };
let mask = if dst.cidrlen() != 0 {
@@ -333,15 +415,14 @@ pub fn get_srcip_for_dstip(routes: &Cache<Route>, ip: Ipv4Addr) -> Option<Ipv4Ad
(mask & dst_addr) == (mask & ip_int)
})
.map(|route| {
.filter_map(|route| {
route
.hop_iter()
.next()
.and_then(|hop| hop.gateway())
.or(route.dst())
})
.flatten()
.map(|hop| hop.gateway())
.flatten()
.map(|gateway| gateway.try_into().ok())
.flatten()
.filter_map(|gateway| gateway.try_into().ok())
.next()
}