|
@@ -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);
|
|
@@ -858,7 +877,7 @@ mod test {
|
|
|
0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
];
|
|
|
|
|
|
- static ACK_BYTES: &[u8] = &[
|
|
|
+ static ACK_DNS_SERVER_BYTES: &[u8] = &[
|
|
|
0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06, 0x91,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17, 0xeb, 0xc9,
|
|
|
0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
@@ -882,6 +901,28 @@ mod test {
|
|
|
0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08, 0xff
|
|
|
];
|
|
|
|
|
|
+ static ACK_LEASE_TIME_BYTES: &[u8] = &[
|
|
|
+ 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91, 0x62, 0xd2,
|
|
|
+ 0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
|
|
|
+ 0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56, 0x01,
|
|
|
+ 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+ ];
|
|
|
+
|
|
|
const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]);
|
|
|
const CLIENT_MAC: EthernetAddress = EthernetAddress([0x0, 0x0b, 0x82, 0x01, 0xfc, 0x42]);
|
|
|
const DHCP_SIZE: u16 = 1500;
|
|
@@ -984,6 +1025,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,
|
|
@@ -1031,8 +1073,9 @@ mod test {
|
|
|
|
|
|
#[test]
|
|
|
fn test_parse_ack_dns_servers() {
|
|
|
- let packet = Packet::new_unchecked(ACK_BYTES);
|
|
|
+ let packet = Packet::new_unchecked(ACK_DNS_SERVER_BYTES);
|
|
|
let repr = Repr::parse(&packet).unwrap();
|
|
|
+
|
|
|
// The packet described by ACK_BYTES advertises 4 DNS servers
|
|
|
// Here we ensure that we correctly parse the first 3 into our fixed
|
|
|
// length-3 array (see issue #305)
|
|
@@ -1041,4 +1084,14 @@ mod test {
|
|
|
Some(Ipv4Address([163, 1, 74, 7])),
|
|
|
Some(Ipv4Address([163, 1, 74, 3]))]));
|
|
|
}
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_parse_ack_lease_duration() {
|
|
|
+ let packet = Packet::new_unchecked(ACK_LEASE_TIME_BYTES);
|
|
|
+ let repr = Repr::parse(&packet).unwrap();
|
|
|
+
|
|
|
+ // Verify that the lease time in the ACK is properly parsed. The packet contains a lease
|
|
|
+ // duration of 598s.
|
|
|
+ assert_eq!(repr.lease_duration, Some(598));
|
|
|
+ }
|
|
|
}
|