浏览代码

dhcp: add max_lease_duration option

Dario Nieuwenhuis 4 年之前
父节点
当前提交
e82ef25e91
共有 2 个文件被更改,包括 32 次插入7 次删除
  1. 10 2
      examples/dhcp_client.rs
  2. 22 5
      src/socket/dhcpv4.rs

+ 10 - 2
examples/dhcp_client.rs

@@ -5,7 +5,7 @@ use std::collections::BTreeMap;
 use std::os::unix::io::AsRawFd;
 use log::*;
 
-use smoltcp::phy::{Device, Medium, wait as phy_wait};
+use smoltcp::{phy::{Device, Medium, wait as phy_wait}, time::Duration};
 use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr};
 use smoltcp::iface::{NeighborCache, InterfaceBuilder, Interface, Routes};
 use smoltcp::socket::{SocketSet, Dhcpv4Socket, Dhcpv4Event};
@@ -42,7 +42,15 @@ fn main() {
     let mut iface = builder.finalize();
 
     let mut sockets = SocketSet::new(vec![]);
-    let dhcp_handle = sockets.add(Dhcpv4Socket::new());
+    let mut dhcp_socket = Dhcpv4Socket::new();
+
+    // Set a ridiculously short max lease time to show DHCP renews work properly.
+    // This will cause the DHCP client to start renewing after 5 seconds, and give up the
+    // lease after 10 seconds if renew hasn't succeeded.
+    // IMPORTANT: This should be removed in production.
+    dhcp_socket.set_max_lease_duration(Some(Duration::from_secs(10)));
+
+    let dhcp_handle = sockets.add(dhcp_socket);
 
     loop {
         let timestamp = Instant::now();

+ 22 - 5
src/socket/dhcpv4.rs

@@ -117,6 +117,10 @@ pub struct Dhcpv4Socket {
     config_changed: bool,
     /// xid of the last sent message.
     transaction_id: u32,
+
+    /// Max lease duration. If set, it sets a maximum cap to the server-provided lease duration.
+    /// Useful to react faster to IP configuration changes and to test whether renews work correctly.
+    max_lease_duration: Option<Duration>,
 }
 
 /// DHCP client socket.
@@ -135,9 +139,18 @@ impl Dhcpv4Socket {
             }),
             config_changed: true,
             transaction_id: 1,
+            max_lease_duration: None,
         }
     }
 
+    pub fn max_lease_duration(&self) -> Option<Duration> {
+        self.max_lease_duration
+    }
+
+    pub fn set_max_lease_duration(&mut self, max_lease_duration: Option<Duration>) {
+        self.max_lease_duration = max_lease_duration;
+    }
+
     pub(crate) fn poll_at(&self) -> PollAt {
         let t = match &self.state {
             ClientState::Discovering(state) => state.retry_at,
@@ -198,7 +211,7 @@ impl Dhcpv4Socket {
                 });
             }
             (ClientState::Requesting(state), DhcpMessageType::Ack) => {
-                if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr) {
+                if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr, self.max_lease_duration) {
                     self.config_changed = true;
                     self.state = ClientState::Renewing(RenewState{
                         server: state.server,
@@ -212,7 +225,7 @@ impl Dhcpv4Socket {
                 self.reset();
             }
             (ClientState::Renewing(state), DhcpMessageType::Ack) => {
-                if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr) {
+                if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr, self.max_lease_duration) {
                     state.renew_at = renew_at;
                     state.expires_at = expires_at;
                     if state.config != config {
@@ -232,7 +245,7 @@ impl Dhcpv4Socket {
         Ok(())
     }
 
-    fn parse_ack(now: Instant, _ip_repr: &Ipv4Repr, dhcp_repr: &DhcpRepr) -> Option<(Config, Instant, Instant)> {
+    fn parse_ack(now: Instant, _ip_repr: &Ipv4Repr, dhcp_repr: &DhcpRepr, max_lease_duration: Option<Duration>) -> Option<(Config, Instant, Instant)> {
         let subnet_mask = match dhcp_repr.subnet_mask {
             Some(subnet_mask) => subnet_mask,
             None => {
@@ -255,6 +268,10 @@ impl Dhcpv4Socket {
         }
 
         let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_LEASE_DURATION);
+        let mut lease_duration = Duration::from_secs(lease_duration as _);
+        if let Some(max_lease_duration) = max_lease_duration {
+            lease_duration = lease_duration.min(max_lease_duration);
+        }
 
         // Cleanup the DNS servers list, keeping only unicasts/
         // TP-Link TD-W8970 sends 0.0.0.0 as second DNS server if there's only one configured :(
@@ -278,8 +295,8 @@ impl Dhcpv4Socket {
         };
 
         // RFC 2131 indicates clients should renew a lease halfway through its expiration.
-        let renew_at = now + Duration::from_secs((lease_duration / 2).into());
-        let expires_at = now + Duration::from_secs(lease_duration.into());
+        let renew_at = now + lease_duration / 2;
+        let expires_at = now + lease_duration;
 
         Some((config, renew_at, expires_at))
     }