use std::net::Ipv6Addr;
use crate::{
DhcpError, DhcpV6Duid, DhcpV6DuidLinkLayerAddr, DhcpV6OptionCode, ETH_ALEN,
};
const ARP_HW_TYPE_ETHERNET: u16 = 1;
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash, Default)]
#[non_exhaustive]
pub enum DhcpV6IaType {
#[default]
NonTemporaryAddresses,
TemporaryAddresses,
PrefixDelegation,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash, Default)]
#[non_exhaustive]
pub enum DhcpV6Mode {
#[default]
NonTemporaryAddresses,
TemporaryAddresses,
PrefixDelegation(u8),
}
impl std::fmt::Display for DhcpV6Mode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NonTemporaryAddresses => {
write!(f, "Non-temporary Addresses(IA_NA)")
}
Self::TemporaryAddresses => write!(f, "Temporary Addresses(IA_TA)"),
Self::PrefixDelegation(d) => {
write!(f, "Prefix Delegation(IA_PD)-{d}")
} }
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[non_exhaustive]
pub struct DhcpV6Config {
pub iface_name: String,
pub iface_index: u32,
pub duid: DhcpV6Duid,
pub mode: DhcpV6Mode,
pub src_ip: Ipv6Addr,
pub(crate) src_mac: Option<[u8; ETH_ALEN]>,
pub timeout_sec: u32,
pub request_opts: Vec<DhcpV6OptionCode>,
}
impl Default for DhcpV6Config {
fn default() -> Self {
Self {
iface_name: String::new(),
iface_index: 0,
duid: DhcpV6Duid::Raw(Vec::new()),
mode: DhcpV6Mode::default(),
src_ip: Ipv6Addr::UNSPECIFIED,
src_mac: None,
timeout_sec: 0,
request_opts: vec![
DhcpV6OptionCode::OptionRequestOption,
DhcpV6OptionCode::Preference,
DhcpV6OptionCode::DnsServers,
DhcpV6OptionCode::DomainList,
DhcpV6OptionCode::NtpServer,
],
}
}
}
impl DhcpV6Config {
pub fn new(iface_name: &str, mode: DhcpV6Mode) -> Self {
Self {
iface_name: iface_name.to_string(),
mode,
..Default::default()
}
}
pub(crate) fn need_resolve(&self) -> bool {
self.iface_index == 0 || self.src_ip.is_unspecified()
}
#[cfg(feature = "netlink")]
pub(crate) async fn resolve(&mut self) -> Result<(), DhcpError> {
self.iface_index =
crate::netlink::get_iface_index(&self.iface_name).await?;
if let Ok((_, src_mac)) =
crate::netlink::get_iface_index_mac(&self.iface_name).await
{
if src_mac.len() == ETH_ALEN {
let mut tmp_src_mac = [0u8; ETH_ALEN];
tmp_src_mac.copy_from_slice(&src_mac[..ETH_ALEN]);
self.src_mac = Some(tmp_src_mac);
}
}
self.src_ip =
crate::netlink::get_link_local_addr(self.iface_index).await?;
self.get_duid_or_init();
Ok(())
}
#[cfg(not(feature = "netlink"))]
pub(crate) async fn resolve(&mut self) -> Result<(), DhcpError> {
Err(DhcpError::new(
crate::ErrorKind::InvalidArgument,
format!(
"Feature `netlink` not enabled, cannot resolve interface {} \
index and IPv6 link local address, please set them manually",
self.iface_name,
),
))
}
pub fn set_iface_index(&mut self, iface_index: u32) -> &mut Self {
self.iface_index = iface_index;
self
}
pub fn set_link_local_ip(&mut self, addr: Ipv6Addr) -> &mut Self {
self.src_ip = addr;
self
}
pub fn set_duid(&mut self, duid: DhcpV6Duid) -> &mut Self {
self.duid = duid;
self
}
pub fn set_duid_by_iface_mac(&mut self, mac: &[u8]) -> &mut Self {
self.duid = DhcpV6Duid::LinkLayerAddress(DhcpV6DuidLinkLayerAddr::new(
ARP_HW_TYPE_ETHERNET,
mac,
));
self
}
pub fn get_duid_or_init(&mut self) -> &DhcpV6Duid {
if self.duid.is_empty() {
self.duid = if let Some(mac) = self.src_mac.as_ref() {
DhcpV6Duid::LinkLayerAddress(DhcpV6DuidLinkLayerAddr::new(
ARP_HW_TYPE_ETHERNET,
mac,
))
} else {
DhcpV6Duid::default()
};
}
&self.duid
}
pub fn set_timeout_sec(&mut self, timeout_sec: u32) -> &mut Self {
self.timeout_sec = timeout_sec;
self
}
pub fn request_extra_dhcp_opts(&mut self, opts: &[u16]) -> &mut Self {
for opt in opts {
self.request_opts.push((*opt).into());
}
self.request_opts.sort_unstable();
self.request_opts.dedup();
self
}
pub fn override_request_dhcp_opts(&mut self, opts: &[u16]) -> &mut Self {
self.request_opts =
opts.iter().map(|c| DhcpV6OptionCode::from(*c)).collect();
self.request_opts.sort_unstable();
self.request_opts.dedup();
self
}
}