Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DHCP: Add an upper bound to the renew/rebind timeout in RetryConfig #835

Merged
merged 1 commit into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
thvdveld marked this conversation as resolved.
Show resolved Hide resolved
};
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
Loading