diff --git a/Cargo.toml b/Cargo.toml index caa7b6c..75a9f64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ verbose = [] "socket-udp" = [] "socket-tcp" = [] "socket-icmp" = [] +"proto-dhcpv4" = ["proto-ipv4"] default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ "phy-raw_socket", "phy-tap_interface", diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index b674ede..81173a5 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -50,6 +50,8 @@ pub enum DhcpOption<'a> { RequestedIp(Ipv4Address), ClientIdentifier(EthernetAddress), ServerIdentifier(Ipv4Address), + Router(Ipv4Address), + SubnetMask(Ipv4Address), Other { kind: u8, data: &'a [u8] } } @@ -91,6 +93,12 @@ impl<'a> DhcpOption<'a> { (field::OPT_SERVER_IDENTIFIER, 4) => { option = DhcpOption::ServerIdentifier(Ipv4Address::from_bytes(data)); } + (field::OPT_ROUTER, 4) => { + option = DhcpOption::Router(Ipv4Address::from_bytes(data)); + } + (field::OPT_SUBNET_MASK, 4) => { + option = DhcpOption::SubnetMask(Ipv4Address::from_bytes(data)); + } (_, _) => { option = DhcpOption::Other { kind: kind, data: data }; } @@ -108,7 +116,10 @@ impl<'a> DhcpOption<'a> { &DhcpOption::ClientIdentifier(eth_addr) => { 3 + eth_addr.as_bytes().len() } - &DhcpOption::RequestedIp(ip) | &DhcpOption::ServerIdentifier(ip) => { + &DhcpOption::RequestedIp(ip) | + &DhcpOption::ServerIdentifier(ip) | + &DhcpOption::Router(ip) | + &DhcpOption::SubnetMask(ip) => { 2 + ip.as_bytes().len() }, &DhcpOption::Other { data, .. } => 2 + data.len() @@ -148,6 +159,14 @@ impl<'a> DhcpOption<'a> { buffer[0] = field::OPT_SERVER_IDENTIFIER; buffer[2..6].copy_from_slice(ip.as_bytes()); } + &DhcpOption::Router(ip) => { + buffer[0] = field::OPT_ROUTER; + buffer[2..6].copy_from_slice(ip.as_bytes()); + } + &DhcpOption::SubnetMask(mask) => { + buffer[0] = field::OPT_SUBNET_MASK; + buffer[2..6].copy_from_slice(mask.as_bytes()); + } &DhcpOption::Other { kind, data: provided } => { buffer[0] = kind; buffer[2..skip_length].copy_from_slice(provided); @@ -165,7 +184,7 @@ pub struct Packet> { buffer: T } -mod field { +pub(crate) mod field { #![allow(non_snake_case)] #![allow(unused)] @@ -604,6 +623,10 @@ pub struct Repr<'a> { /// This field is also known as `siaddr` in the RFC. It may be set by the server in DHCPOFFER /// and DHCPACK messages, and represent the address of the next server to use in bootstrap. pub server_ip: Ipv4Address, + /// Default gateway + pub router: Option, + /// This field comes from a corresponding DhcpOption. + pub subnet_mask: Option, /// This field is also known as `giaddr` in the RFC. In order to allow DHCP clients on subnets /// not directly served by DHCP servers to communicate with DHCP servers, DHCP relay agents can /// be installed on these subnets. The DHCP client broadcasts on the local link; the relay @@ -636,6 +659,8 @@ pub struct Repr<'a> { /// The parameter request list informs the server about which configuration parameters /// the client is interested in. pub parameter_request_list: Option<&'a [u8]>, + /// DNS servers + pub dns_servers: Option<[Option; 3]>, } impl<'a> Repr<'a> { @@ -681,7 +706,10 @@ impl<'a> Repr<'a> { let mut requested_ip = None; let mut client_identifier = None; let mut server_identifier = None; + let mut router = None; + let mut subnet_mask = None; let mut parameter_request_list = None; + let mut dns_servers = None; let mut options = packet.options()?; while options.len() > 0 { @@ -703,9 +731,25 @@ impl<'a> Repr<'a> { DhcpOption::ServerIdentifier(ip) => { server_identifier = Some(ip); } + DhcpOption::Router(ip) => { + router = Some(ip); + } + DhcpOption::SubnetMask(mask) => { + subnet_mask = Some(mask); + } DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => { parameter_request_list = Some(data); } + DhcpOption::Other {kind: field::OPT_DOMAIN_NAME_SERVER, data} => { + let mut dns_servers_inner = [None; 3]; + for i in 0.. { + let offset = 4 * i; + let end = offset + 4; + if end > data.len() { break } + dns_servers_inner[i] = Some(Ipv4Address::from_bytes(&data[offset..end])); + } + dns_servers = Some(dns_servers_inner); + } DhcpOption::Other {..} => {} } options = next_options; @@ -715,7 +759,8 @@ 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, client_identifier, parameter_request_list, + broadcast, requested_ip, server_identifier, router, + subnet_mask, client_identifier, parameter_request_list, dns_servers, message_type: message_type?, }) } @@ -747,6 +792,12 @@ impl<'a> Repr<'a> { if let Some(ip) = self.server_identifier { let tmp = options; options = DhcpOption::ServerIdentifier(ip).emit(tmp); } + if let Some(ip) = self.router { + let tmp = options; options = DhcpOption::Router(ip).emit(tmp); + } + if let Some(ip) = self.subnet_mask { + let tmp = options; options = DhcpOption::SubnetMask(ip).emit(tmp); + } if let Some(ip) = self.requested_ip { let tmp = options; options = DhcpOption::RequestedIp(ip).emit(tmp); } @@ -879,12 +930,15 @@ mod test { client_ip: IP_NULL, your_ip: IP_NULL, server_ip: IP_NULL, + router: None, + subnet_mask: None, relay_agent_ip: IP_NULL, broadcast: false, requested_ip: Some(IP_NULL), client_identifier: Some(CLIENT_MAC), server_identifier: None, parameter_request_list: Some(&[1, 3, 6, 42]), + dns_servers: None, } } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 05ab35a..20b6a34 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -109,8 +109,8 @@ mod ndiscoption; mod mld; mod udp; mod tcp; -#[cfg(feature = "proto-ipv4")] -mod dhcpv4; +#[cfg(feature = "proto-dhcpv4")] +pub(crate) mod dhcpv4; pub use self::pretty_print::PrettyPrinter; @@ -216,6 +216,7 @@ pub use self::tcp::{SeqNumber as TcpSeqNumber, Repr as TcpRepr, Control as TcpControl}; -#[cfg(feature = "proto-ipv4")] +#[cfg(feature = "proto-dhcpv4")] pub use self::dhcpv4::{Packet as DhcpPacket, - Repr as DhcpRepr}; + Repr as DhcpRepr, + MessageType as DhcpMessageType};