blob: a4e01304052817c77fdb15396b5b9bbe3481a224 [file] [log] [blame]
use ipnet::IpNet;
use log::error;
use netlink_packet_route::{
route::{RouteAddress, RouteAttribute, RouteMessage, RouteProtocol},
AddressFamily,
};
use std::{
fmt,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum RouteError {
#[error("Invalid gateway")]
InvalidGateway,
#[error("Invalid destination")]
InvalidDestination,
#[error("Invalid prefix length")]
InvalidPrefixLength,
#[error("Missing gateway")]
MissingGateway,
#[error("Missing destination")]
MissingDestination,
}
pub struct Route {
pub protocol: RouteProtocol,
pub destination: IpNet,
pub gateway: IpAddr,
}
impl fmt::Debug for Route {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} via {}", self.destination, self.gateway)
}
}
impl Route {
pub fn from_message(message: RouteMessage) -> Result<Self, RouteError> {
let mut gateway = None;
let mut destination = None;
for nla in message.attributes.iter() {
if let RouteAttribute::Gateway(ip) = nla {
gateway = match ip {
RouteAddress::Inet(ip) => Some(IpAddr::V4(*ip)),
RouteAddress::Inet6(ip) => Some(IpAddr::V6(*ip)),
_ => return Err(RouteError::InvalidGateway),
};
}
if let RouteAttribute::Destination(ref ip) = nla {
destination = match ip {
RouteAddress::Inet(ip) => Some(
IpNet::new(IpAddr::V4(*ip), message.header.destination_prefix_length)
.map_err(|_| RouteError::InvalidPrefixLength)?,
),
RouteAddress::Inet6(ip) => Some(
IpNet::new(IpAddr::V6(*ip), message.header.destination_prefix_length)
.map_err(|_| RouteError::InvalidPrefixLength)?,
),
_ => return Err(RouteError::InvalidDestination),
};
}
}
let gateway = match gateway {
Some(gateway) => gateway,
None => return Err(RouteError::MissingGateway),
};
let destination = match destination {
Some(destination) => destination,
None => match message.header.address_family {
AddressFamily::Inet => IpNet::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)
.map_err(|_| RouteError::InvalidPrefixLength)?,
AddressFamily::Inet6 => IpNet::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0)
.map_err(|_| RouteError::InvalidPrefixLength)?,
_ => return Err(RouteError::InvalidDestination),
},
};
Ok(Route {
protocol: message.header.protocol,
destination,
gateway,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use netlink_packet_route::AddressFamily;
use std::net::Ipv4Addr;
#[tokio::test]
async fn test_default_ipv4_route() {
let mut message = RouteMessage::default();
message.header.address_family = AddressFamily::Inet;
message.header.destination_prefix_length = 0;
message.header.protocol = RouteProtocol::Static;
message
.attributes
.push(RouteAttribute::Gateway(RouteAddress::Inet(Ipv4Addr::new(
192, 168, 1, 1,
))));
let route = Route::from_message(message).unwrap();
assert_eq!(route.protocol, RouteProtocol::Static);
assert_eq!(
route.destination,
IpNet::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0).unwrap()
);
assert_eq!(route.gateway, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
}
#[tokio::test]
async fn test_default_ipv6_route() {
let mut message = RouteMessage::default();
message.header.address_family = AddressFamily::Inet6;
message.header.destination_prefix_length = 0;
message.header.protocol = RouteProtocol::Static;
message
.attributes
.push(RouteAttribute::Gateway(RouteAddress::Inet6(Ipv6Addr::new(
0, 0, 0, 0, 0, 0, 0, 1,
))));
let route = Route::from_message(message).unwrap();
assert_eq!(route.protocol, RouteProtocol::Static);
assert_eq!(
route.destination,
IpNet::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0).unwrap()
);
assert_eq!(
route.gateway,
IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))
);
}
}