Skip to content

Enable support for DHCP option 15 (domain name) #972

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -60,6 +60,7 @@ defmt = ["dep:defmt", "heapless/defmt-03"]
"proto-sixlowpan" = ["proto-ipv6"]
"proto-sixlowpan-fragmentation" = ["proto-sixlowpan", "_proto-fragmentation"]
"proto-dns" = []
"proto-domainname" = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feature's name could be confusing as "domainname protocol" sounds a lot like DNS. I suggest proto-dhcpv4-domainname.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion!

"proto-ipsec" = ["proto-ipsec-ah", "proto-ipsec-esp"]
"proto-ipsec-ah" = []
"proto-ipsec-esp" = []
@@ -96,7 +97,7 @@ default = [
"std", "log", # needed for `cargo test --no-default-features --features default` :/
"medium-ethernet", "medium-ip", "medium-ieee802154",
"phy-raw_socket", "phy-tuntap_interface",
"proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-dns",
"proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-dns", "proto-domainname",
"proto-ipv4-fragmentation", "proto-sixlowpan-fragmentation",
"socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "socket-dns", "socket-mdns",
"packetmeta-id", "async"
@@ -249,6 +250,11 @@ dns-max-name-size-64 = []
dns-max-name-size-128 = []
dns-max-name-size-255 = [] # Default

dhcp-max-domain-name-size-32 = []
dhcp-max-domain-name-size-64 = [] # Default
dhcp-max-domain-name-size-128 = []
dhcp-max-domain-name-size-256 = []

rpl-relations-buffer-count-1 = []
rpl-relations-buffer-count-2 = []
rpl-relations-buffer-count-4 = []
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ static CONFIGS: &[(&str, usize)] = &[
("DNS_MAX_RESULT_COUNT", 1),
("DNS_MAX_SERVER_COUNT", 1),
("DNS_MAX_NAME_SIZE", 255),
("DHCP_MAX_DOMAIN_NAME_SIZE", 64),
("RPL_RELATIONS_BUFFER_COUNT", 16),
("RPL_PARENTS_BUFFER_COUNT", 8),
// END AUTOGENERATED CONFIG FEATURES
1 change: 1 addition & 0 deletions gen_config.py
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ def feature(name, default, min, max, pow2=None):
feature("dns_max_result_count", default=1, min=1, max=32, pow2=4)
feature("dns_max_server_count", default=1, min=1, max=32, pow2=4)
feature("dns_max_name_size", default=255, min=64, max=255, pow2=True)
feature("dhcp_max_domain_name_size", default=64, min=32, max=256, pow2=True)
feature("rpl_relations_buffer_count", default=16, min=1, max=128, pow2=True)
feature("rpl_parents_buffer_count", default=8, min=2, max=32, pow2=True)

1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -136,6 +136,7 @@ pub mod config {
pub const DNS_MAX_NAME_SIZE: usize = 255;
pub const DNS_MAX_RESULT_COUNT: usize = 1;
pub const DNS_MAX_SERVER_COUNT: usize = 1;
pub const DHCP_MAX_DOMAIN_NAME_SIZE: usize = 64;
pub const FRAGMENTATION_BUFFER_SIZE: usize = 1500;
pub const IFACE_MAX_ADDR_COUNT: usize = 8;
pub const IFACE_MAX_MULTICAST_GROUP_COUNT: usize = 4;
35 changes: 30 additions & 5 deletions src/socket/dhcpv4.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use core::str::FromStr;
#[cfg(feature = "async")]
use core::task::Waker;

#[cfg(feature = "proto-domainname")]
use crate::config::DHCP_MAX_DOMAIN_NAME_SIZE;
use crate::iface::Context;
use crate::time::{Duration, Instant};
use crate::wire::dhcpv4::field as dhcpv4_field;
@@ -9,7 +12,7 @@ use crate::wire::{
UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN,
};
use crate::wire::{DhcpOption, HardwareAddress};
use heapless::Vec;
use heapless::{String, Vec};

#[cfg(feature = "async")]
use super::WakerRegistration;
@@ -22,6 +25,8 @@ const DEFAULT_PARAMETER_REQUEST_LIST: &[u8] = &[
dhcpv4_field::OPT_SUBNET_MASK,
dhcpv4_field::OPT_ROUTER,
dhcpv4_field::OPT_DOMAIN_NAME_SERVER,
#[cfg(feature = "proto-domainname")]
dhcpv4_field::OPT_DOMAIN_NAME,
];

/// IPv4 configuration data provided by the DHCP server.
@@ -38,6 +43,9 @@ pub struct Config<'a> {
pub router: Option<Ipv4Address>,
/// DNS servers
pub dns_servers: Vec<Ipv4Address, DHCP_MAX_DNS_SERVER_COUNT>,
/// Domain name
#[cfg(feature = "proto-domainname")]
pub domain_name: Option<String<DHCP_MAX_DOMAIN_NAME_SIZE>>,
/// Received DHCP packet
pub packet: Option<DhcpPacket<&'a [u8]>>,
}
@@ -494,6 +502,11 @@ impl<'a> Socket<'a> {
address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len),
router: dhcp_repr.router,
dns_servers,
#[cfg(feature = "proto-domainname")]
domain_name: dhcp_repr
.domain_name
.map(String::from_str)
.and_then(Result::ok),
packet: None,
};

@@ -589,6 +602,8 @@ impl<'a> Socket<'a> {
renew_duration: None,
rebind_duration: None,
dns_servers: None,
#[cfg(feature = "proto-domainname")]
domain_name: None,
additional_options: self.outgoing_options,
};

@@ -739,6 +754,8 @@ impl<'a> Socket<'a> {
address: state.config.address,
router: state.config.router,
dns_servers: state.config.dns_servers.clone(),
#[cfg(feature = "proto-domainname")]
domain_name: state.config.domain_name.clone(),
packet: self
.receive_packet_buffer
.as_deref()
@@ -779,6 +796,7 @@ impl<'a> Socket<'a> {
#[cfg(test)]
mod test {

use core::str::FromStr;
use std::ops::{Deref, DerefMut};

use super::*;
@@ -886,6 +904,7 @@ mod test {
const DNS_IP_2: Ipv4Address = Ipv4Address([1, 1, 1, 2]);
const DNS_IP_3: Ipv4Address = Ipv4Address([1, 1, 1, 3]);
const DNS_IPS: &[Ipv4Address] = &[DNS_IP_1, DNS_IP_2, DNS_IP_3];
const DOMAIN_NAME: &str = "my.domain";

const MASK_24: Ipv4Address = Ipv4Address([255, 255, 255, 0]);

@@ -969,6 +988,7 @@ mod test {
server_identifier: None,
parameter_request_list: None,
dns_servers: None,
domain_name: None,
max_size: None,
renew_duration: None,
rebind_duration: None,
@@ -979,7 +999,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, 15]),
max_size: Some(1432),
..DHCP_DEFAULT
};
@@ -994,6 +1014,7 @@ mod test {
router: Some(SERVER_IP),
subnet_mask: Some(MASK_24),
dns_servers: Some(Vec::from_slice(DNS_IPS).unwrap()),
domain_name: Some(DOMAIN_NAME),
lease_duration: Some(1000),

..DHCP_DEFAULT
@@ -1007,7 +1028,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, 15]),
..DHCP_DEFAULT
};

@@ -1021,6 +1042,7 @@ mod test {
router: Some(SERVER_IP),
subnet_mask: Some(MASK_24),
dns_servers: Some(Vec::from_slice(DNS_IPS).unwrap()),
domain_name: Some(DOMAIN_NAME),
lease_duration: Some(1000),

..DHCP_DEFAULT
@@ -1042,7 +1064,7 @@ mod test {
max_size: Some(1432),

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

@@ -1054,7 +1076,7 @@ mod test {
max_size: Some(1432),

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

@@ -1097,6 +1119,7 @@ mod test {
},
address: Ipv4Cidr::new(MY_IP, 24),
dns_servers: Vec::from_slice(DNS_IPS).unwrap(),
domain_name: Some(String::from_str(DOMAIN_NAME).unwrap()),
router: Some(SERVER_IP),
packet: None,
},
@@ -1132,6 +1155,7 @@ mod test {
},
address: Ipv4Cidr::new(MY_IP, 24),
dns_servers: Vec::from_slice(DNS_IPS).unwrap(),
domain_name: Some(String::from_str(DOMAIN_NAME).unwrap()),
router: Some(SERVER_IP),
packet: None,
}))
@@ -1170,6 +1194,7 @@ mod test {
},
address: Ipv4Cidr::new(MY_IP, 24),
dns_servers: Vec::from_slice(DNS_IPS).unwrap(),
domain_name: Some(String::from_str(DOMAIN_NAME).unwrap()),
router: Some(SERVER_IP),
packet: None,
}))
28 changes: 28 additions & 0 deletions src/wire/dhcpv4.rs
Original file line number Diff line number Diff line change
@@ -647,6 +647,9 @@ pub struct Repr<'a> {
pub parameter_request_list: Option<&'a [u8]>,
/// DNS servers
pub dns_servers: Option<Vec<Ipv4Address, MAX_DNS_SERVER_COUNT>>,
/// Domain name
#[cfg(feature = "proto-domainname")]
pub domain_name: Option<&'a str>,
/// The maximum size dhcp packet the interface can receive
pub max_size: Option<u16>,
/// The DHCP IP lease duration, specified in seconds.
@@ -692,6 +695,11 @@ impl<'a> Repr<'a> {
len += 2;
len += dns_servers.iter().count() * core::mem::size_of::<u32>();
}
#[cfg(feature = "proto-domainname")]
if let Some(domain_name) = &self.domain_name {
len += 2;
len += domain_name.as_bytes().len();
}
if let Some(list) = self.parameter_request_list {
len += list.len() + 2;
}
@@ -738,6 +746,8 @@ impl<'a> Repr<'a> {
let mut subnet_mask = None;
let mut parameter_request_list = None;
let mut dns_servers = None;
#[cfg(feature = "proto-domainname")]
let mut domain_name = None;
let mut max_size = None;
let mut lease_duration = None;
let mut renew_duration = None;
@@ -802,6 +812,10 @@ impl<'a> Repr<'a> {
net_trace!("DHCP domain name servers contained invalid address");
}
}
#[cfg(feature = "proto-domainname")]
(field::OPT_DOMAIN_NAME, _) => {
domain_name = core::str::from_utf8(data).ok();
}
_ => {}
}
}
@@ -824,6 +838,8 @@ impl<'a> Repr<'a> {
client_identifier,
parameter_request_list,
dns_servers,
#[cfg(feature = "proto-domainname")]
domain_name,
max_size,
lease_duration,
renew_duration,
@@ -940,6 +956,14 @@ impl<'a> Repr<'a> {
})?;
}

#[cfg(feature = "proto-domainname")]
if let Some(domain_name) = &self.domain_name {
options.emit(DhcpOption {
kind: field::OPT_DOMAIN_NAME,
data: domain_name.as_bytes(),
})?;
}

for option in self.additional_options {
options.emit(*option)?;
}
@@ -1167,6 +1191,8 @@ mod test {
server_identifier: None,
parameter_request_list: None,
dns_servers: None,
#[cfg(feature = "proto-domainname")]
domain_name: None,
max_size: None,
renew_duration: None,
rebind_duration: None,
@@ -1197,6 +1223,8 @@ mod test {
server_identifier: None,
parameter_request_list: Some(&[1, 3, 6, 42]),
dns_servers: None,
#[cfg(feature = "proto-domainname")]
domain_name: None,
additional_options: &[],
}
}