smoltcp/wire/
dhcpv4.rs

1// See https://tools.ietf.org/html/rfc2131 for the DHCP specification.
2
3use bitflags::bitflags;
4use byteorder::{ByteOrder, NetworkEndian};
5use core::iter;
6use heapless::Vec;
7
8use super::{Error, Result};
9use crate::wire::arp::Hardware;
10use crate::wire::{EthernetAddress, Ipv4Address};
11
12pub const SERVER_PORT: u16 = 67;
13pub const CLIENT_PORT: u16 = 68;
14pub const MAX_DNS_SERVER_COUNT: usize = 3;
15
16const DHCP_MAGIC_NUMBER: u32 = 0x63825363;
17
18enum_with_unknown! {
19    /// The possible opcodes of a DHCP packet.
20    pub enum OpCode(u8) {
21        Request = 1,
22        Reply = 2,
23    }
24}
25
26enum_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
40bitflags! {
41    pub struct Flags: u16 {
42        const BROADCAST = 0b1000_0000_0000_0000;
43    }
44}
45
46impl MessageType {
47    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))]
63pub struct DhcpOptionWriter<'a> {
64    /// The underlying buffer, directly from the DHCP packet representation.
65    buffer: &'a mut [u8],
66}
67
68impl<'a> DhcpOptionWriter<'a> {
69    pub fn new(buffer: &'a mut [u8]) -> Self {
70        Self { buffer }
71    }
72
73    /// Emit a  [`DhcpOption`] into a [`DhcpOptionWriter`].
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
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))]
108pub 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))]
116pub struct Packet<T: AsRef<[u8]>> {
117    buffer: T,
118}
119
120pub(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_PLATEAU_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
234impl<T: AsRef<[u8]>> Packet<T> {
235    /// Imbue a raw octet buffer with DHCP packet structure.
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
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
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.
264    pub fn into_inner(self) -> T {
265        self.buffer
266    }
267
268    /// Returns the operation code of this packet.
269    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).
275    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).
281    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.
290    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`.
299    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.
307    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.
315    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`.
323    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”.
333    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.
339    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.
345    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.
351    pub fn relay_agent_ip(&self) -> Ipv4Address {
352        let field = &self.buffer.as_ref()[field::GIADDR];
353        Ipv4Address::from_bytes(field)
354    }
355
356    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]
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
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
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
420impl<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!**
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.
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).
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`.
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.
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.
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.
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.
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`.
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”.
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.
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.
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.
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.
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
534impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
535    /// Return a pointer to the options.
536    #[inline]
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))]
588pub 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
664impl<'a> Repr<'a> {
665    /// Return the length of a packet that will be emitted from this high-level representation.
666    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.
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                    let mut addrs = data.chunks_exact(IP_ADDR_BYTE_LEN);
792                    for chunk in &mut addrs {
793                        // We ignore push failures because that will only happen
794                        // if we attempt to push more than 4 addresses, and the only
795                        // solution to that is to support more addresses.
796                        servers.push(Ipv4Address::from_bytes(chunk)).ok();
797                    }
798                    dns_servers = Some(servers);
799
800                    if !addrs.remainder().is_empty() {
801                        net_trace!("DHCP domain name servers contained invalid address");
802                    }
803                }
804                _ => {}
805            }
806        }
807
808        let broadcast = packet.flags().contains(Flags::BROADCAST);
809
810        Ok(Repr {
811            secs,
812            transaction_id,
813            client_hardware_address,
814            client_ip,
815            your_ip,
816            server_ip,
817            relay_agent_ip,
818            broadcast,
819            requested_ip,
820            server_identifier,
821            router,
822            subnet_mask,
823            client_identifier,
824            parameter_request_list,
825            dns_servers,
826            max_size,
827            lease_duration,
828            renew_duration,
829            rebind_duration,
830            message_type: message_type?,
831            additional_options: &[],
832        })
833    }
834
835    /// Emit a high-level representation into a Dynamic Host
836    /// Configuration Protocol packet.
837    pub fn emit<T>(&self, packet: &mut Packet<&mut T>) -> Result<()>
838    where
839        T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
840    {
841        packet.set_sname_and_boot_file_to_zero();
842        packet.set_opcode(self.message_type.opcode());
843        packet.set_hardware_type(Hardware::Ethernet);
844        packet.set_hardware_len(6);
845        packet.set_transaction_id(self.transaction_id);
846        packet.set_client_hardware_address(self.client_hardware_address);
847        packet.set_hops(0);
848        packet.set_secs(self.secs);
849        packet.set_magic_number(0x63825363);
850        packet.set_client_ip(self.client_ip);
851        packet.set_your_ip(self.your_ip);
852        packet.set_server_ip(self.server_ip);
853        packet.set_relay_agent_ip(self.relay_agent_ip);
854
855        let mut flags = Flags::empty();
856        if self.broadcast {
857            flags |= Flags::BROADCAST;
858        }
859        packet.set_flags(flags);
860
861        {
862            let mut options = packet.options_mut();
863
864            options.emit(DhcpOption {
865                kind: field::OPT_DHCP_MESSAGE_TYPE,
866                data: &[self.message_type.into()],
867            })?;
868
869            if let Some(val) = &self.client_identifier {
870                let mut data = [0; 7];
871                data[0] = u16::from(Hardware::Ethernet) as u8;
872                data[1..].copy_from_slice(val.as_bytes());
873
874                options.emit(DhcpOption {
875                    kind: field::OPT_CLIENT_ID,
876                    data: &data,
877                })?;
878            }
879
880            if let Some(val) = &self.server_identifier {
881                options.emit(DhcpOption {
882                    kind: field::OPT_SERVER_IDENTIFIER,
883                    data: val.as_bytes(),
884                })?;
885            }
886
887            if let Some(val) = &self.router {
888                options.emit(DhcpOption {
889                    kind: field::OPT_ROUTER,
890                    data: val.as_bytes(),
891                })?;
892            }
893            if let Some(val) = &self.subnet_mask {
894                options.emit(DhcpOption {
895                    kind: field::OPT_SUBNET_MASK,
896                    data: val.as_bytes(),
897                })?;
898            }
899            if let Some(val) = &self.requested_ip {
900                options.emit(DhcpOption {
901                    kind: field::OPT_REQUESTED_IP,
902                    data: val.as_bytes(),
903                })?;
904            }
905            if let Some(val) = &self.max_size {
906                options.emit(DhcpOption {
907                    kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
908                    data: &val.to_be_bytes(),
909                })?;
910            }
911            if let Some(val) = &self.lease_duration {
912                options.emit(DhcpOption {
913                    kind: field::OPT_IP_LEASE_TIME,
914                    data: &val.to_be_bytes(),
915                })?;
916            }
917            if let Some(val) = &self.parameter_request_list {
918                options.emit(DhcpOption {
919                    kind: field::OPT_PARAMETER_REQUEST_LIST,
920                    data: val,
921                })?;
922            }
923
924            if let Some(dns_servers) = &self.dns_servers {
925                const IP_SIZE: usize = core::mem::size_of::<u32>();
926                let mut servers = [0; MAX_DNS_SERVER_COUNT * IP_SIZE];
927
928                let data_len = dns_servers
929                    .iter()
930                    .enumerate()
931                    .inspect(|(i, ip)| {
932                        servers[(i * IP_SIZE)..((i + 1) * IP_SIZE)].copy_from_slice(ip.as_bytes());
933                    })
934                    .count()
935                    * IP_SIZE;
936                options.emit(DhcpOption {
937                    kind: field::OPT_DOMAIN_NAME_SERVER,
938                    data: &servers[..data_len],
939                })?;
940            }
941
942            for option in self.additional_options {
943                options.emit(*option)?;
944            }
945
946            options.end()?;
947        }
948
949        Ok(())
950    }
951}
952
953#[cfg(test)]
954mod test {
955    use super::*;
956    use crate::wire::Ipv4Address;
957
958    const MAGIC_COOKIE: u32 = 0x63825363;
959
960    static DISCOVER_BYTES: &[u8] = &[
961        0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
962        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
963        0x82, 0x01, 0xfc, 0x42, 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, 0x00, 0x00, 0x00, 0x00,
972        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
973        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
974        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
975        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
976        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
977        0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00,
978        0x00, 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00,
979        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
980    ];
981
982    static ACK_DNS_SERVER_BYTES: &[u8] = &[
983        0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06,
984        0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17,
985        0xeb, 0xc9, 0xaa, 0x2f, 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, 0x00, 0x00, 0x00, 0x00,
994        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
995        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
996        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
997        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
998        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
999        0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00,
1000        0x2b, 0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68,
1001        0x79, 0x73, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00,
1002        0x03, 0x04, 0x0a, 0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a,
1003        0x07, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03,
1004        0xa3, 0x01, 0x4a, 0x04, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08,
1005        0xff,
1006    ];
1007
1008    static ACK_LEASE_TIME_BYTES: &[u8] = &[
1009        0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1010        0x00, 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91,
1011        0x62, 0xd2, 0xa8, 0x6f, 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, 0x00, 0x00, 0x00, 0x00,
1020        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1021        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
1025        0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56,
1026        0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00,
1027        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1028        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1029    ];
1030
1031    const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]);
1032    const CLIENT_MAC: EthernetAddress = EthernetAddress([0x0, 0x0b, 0x82, 0x01, 0xfc, 0x42]);
1033    const DHCP_SIZE: u16 = 1500;
1034
1035    #[test]
1036    fn test_deconstruct_discover() {
1037        let packet = Packet::new_unchecked(DISCOVER_BYTES);
1038        assert_eq!(packet.magic_number(), MAGIC_COOKIE);
1039        assert_eq!(packet.opcode(), OpCode::Request);
1040        assert_eq!(packet.hardware_type(), Hardware::Ethernet);
1041        assert_eq!(packet.hardware_len(), 6);
1042        assert_eq!(packet.hops(), 0);
1043        assert_eq!(packet.transaction_id(), 0x3d1d);
1044        assert_eq!(packet.secs(), 0);
1045        assert_eq!(packet.client_ip(), IP_NULL);
1046        assert_eq!(packet.your_ip(), IP_NULL);
1047        assert_eq!(packet.server_ip(), IP_NULL);
1048        assert_eq!(packet.relay_agent_ip(), IP_NULL);
1049        assert_eq!(packet.client_hardware_address(), CLIENT_MAC);
1050
1051        let mut options = packet.options();
1052        assert_eq!(
1053            options.next(),
1054            Some(DhcpOption {
1055                kind: field::OPT_DHCP_MESSAGE_TYPE,
1056                data: &[0x01]
1057            })
1058        );
1059        assert_eq!(
1060            options.next(),
1061            Some(DhcpOption {
1062                kind: field::OPT_CLIENT_ID,
1063                data: &[0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42],
1064            })
1065        );
1066        assert_eq!(
1067            options.next(),
1068            Some(DhcpOption {
1069                kind: field::OPT_REQUESTED_IP,
1070                data: &[0x00, 0x00, 0x00, 0x00],
1071            })
1072        );
1073        assert_eq!(
1074            options.next(),
1075            Some(DhcpOption {
1076                kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
1077                data: &DHCP_SIZE.to_be_bytes(),
1078            })
1079        );
1080        assert_eq!(
1081            options.next(),
1082            Some(DhcpOption {
1083                kind: field::OPT_PARAMETER_REQUEST_LIST,
1084                data: &[1, 3, 6, 42]
1085            })
1086        );
1087        assert_eq!(options.next(), None);
1088    }
1089
1090    #[test]
1091    fn test_construct_discover() {
1092        let mut bytes = vec![0xa5; 276];
1093        let mut packet = Packet::new_unchecked(&mut bytes);
1094        packet.set_magic_number(MAGIC_COOKIE);
1095        packet.set_sname_and_boot_file_to_zero();
1096        packet.set_opcode(OpCode::Request);
1097        packet.set_hardware_type(Hardware::Ethernet);
1098        packet.set_hardware_len(6);
1099        packet.set_hops(0);
1100        packet.set_transaction_id(0x3d1d);
1101        packet.set_secs(0);
1102        packet.set_flags(Flags::empty());
1103        packet.set_client_ip(IP_NULL);
1104        packet.set_your_ip(IP_NULL);
1105        packet.set_server_ip(IP_NULL);
1106        packet.set_relay_agent_ip(IP_NULL);
1107        packet.set_client_hardware_address(CLIENT_MAC);
1108
1109        let mut options = packet.options_mut();
1110
1111        options
1112            .emit(DhcpOption {
1113                kind: field::OPT_DHCP_MESSAGE_TYPE,
1114                data: &[0x01],
1115            })
1116            .unwrap();
1117        options
1118            .emit(DhcpOption {
1119                kind: field::OPT_CLIENT_ID,
1120                data: &[0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42],
1121            })
1122            .unwrap();
1123        options
1124            .emit(DhcpOption {
1125                kind: field::OPT_REQUESTED_IP,
1126                data: &[0x00, 0x00, 0x00, 0x00],
1127            })
1128            .unwrap();
1129        options
1130            .emit(DhcpOption {
1131                kind: field::OPT_MAX_DHCP_MESSAGE_SIZE,
1132                data: &DHCP_SIZE.to_be_bytes(),
1133            })
1134            .unwrap();
1135        options
1136            .emit(DhcpOption {
1137                kind: field::OPT_PARAMETER_REQUEST_LIST,
1138                data: &[1, 3, 6, 42],
1139            })
1140            .unwrap();
1141        options.end().unwrap();
1142
1143        let packet = &mut packet.into_inner()[..];
1144        for byte in &mut packet[269..276] {
1145            *byte = 0; // padding bytes
1146        }
1147
1148        assert_eq!(packet, DISCOVER_BYTES);
1149    }
1150
1151    const fn offer_repr() -> Repr<'static> {
1152        Repr {
1153            message_type: MessageType::Offer,
1154            transaction_id: 0x3d1d,
1155            client_hardware_address: CLIENT_MAC,
1156            client_ip: IP_NULL,
1157            your_ip: IP_NULL,
1158            server_ip: IP_NULL,
1159            router: Some(IP_NULL),
1160            subnet_mask: Some(IP_NULL),
1161            relay_agent_ip: IP_NULL,
1162            secs: 0,
1163            broadcast: false,
1164            requested_ip: None,
1165            client_identifier: Some(CLIENT_MAC),
1166            server_identifier: None,
1167            parameter_request_list: None,
1168            dns_servers: None,
1169            max_size: None,
1170            renew_duration: None,
1171            rebind_duration: None,
1172            lease_duration: Some(0xffff_ffff), // Infinite lease
1173            additional_options: &[],
1174        }
1175    }
1176
1177    const fn discover_repr() -> Repr<'static> {
1178        Repr {
1179            message_type: MessageType::Discover,
1180            transaction_id: 0x3d1d,
1181            client_hardware_address: CLIENT_MAC,
1182            client_ip: IP_NULL,
1183            your_ip: IP_NULL,
1184            server_ip: IP_NULL,
1185            router: None,
1186            subnet_mask: None,
1187            relay_agent_ip: IP_NULL,
1188            broadcast: false,
1189            secs: 0,
1190            max_size: Some(DHCP_SIZE),
1191            renew_duration: None,
1192            rebind_duration: None,
1193            lease_duration: None,
1194            requested_ip: Some(IP_NULL),
1195            client_identifier: Some(CLIENT_MAC),
1196            server_identifier: None,
1197            parameter_request_list: Some(&[1, 3, 6, 42]),
1198            dns_servers: None,
1199            additional_options: &[],
1200        }
1201    }
1202
1203    #[test]
1204    fn test_parse_discover() {
1205        let packet = Packet::new_unchecked(DISCOVER_BYTES);
1206        let repr = Repr::parse(&packet).unwrap();
1207        assert_eq!(repr, discover_repr());
1208    }
1209
1210    #[test]
1211    fn test_emit_discover() {
1212        let repr = discover_repr();
1213        let mut bytes = vec![0xa5; repr.buffer_len()];
1214        let mut packet = Packet::new_unchecked(&mut bytes);
1215        repr.emit(&mut packet).unwrap();
1216        let packet = &*packet.into_inner();
1217        let packet_len = packet.len();
1218        assert_eq!(packet, &DISCOVER_BYTES[..packet_len]);
1219        for byte in &DISCOVER_BYTES[packet_len..] {
1220            assert_eq!(*byte, 0); // padding bytes
1221        }
1222    }
1223
1224    #[test]
1225    fn test_emit_offer() {
1226        let repr = offer_repr();
1227        let mut bytes = vec![0xa5; repr.buffer_len()];
1228        let mut packet = Packet::new_unchecked(&mut bytes);
1229        repr.emit(&mut packet).unwrap();
1230    }
1231
1232    #[test]
1233    fn test_emit_offer_dns() {
1234        let repr = {
1235            let mut repr = offer_repr();
1236            repr.dns_servers = Some(
1237                Vec::from_slice(&[
1238                    Ipv4Address([163, 1, 74, 6]),
1239                    Ipv4Address([163, 1, 74, 7]),
1240                    Ipv4Address([163, 1, 74, 3]),
1241                ])
1242                .unwrap(),
1243            );
1244            repr
1245        };
1246        let mut bytes = vec![0xa5; repr.buffer_len()];
1247        let mut packet = Packet::new_unchecked(&mut bytes);
1248        repr.emit(&mut packet).unwrap();
1249
1250        let packet = Packet::new_unchecked(&bytes);
1251        let repr_parsed = Repr::parse(&packet).unwrap();
1252
1253        assert_eq!(
1254            repr_parsed.dns_servers,
1255            Some(
1256                Vec::from_slice(&[
1257                    Ipv4Address([163, 1, 74, 6]),
1258                    Ipv4Address([163, 1, 74, 7]),
1259                    Ipv4Address([163, 1, 74, 3]),
1260                ])
1261                .unwrap()
1262            )
1263        );
1264    }
1265
1266    #[test]
1267    fn test_emit_dhcp_option() {
1268        static DATA: &[u8] = &[1, 3, 6];
1269        let dhcp_option = DhcpOption {
1270            kind: field::OPT_PARAMETER_REQUEST_LIST,
1271            data: DATA,
1272        };
1273
1274        let mut bytes = vec![0xa5; 5];
1275        let mut writer = DhcpOptionWriter::new(&mut bytes);
1276        writer.emit(dhcp_option).unwrap();
1277
1278        assert_eq!(
1279            &bytes[0..2],
1280            &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]
1281        );
1282        assert_eq!(&bytes[2..], DATA);
1283    }
1284
1285    #[test]
1286    fn test_parse_ack_dns_servers() {
1287        let packet = Packet::new_unchecked(ACK_DNS_SERVER_BYTES);
1288        let repr = Repr::parse(&packet).unwrap();
1289
1290        // The packet described by ACK_BYTES advertises 4 DNS servers
1291        // Here we ensure that we correctly parse the first 3 into our fixed
1292        // length-3 array (see issue #305)
1293        assert_eq!(
1294            repr.dns_servers,
1295            Some(
1296                Vec::from_slice(&[
1297                    Ipv4Address([163, 1, 74, 6]),
1298                    Ipv4Address([163, 1, 74, 7]),
1299                    Ipv4Address([163, 1, 74, 3])
1300                ])
1301                .unwrap()
1302            )
1303        );
1304    }
1305
1306    #[test]
1307    fn test_parse_ack_lease_duration() {
1308        let packet = Packet::new_unchecked(ACK_LEASE_TIME_BYTES);
1309        let repr = Repr::parse(&packet).unwrap();
1310
1311        // Verify that the lease time in the ACK is properly parsed. The packet contains a lease
1312        // duration of 598s.
1313        assert_eq!(repr.lease_duration, Some(598));
1314    }
1315}