From 8eb01aca364aefe5f823d68d552d62c76c9be4a3 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Thu, 7 Nov 2019 04:47:36 +0000 Subject: [PATCH] Add suppor for DHCP maximum message size option. --- src/dhcp/clientv4.rs | 2 +- src/wire/dhcpv4.rs | 42 +++++++++++++++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 361e899c3..498a7afb4 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -294,6 +294,7 @@ impl Client { client_identifier: Some(mac), server_identifier: None, parameter_request_list: None, + max_size: Some(raw_socket.payload_recv_capacity() as u16), dns_servers: None, }; let mut send_packet = |iface, endpoint, dhcp_repr| { @@ -305,7 +306,6 @@ impl Client { match self.state { ClientState::Discovering => { self.next_egress = now + Duration::from_secs(DISCOVER_TIMEOUT); - let endpoint = IpEndpoint { addr: Ipv4Address::BROADCAST.into(), port: UDP_SERVER_PORT, diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index d6b728657..7062c9c54 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -52,6 +52,7 @@ pub enum DhcpOption<'a> { ServerIdentifier(Ipv4Address), Router(Ipv4Address), SubnetMask(Ipv4Address), + MaximumDhcpMessageSize(u16), Other { kind: u8, data: &'a [u8] } } @@ -99,6 +100,9 @@ impl<'a> DhcpOption<'a> { (field::OPT_SUBNET_MASK, 4) => { option = DhcpOption::SubnetMask(Ipv4Address::from_bytes(data)); } + (field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => { + option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([data[0], data[1]])); + } (_, _) => { option = DhcpOption::Other { kind: kind, data: data }; } @@ -122,6 +126,9 @@ impl<'a> DhcpOption<'a> { &DhcpOption::SubnetMask(ip) => { 2 + ip.as_bytes().len() }, + &DhcpOption::MaximumDhcpMessageSize(_) => { + 4 + } &DhcpOption::Other { data, .. } => 2 + data.len() } } @@ -167,6 +174,10 @@ impl<'a> DhcpOption<'a> { buffer[0] = field::OPT_SUBNET_MASK; buffer[2..6].copy_from_slice(mask.as_bytes()); } + &DhcpOption::MaximumDhcpMessageSize(size) => { + buffer[0] = field::OPT_MAX_DHCP_MESSAGE_SIZE; + buffer[2..4].copy_from_slice(&size.to_be_bytes()[..]); + } &DhcpOption::Other { kind, data: provided } => { buffer[0] = kind; buffer[2..skip_length].copy_from_slice(provided); @@ -661,6 +672,8 @@ pub struct Repr<'a> { pub parameter_request_list: Option<&'a [u8]>, /// DNS servers pub dns_servers: Option<[Option; 3]>, + /// The maximum size dhcp packet the interface can receive + pub max_size: Option, } impl<'a> Repr<'a> { @@ -672,6 +685,7 @@ impl<'a> Repr<'a> { if self.requested_ip.is_some() { len += 6; } if self.client_identifier.is_some() { len += 9; } if self.server_identifier.is_some() { len += 6; } + if self.max_size.is_some() { len += 4; } if let Some(list) = self.parameter_request_list { len += list.len() + 2; } len @@ -710,6 +724,7 @@ impl<'a> Repr<'a> { let mut subnet_mask = None; let mut parameter_request_list = None; let mut dns_servers = None; + let mut max_size = None; let mut options = packet.options()?; while options.len() > 0 { @@ -736,6 +751,9 @@ impl<'a> Repr<'a> { } DhcpOption::SubnetMask(mask) => { subnet_mask = Some(mask); + }, + DhcpOption::MaximumDhcpMessageSize(size) => { + max_size = Some(size); } DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => { parameter_request_list = Some(data); @@ -760,7 +778,7 @@ impl<'a> Repr<'a> { Ok(Repr { 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, + subnet_mask, client_identifier, parameter_request_list, dns_servers, max_size, message_type: message_type?, }) } @@ -802,6 +820,9 @@ impl<'a> Repr<'a> { if let Some(ip) = self.requested_ip { let tmp = options; options = DhcpOption::RequestedIp(ip).emit(tmp); } + if let Some(size) = self.max_size { + let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(size).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); @@ -837,7 +858,7 @@ mod test { 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, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; static ACK_BYTES: &[u8] = &[ @@ -866,6 +887,7 @@ mod test { 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; #[test] fn test_deconstruct_discover() { @@ -883,18 +905,22 @@ mod test { assert_eq!(packet.relay_agent_ip(), IP_NULL); assert_eq!(packet.client_hardware_address(), CLIENT_MAC); let options = packet.options().unwrap(); - assert_eq!(options.len(), 3 + 9 + 6 + 6 + 1 + 7); + assert_eq!(options.len(), 3 + 9 + 6 + 4 + 6 + 1 + 7); let (options, message_type) = DhcpOption::parse(options).unwrap(); assert_eq!(message_type, DhcpOption::MessageType(MessageType::Discover)); - assert_eq!(options.len(), 9 + 6 + 6 + 1 + 7); + assert_eq!(options.len(), 9 + 6 + 4 + 6 + 1 + 7); let (options, client_id) = DhcpOption::parse(options).unwrap(); assert_eq!(client_id, DhcpOption::ClientIdentifier(CLIENT_MAC)); - assert_eq!(options.len(), 6 + 6 + 1 + 7); + assert_eq!(options.len(), 6 + 4 + 6 + 1 + 7); let (options, client_id) = DhcpOption::parse(options).unwrap(); assert_eq!(client_id, DhcpOption::RequestedIp(IP_NULL)); + assert_eq!(options.len(), 4 + 6 + 1 + 7); + + let (options, msg_size) = DhcpOption::parse(options).unwrap(); + assert_eq!(msg_size, DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE)); assert_eq!(options.len(), 6 + 1 + 7); let (options, client_id) = DhcpOption::parse(options).unwrap(); @@ -910,7 +936,7 @@ mod test { #[test] fn test_construct_discover() { - let mut bytes = vec![0xa5; 272]; + let mut bytes = vec![0xa5; 276]; let mut packet = Packet::new_unchecked(&mut bytes); packet.set_magic_number(MAGIC_COOKIE); packet.set_sname_and_boot_file_to_zero(); @@ -932,6 +958,7 @@ mod test { let tmp = options; options = DhcpOption::MessageType(MessageType::Discover).emit(tmp); let tmp = options; options = DhcpOption::ClientIdentifier(CLIENT_MAC).emit(tmp); let tmp = options; options = DhcpOption::RequestedIp(IP_NULL).emit(tmp); + let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(tmp); let option = DhcpOption::Other { kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42], }; @@ -940,7 +967,7 @@ mod test { } let packet = &mut packet.into_inner()[..]; - for byte in &mut packet[265..272] { + for byte in &mut packet[269..276] { *byte = 0; // padding bytes } @@ -959,6 +986,7 @@ mod test { subnet_mask: None, relay_agent_ip: IP_NULL, broadcast: false, + max_size: Some(DHCP_SIZE), requested_ip: Some(IP_NULL), client_identifier: Some(CLIENT_MAC), server_identifier: None,