Skip to content

Commit cf5b0a7

Browse files
committed
Enable support for DHCP option 15 (domain name)
This makes `smoltcp` work in industrial networks with Windows DHCPs as they do not support option 119.
1 parent 30966f3 commit cf5b0a7

File tree

3 files changed

+52
-8
lines changed

3 files changed

+52
-8
lines changed

src/socket/dhcpv4.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ use crate::time::{Duration, Instant};
66
use crate::wire::dhcpv4::field as dhcpv4_field;
77
use crate::wire::{
88
DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr,
9-
UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN,
9+
UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_MAX_DOMAIN_NAME_LEN,
10+
DHCP_SERVER_PORT, UDP_HEADER_LEN,
1011
};
1112
use crate::wire::{DhcpOption, HardwareAddress};
12-
use heapless::Vec;
13+
use heapless::{String, Vec};
1314

1415
#[cfg(feature = "async")]
1516
use super::WakerRegistration;
@@ -22,6 +23,7 @@ const DEFAULT_PARAMETER_REQUEST_LIST: &[u8] = &[
2223
dhcpv4_field::OPT_SUBNET_MASK,
2324
dhcpv4_field::OPT_ROUTER,
2425
dhcpv4_field::OPT_DOMAIN_NAME_SERVER,
26+
dhcpv4_field::OPT_DOMAIN_NAME,
2527
];
2628

2729
/// IPv4 configuration data provided by the DHCP server.
@@ -38,6 +40,8 @@ pub struct Config<'a> {
3840
pub router: Option<Ipv4Address>,
3941
/// DNS servers
4042
pub dns_servers: Vec<Ipv4Address, DHCP_MAX_DNS_SERVER_COUNT>,
43+
/// Domain name
44+
pub domain_name: Option<String<DHCP_MAX_DOMAIN_NAME_LEN>>,
4145
/// Received DHCP packet
4246
pub packet: Option<DhcpPacket<&'a [u8]>>,
4347
}
@@ -494,6 +498,7 @@ impl<'a> Socket<'a> {
494498
address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len),
495499
router: dhcp_repr.router,
496500
dns_servers,
501+
domain_name: dhcp_repr.domain_name.clone(),
497502
packet: None,
498503
};
499504

@@ -589,6 +594,7 @@ impl<'a> Socket<'a> {
589594
renew_duration: None,
590595
rebind_duration: None,
591596
dns_servers: None,
597+
domain_name: None,
592598
additional_options: self.outgoing_options,
593599
};
594600

@@ -739,6 +745,7 @@ impl<'a> Socket<'a> {
739745
address: state.config.address,
740746
router: state.config.router,
741747
dns_servers: state.config.dns_servers.clone(),
748+
domain_name: state.config.domain_name.clone(),
742749
packet: self
743750
.receive_packet_buffer
744751
.as_deref()
@@ -779,6 +786,7 @@ impl<'a> Socket<'a> {
779786
#[cfg(test)]
780787
mod test {
781788

789+
use core::str::FromStr;
782790
use std::ops::{Deref, DerefMut};
783791

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

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

@@ -969,6 +978,7 @@ mod test {
969978
server_identifier: None,
970979
parameter_request_list: None,
971980
dns_servers: None,
981+
domain_name: None,
972982
max_size: None,
973983
renew_duration: None,
974984
rebind_duration: None,
@@ -979,7 +989,7 @@ mod test {
979989
const DHCP_DISCOVER: DhcpRepr = DhcpRepr {
980990
message_type: DhcpMessageType::Discover,
981991
client_identifier: Some(MY_MAC),
982-
parameter_request_list: Some(&[1, 3, 6]),
992+
parameter_request_list: Some(&[1, 3, 6, 15]),
983993
max_size: Some(1432),
984994
..DHCP_DEFAULT
985995
};
@@ -994,6 +1004,7 @@ mod test {
9941004
router: Some(SERVER_IP),
9951005
subnet_mask: Some(MASK_24),
9961006
dns_servers: Some(Vec::from_slice(DNS_IPS).unwrap()),
1007+
domain_name: Some(String::from_str(DOMAIN_NAME).unwrap()),
9971008
lease_duration: Some(1000),
9981009

9991010
..DHCP_DEFAULT
@@ -1007,7 +1018,7 @@ mod test {
10071018
max_size: Some(1432),
10081019

10091020
requested_ip: Some(MY_IP),
1010-
parameter_request_list: Some(&[1, 3, 6]),
1021+
parameter_request_list: Some(&[1, 3, 6, 15]),
10111022
..DHCP_DEFAULT
10121023
};
10131024

@@ -1021,6 +1032,7 @@ mod test {
10211032
router: Some(SERVER_IP),
10221033
subnet_mask: Some(MASK_24),
10231034
dns_servers: Some(Vec::from_slice(DNS_IPS).unwrap()),
1035+
domain_name: Some(String::from_str(DOMAIN_NAME).unwrap()),
10241036
lease_duration: Some(1000),
10251037

10261038
..DHCP_DEFAULT
@@ -1042,7 +1054,7 @@ mod test {
10421054
max_size: Some(1432),
10431055

10441056
requested_ip: None,
1045-
parameter_request_list: Some(&[1, 3, 6]),
1057+
parameter_request_list: Some(&[1, 3, 6, 15]),
10461058
..DHCP_DEFAULT
10471059
};
10481060

@@ -1054,7 +1066,7 @@ mod test {
10541066
max_size: Some(1432),
10551067

10561068
requested_ip: None,
1057-
parameter_request_list: Some(&[1, 3, 6]),
1069+
parameter_request_list: Some(&[1, 3, 6, 15]),
10581070
..DHCP_DEFAULT
10591071
};
10601072

@@ -1097,6 +1109,7 @@ mod test {
10971109
},
10981110
address: Ipv4Cidr::new(MY_IP, 24),
10991111
dns_servers: Vec::from_slice(DNS_IPS).unwrap(),
1112+
domain_name: Some(String::from_str(DOMAIN_NAME).unwrap()),
11001113
router: Some(SERVER_IP),
11011114
packet: None,
11021115
},
@@ -1132,6 +1145,7 @@ mod test {
11321145
},
11331146
address: Ipv4Cidr::new(MY_IP, 24),
11341147
dns_servers: Vec::from_slice(DNS_IPS).unwrap(),
1148+
domain_name: Some(String::from_str(DOMAIN_NAME).unwrap()),
11351149
router: Some(SERVER_IP),
11361150
packet: None,
11371151
}))
@@ -1170,6 +1184,7 @@ mod test {
11701184
},
11711185
address: Ipv4Cidr::new(MY_IP, 24),
11721186
dns_servers: Vec::from_slice(DNS_IPS).unwrap(),
1187+
domain_name: Some(String::from_str(DOMAIN_NAME).unwrap()),
11731188
router: Some(SERVER_IP),
11741189
packet: None,
11751190
}))

src/wire/dhcpv4.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use bitflags::bitflags;
44
use byteorder::{ByteOrder, NetworkEndian};
55
use core::iter;
6-
use heapless::Vec;
6+
use heapless::{String, Vec};
77

88
use super::{Error, Result};
99
use crate::wire::arp::Hardware;
@@ -12,6 +12,7 @@ use crate::wire::{EthernetAddress, Ipv4Address};
1212
pub const SERVER_PORT: u16 = 67;
1313
pub const CLIENT_PORT: u16 = 68;
1414
pub const MAX_DNS_SERVER_COUNT: usize = 3;
15+
pub const MAX_DOMAIN_NAME_LEN: usize = 255;
1516

1617
const DHCP_MAGIC_NUMBER: u32 = 0x63825363;
1718

@@ -647,6 +648,8 @@ pub struct Repr<'a> {
647648
pub parameter_request_list: Option<&'a [u8]>,
648649
/// DNS servers
649650
pub dns_servers: Option<Vec<Ipv4Address, MAX_DNS_SERVER_COUNT>>,
651+
/// Domain name
652+
pub domain_name: Option<String<MAX_DOMAIN_NAME_LEN>>,
650653
/// The maximum size dhcp packet the interface can receive
651654
pub max_size: Option<u16>,
652655
/// The DHCP IP lease duration, specified in seconds.
@@ -692,6 +695,10 @@ impl<'a> Repr<'a> {
692695
len += 2;
693696
len += dns_servers.iter().count() * core::mem::size_of::<u32>();
694697
}
698+
if let Some(domain_name) = &self.domain_name {
699+
len += 2;
700+
len += domain_name.len();
701+
}
695702
if let Some(list) = self.parameter_request_list {
696703
len += list.len() + 2;
697704
}
@@ -738,6 +745,7 @@ impl<'a> Repr<'a> {
738745
let mut subnet_mask = None;
739746
let mut parameter_request_list = None;
740747
let mut dns_servers = None;
748+
let mut domain_name = None;
741749
let mut max_size = None;
742750
let mut lease_duration = None;
743751
let mut renew_duration = None;
@@ -802,6 +810,16 @@ impl<'a> Repr<'a> {
802810
net_trace!("DHCP domain name servers contained invalid address");
803811
}
804812
}
813+
(field::OPT_DOMAIN_NAME, _) => {
814+
let mut name = String::new();
815+
816+
if let Ok(n) = core::str::from_utf8(data) {
817+
if data.len() <= MAX_DOMAIN_NAME_LEN {
818+
name.push_str(n).ok();
819+
domain_name = Some(name);
820+
}
821+
}
822+
}
805823
_ => {}
806824
}
807825
}
@@ -824,6 +842,7 @@ impl<'a> Repr<'a> {
824842
client_identifier,
825843
parameter_request_list,
826844
dns_servers,
845+
domain_name,
827846
max_size,
828847
lease_duration,
829848
renew_duration,
@@ -940,6 +959,13 @@ impl<'a> Repr<'a> {
940959
})?;
941960
}
942961

962+
if let Some(domain_name) = &self.domain_name {
963+
options.emit(DhcpOption {
964+
kind: field::OPT_DOMAIN_NAME,
965+
data: domain_name.as_bytes(),
966+
})?;
967+
}
968+
943969
for option in self.additional_options {
944970
options.emit(*option)?;
945971
}
@@ -1167,6 +1193,7 @@ mod test {
11671193
server_identifier: None,
11681194
parameter_request_list: None,
11691195
dns_servers: None,
1196+
domain_name: None,
11701197
max_size: None,
11711198
renew_duration: None,
11721199
rebind_duration: None,
@@ -1197,6 +1224,7 @@ mod test {
11971224
server_identifier: None,
11981225
parameter_request_list: Some(&[1, 3, 6, 42]),
11991226
dns_servers: None,
1227+
domain_name: None,
12001228
additional_options: &[],
12011229
}
12021230
}

src/wire/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,8 @@ pub use self::tcp::{
272272
pub use self::dhcpv4::{
273273
DhcpOption, DhcpOptionWriter, Flags as DhcpFlags, MessageType as DhcpMessageType,
274274
OpCode as DhcpOpCode, Packet as DhcpPacket, Repr as DhcpRepr, CLIENT_PORT as DHCP_CLIENT_PORT,
275-
MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, SERVER_PORT as DHCP_SERVER_PORT,
275+
MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT,
276+
MAX_DOMAIN_NAME_LEN as DHCP_MAX_DOMAIN_NAME_LEN, SERVER_PORT as DHCP_SERVER_PORT,
276277
};
277278

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

0 commit comments

Comments
 (0)