use crate::linux::consts::WG_GENL_NAME;
use libc::{IFLA_INFO_KIND, IFLA_LINKINFO};
use neli::consts::{Arphrd, Ifla, NlmF, Rtm};
use neli::err::SerError;
use neli::nl::Nlmsghdr;
use neli::nlattr::Nlattr;
use neli::rtnl::Ifinfomsg;
use neli::rtnl::Rtattr;
use neli::Nl;
use neli::StreamWriteBuffer;
const RTATTR_HEADER_LEN: libc::c_ushort = 4;
pub enum WireGuardDeviceLinkOperation {
Add,
Delete,
}
fn create_rtattr(rta_type: Ifla, rta_payload: Vec<u8>) -> Rtattr<Ifla, Vec<u8>> {
let mut rtattr = Rtattr {
rta_len: 0,
rta_type,
rta_payload,
};
rtattr.rta_len = rtattr.payload_size() as libc::c_ushort + RTATTR_HEADER_LEN;
rtattr
}
pub fn link_message(
ifname: &str,
link_operation: WireGuardDeviceLinkOperation,
) -> Result<Nlmsghdr<Rtm, Ifinfomsg<Ifla>>, SerError> {
let ifname = create_rtattr(Ifla::Ifname, ifname.as_bytes().to_vec());
let link = {
let rta_type = Ifla::UnrecognizedVariant(IFLA_LINKINFO);
let payload = {
let mut payload = StreamWriteBuffer::new_growable(None);
let rtattr =
Nlattr::new::<Vec<u8>>(None, IFLA_INFO_KIND, WG_GENL_NAME.as_bytes().to_vec())?;
rtattr.serialize(&mut payload)?;
payload.as_ref().to_vec()
};
create_rtattr(rta_type, payload)
};
let infomsg = {
let ifi_family =
neli::consts::rtnl::RtAddrFamily::UnrecognizedVariant(libc::AF_UNSPEC as u8);
let ifi_type = Arphrd::Netrom;
let ifi_index = 0;
let ifi_flags = vec![];
let rtattrs = vec![ifname, link];
Ifinfomsg::new(ifi_family, ifi_type, ifi_index, ifi_flags, rtattrs)
};
let nlmsg = {
let len = None;
let nl_type = match link_operation {
WireGuardDeviceLinkOperation::Add => Rtm::Newlink,
WireGuardDeviceLinkOperation::Delete => Rtm::Dellink,
};
let flags = match link_operation {
WireGuardDeviceLinkOperation::Add => {
vec![NlmF::Request, NlmF::Ack, NlmF::Create, NlmF::Excl]
}
WireGuardDeviceLinkOperation::Delete => vec![NlmF::Request, NlmF::Ack],
};
let seq = None;
let pid = None;
let payload = infomsg;
Nlmsghdr::new(len, nl_type, flags, seq, pid, payload)
};
Ok(nlmsg)
}