dhcpv4.rs 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. // See https://tools.ietf.org/html/rfc2131 for the DHCP specification.
  2. use byteorder::{ByteOrder, NetworkEndian};
  3. use crate::{Error, Result};
  4. use crate::wire::{EthernetAddress, Ipv4Address};
  5. use crate::wire::arp::Hardware;
  6. const DHCP_MAGIC_NUMBER: u32 = 0x63825363;
  7. enum_with_unknown! {
  8. /// The possible opcodes of a DHCP packet.
  9. pub enum OpCode(u8) {
  10. Request = 1,
  11. Reply = 2,
  12. }
  13. }
  14. enum_with_unknown! {
  15. /// The possible message types of a DHCP packet.
  16. pub enum MessageType(u8) {
  17. Discover = 1,
  18. Offer = 2,
  19. Request = 3,
  20. Decline = 4,
  21. Ack = 5,
  22. Nak = 6,
  23. Release = 7,
  24. Inform = 8,
  25. }
  26. }
  27. impl MessageType {
  28. fn opcode(&self) -> OpCode {
  29. match *self {
  30. MessageType::Discover | MessageType::Inform | MessageType::Request |
  31. MessageType::Decline | MessageType::Release => OpCode::Request,
  32. MessageType::Offer | MessageType::Ack | MessageType::Nak => OpCode::Reply,
  33. MessageType::Unknown(_) => OpCode::Unknown(0),
  34. }
  35. }
  36. }
  37. /// A representation of a single DHCP option.
  38. #[derive(Debug, PartialEq, Eq, Clone, Copy)]
  39. pub enum DhcpOption<'a> {
  40. EndOfList,
  41. Pad,
  42. MessageType(MessageType),
  43. RequestedIp(Ipv4Address),
  44. ClientIdentifier(EthernetAddress),
  45. ServerIdentifier(Ipv4Address),
  46. Router(Ipv4Address),
  47. SubnetMask(Ipv4Address),
  48. MaximumDhcpMessageSize(u16),
  49. Other { kind: u8, data: &'a [u8] }
  50. }
  51. impl<'a> DhcpOption<'a> {
  52. pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], DhcpOption<'a>)> {
  53. // See https://tools.ietf.org/html/rfc2132 for all possible DHCP options.
  54. let (skip_len, option);
  55. match *buffer.get(0).ok_or(Error::Truncated)? {
  56. field::OPT_END => {
  57. skip_len = 1;
  58. option = DhcpOption::EndOfList;
  59. }
  60. field::OPT_PAD => {
  61. skip_len = 1;
  62. option = DhcpOption::Pad;
  63. }
  64. kind => {
  65. let length = *buffer.get(1).ok_or(Error::Truncated)? as usize;
  66. skip_len = length + 2;
  67. let data = buffer.get(2..skip_len).ok_or(Error::Truncated)?;
  68. match (kind, length) {
  69. (field::OPT_END, _) |
  70. (field::OPT_PAD, _) =>
  71. unreachable!(),
  72. (field::OPT_DHCP_MESSAGE_TYPE, 1) => {
  73. option = DhcpOption::MessageType(MessageType::from(data[0]));
  74. },
  75. (field::OPT_REQUESTED_IP, 4) => {
  76. option = DhcpOption::RequestedIp(Ipv4Address::from_bytes(data));
  77. }
  78. (field::OPT_CLIENT_ID, 7) => {
  79. let hardware_type = Hardware::from(u16::from(data[0]));
  80. if hardware_type != Hardware::Ethernet {
  81. return Err(Error::Unrecognized);
  82. }
  83. option = DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..]));
  84. }
  85. (field::OPT_SERVER_IDENTIFIER, 4) => {
  86. option = DhcpOption::ServerIdentifier(Ipv4Address::from_bytes(data));
  87. }
  88. (field::OPT_ROUTER, 4) => {
  89. option = DhcpOption::Router(Ipv4Address::from_bytes(data));
  90. }
  91. (field::OPT_SUBNET_MASK, 4) => {
  92. option = DhcpOption::SubnetMask(Ipv4Address::from_bytes(data));
  93. }
  94. (field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => {
  95. option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([data[0], data[1]]));
  96. }
  97. (_, _) => {
  98. option = DhcpOption::Other { kind: kind, data: data };
  99. }
  100. }
  101. }
  102. }
  103. Ok((&buffer[skip_len..], option))
  104. }
  105. pub fn buffer_len(&self) -> usize {
  106. match self {
  107. &DhcpOption::EndOfList => 1,
  108. &DhcpOption::Pad => 1,
  109. &DhcpOption::MessageType(_) => 3,
  110. &DhcpOption::ClientIdentifier(eth_addr) => {
  111. 3 + eth_addr.as_bytes().len()
  112. }
  113. &DhcpOption::RequestedIp(ip) |
  114. &DhcpOption::ServerIdentifier(ip) |
  115. &DhcpOption::Router(ip) |
  116. &DhcpOption::SubnetMask(ip) => {
  117. 2 + ip.as_bytes().len()
  118. },
  119. &DhcpOption::MaximumDhcpMessageSize(_) => {
  120. 4
  121. }
  122. &DhcpOption::Other { data, .. } => 2 + data.len()
  123. }
  124. }
  125. pub fn emit<'b>(&self, buffer: &'b mut [u8]) -> &'b mut [u8] {
  126. let skip_length;
  127. match *self {
  128. DhcpOption::EndOfList => {
  129. skip_length = 1;
  130. buffer[0] = field::OPT_END;
  131. }
  132. DhcpOption::Pad => {
  133. skip_length = 1;
  134. buffer[0] = field::OPT_PAD;
  135. }
  136. _ => {
  137. skip_length = self.buffer_len();
  138. buffer[1] = (skip_length - 2) as u8;
  139. match *self {
  140. DhcpOption::EndOfList | DhcpOption::Pad => unreachable!(),
  141. DhcpOption::MessageType(value) => {
  142. buffer[0] = field::OPT_DHCP_MESSAGE_TYPE;
  143. buffer[2] = value.into();
  144. }
  145. DhcpOption::ClientIdentifier(eth_addr) => {
  146. buffer[0] = field::OPT_CLIENT_ID;
  147. buffer[2] = u16::from(Hardware::Ethernet) as u8;
  148. buffer[3..9].copy_from_slice(eth_addr.as_bytes());
  149. }
  150. DhcpOption::RequestedIp(ip) => {
  151. buffer[0] = field::OPT_REQUESTED_IP;
  152. buffer[2..6].copy_from_slice(ip.as_bytes());
  153. }
  154. DhcpOption::ServerIdentifier(ip) => {
  155. buffer[0] = field::OPT_SERVER_IDENTIFIER;
  156. buffer[2..6].copy_from_slice(ip.as_bytes());
  157. }
  158. DhcpOption::Router(ip) => {
  159. buffer[0] = field::OPT_ROUTER;
  160. buffer[2..6].copy_from_slice(ip.as_bytes());
  161. }
  162. DhcpOption::SubnetMask(mask) => {
  163. buffer[0] = field::OPT_SUBNET_MASK;
  164. buffer[2..6].copy_from_slice(mask.as_bytes());
  165. }
  166. DhcpOption::MaximumDhcpMessageSize(size) => {
  167. buffer[0] = field::OPT_MAX_DHCP_MESSAGE_SIZE;
  168. buffer[2..4].copy_from_slice(&size.to_be_bytes()[..]);
  169. }
  170. DhcpOption::Other { kind, data: provided } => {
  171. buffer[0] = kind;
  172. buffer[2..skip_length].copy_from_slice(provided);
  173. }
  174. }
  175. }
  176. }
  177. &mut buffer[skip_length..]
  178. }
  179. }
  180. /// A read/write wrapper around a Dynamic Host Configuration Protocol packet buffer.
  181. #[derive(Debug, PartialEq)]
  182. pub struct Packet<T: AsRef<[u8]>> {
  183. buffer: T
  184. }
  185. pub(crate) mod field {
  186. #![allow(non_snake_case)]
  187. #![allow(unused)]
  188. use crate::wire::field::*;
  189. pub const OP: usize = 0;
  190. pub const HTYPE: usize = 1;
  191. pub const HLEN: usize = 2;
  192. pub const HOPS: usize = 3;
  193. pub const XID: Field = 4..8;
  194. pub const SECS: Field = 8..10;
  195. pub const FLAGS: Field = 10..12;
  196. pub const CIADDR: Field = 12..16;
  197. pub const YIADDR: Field = 16..20;
  198. pub const SIADDR: Field = 20..24;
  199. pub const GIADDR: Field = 24..28;
  200. pub const CHADDR: Field = 28..34;
  201. pub const SNAME: Field = 34..108;
  202. pub const FILE: Field = 108..236;
  203. pub const MAGIC_NUMBER: Field = 236..240;
  204. pub const OPTIONS: Rest = 240..;
  205. // Vendor Extensions
  206. pub const OPT_END: u8 = 255;
  207. pub const OPT_PAD: u8 = 0;
  208. pub const OPT_SUBNET_MASK: u8 = 1;
  209. pub const OPT_TIME_OFFSET: u8 = 2;
  210. pub const OPT_ROUTER: u8 = 3;
  211. pub const OPT_TIME_SERVER: u8 = 4;
  212. pub const OPT_NAME_SERVER: u8 = 5;
  213. pub const OPT_DOMAIN_NAME_SERVER: u8 = 6;
  214. pub const OPT_LOG_SERVER: u8 = 7;
  215. pub const OPT_COOKIE_SERVER: u8 = 8;
  216. pub const OPT_LPR_SERVER: u8 = 9;
  217. pub const OPT_IMPRESS_SERVER: u8 = 10;
  218. pub const OPT_RESOURCE_LOCATION_SERVER: u8 = 11;
  219. pub const OPT_HOST_NAME: u8 = 12;
  220. pub const OPT_BOOT_FILE_SIZE: u8 = 13;
  221. pub const OPT_MERIT_DUMP: u8 = 14;
  222. pub const OPT_DOMAIN_NAME: u8 = 15;
  223. pub const OPT_SWAP_SERVER: u8 = 16;
  224. pub const OPT_ROOT_PATH: u8 = 17;
  225. pub const OPT_EXTENSIONS_PATH: u8 = 18;
  226. // IP Layer Parameters per Host
  227. pub const OPT_IP_FORWARDING: u8 = 19;
  228. pub const OPT_NON_LOCAL_SOURCE_ROUTING: u8 = 20;
  229. pub const OPT_POLICY_FILTER: u8 = 21;
  230. pub const OPT_MAX_DATAGRAM_REASSEMBLY_SIZE: u8 = 22;
  231. pub const OPT_DEFAULT_TTL: u8 = 23;
  232. pub const OPT_PATH_MTU_AGING_TIMEOUT: u8 = 24;
  233. pub const OPT_PATH_MTU_PLATEU_TABLE: u8 = 25;
  234. // IP Layer Parameters per Interface
  235. pub const OPT_INTERFACE_MTU: u8 = 26;
  236. pub const OPT_ALL_SUBNETS_ARE_LOCAL: u8 = 27;
  237. pub const OPT_BROADCAST_ADDRESS: u8 = 28;
  238. pub const OPT_PERFORM_MASK_DISCOVERY: u8 = 29;
  239. pub const OPT_MASK_SUPPLIER: u8 = 30;
  240. pub const OPT_PERFORM_ROUTER_DISCOVERY: u8 = 31;
  241. pub const OPT_ROUTER_SOLICITATION_ADDRESS: u8 = 32;
  242. pub const OPT_STATIC_ROUTE: u8 = 33;
  243. // Link Layer Parameters per Interface
  244. pub const OPT_TRAILER_ENCAPSULATION: u8 = 34;
  245. pub const OPT_ARP_CACHE_TIMEOUT: u8 = 35;
  246. pub const OPT_ETHERNET_ENCAPSULATION: u8 = 36;
  247. // TCP Parameters
  248. pub const OPT_TCP_DEFAULT_TTL: u8 = 37;
  249. pub const OPT_TCP_KEEPALIVE_INTERVAL: u8 = 38;
  250. pub const OPT_TCP_KEEPALIVE_GARBAGE: u8 = 39;
  251. // Application and Service Parameters
  252. pub const OPT_NIS_DOMAIN: u8 = 40;
  253. pub const OPT_NIS_SERVERS: u8 = 41;
  254. pub const OPT_NTP_SERVERS: u8 = 42;
  255. pub const OPT_VENDOR_SPECIFIC_INFO: u8 = 43;
  256. pub const OPT_NETBIOS_NAME_SERVER: u8 = 44;
  257. pub const OPT_NETBIOS_DISTRIBUTION_SERVER: u8 = 45;
  258. pub const OPT_NETBIOS_NODE_TYPE: u8 = 46;
  259. pub const OPT_NETBIOS_SCOPE: u8 = 47;
  260. pub const OPT_X_WINDOW_FONT_SERVER: u8 = 48;
  261. pub const OPT_X_WINDOW_DISPLAY_MANAGER: u8 = 49;
  262. pub const OPT_NIS_PLUS_DOMAIN: u8 = 64;
  263. pub const OPT_NIS_PLUS_SERVERS: u8 = 65;
  264. pub const OPT_MOBILE_IP_HOME_AGENT: u8 = 68;
  265. pub const OPT_SMTP_SERVER: u8 = 69;
  266. pub const OPT_POP3_SERVER: u8 = 70;
  267. pub const OPT_NNTP_SERVER: u8 = 71;
  268. pub const OPT_WWW_SERVER: u8 = 72;
  269. pub const OPT_FINGER_SERVER: u8 = 73;
  270. pub const OPT_IRC_SERVER: u8 = 74;
  271. pub const OPT_STREETTALK_SERVER: u8 = 75;
  272. pub const OPT_STDA_SERVER: u8 = 76;
  273. // DHCP Extensions
  274. pub const OPT_REQUESTED_IP: u8 = 50;
  275. pub const OPT_IP_LEASE_TIME: u8 = 51;
  276. pub const OPT_OPTION_OVERLOAD: u8 = 52;
  277. pub const OPT_TFTP_SERVER_NAME: u8 = 66;
  278. pub const OPT_BOOTFILE_NAME: u8 = 67;
  279. pub const OPT_DHCP_MESSAGE_TYPE: u8 = 53;
  280. pub const OPT_SERVER_IDENTIFIER: u8 = 54;
  281. pub const OPT_PARAMETER_REQUEST_LIST: u8 = 55;
  282. pub const OPT_MESSAGE: u8 = 56;
  283. pub const OPT_MAX_DHCP_MESSAGE_SIZE: u8 = 57;
  284. pub const OPT_RENEWAL_TIME_VALUE: u8 = 58;
  285. pub const OPT_REBINDING_TIME_VALUE: u8 = 59;
  286. pub const OPT_VENDOR_CLASS_ID: u8 = 60;
  287. pub const OPT_CLIENT_ID: u8 = 61;
  288. }
  289. impl<T: AsRef<[u8]>> Packet<T> {
  290. /// Imbue a raw octet buffer with DHCP packet structure.
  291. pub fn new_unchecked(buffer: T) -> Packet<T> {
  292. Packet { buffer }
  293. }
  294. /// Shorthand for a combination of [new_unchecked] and [check_len].
  295. ///
  296. /// [new_unchecked]: #method.new_unchecked
  297. /// [check_len]: #method.check_len
  298. pub fn new_checked(buffer: T) -> Result<Packet<T>> {
  299. let packet = Self::new_unchecked(buffer);
  300. packet.check_len()?;
  301. Ok(packet)
  302. }
  303. /// Ensure that no accessor method will panic if called.
  304. /// Returns `Err(Error::Truncated)` if the buffer is too short.
  305. ///
  306. /// [set_header_len]: #method.set_header_len
  307. pub fn check_len(&self) -> Result<()> {
  308. let len = self.buffer.as_ref().len();
  309. if len < field::MAGIC_NUMBER.end {
  310. Err(Error::Truncated)
  311. } else {
  312. Ok(())
  313. }
  314. }
  315. /// Consume the packet, returning the underlying buffer.
  316. pub fn into_inner(self) -> T {
  317. self.buffer
  318. }
  319. /// Returns the operation code of this packet.
  320. pub fn opcode(&self) -> OpCode {
  321. let data = self.buffer.as_ref();
  322. OpCode::from(data[field::OP])
  323. }
  324. /// Returns the hardware protocol type (e.g. ethernet).
  325. pub fn hardware_type(&self) -> Hardware {
  326. let data = self.buffer.as_ref();
  327. Hardware::from(u16::from(data[field::HTYPE]))
  328. }
  329. /// Returns the length of a hardware address in bytes (e.g. 6 for ethernet).
  330. pub fn hardware_len(&self) -> u8 {
  331. self.buffer.as_ref()[field::HLEN]
  332. }
  333. /// Returns the transaction ID.
  334. ///
  335. /// The transaction ID (called `xid` in the specification) is a random number used to
  336. /// associate messages and responses between client and server. The number is chosen by
  337. /// the client.
  338. pub fn transaction_id(&self) -> u32 {
  339. let field = &self.buffer.as_ref()[field::XID];
  340. NetworkEndian::read_u32(field)
  341. }
  342. /// Returns the hardware address of the client (called `chaddr` in the specification).
  343. ///
  344. /// Only ethernet is supported by `smoltcp`, so this functions returns
  345. /// an `EthernetAddress`.
  346. pub fn client_hardware_address(&self) -> EthernetAddress {
  347. let field = &self.buffer.as_ref()[field::CHADDR];
  348. EthernetAddress::from_bytes(field)
  349. }
  350. /// Returns the value of the `hops` field.
  351. ///
  352. /// The `hops` field is set to zero by clients and optionally used by relay agents.
  353. pub fn hops(&self) -> u8 {
  354. self.buffer.as_ref()[field::HOPS]
  355. }
  356. /// Returns the value of the `secs` field.
  357. ///
  358. /// The secs field is filled by clients and describes the number of seconds elapsed
  359. /// since client began process.
  360. pub fn secs(&self) -> u16 {
  361. let field = &self.buffer.as_ref()[field::SECS];
  362. NetworkEndian::read_u16(field)
  363. }
  364. /// Returns the value of the `magic cookie` field in the DHCP options.
  365. ///
  366. /// This field should be always be `0x63825363`.
  367. pub fn magic_number(&self) -> u32 {
  368. let field = &self.buffer.as_ref()[field::MAGIC_NUMBER];
  369. NetworkEndian::read_u32(field)
  370. }
  371. /// Returns the Ipv4 address of the client, zero if not set.
  372. ///
  373. /// This corresponds to the `ciaddr` field in the DHCP specification. According to it,
  374. /// this field is “only filled in if client is in `BOUND`, `RENEW` or `REBINDING` state
  375. /// and can respond to ARP requests”.
  376. pub fn client_ip(&self) -> Ipv4Address {
  377. let field = &self.buffer.as_ref()[field::CIADDR];
  378. Ipv4Address::from_bytes(field)
  379. }
  380. /// Returns the value of the `yiaddr` field, zero if not set.
  381. pub fn your_ip(&self) -> Ipv4Address {
  382. let field = &self.buffer.as_ref()[field::YIADDR];
  383. Ipv4Address::from_bytes(field)
  384. }
  385. /// Returns the value of the `siaddr` field, zero if not set.
  386. pub fn server_ip(&self) -> Ipv4Address {
  387. let field = &self.buffer.as_ref()[field::SIADDR];
  388. Ipv4Address::from_bytes(field)
  389. }
  390. /// Returns the value of the `giaddr` field, zero if not set.
  391. pub fn relay_agent_ip(&self) -> Ipv4Address {
  392. let field = &self.buffer.as_ref()[field::GIADDR];
  393. Ipv4Address::from_bytes(field)
  394. }
  395. /// Returns true if the broadcast flag is set.
  396. pub fn broadcast_flag(&self) -> bool {
  397. let field = &self.buffer.as_ref()[field::FLAGS];
  398. NetworkEndian::read_u16(field) & 0b1 == 0b1
  399. }
  400. }
  401. impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
  402. /// Return a pointer to the options.
  403. #[inline]
  404. pub fn options(&self) -> Result<&'a [u8]> {
  405. let data = self.buffer.as_ref();
  406. data.get(field::OPTIONS).ok_or(Error::Malformed)
  407. }
  408. }
  409. impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
  410. /// Sets the optional `sname` (“server name”) and `file` (“boot file name”) fields to zero.
  411. ///
  412. /// The fields are not commonly used, so we set their value always to zero. **This method
  413. /// must be called when creating a packet, otherwise the emitted values for these fields
  414. /// are undefined!**
  415. pub fn set_sname_and_boot_file_to_zero(&mut self) {
  416. let data = self.buffer.as_mut();
  417. for byte in &mut data[field::SNAME] {
  418. *byte = 0;
  419. }
  420. for byte in &mut data[field::FILE] {
  421. *byte = 0;
  422. }
  423. }
  424. /// Sets the `OpCode` for the packet.
  425. pub fn set_opcode(&mut self, value: OpCode) {
  426. let data = self.buffer.as_mut();
  427. data[field::OP] = value.into();
  428. }
  429. /// Sets the hardware address type (only ethernet is supported).
  430. pub fn set_hardware_type(&mut self, value: Hardware) {
  431. let data = self.buffer.as_mut();
  432. let number: u16 = value.into();
  433. assert!(number <= u16::from(u8::max_value())); // TODO: Replace with TryFrom when it's stable
  434. data[field::HTYPE] = number as u8;
  435. }
  436. /// Sets the hardware address length.
  437. ///
  438. /// Only ethernet is supported, so this field should be set to the value `6`.
  439. pub fn set_hardware_len(&mut self, value: u8) {
  440. self.buffer.as_mut()[field::HLEN] = value;
  441. }
  442. /// Sets the transaction ID.
  443. ///
  444. /// The transaction ID (called `xid` in the specification) is a random number used to
  445. /// associate messages and responses between client and server. The number is chosen by
  446. /// the client.
  447. pub fn set_transaction_id(&mut self, value: u32) {
  448. let field = &mut self.buffer.as_mut()[field::XID];
  449. NetworkEndian::write_u32(field, value)
  450. }
  451. /// Sets the ethernet address of the client.
  452. ///
  453. /// Sets the `chaddr` field.
  454. pub fn set_client_hardware_address(&mut self, value: EthernetAddress) {
  455. let field = &mut self.buffer.as_mut()[field::CHADDR];
  456. field.copy_from_slice(value.as_bytes());
  457. }
  458. /// Sets the hops field.
  459. ///
  460. /// The `hops` field is set to zero by clients and optionally used by relay agents.
  461. pub fn set_hops(&mut self, value: u8) {
  462. self.buffer.as_mut()[field::HOPS] = value;
  463. }
  464. /// Sets the `secs` field.
  465. ///
  466. /// The secs field is filled by clients and describes the number of seconds elapsed
  467. /// since client began process.
  468. pub fn set_secs(&mut self, value: u16) {
  469. let field = &mut self.buffer.as_mut()[field::SECS];
  470. NetworkEndian::write_u16(field, value);
  471. }
  472. /// Sets the value of the `magic cookie` field in the DHCP options.
  473. ///
  474. /// This field should be always be `0x63825363`.
  475. pub fn set_magic_number(&mut self, value: u32) {
  476. let field = &mut self.buffer.as_mut()[field::MAGIC_NUMBER];
  477. NetworkEndian::write_u32(field, value);
  478. }
  479. /// Sets the Ipv4 address of the client.
  480. ///
  481. /// This corresponds to the `ciaddr` field in the DHCP specification. According to it,
  482. /// this field is “only filled in if client is in `BOUND`, `RENEW` or `REBINDING` state
  483. /// and can respond to ARP requests”.
  484. pub fn set_client_ip(&mut self, value: Ipv4Address) {
  485. let field = &mut self.buffer.as_mut()[field::CIADDR];
  486. field.copy_from_slice(value.as_bytes());
  487. }
  488. /// Sets the value of the `yiaddr` field.
  489. pub fn set_your_ip(&mut self, value: Ipv4Address) {
  490. let field = &mut self.buffer.as_mut()[field::YIADDR];
  491. field.copy_from_slice(value.as_bytes());
  492. }
  493. /// Sets the value of the `siaddr` field.
  494. pub fn set_server_ip(&mut self, value: Ipv4Address) {
  495. let field = &mut self.buffer.as_mut()[field::SIADDR];
  496. field.copy_from_slice(value.as_bytes());
  497. }
  498. /// Sets the value of the `giaddr` field.
  499. pub fn set_relay_agent_ip(&mut self, value: Ipv4Address) {
  500. let field = &mut self.buffer.as_mut()[field::GIADDR];
  501. field.copy_from_slice(value.as_bytes());
  502. }
  503. /// Sets the broadcast flag to the specified value.
  504. pub fn set_broadcast_flag(&mut self, value: bool) {
  505. let field = &mut self.buffer.as_mut()[field::FLAGS];
  506. NetworkEndian::write_u16(field, if value { 1 } else { 0 });
  507. }
  508. }
  509. impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
  510. /// Return a pointer to the options.
  511. #[inline]
  512. pub fn options_mut(&mut self) -> Result<&mut [u8]> {
  513. let data = self.buffer.as_mut();
  514. data.get_mut(field::OPTIONS).ok_or(Error::Truncated)
  515. }
  516. }
  517. /// A high-level representation of a Dynamic Host Configuration Protocol packet.
  518. ///
  519. /// DHCP messages have the following layout (see [RFC 2131](https://tools.ietf.org/html/rfc2131)
  520. /// for details):
  521. ///
  522. /// ```no_rust
  523. /// 0 1 2 3
  524. /// 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
  525. /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  526. /// | message_type | htype (N/A) | hlen (N/A) | hops |
  527. /// +---------------+---------------+---------------+---------------+
  528. /// | transaction_id |
  529. /// +-------------------------------+-------------------------------+
  530. /// | secs | flags |
  531. /// +-------------------------------+-------------------------------+
  532. /// | client_ip |
  533. /// +---------------------------------------------------------------+
  534. /// | your_ip |
  535. /// +---------------------------------------------------------------+
  536. /// | server_ip |
  537. /// +---------------------------------------------------------------+
  538. /// | relay_agent_ip |
  539. /// +---------------------------------------------------------------+
  540. /// | |
  541. /// | client_hardware_address |
  542. /// | |
  543. /// | |
  544. /// +---------------------------------------------------------------+
  545. /// | |
  546. /// | sname (N/A) |
  547. /// +---------------------------------------------------------------+
  548. /// | |
  549. /// | file (N/A) |
  550. /// +---------------------------------------------------------------+
  551. /// | |
  552. /// | options |
  553. /// +---------------------------------------------------------------+
  554. /// ```
  555. ///
  556. /// It is assumed that the access layer is Ethernet, so `htype` (the field representing the
  557. /// hardware address type) is always set to `1`, and `hlen` (which represents the hardware address
  558. /// length) is set to `6`.
  559. ///
  560. /// The `options` field has a variable length.
  561. #[derive(Debug, PartialEq, Eq, Clone, Copy)]
  562. pub struct Repr<'a> {
  563. /// This field is also known as `op` in the RFC. It indicates the type of DHCP message this
  564. /// packet represents.
  565. pub message_type: MessageType,
  566. /// This field is also known as `xid` in the RFC. It is a random number chosen by the client,
  567. /// used by the client and server to associate messages and responses between a client and a
  568. /// server.
  569. pub transaction_id: u32,
  570. /// This field is also known as `chaddr` in the RFC and for networks where the access layer is
  571. /// ethernet, it is the client MAC address.
  572. pub client_hardware_address: EthernetAddress,
  573. /// This field is also known as `ciaddr` in the RFC. It is only filled in if client is in
  574. /// BOUND, RENEW or REBINDING state and can respond to ARP requests.
  575. pub client_ip: Ipv4Address,
  576. /// This field is also known as `yiaddr` in the RFC.
  577. pub your_ip: Ipv4Address,
  578. /// This field is also known as `siaddr` in the RFC. It may be set by the server in DHCPOFFER
  579. /// and DHCPACK messages, and represent the address of the next server to use in bootstrap.
  580. pub server_ip: Ipv4Address,
  581. /// Default gateway
  582. pub router: Option<Ipv4Address>,
  583. /// This field comes from a corresponding DhcpOption.
  584. pub subnet_mask: Option<Ipv4Address>,
  585. /// This field is also known as `giaddr` in the RFC. In order to allow DHCP clients on subnets
  586. /// not directly served by DHCP servers to communicate with DHCP servers, DHCP relay agents can
  587. /// be installed on these subnets. The DHCP client broadcasts on the local link; the relay
  588. /// agent receives the broadcast and transmits it to one or more DHCP servers using unicast.
  589. /// The relay agent stores its own IP address in the `relay_agent_ip` field of the DHCP packet.
  590. /// The DHCP server uses the `relay_agent_ip` to determine the subnet on which the relay agent
  591. /// received the broadcast, and allocates an IP address on that subnet. When the DHCP server
  592. /// replies to the client, it sends the reply to the `relay_agent_ip` address, again using
  593. /// unicast. The relay agent then retransmits the response on the local network
  594. pub relay_agent_ip: Ipv4Address,
  595. /// Broadcast flags. It can be set in DHCPDISCOVER, DHCPINFORM and DHCPREQUEST message if the
  596. /// client requires the response to be broadcasted.
  597. pub broadcast: bool,
  598. /// The "requested IP address" option. It can be used by clients in DHCPREQUEST or DHCPDISCOVER
  599. /// messages, or by servers in DHCPDECLINE messages.
  600. pub requested_ip: Option<Ipv4Address>,
  601. /// The "client identifier" option.
  602. ///
  603. /// The 'client identifier' is an opaque key, not to be interpreted by the server; for example,
  604. /// the 'client identifier' may contain a hardware address, identical to the contents of the
  605. /// 'chaddr' field, or it may contain another type of identifier, such as a DNS name. The
  606. /// 'client identifier' chosen by a DHCP client MUST be unique to that client within the subnet
  607. /// to which the client is attached. If the client uses a 'client identifier' in one message,
  608. /// it MUST use that same identifier in all subsequent messages, to ensure that all servers
  609. /// correctly identify the client.
  610. pub client_identifier: Option<EthernetAddress>,
  611. /// The "server identifier" option. It is used both to identify a DHCP server
  612. /// in a DHCP message and as a destination address from clients to servers.
  613. pub server_identifier: Option<Ipv4Address>,
  614. /// The parameter request list informs the server about which configuration parameters
  615. /// the client is interested in.
  616. pub parameter_request_list: Option<&'a [u8]>,
  617. /// DNS servers
  618. pub dns_servers: Option<[Option<Ipv4Address>; 3]>,
  619. /// The maximum size dhcp packet the interface can receive
  620. pub max_size: Option<u16>,
  621. }
  622. impl<'a> Repr<'a> {
  623. /// Return the length of a packet that will be emitted from this high-level representation.
  624. pub fn buffer_len(&self) -> usize {
  625. let mut len = field::OPTIONS.start;
  626. // message type and end-of-options options
  627. len += 3 + 1;
  628. if self.requested_ip.is_some() { len += 6; }
  629. if self.client_identifier.is_some() { len += 9; }
  630. if self.server_identifier.is_some() { len += 6; }
  631. if self.max_size.is_some() { len += 4; }
  632. if let Some(list) = self.parameter_request_list { len += list.len() + 2; }
  633. len
  634. }
  635. /// Parse a DHCP packet and return a high-level representation.
  636. pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Self>
  637. where T: AsRef<[u8]> + ?Sized {
  638. let transaction_id = packet.transaction_id();
  639. let client_hardware_address = packet.client_hardware_address();
  640. let client_ip = packet.client_ip();
  641. let your_ip = packet.your_ip();
  642. let server_ip = packet.server_ip();
  643. let relay_agent_ip = packet.relay_agent_ip();
  644. // only ethernet is supported right now
  645. match packet.hardware_type() {
  646. Hardware::Ethernet => {
  647. if packet.hardware_len() != 6 {
  648. return Err(Error::Malformed);
  649. }
  650. }
  651. Hardware::Unknown(_) => return Err(Error::Unrecognized), // unimplemented
  652. }
  653. if packet.magic_number() != DHCP_MAGIC_NUMBER {
  654. return Err(Error::Malformed);
  655. }
  656. let mut message_type = Err(Error::Malformed);
  657. let mut requested_ip = None;
  658. let mut client_identifier = None;
  659. let mut server_identifier = None;
  660. let mut router = None;
  661. let mut subnet_mask = None;
  662. let mut parameter_request_list = None;
  663. let mut dns_servers = None;
  664. let mut max_size = None;
  665. let mut options = packet.options()?;
  666. while !options.is_empty() {
  667. let (next_options, option) = DhcpOption::parse(options)?;
  668. match option {
  669. DhcpOption::EndOfList => break,
  670. DhcpOption::Pad => {},
  671. DhcpOption::MessageType(value) => {
  672. if value.opcode() == packet.opcode() {
  673. message_type = Ok(value);
  674. }
  675. },
  676. DhcpOption::RequestedIp(ip) => {
  677. requested_ip = Some(ip);
  678. }
  679. DhcpOption::ClientIdentifier(eth_addr) => {
  680. client_identifier = Some(eth_addr);
  681. }
  682. DhcpOption::ServerIdentifier(ip) => {
  683. server_identifier = Some(ip);
  684. }
  685. DhcpOption::Router(ip) => {
  686. router = Some(ip);
  687. }
  688. DhcpOption::SubnetMask(mask) => {
  689. subnet_mask = Some(mask);
  690. },
  691. DhcpOption::MaximumDhcpMessageSize(size) => {
  692. max_size = Some(size);
  693. }
  694. DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => {
  695. parameter_request_list = Some(data);
  696. }
  697. DhcpOption::Other {kind: field::OPT_DOMAIN_NAME_SERVER, data} => {
  698. let mut servers = [None; 3];
  699. for (server, chunk) in servers.iter_mut().zip(data.chunks(4)) {
  700. *server = Some(Ipv4Address::from_bytes(chunk));
  701. }
  702. dns_servers = Some(servers);
  703. }
  704. DhcpOption::Other {..} => {}
  705. }
  706. options = next_options;
  707. }
  708. let broadcast = packet.broadcast_flag();
  709. Ok(Repr {
  710. transaction_id, client_hardware_address, client_ip, your_ip, server_ip, relay_agent_ip,
  711. broadcast, requested_ip, server_identifier, router,
  712. subnet_mask, client_identifier, parameter_request_list, dns_servers, max_size,
  713. message_type: message_type?,
  714. })
  715. }
  716. /// Emit a high-level representation into a Dynamic Host
  717. /// Configuration Protocol packet.
  718. pub fn emit<T>(&self, packet: &mut Packet<&mut T>) -> Result<()>
  719. where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
  720. packet.set_sname_and_boot_file_to_zero();
  721. packet.set_opcode(self.message_type.opcode());
  722. packet.set_hardware_type(Hardware::Ethernet);
  723. packet.set_hardware_len(6);
  724. packet.set_transaction_id(self.transaction_id);
  725. packet.set_client_hardware_address(self.client_hardware_address);
  726. packet.set_hops(0);
  727. packet.set_secs(0); // TODO
  728. packet.set_magic_number(0x63825363);
  729. packet.set_client_ip(self.client_ip);
  730. packet.set_your_ip(self.your_ip);
  731. packet.set_server_ip(self.server_ip);
  732. packet.set_relay_agent_ip(self.relay_agent_ip);
  733. packet.set_broadcast_flag(self.broadcast);
  734. {
  735. let mut options = packet.options_mut()?;
  736. let tmp = options; options = DhcpOption::MessageType(self.message_type).emit(tmp);
  737. if let Some(eth_addr) = self.client_identifier {
  738. let tmp = options; options = DhcpOption::ClientIdentifier(eth_addr).emit(tmp);
  739. }
  740. if let Some(ip) = self.server_identifier {
  741. let tmp = options; options = DhcpOption::ServerIdentifier(ip).emit(tmp);
  742. }
  743. if let Some(ip) = self.router {
  744. let tmp = options; options = DhcpOption::Router(ip).emit(tmp);
  745. }
  746. if let Some(ip) = self.subnet_mask {
  747. let tmp = options; options = DhcpOption::SubnetMask(ip).emit(tmp);
  748. }
  749. if let Some(ip) = self.requested_ip {
  750. let tmp = options; options = DhcpOption::RequestedIp(ip).emit(tmp);
  751. }
  752. if let Some(size) = self.max_size {
  753. let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(size).emit(tmp);
  754. }
  755. if let Some(list) = self.parameter_request_list {
  756. let option = DhcpOption::Other{ kind: field::OPT_PARAMETER_REQUEST_LIST, data: list };
  757. let tmp = options; options = option.emit(tmp);
  758. }
  759. DhcpOption::EndOfList.emit(options);
  760. }
  761. Ok(())
  762. }
  763. }
  764. #[cfg(test)]
  765. mod test {
  766. use crate::wire::Ipv4Address;
  767. use super::*;
  768. const MAGIC_COOKIE: u32 = 0x63825363;
  769. static DISCOVER_BYTES: &[u8] = &[
  770. 0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  771. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x82, 0x01,
  772. 0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  773. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  774. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  775. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  776. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  777. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  778. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  779. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  780. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  781. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  782. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  783. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  784. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
  785. 0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00, 0x00,
  786. 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  787. ];
  788. static ACK_BYTES: &[u8] = &[
  789. 0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06, 0x91,
  790. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17, 0xeb, 0xc9,
  791. 0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  792. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  793. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  794. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  795. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  796. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  797. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  798. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  799. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  800. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  801. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  802. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  803. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
  804. 0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x2b,
  805. 0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68, 0x79, 0x73,
  806. 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00, 0x03, 0x04, 0x0a,
  807. 0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0xa3, 0x01, 0x4a,
  808. 0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0xa3,
  809. 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08, 0xff
  810. ];
  811. const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]);
  812. const CLIENT_MAC: EthernetAddress = EthernetAddress([0x0, 0x0b, 0x82, 0x01, 0xfc, 0x42]);
  813. const DHCP_SIZE: u16 = 1500;
  814. #[test]
  815. fn test_deconstruct_discover() {
  816. let packet = Packet::new_unchecked(DISCOVER_BYTES);
  817. assert_eq!(packet.magic_number(), MAGIC_COOKIE);
  818. assert_eq!(packet.opcode(), OpCode::Request);
  819. assert_eq!(packet.hardware_type(), Hardware::Ethernet);
  820. assert_eq!(packet.hardware_len(), 6);
  821. assert_eq!(packet.hops(), 0);
  822. assert_eq!(packet.transaction_id(), 0x3d1d);
  823. assert_eq!(packet.secs(), 0);
  824. assert_eq!(packet.client_ip(), IP_NULL);
  825. assert_eq!(packet.your_ip(), IP_NULL);
  826. assert_eq!(packet.server_ip(), IP_NULL);
  827. assert_eq!(packet.relay_agent_ip(), IP_NULL);
  828. assert_eq!(packet.client_hardware_address(), CLIENT_MAC);
  829. let options = packet.options().unwrap();
  830. assert_eq!(options.len(), 3 + 9 + 6 + 4 + 6 + 1 + 7);
  831. let (options, message_type) = DhcpOption::parse(options).unwrap();
  832. assert_eq!(message_type, DhcpOption::MessageType(MessageType::Discover));
  833. assert_eq!(options.len(), 9 + 6 + 4 + 6 + 1 + 7);
  834. let (options, client_id) = DhcpOption::parse(options).unwrap();
  835. assert_eq!(client_id, DhcpOption::ClientIdentifier(CLIENT_MAC));
  836. assert_eq!(options.len(), 6 + 4 + 6 + 1 + 7);
  837. let (options, client_id) = DhcpOption::parse(options).unwrap();
  838. assert_eq!(client_id, DhcpOption::RequestedIp(IP_NULL));
  839. assert_eq!(options.len(), 4 + 6 + 1 + 7);
  840. let (options, msg_size) = DhcpOption::parse(options).unwrap();
  841. assert_eq!(msg_size, DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE));
  842. assert_eq!(options.len(), 6 + 1 + 7);
  843. let (options, client_id) = DhcpOption::parse(options).unwrap();
  844. assert_eq!(client_id, DhcpOption::Other {
  845. kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42]
  846. });
  847. assert_eq!(options.len(), 1 + 7);
  848. let (options, client_id) = DhcpOption::parse(options).unwrap();
  849. assert_eq!(client_id, DhcpOption::EndOfList);
  850. assert_eq!(options.len(), 7); // padding
  851. }
  852. #[test]
  853. fn test_construct_discover() {
  854. let mut bytes = vec![0xa5; 276];
  855. let mut packet = Packet::new_unchecked(&mut bytes);
  856. packet.set_magic_number(MAGIC_COOKIE);
  857. packet.set_sname_and_boot_file_to_zero();
  858. packet.set_opcode(OpCode::Request);
  859. packet.set_hardware_type(Hardware::Ethernet);
  860. packet.set_hardware_len(6);
  861. packet.set_hops(0);
  862. packet.set_transaction_id(0x3d1d);
  863. packet.set_secs(0);
  864. packet.set_broadcast_flag(false);
  865. packet.set_client_ip(IP_NULL);
  866. packet.set_your_ip(IP_NULL);
  867. packet.set_server_ip(IP_NULL);
  868. packet.set_relay_agent_ip(IP_NULL);
  869. packet.set_client_hardware_address(CLIENT_MAC);
  870. {
  871. let mut options = packet.options_mut().unwrap();
  872. let tmp = options; options = DhcpOption::MessageType(MessageType::Discover).emit(tmp);
  873. let tmp = options; options = DhcpOption::ClientIdentifier(CLIENT_MAC).emit(tmp);
  874. let tmp = options; options = DhcpOption::RequestedIp(IP_NULL).emit(tmp);
  875. let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(tmp);
  876. let option = DhcpOption::Other {
  877. kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42],
  878. };
  879. let tmp = options; options = option.emit(tmp);
  880. DhcpOption::EndOfList.emit(options);
  881. }
  882. let packet = &mut packet.into_inner()[..];
  883. for byte in &mut packet[269..276] {
  884. *byte = 0; // padding bytes
  885. }
  886. assert_eq!(packet, DISCOVER_BYTES);
  887. }
  888. fn discover_repr() -> Repr<'static> {
  889. Repr {
  890. message_type: MessageType::Discover,
  891. transaction_id: 0x3d1d,
  892. client_hardware_address: CLIENT_MAC,
  893. client_ip: IP_NULL,
  894. your_ip: IP_NULL,
  895. server_ip: IP_NULL,
  896. router: None,
  897. subnet_mask: None,
  898. relay_agent_ip: IP_NULL,
  899. broadcast: false,
  900. max_size: Some(DHCP_SIZE),
  901. requested_ip: Some(IP_NULL),
  902. client_identifier: Some(CLIENT_MAC),
  903. server_identifier: None,
  904. parameter_request_list: Some(&[1, 3, 6, 42]),
  905. dns_servers: None,
  906. }
  907. }
  908. #[test]
  909. fn test_parse_discover() {
  910. let packet = Packet::new_unchecked(DISCOVER_BYTES);
  911. let repr = Repr::parse(&packet).unwrap();
  912. assert_eq!(repr, discover_repr());
  913. }
  914. #[test]
  915. fn test_emit_discover() {
  916. let repr = discover_repr();
  917. let mut bytes = vec![0xa5; repr.buffer_len()];
  918. let mut packet = Packet::new_unchecked(&mut bytes);
  919. repr.emit(&mut packet).unwrap();
  920. let packet = &packet.into_inner()[..];
  921. let packet_len = packet.len();
  922. assert_eq!(packet, &DISCOVER_BYTES[..packet_len]);
  923. for byte in &DISCOVER_BYTES[packet_len..] {
  924. assert_eq!(*byte, 0); // padding bytes
  925. }
  926. }
  927. #[test]
  928. fn test_emit_dhcp_option() {
  929. static DATA: &[u8] = &[1, 3, 6];
  930. let mut bytes = vec![0xa5; 5];
  931. let dhcp_option = DhcpOption::Other {
  932. kind: field::OPT_PARAMETER_REQUEST_LIST,
  933. data: DATA,
  934. };
  935. {
  936. let rest = dhcp_option.emit(&mut bytes);
  937. assert_eq!(rest.len(), 0);
  938. }
  939. assert_eq!(&bytes[0..2], &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]);
  940. assert_eq!(&bytes[2..], DATA);
  941. }
  942. #[test]
  943. fn test_parse_ack_dns_servers() {
  944. let packet = Packet::new_unchecked(ACK_BYTES);
  945. let repr = Repr::parse(&packet).unwrap();
  946. // The packet described by ACK_BYTES advertises 4 DNS servers
  947. // Here we ensure that we correctly parse the first 3 into our fixed
  948. // length-3 array (see issue #305)
  949. assert_eq!(repr.dns_servers, Some([
  950. Some(Ipv4Address([163, 1, 74, 6])),
  951. Some(Ipv4Address([163, 1, 74, 7])),
  952. Some(Ipv4Address([163, 1, 74, 3]))]));
  953. }
  954. }