pub use crate::dhcp_proxy::lib::g_rpc::{Lease as NetavarkLease, Lease};
pub use crate::dhcp_proxy::types::{CustomErr, ProxyError};
use crate::network::core_utils;
use crate::network::netlink;
use crate::network::netlink::Socket;
use ipnet::IpNet;
use log::debug;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
trait IpConv {
fn to_v4(&self) -> Result<&Ipv4Addr, ProxyError>;
fn to_v6(&self) -> Result<&Ipv6Addr, ProxyError>;
}
impl IpConv for IpAddr {
fn to_v4(&self) -> Result<&Ipv4Addr, ProxyError> {
match self {
IpAddr::V4(ip) => Ok(ip),
IpAddr::V6(_) => Err(ProxyError::new(
"invalid value for ipv4 conversion".to_string(),
)),
}
}
fn to_v6(&self) -> Result<&Ipv6Addr, ProxyError> {
match self {
IpAddr::V4(_) => Err(ProxyError::new(
"invalid value for ipv6 conversion".to_string(),
)),
IpAddr::V6(ip) => Ok(ip),
}
}
}
#[derive(Clone, Debug)]
struct MacVLAN {
address: IpAddr,
gateways: Vec<IpNet>,
interface: String,
prefix_length: u8,
}
trait Address<T> {
fn new(l: &Lease, interface: &str) -> Result<Self, ProxyError>
where
Self: Sized;
fn add_ip(&self, nls: &mut Socket) -> Result<(), ProxyError>;
fn add_gws(&self, nls: &mut Socket) -> Result<(), ProxyError>;
fn remove(self) -> Result<(), ProxyError>;
}
fn handle_gws(g: Vec<String>, netmask: &str) -> Result<Vec<IpNet>, ProxyError> {
let mut gws = Vec::new();
for route in g {
let sub_mask = match Ipv4Addr::from_str(netmask) {
Ok(n) => n,
Err(e) => return Err(ProxyError::new(e.to_string())),
};
let prefix = u32::from(sub_mask).count_ones();
let ip = match Ipv4Addr::from_str(&route) {
Ok(i) => i,
Err(e) => return Err(ProxyError::new(e.to_string())),
};
let gw = match IpNet::new(IpAddr::from(ip), prefix as u8) {
Ok(r) => r,
Err(e) => return Err(ProxyError::new(format!("{e}:'{route}'"))),
};
gws.push(gw);
}
Ok(gws)
}
#[test]
fn test_bad_gw_handle_gws() {
let gws = vec!["192.168.1.1".to_string(), "10.10.10".into()];
let netmask = "255.255.255.0";
assert!(handle_gws(gws, netmask).is_err())
}
#[test]
fn test_bad_subnet_handle_gws() {
let gws = vec!["192.168.1.1".to_string(), "10.10.10.1".into()];
let netmask = "255.255.255";
assert!(handle_gws(gws, netmask).is_err())
}
#[test]
fn test_handle_gws() {
let gws = vec!["192.168.1.1".to_string(), "10.10.10.1".into()];
let netmask = "255.255.255.0";
assert!(handle_gws(gws, netmask).is_ok())
}
impl Address<Ipv4Addr> for MacVLAN {
fn new(l: &NetavarkLease, interface: &str) -> Result<MacVLAN, ProxyError> {
debug!("new ipv4 macvlan for {}", interface);
let address = match IpAddr::from_str(&l.yiaddr) {
Ok(a) => a,
Err(e) => {
return Err(ProxyError::new(format!("bad address: {e}")));
}
};
let gateways = match handle_gws(l.gateways.clone(), &l.subnet_mask) {
Ok(g) => g,
Err(e) => {
return Err(ProxyError::new(format!("bad gateways: {}", e.to_string())));
}
};
let prefix_length = match get_prefix_length_v4(&l.subnet_mask) {
Ok(u) => u as u8,
Err(e) => return Err(ProxyError::new(e.to_string())),
};
Ok(MacVLAN {
address,
gateways,
interface: interface.to_string(),
prefix_length,
})
}
fn add_ip(&self, nls: &mut Socket) -> Result<(), ProxyError> {
debug!("adding network information for {}", self.interface);
let ip = IpNet::new(self.address, self.prefix_length)?;
let dev = nls.get_link(netlink::LinkID::Name(self.interface.clone()))?;
match nls.add_addr(dev.header.index, &ip) {
Ok(_) => Ok(()),
Err(e) => Err(ProxyError::new(e.to_string())),
}
}
fn add_gws(&self, nls: &mut Socket) -> Result<(), ProxyError> {
debug!("adding gateways to {}", self.interface);
match core_utils::add_default_routes(nls, &self.gateways, None) {
Ok(_) => Ok(()),
Err(e) => Err(ProxyError::new(e.to_string())),
}
}
fn remove(self) -> Result<(), ProxyError> {
debug!("removing interface {}", self.interface);
todo!()
}
}
pub fn setup(lease: &NetavarkLease, interface: &str, ns_path: &str) -> Result<(), ProxyError> {
debug!("setting up {}", interface);
let vlan = match MacVLAN::new(lease, interface) {
Ok(f) => f,
Err(e) => return Err(e),
};
let (_, mut netns) = core_utils::open_netlink_sockets(ns_path)?;
vlan.add_ip(&mut netns.netlink)?;
vlan.add_gws(&mut netns.netlink)
}
pub fn teardown() -> Result<(), ProxyError> {
todo!()
}
fn get_prefix_length_v4(netmask: &str) -> Result<u32, ProxyError> {
let sub_mask = match Ipv4Addr::from_str(netmask) {
Ok(n) => n,
Err(e) => return Err(ProxyError::new(e.to_string())),
};
Ok(u32::from(sub_mask).count_ones())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_24() {
assert_eq!(get_prefix_length_v4("255.255.255.0").unwrap(), 24_u32)
}
#[test]
fn test_16() {
assert_eq!(get_prefix_length_v4("255.255.0.0").unwrap(), 16_u32)
}
#[test]
fn test_25() {
assert_eq!(get_prefix_length_v4("255.255.255.128").unwrap(), 25_u32)
}
#[test]
fn test_bad_input() {
assert!(get_prefix_length_v4("255.255.128").is_err())
}
}