Skip to content

Commit

Permalink
Enable support for DHCP option 15 (domain name)
Browse files Browse the repository at this point in the history
This makes `smoltcp` work in industrial networks
with Windows DHCPs as they do not support option
119.
  • Loading branch information
korken89 committed Aug 9, 2024
1 parent 30966f3 commit cf5b0a7
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 8 deletions.
27 changes: 21 additions & 6 deletions src/socket/dhcpv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ use crate::time::{Duration, Instant};
use crate::wire::dhcpv4::field as dhcpv4_field;
use crate::wire::{
DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr,
UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN,
UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_MAX_DOMAIN_NAME_LEN,
DHCP_SERVER_PORT, UDP_HEADER_LEN,
};
use crate::wire::{DhcpOption, HardwareAddress};
use heapless::Vec;
use heapless::{String, Vec};

#[cfg(feature = "async")]
use super::WakerRegistration;
Expand All @@ -22,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_DOMAIN_NAME,
];

/// IPv4 configuration data provided by the DHCP server.
Expand All @@ -38,6 +40,8 @@ pub struct Config<'a> {
pub router: Option<Ipv4Address>,
/// DNS servers
pub dns_servers: Vec<Ipv4Address, DHCP_MAX_DNS_SERVER_COUNT>,
/// Domain name
pub domain_name: Option<String<DHCP_MAX_DOMAIN_NAME_LEN>>,
/// Received DHCP packet
pub packet: Option<DhcpPacket<&'a [u8]>>,
}
Expand Down Expand Up @@ -494,6 +498,7 @@ impl<'a> Socket<'a> {
address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len),
router: dhcp_repr.router,
dns_servers,
domain_name: dhcp_repr.domain_name.clone(),
packet: None,
};

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

Expand Down Expand Up @@ -739,6 +745,7 @@ impl<'a> Socket<'a> {
address: state.config.address,
router: state.config.router,
dns_servers: state.config.dns_servers.clone(),
domain_name: state.config.domain_name.clone(),
packet: self
.receive_packet_buffer
.as_deref()
Expand Down Expand Up @@ -779,6 +786,7 @@ impl<'a> Socket<'a> {
#[cfg(test)]
mod test {

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

use super::*;
Expand Down Expand Up @@ -886,6 +894,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]);

Expand Down Expand Up @@ -969,6 +978,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,
Expand All @@ -979,7 +989,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
};
Expand All @@ -994,6 +1004,7 @@ mod test {
router: Some(SERVER_IP),
subnet_mask: Some(MASK_24),
dns_servers: Some(Vec::from_slice(DNS_IPS).unwrap()),
domain_name: Some(String::from_str(DOMAIN_NAME).unwrap()),
lease_duration: Some(1000),

..DHCP_DEFAULT
Expand All @@ -1007,7 +1018,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
};

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

..DHCP_DEFAULT
Expand All @@ -1042,7 +1054,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
};

Expand All @@ -1054,7 +1066,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
};

Expand Down Expand Up @@ -1097,6 +1109,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,
},
Expand Down Expand Up @@ -1132,6 +1145,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,
}))
Expand Down Expand Up @@ -1170,6 +1184,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,
}))
Expand Down
30 changes: 29 additions & 1 deletion src/wire/dhcpv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use bitflags::bitflags;
use byteorder::{ByteOrder, NetworkEndian};
use core::iter;
use heapless::Vec;
use heapless::{String, Vec};

use super::{Error, Result};
use crate::wire::arp::Hardware;
Expand All @@ -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_DOMAIN_NAME_LEN: usize = 255;

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>>,
/// Domain name
pub domain_name: Option<String<MAX_DOMAIN_NAME_LEN>>,
/// 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(domain_name) = &self.domain_name {
len += 2;
len += domain_name.len();
}
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 domain_name = None;
let mut max_size = None;
let mut lease_duration = None;
let mut renew_duration = None;
Expand Down Expand Up @@ -802,6 +810,16 @@ impl<'a> Repr<'a> {
net_trace!("DHCP domain name servers contained invalid address");
}
}
(field::OPT_DOMAIN_NAME, _) => {
let mut name = String::new();

if let Ok(n) = core::str::from_utf8(data) {
if data.len() <= MAX_DOMAIN_NAME_LEN {
name.push_str(n).ok();
domain_name = Some(name);
}
}
}
_ => {}
}
}
Expand All @@ -824,6 +842,7 @@ impl<'a> Repr<'a> {
client_identifier,
parameter_request_list,
dns_servers,
domain_name,
max_size,
lease_duration,
renew_duration,
Expand Down Expand Up @@ -940,6 +959,13 @@ impl<'a> Repr<'a> {
})?;
}

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)?;
}
Expand Down Expand Up @@ -1167,6 +1193,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,
Expand Down Expand Up @@ -1197,6 +1224,7 @@ mod test {
server_identifier: None,
parameter_request_list: Some(&[1, 3, 6, 42]),
dns_servers: None,
domain_name: None,
additional_options: &[],
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/wire/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ pub use self::tcp::{
pub use self::dhcpv4::{
DhcpOption, DhcpOptionWriter, Flags as DhcpFlags, MessageType as DhcpMessageType,
OpCode as DhcpOpCode, Packet as DhcpPacket, Repr as DhcpRepr, CLIENT_PORT as DHCP_CLIENT_PORT,
MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, SERVER_PORT as DHCP_SERVER_PORT,
MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT,
MAX_DOMAIN_NAME_LEN as DHCP_MAX_DOMAIN_NAME_LEN, SERVER_PORT as DHCP_SERVER_PORT,
};

#[cfg(feature = "proto-dns")]
Expand Down

0 comments on commit cf5b0a7

Please sign in to comment.