Adding DHCP lease management
This commit is contained in:
parent
b1db516b10
commit
647e27ed2b
|
@ -13,7 +13,7 @@ use super::{UDP_SERVER_PORT, UDP_CLIENT_PORT};
|
|||
const DISCOVER_TIMEOUT: u64 = 10;
|
||||
const REQUEST_TIMEOUT: u64 = 1;
|
||||
const REQUEST_RETRIES: u16 = 15;
|
||||
const RENEW_INTERVAL: u64 = 60;
|
||||
const DEFAULT_RENEW_INTERVAL: u32 = 60;
|
||||
const RENEW_RETRIES: u16 = 3;
|
||||
const PARAMETER_REQUEST_LIST: &[u8] = &[
|
||||
dhcpv4_field::OPT_SUBNET_MASK,
|
||||
|
@ -59,6 +59,8 @@ pub struct Client {
|
|||
raw_handle: SocketHandle,
|
||||
/// When to send next request
|
||||
next_egress: Instant,
|
||||
/// When any existing DHCP address will expire.
|
||||
lease_expiration: Option<Instant>,
|
||||
transaction_id: u32,
|
||||
}
|
||||
|
||||
|
@ -104,6 +106,7 @@ impl Client {
|
|||
raw_handle,
|
||||
next_egress: now,
|
||||
transaction_id: 1,
|
||||
lease_expiration: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,6 +117,25 @@ impl Client {
|
|||
self.next_egress - now
|
||||
}
|
||||
|
||||
/// Check if the existing IP address lease has expired.
|
||||
///
|
||||
/// # Note
|
||||
/// RC 2131 requires that a client immediately cease usage of an
|
||||
/// address once the lease has expired.
|
||||
///
|
||||
/// # Args
|
||||
/// * `now` - The current network time instant.
|
||||
///
|
||||
/// # Returns
|
||||
/// True if a lease exists and it has expired. False otherwise.
|
||||
pub fn lease_expired(&self, now: Instant) -> bool {
|
||||
if let Some(expiration) = self.lease_expiration {
|
||||
return now > expiration
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Process incoming packets on the contained RawSocket, and send
|
||||
/// DHCP requests when timeouts are ready.
|
||||
///
|
||||
|
@ -199,12 +221,18 @@ impl Client {
|
|||
|
||||
// once we receive the ack, we can pass the config to the user
|
||||
let config = if dhcp_repr.message_type == DhcpMessageType::Ack {
|
||||
let address = dhcp_repr.subnet_mask
|
||||
.and_then(|mask| IpAddress::Ipv4(mask).to_prefix_len())
|
||||
.map(|prefix_len| Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len));
|
||||
let router = dhcp_repr.router;
|
||||
let dns_servers = dhcp_repr.dns_servers
|
||||
.unwrap_or([None; 3]);
|
||||
let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_RENEW_INTERVAL * 2);
|
||||
self.lease_expiration = Some(now + Duration::from_secs(lease_duration.into()));
|
||||
|
||||
// RFC 2131 indicates clients should renew a lease halfway through its expiration.
|
||||
self.next_egress = now + Duration::from_secs((lease_duration / 2).into());
|
||||
|
||||
let address = dhcp_repr.subnet_mask
|
||||
.and_then(|mask| IpAddress::Ipv4(mask).to_prefix_len())
|
||||
.map(|prefix_len| Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len));
|
||||
let router = dhcp_repr.router;
|
||||
let dns_servers = dhcp_repr.dns_servers
|
||||
.unwrap_or([None; 3]);
|
||||
Some(Config { address, router, dns_servers })
|
||||
} else {
|
||||
None
|
||||
|
@ -227,7 +255,6 @@ impl Client {
|
|||
if dhcp_repr.message_type == DhcpMessageType::Ack &&
|
||||
server_identifier == r_state.server_identifier =>
|
||||
{
|
||||
self.next_egress = now + Duration::from_secs(RENEW_INTERVAL);
|
||||
let p_state = RenewState {
|
||||
retry: 0,
|
||||
endpoint_ip: *src_ip,
|
||||
|
@ -239,7 +266,7 @@ impl Client {
|
|||
if dhcp_repr.message_type == DhcpMessageType::Ack &&
|
||||
server_identifier == p_state.server_identifier =>
|
||||
{
|
||||
self.next_egress = now + Duration::from_secs(RENEW_INTERVAL);
|
||||
self.next_egress = now + Duration::from_secs(DEFAULT_RENEW_INTERVAL.into());
|
||||
p_state.retry = 0;
|
||||
None
|
||||
}
|
||||
|
@ -294,6 +321,7 @@ impl Client {
|
|||
server_identifier: None,
|
||||
parameter_request_list: None,
|
||||
max_size: Some(raw_socket.payload_recv_capacity() as u16),
|
||||
lease_duration: None,
|
||||
dns_servers: None,
|
||||
};
|
||||
let mut send_packet = |iface, endpoint, dhcp_repr| {
|
||||
|
@ -330,7 +358,7 @@ impl Client {
|
|||
}
|
||||
ClientState::Renew(ref mut p_state) => {
|
||||
p_state.retry += 1;
|
||||
self.next_egress = now + Duration::from_secs(RENEW_INTERVAL);
|
||||
self.next_egress = now + Duration::from_secs(DEFAULT_RENEW_INTERVAL.into());
|
||||
|
||||
let endpoint = IpEndpoint {
|
||||
addr: p_state.endpoint_ip.into(),
|
||||
|
|
|
@ -50,6 +50,7 @@ pub enum DhcpOption<'a> {
|
|||
RequestedIp(Ipv4Address),
|
||||
ClientIdentifier(EthernetAddress),
|
||||
ServerIdentifier(Ipv4Address),
|
||||
IpLeaseTime(u32),
|
||||
Router(Ipv4Address),
|
||||
SubnetMask(Ipv4Address),
|
||||
MaximumDhcpMessageSize(u16),
|
||||
|
@ -103,6 +104,9 @@ impl<'a> DhcpOption<'a> {
|
|||
(field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => {
|
||||
option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([data[0], data[1]]));
|
||||
}
|
||||
(field::OPT_IP_LEASE_TIME, 4) => {
|
||||
option = DhcpOption::IpLeaseTime(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
|
||||
}
|
||||
(_, _) => {
|
||||
option = DhcpOption::Other { kind: kind, data: data };
|
||||
}
|
||||
|
@ -129,6 +133,7 @@ impl<'a> DhcpOption<'a> {
|
|||
&DhcpOption::MaximumDhcpMessageSize(_) => {
|
||||
4
|
||||
}
|
||||
&DhcpOption::IpLeaseTime(_) => 6,
|
||||
&DhcpOption::Other { data, .. } => 2 + data.len()
|
||||
}
|
||||
}
|
||||
|
@ -178,6 +183,10 @@ impl<'a> DhcpOption<'a> {
|
|||
buffer[0] = field::OPT_MAX_DHCP_MESSAGE_SIZE;
|
||||
buffer[2..4].copy_from_slice(&size.to_be_bytes()[..]);
|
||||
}
|
||||
DhcpOption::IpLeaseTime(lease_time) => {
|
||||
buffer[0] = field::OPT_IP_LEASE_TIME;
|
||||
buffer[2..6].copy_from_slice(&lease_time.to_be_bytes()[..]);
|
||||
}
|
||||
DhcpOption::Other { kind, data: provided } => {
|
||||
buffer[0] = kind;
|
||||
buffer[2..skip_length].copy_from_slice(provided);
|
||||
|
@ -674,6 +683,8 @@ pub struct Repr<'a> {
|
|||
pub dns_servers: Option<[Option<Ipv4Address>; 3]>,
|
||||
/// The maximum size dhcp packet the interface can receive
|
||||
pub max_size: Option<u16>,
|
||||
/// The DHCP IP lease duration, specified in seconds.
|
||||
pub lease_duration: Option<u32>
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
|
@ -725,6 +736,7 @@ impl<'a> Repr<'a> {
|
|||
let mut parameter_request_list = None;
|
||||
let mut dns_servers = None;
|
||||
let mut max_size = None;
|
||||
let mut lease_duration = None;
|
||||
|
||||
let mut options = packet.options()?;
|
||||
while !options.is_empty() {
|
||||
|
@ -755,6 +767,9 @@ impl<'a> Repr<'a> {
|
|||
DhcpOption::MaximumDhcpMessageSize(size) => {
|
||||
max_size = Some(size);
|
||||
}
|
||||
DhcpOption::IpLeaseTime(duration) => {
|
||||
lease_duration = Some(duration);
|
||||
}
|
||||
DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => {
|
||||
parameter_request_list = Some(data);
|
||||
}
|
||||
|
@ -776,6 +791,7 @@ impl<'a> Repr<'a> {
|
|||
transaction_id, client_hardware_address, client_ip, your_ip, server_ip, relay_agent_ip,
|
||||
broadcast, requested_ip, server_identifier, router,
|
||||
subnet_mask, client_identifier, parameter_request_list, dns_servers, max_size,
|
||||
lease_duration,
|
||||
message_type: message_type?,
|
||||
})
|
||||
}
|
||||
|
@ -820,6 +836,9 @@ impl<'a> Repr<'a> {
|
|||
if let Some(size) = self.max_size {
|
||||
let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(size).emit(tmp);
|
||||
}
|
||||
if let Some(duration) = self.lease_duration {
|
||||
let tmp = options; options = DhcpOption::IpLeaseTime(duration).emit(tmp);
|
||||
}
|
||||
if let Some(list) = self.parameter_request_list {
|
||||
let option = DhcpOption::Other{ kind: field::OPT_PARAMETER_REQUEST_LIST, data: list };
|
||||
let tmp = options; options = option.emit(tmp);
|
||||
|
@ -984,6 +1003,7 @@ mod test {
|
|||
relay_agent_ip: IP_NULL,
|
||||
broadcast: false,
|
||||
max_size: Some(DHCP_SIZE),
|
||||
lease_duration: None,
|
||||
requested_ip: Some(IP_NULL),
|
||||
client_identifier: Some(CLIENT_MAC),
|
||||
server_identifier: None,
|
||||
|
|
Loading…
Reference in New Issue