use std::time::{Duration, Instant};
use super::{
msg::{DhcpV6Message, DhcpV6MessageType},
socket::DhcpUdpV6Socket,
};
use crate::{
DhcpError, DhcpTimer, DhcpV6Config, DhcpV6Lease, DhcpV6Mode, DhcpV6State,
ErrorKind,
};
#[derive(Debug)]
pub struct DhcpV6Client {
pub(crate) config: DhcpV6Config,
pub(crate) lease: Option<DhcpV6Lease>,
pub(crate) state: DhcpV6State,
pub(crate) xid: u32,
pub(crate) pending_lease: Option<DhcpV6Lease>,
pub(crate) udp_socket: Option<DhcpUdpV6Socket>,
pub(crate) retransmit_count: u32,
pub(crate) trans_begin_time: Instant,
pub(crate) retransmit_timeout: Duration,
pub(crate) t1_timer: Option<DhcpTimer>,
pub(crate) t2_timer: Option<DhcpTimer>,
pub(crate) valid_timer: Option<DhcpTimer>,
error: Option<DhcpError>,
timeout_timer: Option<DhcpTimer>,
}
impl DhcpV6Client {
pub async fn init(
mut config: DhcpV6Config,
lease: Option<DhcpV6Lease>,
) -> Result<Self, DhcpError> {
if config.need_resolve() {
config.resolve().await?;
}
let state = if lease.is_some() {
DhcpV6State::Renew
} else {
DhcpV6State::Solicit
};
Ok(Self {
config,
lease,
state,
xid: rand::random_range(0..0x00FFFFFF),
pending_lease: Default::default(),
udp_socket: Default::default(),
retransmit_count: Default::default(),
trans_begin_time: Instant::now(),
retransmit_timeout: Duration::new(0, 0),
t1_timer: Default::default(),
t2_timer: Default::default(),
valid_timer: Default::default(),
error: Default::default(),
timeout_timer: None,
})
}
pub(crate) fn regen_xid(&mut self) {
self.xid = rand::random_range(0..0x00FFFFFF);
}
pub async fn run(&mut self) -> Result<DhcpV6State, DhcpError> {
if let Some(e) = self.error.as_ref() {
log::error!(
"Previous error found, please run DhcpV6Client::clean_up() if \
you want to start the DHCP process again"
);
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
Err(e.clone())
} else if !self.state.is_done() && self.config.timeout_sec != 0 {
let remains = if let Some(t) = self.timeout_timer.as_ref() {
t.remains()?
} else {
std::time::Duration::from_secs(self.config.timeout_sec.into())
};
tokio::select! {
_ = tokio::time::sleep(remains) => {
let e = DhcpError::new(
ErrorKind::Timeout,
format!(
"Timeout on acquiring DHCPv6 lease on {}",
self.config.iface_name
)
);
self.error = Some(e.clone());
Err(e)
},
result = self.run_without_timeout() => {
result
}
}
} else {
self.run_without_timeout().await
}
}
async fn run_without_timeout(&mut self) -> Result<DhcpV6State, DhcpError> {
let result = match self.state {
DhcpV6State::Solicit => self.solicit().await,
DhcpV6State::Request => self.request().await,
DhcpV6State::Renew => self.renew().await,
DhcpV6State::Rebind => self.rebind().await,
DhcpV6State::Done(_) => self.wait_timer().await,
};
if let Err(e) = result {
self.error = Some(e.clone());
Err(e)
} else {
Ok(self.state.clone())
}
}
pub async fn release(
&mut self,
lease: &DhcpV6Lease,
) -> Result<(), DhcpError> {
let mut dhcp_msg = DhcpV6Message::new(
DhcpV6MessageType::Release,
self.xid,
&self.config.duid,
&Instant::now(),
);
dhcp_msg.load_lease(lease);
let udp_socket = self.get_udp_socket_or_init().await?;
udp_socket.send_multicast(&dhcp_msg.emit()).await?;
self.clean_up();
Ok(())
}
pub fn clean_up(&mut self) {
self.state = DhcpV6State::Solicit;
self.lease = None;
self.pending_lease = None;
self.udp_socket = None;
self.t1_timer = None;
self.t2_timer = None;
self.valid_timer = None;
self.error = None;
self.retransmit_count = 0;
self.retransmit_timeout = Duration::new(0, 0);
}
pub fn reset_retransmit_counters(&mut self) {
self.retransmit_count = 0;
self.retransmit_timeout = Duration::new(0, 0);
self.trans_begin_time = Instant::now();
}
pub fn done(&mut self, lease: DhcpV6Lease) -> Result<(), DhcpError> {
self.set_lease_timer(&lease)?;
self.reset_retransmit_counters();
self.timeout_timer = None;
self.udp_socket = None;
self.pending_lease = None;
self.lease = Some(lease.clone());
self.state = DhcpV6State::Done(Box::new(lease));
Ok(())
}
pub(crate) async fn get_udp_socket_or_init(
&mut self,
) -> Result<&mut DhcpUdpV6Socket, DhcpError> {
if self.udp_socket.is_none() {
self.udp_socket = Some(
DhcpUdpV6Socket::new(
self.config.iface_name.as_str(),
self.config.iface_index,
self.config.src_ip,
)
.await?,
);
}
Ok(self.udp_socket.as_mut().unwrap())
}
async fn wait_timer(&mut self) -> Result<(), DhcpError> {
let timer = if self.config.mode == DhcpV6Mode::TemporaryAddresses {
self.valid_timer.as_ref()
} else {
self.t1_timer.as_ref()
};
if let Some(timer) = timer {
timer.wait().await?;
self.reset_retransmit_counters();
if self.config.mode == DhcpV6Mode::TemporaryAddresses {
self.state = DhcpV6State::Solicit;
} else {
self.state = DhcpV6State::Renew;
}
} else {
log::error!("BUG: wait_timer() got no timer: {self:?}");
self.reset_retransmit_counters();
self.state = DhcpV6State::Solicit;
}
Ok(())
}
}