Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions src/socket/dhcpv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use crate::iface::Context;
use crate::time::{Duration, Instant};
use crate::wire::dhcpv4::field as dhcpv4_field;
use crate::wire::{
DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, DhcpMessageType, DhcpPacket,
DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4AddressExt, Ipv4Cidr, Ipv4Repr,
UDP_HEADER_LEN, UdpRepr,
DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_MAX_NTP_SERVER_COUNT, DHCP_SERVER_PORT,
DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4AddressExt,
Ipv4Cidr, Ipv4Repr, UDP_HEADER_LEN, UdpRepr,
};
use crate::wire::{DhcpOption, HardwareAddress};
use heapless::Vec;
Expand All @@ -23,6 +23,7 @@ const DEFAULT_PARAMETER_REQUEST_LIST: &[u8] = &[
dhcpv4_field::OPT_SUBNET_MASK,
dhcpv4_field::OPT_ROUTER,
dhcpv4_field::OPT_DOMAIN_NAME_SERVER,
dhcpv4_field::OPT_NTP_SERVERS,
];

/// IPv4 configuration data provided by the DHCP server.
Expand All @@ -39,6 +40,8 @@ pub struct Config<'a> {
pub router: Option<Ipv4Address>,
/// DNS servers
pub dns_servers: Vec<Ipv4Address, DHCP_MAX_DNS_SERVER_COUNT>,
/// NTP servers (RFC 2132 §8.3)
pub ntp_servers: Vec<Ipv4Address, DHCP_MAX_NTP_SERVER_COUNT>,
/// Received DHCP packet
pub packet: Option<DhcpPacket<&'a [u8]>>,
}
Expand Down Expand Up @@ -490,11 +493,24 @@ impl<'a> Socket<'a> {
dns_servers.push(*a).ok();
});

// Cleanup the NTP servers list, keeping only unicasts
let mut ntp_servers = Vec::new();

dhcp_repr
.ntp_servers
.iter()
.flatten()
.filter(|s| s.x_is_unicast())
.for_each(|a| {
ntp_servers.push(*a).ok();
});

let config = Config {
server,
address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len),
router: dhcp_repr.router,
dns_servers,
ntp_servers,
packet: None,
};

Expand Down Expand Up @@ -598,6 +614,7 @@ impl<'a> Socket<'a> {
renew_duration: None,
rebind_duration: None,
dns_servers: None,
ntp_servers: None,
additional_options: self.outgoing_options,
};

Expand Down Expand Up @@ -753,6 +770,7 @@ impl<'a> Socket<'a> {
address: state.config.address,
router: state.config.router,
dns_servers: state.config.dns_servers.clone(),
ntp_servers: state.config.ntp_servers.clone(),
packet: self
.receive_packet_buffer
.as_deref()
Expand Down Expand Up @@ -901,6 +919,11 @@ mod test {
const DNS_IP_3: Ipv4Address = Ipv4Address::new(1, 1, 1, 3);
const DNS_IPS: &[Ipv4Address] = &[DNS_IP_1, DNS_IP_2, DNS_IP_3];

const NTP_IP_1: Ipv4Address = Ipv4Address::new(129, 6, 15, 28);
const NTP_IP_2: Ipv4Address = Ipv4Address::new(129, 6, 15, 29);
const NTP_IP_3: Ipv4Address = Ipv4Address::new(129, 6, 15, 30);
const NTP_IPS: &[Ipv4Address] = &[NTP_IP_1, NTP_IP_2, NTP_IP_3];

const MASK_24: Ipv4Address = Ipv4Address::new(255, 255, 255, 0);

const MY_MAC: EthernetAddress = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]);
Expand Down Expand Up @@ -983,6 +1006,7 @@ mod test {
server_identifier: None,
parameter_request_list: None,
dns_servers: None,
ntp_servers: None,
max_size: None,
renew_duration: None,
rebind_duration: None,
Expand All @@ -993,7 +1017,7 @@ mod test {
const DHCP_DISCOVER: DhcpRepr = DhcpRepr {
message_type: DhcpMessageType::Discover,
client_identifier: Some(MY_MAC),
parameter_request_list: Some(&[1, 3, 6]),
parameter_request_list: Some(&[1, 3, 6, 42]),
max_size: Some(1432),
..DHCP_DEFAULT
};
Expand All @@ -1008,6 +1032,7 @@ mod test {
router: Some(SERVER_IP),
subnet_mask: Some(MASK_24),
dns_servers: Some(Vec::from_slice(DNS_IPS).unwrap()),
ntp_servers: Some(Vec::from_slice(NTP_IPS).unwrap()),
lease_duration: Some(1000),

..DHCP_DEFAULT
Expand All @@ -1021,7 +1046,7 @@ mod test {
max_size: Some(1432),

requested_ip: Some(MY_IP),
parameter_request_list: Some(&[1, 3, 6]),
parameter_request_list: Some(&[1, 3, 6, 42]),
..DHCP_DEFAULT
};

Expand All @@ -1035,6 +1060,7 @@ mod test {
router: Some(SERVER_IP),
subnet_mask: Some(MASK_24),
dns_servers: Some(Vec::from_slice(DNS_IPS).unwrap()),
ntp_servers: Some(Vec::from_slice(NTP_IPS).unwrap()),
lease_duration: Some(1000),

..DHCP_DEFAULT
Expand All @@ -1056,7 +1082,7 @@ mod test {
max_size: Some(1432),

requested_ip: None,
parameter_request_list: Some(&[1, 3, 6]),
parameter_request_list: Some(&[1, 3, 6, 42]),
..DHCP_DEFAULT
};

Expand All @@ -1068,7 +1094,7 @@ mod test {
max_size: Some(1432),

requested_ip: None,
parameter_request_list: Some(&[1, 3, 6]),
parameter_request_list: Some(&[1, 3, 6, 42]),
..DHCP_DEFAULT
};

Expand Down Expand Up @@ -1111,6 +1137,7 @@ mod test {
},
address: Ipv4Cidr::new(MY_IP, 24),
dns_servers: Vec::from_slice(DNS_IPS).unwrap(),
ntp_servers: Vec::from_slice(NTP_IPS).unwrap(),
router: Some(SERVER_IP),
packet: None,
},
Expand Down Expand Up @@ -1146,6 +1173,7 @@ mod test {
},
address: Ipv4Cidr::new(MY_IP, 24),
dns_servers: Vec::from_slice(DNS_IPS).unwrap(),
ntp_servers: Vec::from_slice(NTP_IPS).unwrap(),
router: Some(SERVER_IP),
packet: None,
}))
Expand Down Expand Up @@ -1184,6 +1212,7 @@ mod test {
},
address: Ipv4Cidr::new(MY_IP, 24),
dns_servers: Vec::from_slice(DNS_IPS).unwrap(),
ntp_servers: Vec::from_slice(NTP_IPS).unwrap(),
router: Some(SERVER_IP),
packet: None,
}))
Expand Down
78 changes: 78 additions & 0 deletions src/wire/dhcpv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::wire::{EthernetAddress, Ipv4Address};
pub const SERVER_PORT: u16 = 67;
pub const CLIENT_PORT: u16 = 68;
pub const MAX_DNS_SERVER_COUNT: usize = 3;
pub const MAX_NTP_SERVER_COUNT: usize = 3;

const DHCP_MAGIC_NUMBER: u32 = 0x63825363;

Expand Down Expand Up @@ -647,6 +648,8 @@ pub struct Repr<'a> {
pub parameter_request_list: Option<&'a [u8]>,
/// DNS servers
pub dns_servers: Option<Vec<Ipv4Address, MAX_DNS_SERVER_COUNT>>,
/// NTP servers (RFC 2132 §8.3)
pub ntp_servers: Option<Vec<Ipv4Address, MAX_NTP_SERVER_COUNT>>,
/// The maximum size dhcp packet the interface can receive
pub max_size: Option<u16>,
/// The DHCP IP lease duration, specified in seconds.
Expand Down Expand Up @@ -692,6 +695,10 @@ impl<'a> Repr<'a> {
len += 2;
len += dns_servers.iter().count() * core::mem::size_of::<u32>();
}
if let Some(ntp_servers) = &self.ntp_servers {
len += 2;
len += ntp_servers.iter().count() * core::mem::size_of::<u32>();
}
if let Some(list) = self.parameter_request_list {
len += list.len() + 2;
}
Expand Down Expand Up @@ -738,6 +745,7 @@ impl<'a> Repr<'a> {
let mut subnet_mask = None;
let mut parameter_request_list = None;
let mut dns_servers = None;
let mut ntp_servers = None;
let mut max_size = None;
let mut lease_duration = None;
let mut renew_duration = None;
Expand Down Expand Up @@ -804,6 +812,21 @@ impl<'a> Repr<'a> {
net_trace!("DHCP domain name servers contained invalid address");
}
}
(field::OPT_NTP_SERVERS, _) => {
let mut servers = Vec::new();
const IP_ADDR_BYTE_LEN: usize = 4;
let mut addrs = data.chunks_exact(IP_ADDR_BYTE_LEN);
for chunk in &mut addrs {
servers
.push(Ipv4Address::from_octets(chunk.try_into().unwrap()))
.ok();
}
ntp_servers = Some(servers);

if !addrs.remainder().is_empty() {
net_trace!("DHCP NTP servers contained invalid address");
}
}
_ => {}
}
}
Expand All @@ -826,6 +849,7 @@ impl<'a> Repr<'a> {
client_identifier,
parameter_request_list,
dns_servers,
ntp_servers,
max_size,
lease_duration,
renew_duration,
Expand Down Expand Up @@ -942,6 +966,24 @@ impl<'a> Repr<'a> {
})?;
}

if let Some(ntp_servers) = &self.ntp_servers {
const IP_SIZE: usize = core::mem::size_of::<u32>();
let mut servers = [0; MAX_NTP_SERVER_COUNT * IP_SIZE];

let data_len = ntp_servers
.iter()
.enumerate()
.inspect(|(i, ip)| {
servers[(i * IP_SIZE)..((i + 1) * IP_SIZE)].copy_from_slice(&ip.octets());
})
.count()
* IP_SIZE;
options.emit(DhcpOption {
kind: field::OPT_NTP_SERVERS,
data: &servers[..data_len],
})?;
}

for option in self.additional_options {
options.emit(*option)?;
}
Expand Down Expand Up @@ -1169,6 +1211,7 @@ mod test {
server_identifier: None,
parameter_request_list: None,
dns_servers: None,
ntp_servers: None,
max_size: None,
renew_duration: None,
rebind_duration: None,
Expand Down Expand Up @@ -1199,6 +1242,7 @@ mod test {
server_identifier: None,
parameter_request_list: Some(&[1, 3, 6, 42]),
dns_servers: None,
ntp_servers: None,
additional_options: &[],
}
}
Expand Down Expand Up @@ -1266,6 +1310,40 @@ mod test {
);
}

#[test]
fn test_emit_offer_ntp() {
let repr = {
let mut repr = offer_repr();
repr.ntp_servers = Some(
Vec::from_slice(&[
Ipv4Address::new(129, 6, 15, 28),
Ipv4Address::new(129, 6, 15, 29),
Ipv4Address::new(129, 6, 15, 30),
])
.unwrap(),
);
repr
};
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit(&mut packet).unwrap();

let packet = Packet::new_unchecked(&bytes);
let repr_parsed = Repr::parse(&packet).unwrap();

assert_eq!(
repr_parsed.ntp_servers,
Some(
Vec::from_slice(&[
Ipv4Address::new(129, 6, 15, 28),
Ipv4Address::new(129, 6, 15, 29),
Ipv4Address::new(129, 6, 15, 30),
])
.unwrap()
)
);
}

#[test]
fn test_emit_dhcp_option() {
static DATA: &[u8] = &[1, 3, 6];
Expand Down
3 changes: 2 additions & 1 deletion src/wire/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ pub use self::tcp::{
#[cfg(feature = "proto-dhcpv4")]
pub use self::dhcpv4::{
CLIENT_PORT as DHCP_CLIENT_PORT, DhcpOption, DhcpOptionWriter, Flags as DhcpFlags,
MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, MessageType as DhcpMessageType,
MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT,
MAX_NTP_SERVER_COUNT as DHCP_MAX_NTP_SERVER_COUNT, MessageType as DhcpMessageType,
OpCode as DhcpOpCode, Packet as DhcpPacket, Repr as DhcpRepr, SERVER_PORT as DHCP_SERVER_PORT,
};

Expand Down
Loading