use crate::error::Error;
use macaddr::MacAddr6;
use neli::consts::{
nl::NlmF,
rtnl::{Ifla, IflaInfo, RtAddrFamily, Rtm},
socket::NlFamily,
};
use neli::nl::{NlPayload, Nlmsghdr};
use neli::router::asynchronous::NlRouter;
use neli::rtnl::{Ifinfomsg, IfinfomsgBuilder, RtattrBuilder};
use neli::types::{Buffer, RtBuffer};
use neli::utils::Groups;
use std::io::Read;
fn generate_random_mac() -> Result<MacAddr6, Error> {
let mut file = std::fs::File::open("/dev/urandom")
.map_err(|e| Error::Netlink(format!("Failed to open /dev/urandom: {e}")))?;
let mut bytes = [0u8; 6];
file.read_exact(&mut bytes)
.map_err(|e| Error::Netlink(format!("Failed to read /dev/urandom: {e}")))?;
bytes[0] = (bytes[0] & 0xFE) | 0x02;
Ok(MacAddr6::from(bytes))
}
pub async fn create_mesh(
mesh_if: &str,
routing_algo: Option<&str>,
mac_addr: Option<MacAddr6>,
) -> Result<(), Error> {
const IFLA_BATADV_ALGO_NAME: u16 = 1;
let (rtnl, _) = NlRouter::connect(NlFamily::Route, None, Groups::empty())
.await
.map_err(|_| Error::Netlink("Error - failed to connect to netlink router".to_string()))?;
rtnl.enable_ext_ack(true)
.map_err(|_| Error::Netlink("Error - failed to enable extended ACK".to_string()))?;
rtnl.enable_strict_checking(true)
.map_err(|_| Error::Netlink("Error - failed to enable strict checking".to_string()))?;
let ifname_attr = RtattrBuilder::default()
.rta_type(Ifla::Ifname)
.rta_payload(mesh_if)
.build()
.map_err(|_| Error::Netlink("Error - failed to build IFNAME attribute".to_string()))?;
let kind_attr = RtattrBuilder::default()
.rta_type(IflaInfo::Kind)
.rta_payload("batadv")
.build()
.map_err(|_| Error::Netlink("Error - failed to build INFO_KIND attribute".to_string()))?;
let mut info_data_attrs: RtBuffer<u16, Buffer> = RtBuffer::new();
if let Some(algo) = routing_algo {
let algo_attr = RtattrBuilder::default()
.rta_type(IFLA_BATADV_ALGO_NAME)
.rta_payload(algo)
.build()
.map_err(|_| {
Error::Netlink("Error - failed to build ALGO_NAME attribute".to_string())
})?;
info_data_attrs.push(algo_attr);
}
let info_data_attr = RtattrBuilder::default()
.rta_type(IflaInfo::Data)
.rta_payload(info_data_attrs)
.build()
.map_err(|_| Error::Netlink("Error - failed to build INFO_DATA attribute".to_string()))?;
let mut linkinfo_attrs: RtBuffer<IflaInfo, Buffer> = RtBuffer::new();
linkinfo_attrs.push(kind_attr);
linkinfo_attrs.push(info_data_attr);
let linkinfo_attr = RtattrBuilder::default()
.rta_type(Ifla::Linkinfo)
.rta_payload(linkinfo_attrs)
.build()
.map_err(|_| Error::Netlink("Error - failed to build LINKINFO attribute".to_string()))?;
let hw_addr = match mac_addr {
Some(addr) => addr,
None => generate_random_mac()?,
};
let address_attr = RtattrBuilder::default()
.rta_type(Ifla::Address)
.rta_payload(Buffer::from(hw_addr.as_bytes()))
.build()
.map_err(|_| Error::Netlink("Error - failed to build ADDRESS attribute".to_string()))?;
let mut rtattrs: RtBuffer<Ifla, Buffer> = RtBuffer::new();
rtattrs.push(ifname_attr);
rtattrs.push(address_attr);
rtattrs.push(linkinfo_attr);
let msg = IfinfomsgBuilder::default()
.ifi_family(RtAddrFamily::Unspecified)
.rtattrs(rtattrs)
.build()
.map_err(|_| Error::Netlink("Error - failed to build Ifinfomsg".to_string()))?;
rtnl.send::<_, _, Rtm, Ifinfomsg>(
Rtm::Newlink,
NlmF::REQUEST | NlmF::CREATE | NlmF::EXCL | NlmF::ACK,
NlPayload::Payload(msg),
)
.await
.map_err(|_| Error::Netlink("Error - failed to create mesh interface".to_string()))?;
Ok(())
}
pub async fn delete_mesh(mesh_ifindex: u32) -> Result<(), Error> {
let (rtnl, _) = NlRouter::connect(NlFamily::Route, None, Groups::empty())
.await
.map_err(|_| Error::Netlink("Error - failed to connect to netlink router".to_string()))?;
rtnl.enable_ext_ack(true)
.map_err(|_| Error::Netlink("Error - failed to enable extended ACK".to_string()))?;
rtnl.enable_strict_checking(true)
.map_err(|_| Error::Netlink("Error - failed to enable strict checking".to_string()))?;
let msg = IfinfomsgBuilder::default()
.ifi_family(RtAddrFamily::Unspecified)
.ifi_index(mesh_ifindex.cast_signed())
.build()
.map_err(|_| Error::Netlink("Error - failed to build Ifinfomsg".to_string()))?;
rtnl.send::<_, _, Rtm, Ifinfomsg>(
Rtm::Dellink,
NlmF::REQUEST | NlmF::ACK,
NlPayload::Payload(msg),
)
.await
.map_err(|_| Error::Netlink("Error - failed to destroy mesh interface".to_string()))?;
Ok(())
}
pub async fn list_meshes() -> Result<Vec<String>, Error> {
let (rtnl, _) = NlRouter::connect(NlFamily::Route, None, Groups::empty())
.await
.map_err(|_| Error::Netlink("Error - failed to connect to netlink router".to_string()))?;
rtnl.enable_ext_ack(true)
.map_err(|_| Error::Netlink("Error - failed to enable extended ACK".to_string()))?;
rtnl.enable_strict_checking(true)
.map_err(|_| Error::Netlink("Error - failed to enable strict checking".to_string()))?;
let msg = IfinfomsgBuilder::default()
.ifi_family(RtAddrFamily::Unspecified)
.build()
.map_err(|_| Error::Netlink("Error - failed to build Ifinfomsg".to_string()))?;
let mut response = rtnl
.send::<_, _, Rtm, Ifinfomsg>(
Rtm::Getlink,
NlmF::REQUEST | NlmF::DUMP | NlmF::ACK,
NlPayload::Payload(msg),
)
.await
.map_err(|_| Error::Netlink("Error - failed to send Getlink request".to_string()))?;
let mut meshes = Vec::new();
while let Some(msg) = response.next().await {
let msg: Nlmsghdr<Rtm, Ifinfomsg> =
msg.map_err(|_| Error::Netlink("Error - failed to parse netlink message".to_string()))?;
let payload = match msg.get_payload() {
Some(payload) => payload,
None => continue,
};
let attrs = payload.rtattrs().get_attr_handle();
let ifname = match attrs.get_attr_payload_as_with_len::<String>(Ifla::Ifname) {
Ok(ifname) => ifname,
Err(_) => continue,
};
let linkinfo = match attrs.get_nested_attributes::<IflaInfo>(Ifla::Linkinfo) {
Ok(linkinfo) => linkinfo,
Err(_) => continue,
};
let kind = match linkinfo.get_attr_payload_as_with_len::<String>(IflaInfo::Kind) {
Ok(kind) => kind,
Err(_) => continue,
};
if kind != "batadv" {
continue;
}
meshes.push(ifname);
}
Ok(meshes)
}