Add a few DHCP options

Closes: #255
Approved by: whitequark
v0.7.x
Astro 2018-07-24 21:56:01 +02:00 committed by Homu
parent f3d34b09a2
commit 633d89b78e
3 changed files with 63 additions and 7 deletions

View File

@ -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",

View File

@ -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<T: AsRef<[u8]>> {
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<Ipv4Address>,
/// This field comes from a corresponding DhcpOption.
pub subnet_mask: Option<Ipv4Address>,
/// 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<Ipv4Address>; 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,
}
}

View File

@ -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};