diff --git a/Cargo.toml b/Cargo.toml index 2f3e266bd..9db413e0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" = [] "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 = [] diff --git a/build.rs b/build.rs index e1746d23f..69098aee5 100644 --- a/build.rs +++ b/build.rs @@ -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 diff --git a/gen_config.py b/gen_config.py index 1407ca2d6..9792ee9fd 100644 --- a/gen_config.py +++ b/gen_config.py @@ -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) diff --git a/src/lib.rs b/src/lib.rs index 2e99c9576..a6a10edc4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index b1b3cb583..a92ad21ae 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -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, /// DNS servers pub dns_servers: Vec, + /// Domain name + #[cfg(feature = "proto-domainname")] + pub domain_name: Option>, /// Received DHCP packet pub packet: Option>, } @@ -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, })) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index b00f26ff7..377ac1cbe 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -647,6 +647,9 @@ pub struct Repr<'a> { pub parameter_request_list: Option<&'a [u8]>, /// DNS servers pub dns_servers: Option>, + /// 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, /// 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::(); } + #[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: &[], } }