Skip to content

Commit

Permalink
Add max_renew_timeout field to DHCP RetryConfig struct
Browse files Browse the repository at this point in the history
  • Loading branch information
JarredAllen committed Aug 21, 2023
1 parent 01e1acb commit 3755e86
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 2 deletions.
50 changes: 48 additions & 2 deletions src/socket/dhcpv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,17 @@ enum ClientState {
/// Timeout and retry configuration.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct RetryConfig {
pub discover_timeout: Duration,
/// The REQUEST timeout doubles every 2 tries.
pub initial_request_timeout: Duration,
pub request_retries: u16,
pub min_renew_timeout: Duration,
/// An upper bound on how long to wait between retrying a renew or rebind.
///
/// Set this to [`Duration::MAX`] if you don't want to impose an upper bound.
pub max_renew_timeout: Duration,
}

impl Default for RetryConfig {
Expand All @@ -129,6 +134,7 @@ impl Default for RetryConfig {
initial_request_timeout: Duration::from_secs(5),
request_retries: 5,
min_renew_timeout: Duration::from_secs(60),
max_renew_timeout: Duration::MAX,
}
}
}
Expand Down Expand Up @@ -214,6 +220,11 @@ impl<'a> Socket<'a> {
self.retry_config = config;
}

/// Gets the current retry/timeouts configuration
pub fn get_retry_config(&self) -> RetryConfig {
self.retry_config
}

/// Set the outgoing options.
pub fn set_outgoing_options(&mut self, options: &'a [DhcpOption<'a>]) {
self.outgoing_options = options;
Expand Down Expand Up @@ -682,14 +693,16 @@ impl<'a> Socket<'a> {
+ self
.retry_config
.min_renew_timeout
.max((state.expires_at - now) / 2);
.max((state.expires_at - now) / 2)
.min(self.retry_config.max_renew_timeout);
} else {
state.renew_at = now
+ self
.retry_config
.min_renew_timeout
.max((state.rebind_at - now) / 2)
.min(state.rebind_at - now);
.min(state.rebind_at - now)
.min(self.retry_config.max_renew_timeout);
}

self.transaction_id = next_transaction_id;
Expand Down Expand Up @@ -1358,6 +1371,39 @@ mod test {
}
}

#[rstest]
#[case::ip(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
fn test_min_max_renew_timeout(#[case] medium: Medium) {
let mut s = socket_bound(medium);
// Set a minimum of 45s and a maximum of 120s
let config = RetryConfig {
max_renew_timeout: Duration::from_secs(120),
min_renew_timeout: Duration::from_secs(45),
..s.get_retry_config()
};
s.set_retry_config(config);
recv!(s, []);
// First renew attempt at T1
recv!(s, time 499_999, []);
recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
// Next renew attempt 120s after T1 because we hit the max
recv!(s, time 619_999, []);
recv!(s, time 620_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
// Next renew attempt 120s after previous because we hit the max again
recv!(s, time 739_999, []);
recv!(s, time 740_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
// Next renew attempt half way to T2
recv!(s, time 807_499, []);
recv!(s, time 807_500, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
// Next renew attempt 45s after previous because we hit the min
recv!(s, time 852_499, []);
recv!(s, time 852_500, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
// Next is a rebind, because the min puts us after T2
recv!(s, time 874_999, []);
recv!(s, time 875_000, [(IP_BROADCAST_ADDRESSED, UDP_SEND, DHCP_REBIND)]);
}

#[rstest]
#[case::ip(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
Expand Down
2 changes: 2 additions & 0 deletions src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ pub struct Duration {

impl Duration {
pub const ZERO: Duration = Duration::from_micros(0);
/// The longest possible duration we can encode.
pub const MAX: Duration = Duration::from_micros(u64::MAX);
/// Create a new `Duration` from a number of microseconds.
pub const fn from_micros(micros: u64) -> Duration {
Duration { micros }
Expand Down

0 comments on commit 3755e86

Please sign in to comment.