1 // See https://tools.ietf.org/html/rfc2131 for the DHCP specification.
2 
3 use bitflags::bitflags;
4 use byteorder::{ByteOrder, NetworkEndian};
5 use core::iter;
6 use heapless::Vec;
7 
8 use super::{Error, Result};
9 use crate::wire::arp::Hardware;
10 use crate::wire::{EthernetAddress, Ipv4Address};
11 
12 pub const SERVER_PORT: u16 = 67;
13 pub const CLIENT_PORT: u16 = 68;
14 pub const MAX_DNS_SERVER_COUNT: usize = 3;
15 
16 const DHCP_MAGIC_NUMBER: u32 = 0x63825363;
17 
18 enum_with_unknown! {
19     /// The possible opcodes of a DHCP packet.
20     pub enum OpCode(u8) {
21         Request = 1,
22         Reply = 2,
23     }
24 }
25 
26 enum_with_unknown! {
27     /// The possible message types of a DHCP packet.
28     pub enum MessageType(u8) {
29         Discover = 1,
30         Offer = 2,
31         Request = 3,
32         Decline = 4,
33         Ack = 5,
34         Nak = 6,
35         Release = 7,
36         Inform = 8,
37     }
38 }
39 
40 bitflags! {
41     pub struct Flags: u16 {
42         const BROADCAST = 0b1000_0000_0000_0000;
43     }
44 }
45 
46 impl MessageType {
opcode(&self) -> OpCode47     const fn opcode(&self) -> OpCode {
48         match *self {
49             MessageType::Discover
50             | MessageType::Inform
51             | MessageType::Request
52             | MessageType::Decline
53             | MessageType::Release => OpCode::Request,
54             MessageType::Offer | MessageType::Ack | MessageType::Nak => OpCode::Reply,
55             MessageType::Unknown(_) => OpCode::Unknown(0),
56         }
57     }
58 }
59 
60 /// A buffer for DHCP options.
61 #[derive(Debug)]
62 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
63 pub struct DhcpOptionWriter<'a> {
64     /// The underlying buffer, directly from the DHCP packet representation.
65     buffer: &'a mut [u8],
66 }
67 
68 impl<'a> DhcpOptionWriter<'a> {
new(buffer: &'a mut [u8]) -> Self69     pub fn new(buffer: &'a mut [u8]) -> Self {
70         Self { buffer }
71     }
72 
73     /// Emit a  [`DhcpOption`] into a [`DhcpOptionWriter`].
emit(&mut self, option: DhcpOption<'_>) -> Result<()>74     pub fn emit(&mut self, option: DhcpOption<'_>) -> Result<()> {
75         if option.data.len() > u8::MAX as _ {
76             return Err(Error);
77         }
78 
79         let total_len = 2 + option.data.len();
80         if self.buffer.len() < total_len {
81             return Err(Error);
82         }
83 
84         let (buf, rest) = core::mem::take(&mut self.buffer).split_at_mut(total_len);
85         self.buffer = rest;
86 
87         buf[0] = option.kind;
88         buf[1] = option.data.len() as _;
89         buf[2..].copy_from_slice(option.data);
90 
91         Ok(())
92     }
93 
end(&mut self) -> Result<()>94     pub fn end(&mut self) -> Result<()> {
95         if self.buffer.is_empty() {
96             return Err(Error);
97         }
98 
99         self.buffer[0] = field::OPT_END;
100         self.buffer = &mut [];
101         Ok(())
102     }
103 }
104 
105 /// A representation of a single DHCP option.
106 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
107 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
108 pub struct DhcpOption<'a> {
109     pub kind: u8,
110     pub data: &'a [u8],
111 }
112 
113 /// A read/write wrapper around a Dynamic Host Configuration Protocol packet buffer.
114 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
115 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
116 pub struct Packet<T: AsRef<[u8]>> {
117     buffer: T,
118 }
119 
120 pub(crate) mod field {
121     #![allow(non_snake_case)]
122     #![allow(unused)]
123 
124     use crate::wire::field::*;
125 
126     pub const OP: usize = 0;
127     pub const HTYPE: usize = 1;
128     pub const HLEN: usize = 2;
129     pub const HOPS: usize = 3;
130     pub const XID: Field = 4..8;
131     pub const SECS: Field = 8..10;
132     pub const FLAGS: Field = 10..12;
133     pub const CIADDR: Field = 12..16;
134     pub const YIADDR: Field = 16..20;
135     pub const SIADDR: Field = 20..24;
136     pub const GIADDR: Field = 24..28;
137     pub const CHADDR: Field = 28..34;
138     pub const SNAME: Field = 34..108;
139     pub const FILE: Field = 108..236;
140     pub const MAGIC_NUMBER: Field = 236..240;
141     pub const OPTIONS: Rest = 240..;
142 
143     // Vendor Extensions
144     pub const OPT_END: u8 = 255;
145     pub const OPT_PAD: u8 = 0;
146     pub const OPT_SUBNET_MASK: u8 = 1;
147     pub const OPT_TIME_OFFSET: u8 = 2;
148     pub const OPT_ROUTER: u8 = 3;
149     pub const OPT_TIME_SERVER: u8 = 4;
150     pub const OPT_NAME_SERVER: u8 = 5;
151     pub const OPT_DOMAIN_NAME_SERVER: u8 = 6;
152     pub const OPT_LOG_SERVER: u8 = 7;
153     pub const OPT_COOKIE_SERVER: u8 = 8;
154     pub const OPT_LPR_SERVER: u8 = 9;
155     pub const OPT_IMPRESS_SERVER: u8 = 10;
156     pub const OPT_RESOURCE_LOCATION_SERVER: u8 = 11;
157     pub const OPT_HOST_NAME: u8 = 12;
158     pub const OPT_BOOT_FILE_SIZE: u8 = 13;
159     pub const OPT_MERIT_DUMP: u8 = 14;
160     pub const OPT_DOMAIN_NAME: u8 = 15;
161     pub const OPT_SWAP_SERVER: u8 = 16;
162     pub const OPT_ROOT_PATH: u8 = 17;
163     pub const OPT_EXTENSIONS_PATH: u8 = 18;
164 
165     // IP Layer Parameters per Host
166     pub const OPT_IP_FORWARDING: u8 = 19;
167     pub const OPT_NON_LOCAL_SOURCE_ROUTING: u8 = 20;
168     pub const OPT_POLICY_FILTER: u8 = 21;
169     pub const OPT_MAX_DATAGRAM_REASSEMBLY_SIZE: u8 = 22;
170     pub const OPT_DEFAULT_TTL: u8 = 23;
171     pub const OPT_PATH_MTU_AGING_TIMEOUT: u8 = 24;
172     pub const OPT_PATH_MTU_PLATEU_TABLE: u8 = 25;
173 
174     // IP Layer Parameters per Interface
175     pub const OPT_INTERFACE_MTU: u8 = 26;
176     pub const OPT_ALL_SUBNETS_ARE_LOCAL: u8 = 27;
177     pub const OPT_BROADCAST_ADDRESS: u8 = 28;
178     pub const OPT_PERFORM_MASK_DISCOVERY: u8 = 29;
179     pub const OPT_MASK_SUPPLIER: u8 = 30;
180     pub const OPT_PERFORM_ROUTER_DISCOVERY: u8 = 31;
181     pub const OPT_ROUTER_SOLICITATION_ADDRESS: u8 = 32;
182     pub const OPT_STATIC_ROUTE: u8 = 33;
183 
184     // Link Layer Parameters per Interface
185     pub const OPT_TRAILER_ENCAPSULATION: u8 = 34;
186     pub const OPT_ARP_CACHE_TIMEOUT: u8 = 35;
187     pub const OPT_ETHERNET_ENCAPSULATION: u8 = 36;
188 
189     // TCP Parameters
190     pub const OPT_TCP_DEFAULT_TTL: u8 = 37;
191     pub const OPT_TCP_KEEPALIVE_INTERVAL: u8 = 38;
192     pub const OPT_TCP_KEEPALIVE_GARBAGE: u8 = 39;
193 
194     // Application and Service Parameters
195     pub const OPT_NIS_DOMAIN: u8 = 40;
196     pub const OPT_NIS_SERVERS: u8 = 41;
197     pub const OPT_NTP_SERVERS: u8 = 42;
198     pub const OPT_VENDOR_SPECIFIC_INFO: u8 = 43;
199     pub const OPT_NETBIOS_NAME_SERVER: u8 = 44;
200     pub const OPT_NETBIOS_DISTRIBUTION_SERVER: u8 = 45;
201     pub const OPT_NETBIOS_NODE_TYPE: u8 = 46;
202     pub const OPT_NETBIOS_SCOPE: u8 = 47;
203     pub const OPT_X_WINDOW_FONT_SERVER: u8 = 48;
204     pub const OPT_X_WINDOW_DISPLAY_MANAGER: u8 = 49;
205     pub const OPT_NIS_PLUS_DOMAIN: u8 = 64;
206     pub const OPT_NIS_PLUS_SERVERS: u8 = 65;
207     pub const OPT_MOBILE_IP_HOME_AGENT: u8 = 68;
208     pub const OPT_SMTP_SERVER: u8 = 69;
209     pub const OPT_POP3_SERVER: u8 = 70;
210     pub const OPT_NNTP_SERVER: u8 = 71;
211     pub const OPT_WWW_SERVER: u8 = 72;
212     pub const OPT_FINGER_SERVER: u8 = 73;
213     pub const OPT_IRC_SERVER: u8 = 74;
214     pub const OPT_STREETTALK_SERVER: u8 = 75;
215     pub const OPT_STDA_SERVER: u8 = 76;
216 
217     // DHCP Extensions
218     pub const OPT_REQUESTED_IP: u8 = 50;
219     pub const OPT_IP_LEASE_TIME: u8 = 51;
220     pub const OPT_OPTION_OVERLOAD: u8 = 52;
221     pub const OPT_TFTP_SERVER_NAME: u8 = 66;
222     pub const OPT_BOOTFILE_NAME: u8 = 67;
223     pub const OPT_DHCP_MESSAGE_TYPE: u8 = 53;
224     pub const OPT_SERVER_IDENTIFIER: u8 = 54;
225     pub const OPT_PARAMETER_REQUEST_LIST: u8 = 55;
226     pub const OPT_MESSAGE: u8 = 56;
227     pub const OPT_MAX_DHCP_MESSAGE_SIZE: u8 = 57;
228     pub const OPT_RENEWAL_TIME_VALUE: u8 = 58;
229     pub const OPT_REBINDING_TIME_VALUE: u8 = 59;
230     pub const OPT_VENDOR_CLASS_ID: u8 = 60;
231     pub const OPT_CLIENT_ID: u8 = 61;
232 }
233 
234 impl<T: AsRef<[u8]>> Packet<T> {
235     /// Imbue a raw octet buffer with DHCP packet structure.
new_unchecked(buffer: T) -> Packet<T>236     pub const fn new_unchecked(buffer: T) -> Packet<T> {
237         Packet { buffer }
238     }
239 
240     /// Shorthand for a combination of [new_unchecked] and [check_len].
241     ///
242     /// [new_unchecked]: #method.new_unchecked
243     /// [check_len]: #method.check_len
new_checked(buffer: T) -> Result<Packet<T>>244     pub fn new_checked(buffer: T) -> Result<Packet<T>> {
245         let packet = Self::new_unchecked(buffer);
246         packet.check_len()?;
247         Ok(packet)
248     }
249 
250     /// Ensure that no accessor method will panic if called.
251     /// Returns `Err(Error)` if the buffer is too short.
252     ///
253     /// [set_header_len]: #method.set_header_len
check_len(&self) -> Result<()>254     pub fn check_len(&self) -> Result<()> {
255         let len = self.buffer.as_ref().len();
256         if len < field::MAGIC_NUMBER.end {
257             Err(Error)
258         } else {
259             Ok(())
260         }
261     }
262 
263     /// Consume the packet, returning the underlying buffer.
into_inner(self) -> T264     pub fn into_inner(self) -> T {
265         self.buffer
266     }
267 
268     /// Returns the operation code of this packet.
opcode(&self) -> OpCode269     pub fn opcode(&self) -> OpCode {
270         let data = self.buffer.as_ref();
271         OpCode::from(data[field::OP])
272     }
273 
274     /// Returns the hardware protocol type (e.g. ethernet).
hardware_type(&self) -> Hardware275     pub fn hardware_type(&self) -> Hardware {
276         let data = self.buffer.as_ref();
277         Hardware::from(u16::from(data[field::HTYPE]))
278     }
279 
280     /// Returns the length of a hardware address in bytes (e.g. 6 for ethernet).
hardware_len(&self) -> u8281     pub fn hardware_len(&self) -> u8 {
282         self.buffer.as_ref()[field::HLEN]
283     }
284 
285     /// Returns the transaction ID.
286     ///
287     /// The transaction ID (called `xid` in the specification) is a random number used to
288     /// associate messages and responses between client and server. The number is chosen by
289     /// the client.
transaction_id(&self) -> u32290     pub fn transaction_id(&self) -> u32 {
291         let field = &self.buffer.as_ref()[field::XID];
292         NetworkEndian::read_u32(field)
293     }
294 
295     /// Returns the hardware address of the client (called `chaddr` in the specification).
296     ///
297     /// Only ethernet is supported by `smoltcp`, so this functions returns
298     /// an `EthernetAddress`.
client_hardware_address(&self) -> EthernetAddress299     pub fn client_hardware_address(&self) -> EthernetAddress {
300         let field = &self.buffer.as_ref()[field::CHADDR];
301         EthernetAddress::from_bytes(field)
302     }
303 
304     /// Returns the value of the `hops` field.
305     ///
306     /// The `hops` field is set to zero by clients and optionally used by relay agents.
hops(&self) -> u8307     pub fn hops(&self) -> u8 {
308         self.buffer.as_ref()[field::HOPS]
309     }
310 
311     /// Returns the value of the `secs` field.
312     ///
313     /// The secs field is filled by clients and describes the number of seconds elapsed
314     /// since client began process.
secs(&self) -> u16315     pub fn secs(&self) -> u16 {
316         let field = &self.buffer.as_ref()[field::SECS];
317         NetworkEndian::read_u16(field)
318     }
319 
320     /// Returns the value of the `magic cookie` field in the DHCP options.
321     ///
322     /// This field should be always be `0x63825363`.
magic_number(&self) -> u32323     pub fn magic_number(&self) -> u32 {
324         let field = &self.buffer.as_ref()[field::MAGIC_NUMBER];
325         NetworkEndian::read_u32(field)
326     }
327 
328     /// Returns the Ipv4 address of the client, zero if not set.
329     ///
330     /// This corresponds to the `ciaddr` field in the DHCP specification. According to it,
331     /// this field is “only filled in if client is in `BOUND`, `RENEW` or `REBINDING` state
332     /// and can respond to ARP requests”.
client_ip(&self) -> Ipv4Address333     pub fn client_ip(&self) -> Ipv4Address {
334         let field = &self.buffer.as_ref()[field::CIADDR];
335         Ipv4Address::from_bytes(field)
336     }
337 
338     /// Returns the value of the `yiaddr` field, zero if not set.
your_ip(&self) -> Ipv4Address339     pub fn your_ip(&self) -> Ipv4Address {
340         let field = &self.buffer.as_ref()[field::YIADDR];
341         Ipv4Address::from_bytes(field)
342     }
343 
344     /// Returns the value of the `siaddr` field, zero if not set.
server_ip(&self) -> Ipv4Address345     pub fn server_ip(&self) -> Ipv4Address {
346         let field = &self.buffer.as_ref()[field::SIADDR];
347         Ipv4Address::from_bytes(field)
348     }
349 
350     /// Returns the value of the `giaddr` field, zero if not set.
relay_agent_ip(&self) -> Ipv4Address351     pub fn relay_agent_ip(&self) -> Ipv4Address {
352         let field = &self.buffer.as_ref()[field::GIADDR];
353         Ipv4Address::from_bytes(field)
354     }
355 
flags(&self) -> Flags356     pub fn flags(&self) -> Flags {
357         let field = &self.buffer.as_ref()[field::FLAGS];
358         Flags::from_bits_truncate(NetworkEndian::read_u16(field))
359     }
360 
361     /// Return an iterator over the options.
362     #[inline]
options(&self) -> impl Iterator<Item = DhcpOption<'_>> + '_363     pub fn options(&self) -> impl Iterator<Item = DhcpOption<'_>> + '_ {
364         let mut buf = &self.buffer.as_ref()[field::OPTIONS];
365         iter::from_fn(move || {
366             loop {
367                 match buf.first().copied() {
368                     // No more options, return.
369                     None => return None,
370                     Some(field::OPT_END) => return None,
371 
372                     // Skip padding.
373                     Some(field::OPT_PAD) => buf = &buf[1..],
374                     Some(kind) => {
375                         if buf.len() < 2 {
376                             return None;
377                         }
378 
379                         let len = buf[1] as usize;
380 
381                         if buf.len() < 2 + len {
382                             return None;
383                         }
384 
385                         let opt = DhcpOption {
386                             kind,
387                             data: &buf[2..2 + len],
388                         };
389 
390                         buf = &buf[2 + len..];
391                         return Some(opt);
392                     }
393                 }
394             }
395         })
396     }
397 
get_sname(&self) -> Result<&str>398     pub fn get_sname(&self) -> Result<&str> {
399         let data = &self.buffer.as_ref()[field::SNAME];
400         let len = data.iter().position(|&x| x == 0).ok_or(Error)?;
401         if len == 0 {
402             return Err(Error);
403         }
404 
405         let data = core::str::from_utf8(&data[..len]).map_err(|_| Error)?;
406         Ok(data)
407     }
408 
get_boot_file(&self) -> Result<&str>409     pub fn get_boot_file(&self) -> Result<&str> {
410         let data = &self.buffer.as_ref()[field::FILE];
411         let len = data.iter().position(|&x| x == 0).ok_or(Error)?;
412         if len == 0 {
413             return Err(Error);
414         }
415         let data = core::str::from_utf8(&data[..len]).map_err(|_| Error)?;
416         Ok(data)
417     }
418 }
419 
420 impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
421     /// Sets the optional `sname` (“server name”) and `file` (“boot file name”) fields to zero.
422     ///
423     /// The fields are not commonly used, so we set their value always to zero. **This method
424     /// must be called when creating a packet, otherwise the emitted values for these fields
425     /// are undefined!**
set_sname_and_boot_file_to_zero(&mut self)426     pub fn set_sname_and_boot_file_to_zero(&mut self) {
427         let data = self.buffer.as_mut();
428         for byte in &mut data[field::SNAME] {
429             *byte = 0;
430         }
431         for byte in &mut data[field::FILE] {
432             *byte = 0;
433         }
434     }
435 
436     /// Sets the `OpCode` for the packet.
set_opcode(&mut self, value: OpCode)437     pub fn set_opcode(&mut self, value: OpCode) {
438         let data = self.buffer.as_mut();
439         data[field::OP] = value.into();
440     }
441 
442     /// Sets the hardware address type (only ethernet is supported).
set_hardware_type(&mut self, value: Hardware)443     pub fn set_hardware_type(&mut self, value: Hardware) {
444         let data = self.buffer.as_mut();
445         let number: u16 = value.into();
446         assert!(number <= u16::from(u8::max_value())); // TODO: Replace with TryFrom when it's stable
447         data[field::HTYPE] = number as u8;
448     }
449 
450     /// Sets the hardware address length.
451     ///
452     /// Only ethernet is supported, so this field should be set to the value `6`.
set_hardware_len(&mut self, value: u8)453     pub fn set_hardware_len(&mut self, value: u8) {
454         self.buffer.as_mut()[field::HLEN] = value;
455     }
456 
457     /// Sets the transaction ID.
458     ///
459     /// The transaction ID (called `xid` in the specification) is a random number used to
460     /// associate messages and responses between client and server. The number is chosen by
461     /// the client.
set_transaction_id(&mut self, value: u32)462     pub fn set_transaction_id(&mut self, value: u32) {
463         let field = &mut self.buffer.as_mut()[field::XID];
464         NetworkEndian::write_u32(field, value)
465     }
466 
467     /// Sets the ethernet address of the client.
468     ///
469     /// Sets the `chaddr` field.
set_client_hardware_address(&mut self, value: EthernetAddress)470     pub fn set_client_hardware_address(&mut self, value: EthernetAddress) {
471         let field = &mut self.buffer.as_mut()[field::CHADDR];
472         field.copy_from_slice(value.as_bytes());
473     }
474 
475     /// Sets the hops field.
476     ///
477     /// The `hops` field is set to zero by clients and optionally used by relay agents.
set_hops(&mut self, value: u8)478     pub fn set_hops(&mut self, value: u8) {
479         self.buffer.as_mut()[field::HOPS] = value;
480     }
481 
482     /// Sets the `secs` field.
483     ///
484     /// The secs field is filled by clients and describes the number of seconds elapsed
485     /// since client began process.
set_secs(&mut self, value: u16)486     pub fn set_secs(&mut self, value: u16) {
487         let field = &mut self.buffer.as_mut()[field::SECS];
488         NetworkEndian::write_u16(field, value);
489     }
490 
491     /// Sets the value of the `magic cookie` field in the DHCP options.
492     ///
493     /// This field should be always be `0x63825363`.
set_magic_number(&mut self, value: u32)494     pub fn set_magic_number(&mut self, value: u32) {
495         let field = &mut self.buffer.as_mut()[field::MAGIC_NUMBER];
496         NetworkEndian::write_u32(field, value);
497     }
498 
499     /// Sets the Ipv4 address of the client.
500     ///
501     /// This corresponds to the `ciaddr` field in the DHCP specification. According to it,
502     /// this field is “only filled in if client is in `BOUND`, `RENEW` or `REBINDING` state
503     /// and can respond to ARP requests”.
set_client_ip(&mut self, value: Ipv4Address)504     pub fn set_client_ip(&mut self, value: Ipv4Address) {
505         let field = &mut self.buffer.as_mut()[field::CIADDR];
506         field.copy_from_slice(value.as_bytes());
507     }
508 
509     /// Sets the value of the `yiaddr` field.
set_your_ip(&mut self, value: Ipv4Address)510     pub fn set_your_ip(&mut self, value: Ipv4Address) {
511         let field = &mut self.buffer.as_mut()[field::YIADDR];
512         field.copy_from_slice(value.as_bytes());
513     }
514 
515     /// Sets the value of the `siaddr` field.
set_server_ip(&mut self, value: Ipv4Address)516     pub fn set_server_ip(&mut self, value: Ipv4Address) {
517         let field = &mut self.buffer.as_mut()[field::SIADDR];
518         field.copy_from_slice(value.as_bytes());
519     }
520 
521     /// Sets the value of the `giaddr` field.
set_relay_agent_ip(&mut self, value: Ipv4Address)522     pub fn set_relay_agent_ip(&mut self, value: Ipv4Address) {
523         let field = &mut self.buffer.as_mut()[field::GIADDR];
524         field.copy_from_slice(value.as_bytes());
525     }
526 
527     /// Sets the flags to the specified value.
set_flags(&mut self, val: Flags)528     pub fn set_flags(&mut self, val: Flags) {
529         let field = &mut self.buffer.as_mut()[field::FLAGS];
530         NetworkEndian::write_u16(field, val.bits());
531     }
532 }
533 
534 impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
535     /// Return a pointer to the options.
536     #[inline]
options_mut(&mut self) -> DhcpOptionWriter<'_>537     pub fn options_mut(&mut self) -> DhcpOptionWriter<'_> {
538         DhcpOptionWriter::new(&mut self.buffer.as_mut()[field::OPTIONS])
539     }
540 }
541 
542 /// A high-level representation of a Dynamic Host Configuration Protocol packet.
543 ///
544 /// DHCP messages have the following layout (see [RFC 2131](https://tools.ietf.org/html/rfc2131)
545 /// for details):
546 ///
547 /// ```no_rust
548 /// 0                   1                   2                   3
549 /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
550 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
551 /// | message_type  | htype (N/A)   |   hlen (N/A)  |   hops        |
552 /// +---------------+---------------+---------------+---------------+
553 /// |                       transaction_id                          |
554 /// +-------------------------------+-------------------------------+
555 /// |           secs                |           flags               |
556 /// +-------------------------------+-------------------------------+
557 /// |                           client_ip                           |
558 /// +---------------------------------------------------------------+
559 /// |                            your_ip                            |
560 /// +---------------------------------------------------------------+
561 /// |                           server_ip                           |
562 /// +---------------------------------------------------------------+
563 /// |                        relay_agent_ip                         |
564 /// +---------------------------------------------------------------+
565 /// |                                                               |
566 /// |                    client_hardware_address                    |
567 /// |                                                               |
568 /// |                                                               |
569 /// +---------------------------------------------------------------+
570 /// |                                                               |
571 /// |                          sname  (N/A)                         |
572 /// +---------------------------------------------------------------+
573 /// |                                                               |
574 /// |                          file    (N/A)                        |
575 /// +---------------------------------------------------------------+
576 /// |                                                               |
577 /// |                          options                              |
578 /// +---------------------------------------------------------------+
579 /// ```
580 ///
581 /// It is assumed that the access layer is Ethernet, so `htype` (the field representing the
582 /// hardware address type) is always set to `1`, and `hlen` (which represents the hardware address
583 /// length) is set to `6`.
584 ///
585 /// The `options` field has a variable length.
586 #[derive(Debug, PartialEq, Eq, Clone)]
587 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
588 pub struct Repr<'a> {
589     /// This field is also known as `op` in the RFC. It indicates the type of DHCP message this
590     /// packet represents.
591     pub message_type: MessageType,
592     /// This field is also known as `xid` in the RFC. It is a random number chosen by the client,
593     /// used by the client and server to associate messages and responses between a client and a
594     /// server.
595     pub transaction_id: u32,
596     /// seconds elapsed since client began address acquisition or renewal
597     /// process the DHCPREQUEST message MUST use the same value in the DHCP
598     /// message header's 'secs' field and be sent to the same IP broadcast
599     /// address as the original DHCPDISCOVER message.
600     pub secs: u16,
601     /// This field is also known as `chaddr` in the RFC and for networks where the access layer is
602     /// ethernet, it is the client MAC address.
603     pub client_hardware_address: EthernetAddress,
604     /// This field is also known as `ciaddr` in the RFC. It is only filled in if client is in
605     /// BOUND, RENEW or REBINDING state and can respond to ARP requests.
606     pub client_ip: Ipv4Address,
607     /// This field is also known as `yiaddr` in the RFC.
608     pub your_ip: Ipv4Address,
609     /// This field is also known as `siaddr` in the RFC. It may be set by the server in DHCPOFFER
610     /// and DHCPACK messages, and represent the address of the next server to use in bootstrap.
611     pub server_ip: Ipv4Address,
612     /// Default gateway
613     pub router: Option<Ipv4Address>,
614     /// This field comes from a corresponding DhcpOption.
615     pub subnet_mask: Option<Ipv4Address>,
616     /// This field is also known as `giaddr` in the RFC. In order to allow DHCP clients on subnets
617     /// not directly served by DHCP servers to communicate with DHCP servers, DHCP relay agents can
618     /// be installed on these subnets. The DHCP client broadcasts on the local link; the relay
619     /// agent receives the broadcast and transmits it to one or more DHCP servers using unicast.
620     /// The relay agent stores its own IP address in the `relay_agent_ip` field of the DHCP packet.
621     /// The DHCP server uses the `relay_agent_ip` to determine the subnet on which the relay agent
622     /// received the broadcast, and allocates an IP address on that subnet. When the DHCP server
623     /// replies to the client, it sends the reply to the `relay_agent_ip` address, again using
624     /// unicast. The relay agent then retransmits the response on the local network
625     pub relay_agent_ip: Ipv4Address,
626     /// Broadcast flags. It can be set in DHCPDISCOVER, DHCPINFORM and DHCPREQUEST message if the
627     /// client requires the response to be broadcasted.
628     pub broadcast: bool,
629     /// The "requested IP address" option. It can be used by clients in DHCPREQUEST or DHCPDISCOVER
630     /// messages, or by servers in DHCPDECLINE messages.
631     pub requested_ip: Option<Ipv4Address>,
632     /// The "client identifier" option.
633     ///
634     /// The 'client identifier' is an opaque key, not to be interpreted by the server; for example,
635     /// the 'client identifier' may contain a hardware address, identical to the contents of the
636     /// 'chaddr' field, or it may contain another type of identifier, such as a DNS name.  The
637     /// 'client identifier' chosen by a DHCP client MUST be unique to that client within the subnet
638     /// to which the client is attached. If the client uses a 'client identifier' in one message,
639     /// it MUST use that same identifier in all subsequent messages, to ensure that all servers
640     /// correctly identify the client.
641     pub client_identifier: Option<EthernetAddress>,
642     /// The "server identifier" option. It is used both to identify a DHCP server
643     /// in a DHCP message and as a destination address from clients to servers.
644     pub server_identifier: Option<Ipv4Address>,
645     /// The parameter request list informs the server about which configuration parameters
646     /// the client is interested in.
647     pub parameter_request_list: Option<&'a [u8]>,
648     /// DNS servers
649     pub dns_servers: Option<Vec<Ipv4Address, MAX_DNS_SERVER_COUNT>>,
650     /// The maximum size dhcp packet the interface can receive
651     pub max_size: Option<u16>,
652     /// The DHCP IP lease duration, specified in seconds.
653     pub lease_duration: Option<u32>,
654     /// The DHCP IP renew duration (T1 interval), in seconds, if specified in the packet.
655     pub renew_duration: Option<u32>,
656     /// The DHCP IP rebind duration (T2 interval), in seconds, if specified in the packet.
657     pub rebind_duration: Option<u32>,
658     /// When returned from [`Repr::parse`], this field will be `None`.
659     /// However, when calling [`Repr::emit`], this field should contain only
660     /// additional DHCP options not known to smoltcp.
661     pub additional_options: &'a [DhcpOption<'a>],
662 }
663 
664 impl<'a> Repr<'a> {
665     /// Return the length of a packet that will be emitted from this high-level representation.
buffer_len(&self) -> usize666     pub fn buffer_len(&self) -> usize {
667         let mut len = field::OPTIONS.start;
668         // message type and end-of-options options
669         len += 3 + 1;
670         if self.requested_ip.is_some() {
671             len += 6;
672         }
673         if self.client_identifier.is_some() {
674             len += 9;
675         }
676         if self.server_identifier.is_some() {
677             len += 6;
678         }
679         if self.max_size.is_some() {
680             len += 4;
681         }
682         if self.router.is_some() {
683             len += 6;
684         }
685         if self.subnet_mask.is_some() {
686             len += 6;
687         }
688         if self.lease_duration.is_some() {
689             len += 6;
690         }
691         if let Some(dns_servers) = &self.dns_servers {
692             len += 2;
693             len += dns_servers.iter().count() * core::mem::size_of::<u32>();
694         }
695         if let Some(list) = self.parameter_request_list {
696             len += list.len() + 2;
697         }
698         for opt in self.additional_options {
699             len += 2 + opt.data.len()
700         }
701 
702         len
703     }
704 
705     /// Parse a DHCP packet and return a high-level representation.
parse<T>(packet: &'a Packet<&'a T>) -> Result<Self> where T: AsRef<[u8]> + ?Sized,706     pub fn parse<T>(packet: &'a Packet<&'a T>) -> Result<Self>
707     where
708         T: AsRef<[u8]> + ?Sized,
709     {
710         let transaction_id = packet.transaction_id();
711         let client_hardware_address = packet.client_hardware_address();
712         let client_ip = packet.client_ip();
713         let your_ip = packet.your_ip();
714         let server_ip = packet.server_ip();
715         let relay_agent_ip = packet.relay_agent_ip();
716         let secs = packet.secs();
717 
718         // only ethernet is supported right now
719         match packet.hardware_type() {
720             Hardware::Ethernet => {
721                 if packet.hardware_len() != 6 {
722                     return Err(Error);
723                 }
724             }
725             Hardware::Unknown(_) => return Err(Error), // unimplemented
726         }
727 
728         if packet.magic_number() != DHCP_MAGIC_NUMBER {
729             return Err(Error);
730         }
731 
732         let mut message_type = Err(Error);
733         let mut requested_ip = None;
734         let mut client_identifier = None;
735         let mut server_identifier = None;
736         let mut router = None;
737         let mut subnet_mask = None;
738         let mut parameter_request_list = None;
739         let mut dns_servers = None;
740         let mut max_size = None;
741         let mut lease_duration = None;
742         let mut renew_duration = None;
743         let mut rebind_duration = None;
744 
745         for option in packet.options() {
746             let data = option.data;
747             match (option.kind, data.len()) {
748                 (field::OPT_DHCP_MESSAGE_TYPE, 1) => {
749                     let value = MessageType::from(data[0]);
750                     if value.opcode() == packet.opcode() {
751                         message_type = Ok(value);
752                     }
753                 }
754                 (field::OPT_REQUESTED_IP, 4) => {
755                     requested_ip = Some(Ipv4Address::from_bytes(data));
756                 }
757                 (field::OPT_CLIENT_ID, 7) => {
758                     let hardware_type = Hardware::from(u16::from(data[0]));
759                     if hardware_type != Hardware::Ethernet {
760                         return Err(Error);
761                     }
762                     client_identifier = Some(EthernetAddress::from_bytes(&data[1..]));
763                 }
764                 (field::OPT_SERVER_IDENTIFIER, 4) => {
765                     server_identifier = Some(Ipv4Address::from_bytes(data));
766                 }
767                 (field::OPT_ROUTER, 4) => {
768                     router = Some(Ipv4Address::from_bytes(data));
769                 }
770                 (field::OPT_SUBNET_MASK, 4) => {
771                     subnet_mask = Some(Ipv4Address::from_bytes(data));
772                 }
773                 (field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => {
774                     max_size = Some(u16::from_be_bytes([data[0], data[1]]));
775                 }
776                 (field::OPT_RENEWAL_TIME_VALUE, 4) => {
777                     renew_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
778                 }
779                 (field::OPT_REBINDING_TIME_VALUE, 4) => {
780                     rebind_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
781                 }
782                 (field::OPT_IP_LEASE_TIME, 4) => {
783                     lease_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
784                 }
785                 (field::OPT_PARAMETER_REQUEST_LIST, _) => {
786                     parameter_request_list = Some(data);
787                 }
788                 (field::OPT_DOMAIN_NAME_SERVER, _) => {
789                     let mut servers = Vec::new();
790                     const IP_ADDR_BYTE_LEN: usize = 4;
791                     for chunk in data.chunks(IP_ADDR_BYTE_LEN) {
792                         // We ignore push failures because that will only happen
793                         // if we attempt to push more than 4 addresses, and the only
794                         // solution to that is to support more addresses.
795                         servers.push(Ipv4Address::from_bytes(chunk)).ok();
796                     }
797                     dns_servers = Some(servers);
798                 }
799                 _ => {}
800             }
801         }
802 
803         let broadcast = packet.flags().contains(Flags::BROADCAST);
804 
805         Ok(Repr {
806             secs,
807             transaction_id,
808             client_hardware_address,
809             client_ip,
810             your_ip,
811             server_ip,
812             relay_agent_ip,
813             broadcast,
814             requested_ip,
815             server_identifier,
816             router,
817             subnet_mask,
818             client_identifier,
819             parameter_request_list,
820             dns_servers,
821             max_size,
822             lease_duration,
823             renew_duration,
824             rebind_duration,
825             message_type: message_type?,
826             additional_options: &[],
827         })
828     }
829 
830     /// Emit a high-level representation into a Dynamic Host
831     /// Configuration Protocol packet.
emit<T>(&self, packet: &mut Packet<&mut T>) -> Result<()> where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,832     pub fn emit<T>(&self, packet: &mut Packet<&mut T>) -> Result<()>
833     where
834         T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
835     {
836         packet.set_sname_and_boot_file_to_zero();
837         packet.set_opcode(self.message_type.opcode());
838         packet.set_hardware_type(Hardware::Ethernet);
839         packet.set_hardware_len(6);
840         packet.set_transaction_id(self.transaction_id);
841         packet.set_client_hardware_address(self.client_hardware_address);
842         packet.set_hops(0);
843         packet.set_secs(self.secs);
844         packet.set_magic_number(0x63825363);
845         packet.set_client_ip(self.client_ip);
846         packet.set_your_ip(self.your_ip);
847         packet.set_server_ip(self.server_ip);
848         packet.set_relay_agent_ip(self.relay_agent_ip);
849 
850         let mut flags = Flags::empty();
851         if self.broadcast {
852             flags |= Flags::BROADCAST;
853         }
854         packet.set_flags(flags);
855 
856         {
857             let mut options = packet.options_mut();
858 
859             options.emit(DhcpOption {
860                 kind: field::OPT_DHCP_MESSAGE_TYPE,
861                 data: &[self.message_type.into()],
862             })?;
863 
864             if let Some(val) = &self.client_identifier {
865                 let mut data = [0; 7];
866                 data[0] = u16::from(Hardware::Ethernet) as u8;
867                 data[1..].copy_from_slice(val.as_bytes());
868 
869                 options.emit(DhcpOption {
870                     kind: field::OPT_CLIENT_ID,
871                     data: &data,
872                 })?;
873             }
874 
875             if let Some(val) = &self.server_identifier {
876                 options.emit(DhcpOption {
877                     kind: field::OPT_SERVER_IDENTIFIER,
878                     data: val.as_bytes(),
879                 })?;
880             }
881 
882             if let Some(val) = &self.router {
883                 options.emit(DhcpOption {
884                     kind: field::OPT_ROUTER,
885                     data: val.as_bytes(),
886                 })?;
887             }
888             if let Some(val) = &self.subnet_mask {
889                 options.emit(DhcpOption {
890                     kind: field::OPT_SUBNET_MASK,
891                     data: val.as_bytes(),
892                 })?;
893             }
894             if let Some(val) = &self.requested_ip {
895                 options.emit(DhcpOption {
896                     kind: field::OPT_REQUESTED_IP,
897                     data: val.as_bytes(),
898                 })?;
899             }
900             if let Some(val) = &self.max_size {
901                 options.emit(DhcpOption {
902                     kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
903                     data: &val.to_be_bytes(),
904                 })?;
905             }
906             if let Some(val) = &self.lease_duration {
907                 options.emit(DhcpOption {
908                     kind: field::OPT_IP_LEASE_TIME,
909                     data: &val.to_be_bytes(),
910                 })?;
911             }
912             if let Some(val) = &self.parameter_request_list {
913                 options.emit(DhcpOption {
914                     kind: field::OPT_PARAMETER_REQUEST_LIST,
915                     data: val,
916                 })?;
917             }
918 
919             if let Some(dns_servers) = &self.dns_servers {
920                 const IP_SIZE: usize = core::mem::size_of::<u32>();
921                 let mut servers = [0; MAX_DNS_SERVER_COUNT * IP_SIZE];
922 
923                 let data_len = dns_servers
924                     .iter()
925                     .enumerate()
926                     .inspect(|(i, ip)| {
927                         servers[(i * IP_SIZE)..((i + 1) * IP_SIZE)].copy_from_slice(ip.as_bytes());
928                     })
929                     .count()
930                     * IP_SIZE;
931                 options.emit(DhcpOption {
932                     kind: field::OPT_DOMAIN_NAME_SERVER,
933                     data: &servers[..data_len],
934                 })?;
935             }
936 
937             for option in self.additional_options {
938                 options.emit(*option)?;
939             }
940 
941             options.end()?;
942         }
943 
944         Ok(())
945     }
946 }
947 
948 #[cfg(test)]
949 mod test {
950     use super::*;
951     use crate::wire::Ipv4Address;
952 
953     const MAGIC_COOKIE: u32 = 0x63825363;
954 
955     static DISCOVER_BYTES: &[u8] = &[
956         0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
957         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
958         0x82, 0x01, 0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
959         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
960         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
961         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
962         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
963         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
964         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
965         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
966         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
967         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
968         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
969         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
970         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
971         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
972         0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00,
973         0x00, 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00,
974         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
975     ];
976 
977     static ACK_DNS_SERVER_BYTES: &[u8] = &[
978         0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06,
979         0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17,
980         0xeb, 0xc9, 0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
981         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
982         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
983         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
984         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
985         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
986         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
987         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
988         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
989         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
990         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
991         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
992         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
993         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
994         0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00,
995         0x2b, 0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68,
996         0x79, 0x73, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00,
997         0x03, 0x04, 0x0a, 0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a,
998         0x07, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03,
999         0xa3, 0x01, 0x4a, 0x04, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08,
1000         0xff,
1001     ];
1002 
1003     static ACK_LEASE_TIME_BYTES: &[u8] = &[
1004         0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1005         0x00, 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91,
1006         0x62, 0xd2, 0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1007         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1008         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1009         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1010         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1011         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1012         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1013         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1014         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1015         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1016         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1017         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1018         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1019         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
1020         0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56,
1021         0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00,
1022         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1023         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1024     ];
1025 
1026     const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]);
1027     const CLIENT_MAC: EthernetAddress = EthernetAddress([0x0, 0x0b, 0x82, 0x01, 0xfc, 0x42]);
1028     const DHCP_SIZE: u16 = 1500;
1029 
1030     #[test]
test_deconstruct_discover()1031     fn test_deconstruct_discover() {
1032         let packet = Packet::new_unchecked(DISCOVER_BYTES);
1033         assert_eq!(packet.magic_number(), MAGIC_COOKIE);
1034         assert_eq!(packet.opcode(), OpCode::Request);
1035         assert_eq!(packet.hardware_type(), Hardware::Ethernet);
1036         assert_eq!(packet.hardware_len(), 6);
1037         assert_eq!(packet.hops(), 0);
1038         assert_eq!(packet.transaction_id(), 0x3d1d);
1039         assert_eq!(packet.secs(), 0);
1040         assert_eq!(packet.client_ip(), IP_NULL);
1041         assert_eq!(packet.your_ip(), IP_NULL);
1042         assert_eq!(packet.server_ip(), IP_NULL);
1043         assert_eq!(packet.relay_agent_ip(), IP_NULL);
1044         assert_eq!(packet.client_hardware_address(), CLIENT_MAC);
1045 
1046         let mut options = packet.options();
1047         assert_eq!(
1048             options.next(),
1049             Some(DhcpOption {
1050                 kind: field::OPT_DHCP_MESSAGE_TYPE,
1051                 data: &[0x01]
1052             })
1053         );
1054         assert_eq!(
1055             options.next(),
1056             Some(DhcpOption {
1057                 kind: field::OPT_CLIENT_ID,
1058                 data: &[0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42],
1059             })
1060         );
1061         assert_eq!(
1062             options.next(),
1063             Some(DhcpOption {
1064                 kind: field::OPT_REQUESTED_IP,
1065                 data: &[0x00, 0x00, 0x00, 0x00],
1066             })
1067         );
1068         assert_eq!(
1069             options.next(),
1070             Some(DhcpOption {
1071                 kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
1072                 data: &DHCP_SIZE.to_be_bytes(),
1073             })
1074         );
1075         assert_eq!(
1076             options.next(),
1077             Some(DhcpOption {
1078                 kind: field::OPT_PARAMETER_REQUEST_LIST,
1079                 data: &[1, 3, 6, 42]
1080             })
1081         );
1082         assert_eq!(options.next(), None);
1083     }
1084 
1085     #[test]
test_construct_discover()1086     fn test_construct_discover() {
1087         let mut bytes = vec![0xa5; 276];
1088         let mut packet = Packet::new_unchecked(&mut bytes);
1089         packet.set_magic_number(MAGIC_COOKIE);
1090         packet.set_sname_and_boot_file_to_zero();
1091         packet.set_opcode(OpCode::Request);
1092         packet.set_hardware_type(Hardware::Ethernet);
1093         packet.set_hardware_len(6);
1094         packet.set_hops(0);
1095         packet.set_transaction_id(0x3d1d);
1096         packet.set_secs(0);
1097         packet.set_flags(Flags::empty());
1098         packet.set_client_ip(IP_NULL);
1099         packet.set_your_ip(IP_NULL);
1100         packet.set_server_ip(IP_NULL);
1101         packet.set_relay_agent_ip(IP_NULL);
1102         packet.set_client_hardware_address(CLIENT_MAC);
1103 
1104         let mut options = packet.options_mut();
1105 
1106         options
1107             .emit(DhcpOption {
1108                 kind: field::OPT_DHCP_MESSAGE_TYPE,
1109                 data: &[0x01],
1110             })
1111             .unwrap();
1112         options
1113             .emit(DhcpOption {
1114                 kind: field::OPT_CLIENT_ID,
1115                 data: &[0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42],
1116             })
1117             .unwrap();
1118         options
1119             .emit(DhcpOption {
1120                 kind: field::OPT_REQUESTED_IP,
1121                 data: &[0x00, 0x00, 0x00, 0x00],
1122             })
1123             .unwrap();
1124         options
1125             .emit(DhcpOption {
1126                 kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
1127                 data: &DHCP_SIZE.to_be_bytes(),
1128             })
1129             .unwrap();
1130         options
1131             .emit(DhcpOption {
1132                 kind: field::OPT_PARAMETER_REQUEST_LIST,
1133                 data: &[1, 3, 6, 42],
1134             })
1135             .unwrap();
1136         options.end().unwrap();
1137 
1138         let packet = &mut packet.into_inner()[..];
1139         for byte in &mut packet[269..276] {
1140             *byte = 0; // padding bytes
1141         }
1142 
1143         assert_eq!(packet, DISCOVER_BYTES);
1144     }
1145 
offer_repr() -> Repr<'static>1146     const fn offer_repr() -> Repr<'static> {
1147         Repr {
1148             message_type: MessageType::Offer,
1149             transaction_id: 0x3d1d,
1150             client_hardware_address: CLIENT_MAC,
1151             client_ip: IP_NULL,
1152             your_ip: IP_NULL,
1153             server_ip: IP_NULL,
1154             router: Some(IP_NULL),
1155             subnet_mask: Some(IP_NULL),
1156             relay_agent_ip: IP_NULL,
1157             secs: 0,
1158             broadcast: false,
1159             requested_ip: None,
1160             client_identifier: Some(CLIENT_MAC),
1161             server_identifier: None,
1162             parameter_request_list: None,
1163             dns_servers: None,
1164             max_size: None,
1165             renew_duration: None,
1166             rebind_duration: None,
1167             lease_duration: Some(0xffff_ffff), // Infinite lease
1168             additional_options: &[],
1169         }
1170     }
1171 
discover_repr() -> Repr<'static>1172     const fn discover_repr() -> Repr<'static> {
1173         Repr {
1174             message_type: MessageType::Discover,
1175             transaction_id: 0x3d1d,
1176             client_hardware_address: CLIENT_MAC,
1177             client_ip: IP_NULL,
1178             your_ip: IP_NULL,
1179             server_ip: IP_NULL,
1180             router: None,
1181             subnet_mask: None,
1182             relay_agent_ip: IP_NULL,
1183             broadcast: false,
1184             secs: 0,
1185             max_size: Some(DHCP_SIZE),
1186             renew_duration: None,
1187             rebind_duration: None,
1188             lease_duration: None,
1189             requested_ip: Some(IP_NULL),
1190             client_identifier: Some(CLIENT_MAC),
1191             server_identifier: None,
1192             parameter_request_list: Some(&[1, 3, 6, 42]),
1193             dns_servers: None,
1194             additional_options: &[],
1195         }
1196     }
1197 
1198     #[test]
test_parse_discover()1199     fn test_parse_discover() {
1200         let packet = Packet::new_unchecked(DISCOVER_BYTES);
1201         let repr = Repr::parse(&packet).unwrap();
1202         assert_eq!(repr, discover_repr());
1203     }
1204 
1205     #[test]
test_emit_discover()1206     fn test_emit_discover() {
1207         let repr = discover_repr();
1208         let mut bytes = vec![0xa5; repr.buffer_len()];
1209         let mut packet = Packet::new_unchecked(&mut bytes);
1210         repr.emit(&mut packet).unwrap();
1211         let packet = &*packet.into_inner();
1212         let packet_len = packet.len();
1213         assert_eq!(packet, &DISCOVER_BYTES[..packet_len]);
1214         for byte in &DISCOVER_BYTES[packet_len..] {
1215             assert_eq!(*byte, 0); // padding bytes
1216         }
1217     }
1218 
1219     #[test]
test_emit_offer()1220     fn test_emit_offer() {
1221         let repr = offer_repr();
1222         let mut bytes = vec![0xa5; repr.buffer_len()];
1223         let mut packet = Packet::new_unchecked(&mut bytes);
1224         repr.emit(&mut packet).unwrap();
1225     }
1226 
1227     #[test]
test_emit_offer_dns()1228     fn test_emit_offer_dns() {
1229         let repr = {
1230             let mut repr = offer_repr();
1231             repr.dns_servers = Some(
1232                 Vec::from_slice(&[
1233                     Ipv4Address([163, 1, 74, 6]),
1234                     Ipv4Address([163, 1, 74, 7]),
1235                     Ipv4Address([163, 1, 74, 3]),
1236                 ])
1237                 .unwrap(),
1238             );
1239             repr
1240         };
1241         let mut bytes = vec![0xa5; repr.buffer_len()];
1242         let mut packet = Packet::new_unchecked(&mut bytes);
1243         repr.emit(&mut packet).unwrap();
1244 
1245         let packet = Packet::new_unchecked(&bytes);
1246         let repr_parsed = Repr::parse(&packet).unwrap();
1247 
1248         assert_eq!(
1249             repr_parsed.dns_servers,
1250             Some(
1251                 Vec::from_slice(&[
1252                     Ipv4Address([163, 1, 74, 6]),
1253                     Ipv4Address([163, 1, 74, 7]),
1254                     Ipv4Address([163, 1, 74, 3]),
1255                 ])
1256                 .unwrap()
1257             )
1258         );
1259     }
1260 
1261     #[test]
test_emit_dhcp_option()1262     fn test_emit_dhcp_option() {
1263         static DATA: &[u8] = &[1, 3, 6];
1264         let dhcp_option = DhcpOption {
1265             kind: field::OPT_PARAMETER_REQUEST_LIST,
1266             data: DATA,
1267         };
1268 
1269         let mut bytes = vec![0xa5; 5];
1270         let mut writer = DhcpOptionWriter::new(&mut bytes);
1271         writer.emit(dhcp_option).unwrap();
1272 
1273         assert_eq!(
1274             &bytes[0..2],
1275             &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]
1276         );
1277         assert_eq!(&bytes[2..], DATA);
1278     }
1279 
1280     #[test]
test_parse_ack_dns_servers()1281     fn test_parse_ack_dns_servers() {
1282         let packet = Packet::new_unchecked(ACK_DNS_SERVER_BYTES);
1283         let repr = Repr::parse(&packet).unwrap();
1284 
1285         // The packet described by ACK_BYTES advertises 4 DNS servers
1286         // Here we ensure that we correctly parse the first 3 into our fixed
1287         // length-3 array (see issue #305)
1288         assert_eq!(
1289             repr.dns_servers,
1290             Some(
1291                 Vec::from_slice(&[
1292                     Ipv4Address([163, 1, 74, 6]),
1293                     Ipv4Address([163, 1, 74, 7]),
1294                     Ipv4Address([163, 1, 74, 3])
1295                 ])
1296                 .unwrap()
1297             )
1298         );
1299     }
1300 
1301     #[test]
test_parse_ack_lease_duration()1302     fn test_parse_ack_lease_duration() {
1303         let packet = Packet::new_unchecked(ACK_LEASE_TIME_BYTES);
1304         let repr = Repr::parse(&packet).unwrap();
1305 
1306         // Verify that the lease time in the ACK is properly parsed. The packet contains a lease
1307         // duration of 598s.
1308         assert_eq!(repr.lease_duration, Some(598));
1309     }
1310 }
1311