use std::time::{Duration, Instant};
use super::{
msg::{DhcpV6Message, DhcpV6MessageType},
time::gen_retransmit_time,
DhcpV6Config, DhcpV6Lease, DhcpV6Option, DhcpV6OptionCode, DhcpV6State,
};
use crate::{DhcpError, DhcpV6Client, ErrorKind};
const REB_TIMEOUT: Duration = Duration::from_secs(10);
const REB_MAX_RT: Duration = Duration::from_secs(600);
impl DhcpV6Client {
fn gen_rebind_wait_time(&self) -> Result<Duration, DhcpError> {
let valid_sec = if let Some(valid_sec) =
self.lease.as_ref().map(|l| l.valid_time_sec)
{
valid_sec
} else {
return Err(DhcpError::new(
ErrorKind::Bug,
format!("In Rebind state without lease: {self:?}"),
));
};
if let Some(valid) = self.valid_timer.as_ref() {
let remains = valid.remains()?;
Ok(std::cmp::min(
remains,
gen_retransmit_time(
self.trans_begin_time,
self.retransmit_count,
self.retransmit_timeout,
REB_TIMEOUT,
REB_MAX_RT,
0,
Duration::from_secs(valid_sec.into()),
)
.unwrap_or(Duration::new(0, 0)),
))
} else {
Err(DhcpError::new(
ErrorKind::Bug,
format!("In Rebind state without lease valid timer: {self:?}"),
))
}
}
pub(crate) async fn rebind(&mut self) -> Result<(), DhcpError> {
if self.retransmit_count == 0 {
self.trans_begin_time = Instant::now();
self.regen_xid();
}
loop {
self.retransmit_timeout = self.gen_rebind_wait_time()?;
if self.retransmit_timeout.is_zero() {
log::info!(
"Exceeded lease valid time for Rebind stage, move to \
Solicit stage"
);
self.reset_retransmit_counters();
self.lease = None;
self.state = DhcpV6State::Solicit;
return Ok(());
};
match tokio::time::timeout(self.retransmit_timeout, self._rebind())
.await
{
Ok(Ok(())) => return Ok(()),
Ok(Err(e)) => {
log::info!(
"Retrying on error {e} after {} seconds",
self.retransmit_timeout.as_secs()
);
tokio::time::sleep(self.retransmit_timeout).await;
}
Err(_) => {
log::info!(
"Timeout({}s) on waiting DHCP server Reply for \
Rebind, retrying",
self.retransmit_timeout.as_secs(),
);
self.retransmit_count += 1;
}
}
}
}
async fn _rebind(&mut self) -> Result<(), DhcpError> {
let lease = if let Some(l) = self.lease.as_ref() {
l
} else {
return Err(DhcpError::new(
ErrorKind::Bug,
format!("In Rebind state without lease: {self:?}"),
));
};
let dhcp_msg = new_rebind_msg(
self.xid,
&self.config,
&self.trans_begin_time,
lease,
);
let xid = self.xid;
let udp_socket = self.get_udp_socket_or_init().await?;
log::debug!("Sending Rebind");
log::trace!("Sending Rebind {dhcp_msg:?}");
udp_socket.send_multicast(&dhcp_msg.emit()).await?;
log::debug!("Waiting server reply with Reply");
loop {
match udp_socket
.recv_dhcp_lease(DhcpV6MessageType::Reply, xid)
.await
{
Ok(Some(l)) => {
self.done(l)?;
return Ok(());
}
Ok(None) => (),
Err(e) => {
log::info!("Ignoring invalid DHCP package: {e}");
}
};
}
}
}
fn new_rebind_msg(
xid: u32,
config: &DhcpV6Config,
trans_begin_time: &Instant,
lease: &DhcpV6Lease,
) -> DhcpV6Message {
let mut ret = DhcpV6Message::new(
DhcpV6MessageType::Rebind,
xid,
&config.duid,
trans_begin_time,
);
ret.options.insert(DhcpV6Option::OptionRequestOption(
config.request_opts.clone(),
));
ret.load_lease(lease);
ret.options.remove(DhcpV6OptionCode::ServerId);
ret
}