use std::net::{
IpAddr,
Ipv4Addr,
Ipv6Addr,
SocketAddrV4,
SocketAddrV6
};
use ipnetwork::{
IpNetwork,
Ipv4Network,
Ipv6Network
};
use netlink_packet_route::{
AddressFamily,
address::AddressAttribute,
link::LinkAttribute,
link::State
};
use netlink_packet_route::route::{
RouteAddress,
RouteAttribute,
RouteMessage,
RouteHeader,
RouteScope,
RouteType
};
use crate::config::DEFAULT_PORT;
use crate::net::address::InterfaceAddress;
#[derive(Debug, Default)]
pub(crate) struct Devices {
pub inner: Vec<Device>
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Device {
index: u32,
pub name: String,
addresses: Vec<(IpAddr, Vec<IpNetwork>)>,
state: State
}
#[derive(Debug)]
struct Route {
if_index: u32,
preferred_source: Option<IpAddr>,
destination: IpNetwork,
kind: RouteType
}
trait RouteSet {
fn push_route(&mut self, route: IpNetwork);
}
impl RouteSet for Vec<IpNetwork> {
fn push_route(&mut self, route: IpNetwork) {
if !self.contains(&route) {
self.push(route)
}
}
}
impl RouteSet for (IpAddr, Vec<IpNetwork>) {
fn push_route(&mut self, route: IpNetwork) {
self.1.push_route(route)
}
}
impl Device {
pub fn init(index: u32, name: String) -> Self {
let addresses = Vec::new();
let state = State::Unknown;
Self{ index, name, addresses, state }
}
fn unwrap_address(&self, address: IpAddr, routes: &[IpNetwork])
-> InterfaceAddress
{
match address {
IpAddr::V4(ip_address) => {
let routes = routes.iter().filter_map(|n| match n {
&IpNetwork::V4(route) => Some(route),
IpNetwork::V6(route) => {
error!(
device=?self,
%ip_address,
%route,
"IP version mismatch in assigned routes"
);
None
}
}).collect();
InterfaceAddress::V4UdpChaCha20 {
if_name: Some(self.name.clone()),
addr: SocketAddrV4::new(ip_address, DEFAULT_PORT),
routes
}
},
IpAddr::V6(ip_address) => {
let routes = routes.iter().filter_map(|n| match n {
IpNetwork::V4(route) => {
error!(
device=?self,
%ip_address,
%route,
"IP version mismatch in assigned routes"
);
None
},
&IpNetwork::V6(route) => Some(route)
}).collect();
InterfaceAddress::V6UdpChaCha20 {
if_name: Some(self.name.clone()),
addr: SocketAddrV6::new(
ip_address,
DEFAULT_PORT,
0,
self.index
),
routes
}
}
}
}
pub fn make_addresses(&self) -> Vec<InterfaceAddress> {
self.addresses.iter()
.map(|(ip, routes)| self.unwrap_address(*ip, routes))
.collect()
}
pub fn absorb_link_nla(&mut self, nla: LinkAttribute) {
match nla {
LinkAttribute::IfName(n) => self.name = n,
LinkAttribute::OperState(s) => self.state = s,
_ => {}
}
}
pub fn absorb_addr_nla(&mut self, nla: AddressAttribute) {
match nla {
AddressAttribute::Address(a) => self.add_address(a),
AddressAttribute::Label(n) => self.name = n,
_ => {}
}
}
pub fn add_address(&mut self, address: IpAddr) {
if !self.addresses.iter().any(|(a, _routes)| a == &address) {
self.addresses.push((address, Vec::new()));
}
}
pub fn delete_address(&mut self, address: IpAddr) {
self.addresses.retain(|(a, _routes)| a != &address);
}
pub fn add_route(&mut self, address: IpAddr, route: IpNetwork) {
let entry = self.addresses
.iter_mut()
.find(|(a, _routes)| a == &address);
match entry {
Some((_addr, routes)) => if !routes.contains(&route) {
routes.push(route);
},
None => {
self.addresses.push((address, vec![route]));
}
}
}
fn delete_route(&mut self, route: Route) {
let mut count = 0;
for (_, rts) in self.addresses.iter_mut() {
if let Some(i) = rts.iter().position(|&r| r == route.destination) {
rts.swap_remove(i);
count += 1;
}
}
match count {
0 => debug!(?route, ?self, "no such route to delete"),
1 => trace!(?route, ?self, "route removed once"),
count => info!(?route, ?self, count, "route removed")
}
}
fn process_route(&mut self, route: Route) {
let count = if let Some(src_addr) = route.preferred_source {
self.add_route(src_addr, route.destination);
1
}
else if let IpAddr::V6(dest_ip) = route.destination.ip() {
let mut v6_addresses = self.addresses
.iter_mut()
.filter_map(|(addr, routes)| match *addr {
IpAddr::V6(addr) => Some((addr, routes)),
_ => None
});
let first_two = [v6_addresses.next(), v6_addresses.next()];
if let [Some((_addr, routes)), None] = first_two {
routes.push_route(route.destination);
1
}
else if let [Some(a), Some(b)] = first_two {
let mut count = 0;
for (addr, routes) in [a, b].into_iter().chain(v6_addresses) {
let addr_route_match = (
!dest_ip.is_unspecified()
&& route.destination.contains(IpAddr::V6(addr))
)
|| (
addr.is_global()
&& (dest_ip.is_global() || dest_ip.is_unspecified())
)
|| (
dest_ip.is_unicast_link_local()
&& addr.is_unicast_link_local()
)
|| (
matches!(route.kind, RouteType::Multicast)
);
if addr_route_match {
routes.push_route(route.destination);
count += 1;
continue;
}
}
count
}
else {0}
}
else if let IpAddr::V4(_dest_ip) = route.destination.ip() {
self.addresses
.iter_mut()
.filter_map(|(addr, routes)| match *addr {
IpAddr::V4(addr) => Some((addr, routes)),
_ => None
})
.map(|(_, r)| r.push_route(route.destination))
.count()
}
else {unreachable!()};
match count {
0 if self.addresses.is_empty() => trace!(
?route,
?self,
"no addresses to assign route to"
),
0 => warn!(?route, ?self, "unable to assign route to an address"),
1 => trace!(?route, ?self, "route assigned once"),
count => info!(?route, ?self, count, "route assigned")
}
}
}
impl Devices {
pub fn new() -> Self { Self {inner: Vec::new()} }
pub fn get_or_insert(&mut self, idx: u32) -> &mut Device {
let pos = self.inner
.iter()
.position(|d| d.index == idx);
let pos = match pos {
Some(p) => p,
None => {
let last = self.inner.len();
self.inner.push(Device::init(idx, String::new()));
last
}
};
self.inner.get_mut(pos).unwrap()
}
pub fn delete(&mut self, idx: u32) {
match self.inner.iter().position(|d| d.index == idx) {
Some(i) => {
let dev = self.inner.swap_remove(i);
trace!(?dev, "removed device");
},
None => warn!(idx, "tried to delete unknown device")
}
}
pub fn add_route(&mut self, msg: RouteMessage) {
if let Some(route) = Self::extract_route(msg) {
let dev = self.get_or_insert(route.if_index);
dev.process_route(route);
}
}
pub fn delete_route(&mut self, msg: RouteMessage) {
if let Some(route) = Self::extract_route(msg) {
let dev = self.get_or_insert(route.if_index);
dev.delete_route(route);
}
}
fn extract_route(msg: RouteMessage) -> Option<Route> {
let mut if_idx = None;
let mut src = None;
let mut dest = None;
for attr in msg.attributes {
match attr {
RouteAttribute::PrefSource(s) => src = Some(s),
RouteAttribute::Destination(s) => dest = Some(s),
RouteAttribute::Oif(i) => if_idx = Some(i),
_ => {}
}
}
let source = match src {
Some(RouteAddress::Inet(src)) => Some(IpAddr::V4(src)),
Some(RouteAddress::Inet6(src)) => Some(IpAddr::V6(src)),
Some(invalid) => {
warn!(?invalid, "received route with non-IP source");
return None;
},
None => None
};
let network = match dest {
Some(RouteAddress::Inet(dest)) => {
let prefix = msg.header.destination_prefix_length;
IpNetwork::new(dest.into(), prefix).ok()?
}
Some(RouteAddress::Inet6(dest)) => {
let prefix = msg.header.destination_prefix_length;
IpNetwork::new(dest.into(), prefix).ok()?
}
Some(invalid) => {
warn!(?invalid, "received route with non-IP destination");
return None;
},
None => Self::default_route(&msg.header)
};
Some(Route {
if_index: if_idx?,
preferred_source: source,
destination: network,
kind: msg.header.kind
})
}
fn default_route(header: &RouteHeader) -> IpNetwork {
let is_default = header.scope == RouteScope::Universe
&& header.destination_prefix_length == 0;
if !is_default {
error!(?header, "invalid non-default RouteMessage without dest");
}
match header.address_family {
AddressFamily::Inet => Ipv4Network::new(Ipv4Addr::UNSPECIFIED, 0)
.map(IpNetwork::V4)
.unwrap(),
AddressFamily::Inet6 => Ipv6Network::new(Ipv6Addr::UNSPECIFIED, 0)
.map(IpNetwork::V6)
.unwrap(),
_ => unreachable!("invalid non-IP address family received")
}
}
}