use crate::{config::VrrpConfig, error::NetError, router::VirtualRouter, NetResult};
use ipnet::Ipv4Net;
use pnet::datalink::{self, Channel, DataLinkReceiver, DataLinkSender, NetworkInterface};
use rand::{distributions::Alphanumeric, Rng};
use std::{process::Command, str::FromStr};
pub(crate) fn get_interface(name: &str) -> NetResult<NetworkInterface> {
let interface_names_match = |iface: &NetworkInterface| iface.name == name;
let interfaces = datalink::linux::interfaces();
match interfaces.into_iter().find(interface_names_match) {
Some(interface) => Ok(interface),
None => Err(NetError(format!(
"unable to find interface with name {name}"
))),
}
}
pub(crate) fn create_datalink_channel(
interface: &NetworkInterface,
) -> NetResult<(Box<dyn DataLinkSender>, Box<dyn DataLinkReceiver>)> {
match pnet::datalink::channel(interface, Default::default()) {
Ok(Channel::Ethernet(tx, rx)) => Ok((tx, rx)),
Ok(_) => {
let err = "Unknown channel type";
log::error!("{err}");
Err(NetError(err.to_string()))
}
Err(err) => {
log::error!("Problem creating datalink channel");
log::error!("{err}");
Err(NetError("Problem creating datalink channel".to_string()))
}
}
}
pub fn config_to_vr(conf: VrrpConfig) -> VirtualRouter {
let mut ips: Vec<Ipv4Net> = vec![];
if conf.ip_addresses().len() > 20 {
log::warn!("({}) More than 20 IP addresses(max for VRRP) have been configured. Only first 20 addresses will be used..", conf.name());
}
let addresses = if conf.ip_addresses().len() <= 20 {
conf.ip_addresses()
} else {
conf.ip_addresses()[0..20].to_vec()
};
for ip_config in addresses.iter() {
match Ipv4Net::from_str(ip_config) {
Ok(ip_addr) => ips.push(ip_addr),
Err(_) => {
}
}
}
let vr = VirtualRouter::new(
conf.name(),
conf.vrid(),
ips,
conf.priority(),
conf.advert_interval(),
conf.preempt_mode(),
conf.interface_name(),
);
log::info!("({}) Entered {:?} state.", vr.name, vr.fsm.state);
vr
}
pub(crate) fn virtual_address_action(action: &str, addresses: &[String], interface_name: &str) {
for addr in addresses {
let cmd_args = vec!["address", action, &addr, "dev", interface_name];
let _ = Command::new("ip").args(cmd_args).output();
}
}
pub(crate) fn random_vr_name() -> String {
let val: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(10)
.map(char::from)
.collect();
log::info!("Name for Virtual Router not given. generated name VR_{val}");
format!("VR_{val}")
}