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