dhcpv4.rs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  1. use crate::socket::SocketHandle;
  2. use crate::socket::{Context, SocketMeta};
  3. use crate::time::{Duration, Instant};
  4. use crate::wire::dhcpv4::field as dhcpv4_field;
  5. use crate::wire::{
  6. DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr,
  7. UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN,
  8. };
  9. use crate::{Error, Result};
  10. use super::{PollAt, Socket};
  11. const DISCOVER_TIMEOUT: Duration = Duration::from_secs(10);
  12. // timeout doubles every 2 tries.
  13. // total time 5 + 5 + 10 + 10 + 20 = 50s
  14. const REQUEST_TIMEOUT: Duration = Duration::from_secs(5);
  15. const REQUEST_RETRIES: u16 = 5;
  16. const MIN_RENEW_TIMEOUT: Duration = Duration::from_secs(60);
  17. const DEFAULT_LEASE_DURATION: Duration = Duration::from_secs(120);
  18. const PARAMETER_REQUEST_LIST: &[u8] = &[
  19. dhcpv4_field::OPT_SUBNET_MASK,
  20. dhcpv4_field::OPT_ROUTER,
  21. dhcpv4_field::OPT_DOMAIN_NAME_SERVER,
  22. ];
  23. /// IPv4 configuration data provided by the DHCP server.
  24. #[derive(Debug, Eq, PartialEq)]
  25. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  26. pub struct Config {
  27. /// IP address
  28. pub address: Ipv4Cidr,
  29. /// Router address, also known as default gateway. Does not necessarily
  30. /// match the DHCP server's address.
  31. pub router: Option<Ipv4Address>,
  32. /// DNS servers
  33. pub dns_servers: [Option<Ipv4Address>; DHCP_MAX_DNS_SERVER_COUNT],
  34. }
  35. /// Information on how to reach a DHCP server.
  36. #[derive(Debug, Clone, Copy)]
  37. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  38. struct ServerInfo {
  39. /// IP address to use as destination in outgoing packets
  40. address: Ipv4Address,
  41. /// Server identifier to use in outgoing packets. Usually equal to server_address,
  42. /// but may differ in some situations (eg DHCP relays)
  43. identifier: Ipv4Address,
  44. }
  45. #[derive(Debug)]
  46. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  47. struct DiscoverState {
  48. /// When to send next request
  49. retry_at: Instant,
  50. }
  51. #[derive(Debug)]
  52. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  53. struct RequestState {
  54. /// When to send next request
  55. retry_at: Instant,
  56. /// How many retries have been done
  57. retry: u16,
  58. /// Server we're trying to request from
  59. server: ServerInfo,
  60. /// IP address that we're trying to request.
  61. requested_ip: Ipv4Address,
  62. }
  63. #[derive(Debug)]
  64. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  65. struct RenewState {
  66. /// Server that gave us the lease
  67. server: ServerInfo,
  68. /// Active network config
  69. config: Config,
  70. /// Renew timer. When reached, we will start attempting
  71. /// to renew this lease with the DHCP server.
  72. /// Must be less or equal than `expires_at`.
  73. renew_at: Instant,
  74. /// Expiration timer. When reached, this lease is no longer valid, so it must be
  75. /// thrown away and the ethernet interface deconfigured.
  76. expires_at: Instant,
  77. }
  78. #[derive(Debug)]
  79. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  80. enum ClientState {
  81. /// Discovering the DHCP server
  82. Discovering(DiscoverState),
  83. /// Requesting an address
  84. Requesting(RequestState),
  85. /// Having an address, refresh it periodically.
  86. Renewing(RenewState),
  87. }
  88. /// Return value for the `Dhcpv4Socket::poll` function
  89. #[derive(Debug, PartialEq, Eq)]
  90. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  91. pub enum Event<'a> {
  92. /// Configuration has been lost (for example, the lease has expired)
  93. Deconfigured,
  94. /// Configuration has been newly acquired, or modified.
  95. Configured(&'a Config),
  96. }
  97. #[derive(Debug)]
  98. pub struct Dhcpv4Socket {
  99. pub(crate) meta: SocketMeta,
  100. /// State of the DHCP client.
  101. state: ClientState,
  102. /// Set to true on config/state change, cleared back to false by the `config` function.
  103. config_changed: bool,
  104. /// xid of the last sent message.
  105. transaction_id: u32,
  106. /// Max lease duration. If set, it sets a maximum cap to the server-provided lease duration.
  107. /// Useful to react faster to IP configuration changes and to test whether renews work correctly.
  108. max_lease_duration: Option<Duration>,
  109. }
  110. /// DHCP client socket.
  111. ///
  112. /// The socket acquires an IP address configuration through DHCP autonomously.
  113. /// You must query the configuration with `.poll()` after every call to `Interface::poll()`,
  114. /// and apply the configuration to the `Interface`.
  115. impl Dhcpv4Socket {
  116. /// Create a DHCPv4 socket
  117. #[allow(clippy::new_without_default)]
  118. pub fn new() -> Self {
  119. Dhcpv4Socket {
  120. meta: SocketMeta::default(),
  121. state: ClientState::Discovering(DiscoverState {
  122. retry_at: Instant::from_millis(0),
  123. }),
  124. config_changed: true,
  125. transaction_id: 1,
  126. max_lease_duration: None,
  127. }
  128. }
  129. pub fn max_lease_duration(&self) -> Option<Duration> {
  130. self.max_lease_duration
  131. }
  132. pub fn set_max_lease_duration(&mut self, max_lease_duration: Option<Duration>) {
  133. self.max_lease_duration = max_lease_duration;
  134. }
  135. pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt {
  136. let t = match &self.state {
  137. ClientState::Discovering(state) => state.retry_at,
  138. ClientState::Requesting(state) => state.retry_at,
  139. ClientState::Renewing(state) => state.renew_at.min(state.expires_at),
  140. };
  141. PollAt::Time(t)
  142. }
  143. pub(crate) fn process(
  144. &mut self,
  145. cx: &Context,
  146. ip_repr: &Ipv4Repr,
  147. repr: &UdpRepr,
  148. payload: &[u8],
  149. ) -> Result<()> {
  150. let src_ip = ip_repr.src_addr;
  151. // This is enforced in interface.rs.
  152. assert!(repr.src_port == DHCP_SERVER_PORT && repr.dst_port == DHCP_CLIENT_PORT);
  153. let dhcp_packet = match DhcpPacket::new_checked(payload) {
  154. Ok(dhcp_packet) => dhcp_packet,
  155. Err(e) => {
  156. net_debug!("DHCP invalid pkt from {}: {:?}", src_ip, e);
  157. return Ok(());
  158. }
  159. };
  160. let dhcp_repr = match DhcpRepr::parse(&dhcp_packet) {
  161. Ok(dhcp_repr) => dhcp_repr,
  162. Err(e) => {
  163. net_debug!("DHCP error parsing pkt from {}: {:?}", src_ip, e);
  164. return Ok(());
  165. }
  166. };
  167. if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() {
  168. return Ok(());
  169. }
  170. if dhcp_repr.transaction_id != self.transaction_id {
  171. return Ok(());
  172. }
  173. let server_identifier = match dhcp_repr.server_identifier {
  174. Some(server_identifier) => server_identifier,
  175. None => {
  176. net_debug!(
  177. "DHCP ignoring {:?} because missing server_identifier",
  178. dhcp_repr.message_type
  179. );
  180. return Ok(());
  181. }
  182. };
  183. net_debug!(
  184. "DHCP recv {:?} from {}: {:?}",
  185. dhcp_repr.message_type,
  186. src_ip,
  187. dhcp_repr
  188. );
  189. match (&mut self.state, dhcp_repr.message_type) {
  190. (ClientState::Discovering(_state), DhcpMessageType::Offer) => {
  191. if !dhcp_repr.your_ip.is_unicast() {
  192. net_debug!("DHCP ignoring OFFER because your_ip is not unicast");
  193. return Ok(());
  194. }
  195. self.state = ClientState::Requesting(RequestState {
  196. retry_at: cx.now,
  197. retry: 0,
  198. server: ServerInfo {
  199. address: src_ip,
  200. identifier: server_identifier,
  201. },
  202. requested_ip: dhcp_repr.your_ip, // use the offered ip
  203. });
  204. }
  205. (ClientState::Requesting(state), DhcpMessageType::Ack) => {
  206. if let Some((config, renew_at, expires_at)) =
  207. Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration)
  208. {
  209. self.config_changed = true;
  210. self.state = ClientState::Renewing(RenewState {
  211. server: state.server,
  212. config,
  213. renew_at,
  214. expires_at,
  215. });
  216. }
  217. }
  218. (ClientState::Requesting(_), DhcpMessageType::Nak) => {
  219. self.reset();
  220. }
  221. (ClientState::Renewing(state), DhcpMessageType::Ack) => {
  222. if let Some((config, renew_at, expires_at)) =
  223. Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration)
  224. {
  225. state.renew_at = renew_at;
  226. state.expires_at = expires_at;
  227. if state.config != config {
  228. self.config_changed = true;
  229. state.config = config;
  230. }
  231. }
  232. }
  233. (ClientState::Renewing(_), DhcpMessageType::Nak) => {
  234. self.reset();
  235. }
  236. _ => {
  237. net_debug!(
  238. "DHCP ignoring {:?}: unexpected in current state",
  239. dhcp_repr.message_type
  240. );
  241. }
  242. }
  243. Ok(())
  244. }
  245. fn parse_ack(
  246. now: Instant,
  247. dhcp_repr: &DhcpRepr,
  248. max_lease_duration: Option<Duration>,
  249. ) -> Option<(Config, Instant, Instant)> {
  250. let subnet_mask = match dhcp_repr.subnet_mask {
  251. Some(subnet_mask) => subnet_mask,
  252. None => {
  253. net_debug!("DHCP ignoring ACK because missing subnet_mask");
  254. return None;
  255. }
  256. };
  257. let prefix_len = match IpAddress::Ipv4(subnet_mask).prefix_len() {
  258. Some(prefix_len) => prefix_len,
  259. None => {
  260. net_debug!("DHCP ignoring ACK because subnet_mask is not a valid mask");
  261. return None;
  262. }
  263. };
  264. if !dhcp_repr.your_ip.is_unicast() {
  265. net_debug!("DHCP ignoring ACK because your_ip is not unicast");
  266. return None;
  267. }
  268. let mut lease_duration = dhcp_repr
  269. .lease_duration
  270. .map(|d| Duration::from_secs(d as _))
  271. .unwrap_or(DEFAULT_LEASE_DURATION);
  272. if let Some(max_lease_duration) = max_lease_duration {
  273. lease_duration = lease_duration.min(max_lease_duration);
  274. }
  275. // Cleanup the DNS servers list, keeping only unicasts/
  276. // TP-Link TD-W8970 sends 0.0.0.0 as second DNS server if there's only one configured :(
  277. let mut dns_servers = [None; DHCP_MAX_DNS_SERVER_COUNT];
  278. if let Some(received) = dhcp_repr.dns_servers {
  279. let mut i = 0;
  280. for addr in received.iter().flatten() {
  281. if addr.is_unicast() {
  282. // This can never be out-of-bounds since both arrays have length DHCP_MAX_DNS_SERVER_COUNT
  283. dns_servers[i] = Some(*addr);
  284. i += 1;
  285. }
  286. }
  287. }
  288. let config = Config {
  289. address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len),
  290. router: dhcp_repr.router,
  291. dns_servers: dns_servers,
  292. };
  293. // RFC 2131 indicates clients should renew a lease halfway through its expiration.
  294. let renew_at = now + lease_duration / 2;
  295. let expires_at = now + lease_duration;
  296. Some((config, renew_at, expires_at))
  297. }
  298. #[cfg(not(test))]
  299. fn random_transaction_id() -> u32 {
  300. crate::rand::rand_u32()
  301. }
  302. #[cfg(test)]
  303. fn random_transaction_id() -> u32 {
  304. 0x12345678
  305. }
  306. pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()>
  307. where
  308. F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()>,
  309. {
  310. // note: Dhcpv4Socket is only usable in ethernet mediums, so the
  311. // unwrap can never fail.
  312. let ethernet_addr = cx.ethernet_address.unwrap();
  313. // Worst case biggest IPv4 header length.
  314. // 0x0f * 4 = 60 bytes.
  315. const MAX_IPV4_HEADER_LEN: usize = 60;
  316. // We don't directly modify self.transaction_id because sending the packet
  317. // may fail. We only want to update state after succesfully sending.
  318. let next_transaction_id = Self::random_transaction_id();
  319. let mut dhcp_repr = DhcpRepr {
  320. message_type: DhcpMessageType::Discover,
  321. transaction_id: next_transaction_id,
  322. client_hardware_address: ethernet_addr,
  323. client_ip: Ipv4Address::UNSPECIFIED,
  324. your_ip: Ipv4Address::UNSPECIFIED,
  325. server_ip: Ipv4Address::UNSPECIFIED,
  326. router: None,
  327. subnet_mask: None,
  328. relay_agent_ip: Ipv4Address::UNSPECIFIED,
  329. broadcast: false,
  330. requested_ip: None,
  331. client_identifier: Some(ethernet_addr),
  332. server_identifier: None,
  333. parameter_request_list: Some(PARAMETER_REQUEST_LIST),
  334. max_size: Some((cx.caps.ip_mtu() - MAX_IPV4_HEADER_LEN - UDP_HEADER_LEN) as u16),
  335. lease_duration: None,
  336. dns_servers: None,
  337. };
  338. let udp_repr = UdpRepr {
  339. src_port: DHCP_CLIENT_PORT,
  340. dst_port: DHCP_SERVER_PORT,
  341. };
  342. let mut ipv4_repr = Ipv4Repr {
  343. src_addr: Ipv4Address::UNSPECIFIED,
  344. dst_addr: Ipv4Address::BROADCAST,
  345. protocol: IpProtocol::Udp,
  346. payload_len: 0, // filled right before emit
  347. hop_limit: 64,
  348. };
  349. match &mut self.state {
  350. ClientState::Discovering(state) => {
  351. if cx.now < state.retry_at {
  352. return Err(Error::Exhausted);
  353. }
  354. // send packet
  355. net_debug!(
  356. "DHCP send DISCOVER to {}: {:?}",
  357. ipv4_repr.dst_addr,
  358. dhcp_repr
  359. );
  360. ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len();
  361. emit((ipv4_repr, udp_repr, dhcp_repr))?;
  362. // Update state AFTER the packet has been successfully sent.
  363. state.retry_at = cx.now + DISCOVER_TIMEOUT;
  364. self.transaction_id = next_transaction_id;
  365. Ok(())
  366. }
  367. ClientState::Requesting(state) => {
  368. if cx.now < state.retry_at {
  369. return Err(Error::Exhausted);
  370. }
  371. if state.retry >= REQUEST_RETRIES {
  372. net_debug!("DHCP request retries exceeded, restarting discovery");
  373. self.reset();
  374. // return Ok so we get polled again
  375. return Ok(());
  376. }
  377. dhcp_repr.message_type = DhcpMessageType::Request;
  378. dhcp_repr.requested_ip = Some(state.requested_ip);
  379. dhcp_repr.server_identifier = Some(state.server.identifier);
  380. net_debug!(
  381. "DHCP send request to {}: {:?}",
  382. ipv4_repr.dst_addr,
  383. dhcp_repr
  384. );
  385. ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len();
  386. emit((ipv4_repr, udp_repr, dhcp_repr))?;
  387. // Exponential backoff: Double every 2 retries.
  388. state.retry_at = cx.now + (REQUEST_TIMEOUT << (state.retry as u32 / 2));
  389. state.retry += 1;
  390. self.transaction_id = next_transaction_id;
  391. Ok(())
  392. }
  393. ClientState::Renewing(state) => {
  394. if state.expires_at <= cx.now {
  395. net_debug!("DHCP lease expired");
  396. self.reset();
  397. // return Ok so we get polled again
  398. return Ok(());
  399. }
  400. if cx.now < state.renew_at {
  401. return Err(Error::Exhausted);
  402. }
  403. ipv4_repr.src_addr = state.config.address.address();
  404. ipv4_repr.dst_addr = state.server.address;
  405. dhcp_repr.message_type = DhcpMessageType::Request;
  406. dhcp_repr.client_ip = state.config.address.address();
  407. net_debug!("DHCP send renew to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr);
  408. ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len();
  409. emit((ipv4_repr, udp_repr, dhcp_repr))?;
  410. // In both RENEWING and REBINDING states, if the client receives no
  411. // response to its DHCPREQUEST message, the client SHOULD wait one-half
  412. // of the remaining time until T2 (in RENEWING state) and one-half of
  413. // the remaining lease time (in REBINDING state), down to a minimum of
  414. // 60 seconds, before retransmitting the DHCPREQUEST message.
  415. state.renew_at = cx.now + MIN_RENEW_TIMEOUT.max((state.expires_at - cx.now) / 2);
  416. self.transaction_id = next_transaction_id;
  417. Ok(())
  418. }
  419. }
  420. }
  421. /// Return the socket handle.
  422. #[inline]
  423. pub fn handle(&self) -> SocketHandle {
  424. self.meta.handle
  425. }
  426. /// Reset state and restart discovery phase.
  427. ///
  428. /// Use this to speed up acquisition of an address in a new
  429. /// network if a link was down and it is now back up.
  430. pub fn reset(&mut self) {
  431. net_trace!("DHCP reset");
  432. if let ClientState::Renewing(_) = &self.state {
  433. self.config_changed = true;
  434. }
  435. self.state = ClientState::Discovering(DiscoverState {
  436. retry_at: Instant::from_millis(0),
  437. });
  438. }
  439. /// Query the socket for configuration changes.
  440. ///
  441. /// The socket has an internal "configuration changed" flag. If
  442. /// set, this function returns the configuration and resets the flag.
  443. pub fn poll(&mut self) -> Option<Event<'_>> {
  444. if !self.config_changed {
  445. None
  446. } else if let ClientState::Renewing(state) = &self.state {
  447. self.config_changed = false;
  448. Some(Event::Configured(&state.config))
  449. } else {
  450. self.config_changed = false;
  451. Some(Event::Deconfigured)
  452. }
  453. }
  454. }
  455. impl<'a> From<Dhcpv4Socket> for Socket<'a> {
  456. fn from(val: Dhcpv4Socket) -> Self {
  457. Socket::Dhcpv4(val)
  458. }
  459. }
  460. #[cfg(test)]
  461. mod test {
  462. use super::*;
  463. use crate::wire::EthernetAddress;
  464. // =========================================================================================//
  465. // Helper functions
  466. fn send(
  467. socket: &mut Dhcpv4Socket,
  468. timestamp: Instant,
  469. (ip_repr, udp_repr, dhcp_repr): (Ipv4Repr, UdpRepr, DhcpRepr),
  470. ) -> Result<()> {
  471. net_trace!("send: {:?}", ip_repr);
  472. net_trace!(" {:?}", udp_repr);
  473. net_trace!(" {:?}", dhcp_repr);
  474. let mut payload = vec![0; dhcp_repr.buffer_len()];
  475. dhcp_repr
  476. .emit(&mut DhcpPacket::new_unchecked(&mut payload))
  477. .unwrap();
  478. let mut cx = Context::DUMMY.clone();
  479. cx.now = timestamp;
  480. socket.process(&cx, &ip_repr, &udp_repr, &payload)
  481. }
  482. fn recv(
  483. socket: &mut Dhcpv4Socket,
  484. timestamp: Instant,
  485. reprs: &[(Ipv4Repr, UdpRepr, DhcpRepr)],
  486. ) {
  487. let mut cx = Context::DUMMY.clone();
  488. cx.now = timestamp;
  489. let mut i = 0;
  490. while socket.poll_at(&cx) <= PollAt::Time(timestamp) {
  491. let _ = socket.dispatch(&cx, |(mut ip_repr, udp_repr, dhcp_repr)| {
  492. assert_eq!(ip_repr.protocol, IpProtocol::Udp);
  493. assert_eq!(
  494. ip_repr.payload_len,
  495. udp_repr.header_len() + dhcp_repr.buffer_len()
  496. );
  497. // We validated the payload len, change it to 0 to make equality testing easier
  498. ip_repr.payload_len = 0;
  499. net_trace!("recv: {:?}", ip_repr);
  500. net_trace!(" {:?}", udp_repr);
  501. net_trace!(" {:?}", dhcp_repr);
  502. let got_repr = (ip_repr, udp_repr, dhcp_repr);
  503. match reprs.get(i) {
  504. Some(want_repr) => assert_eq!(want_repr, &got_repr),
  505. None => panic!("Too many reprs emitted"),
  506. }
  507. i += 1;
  508. Ok(())
  509. });
  510. }
  511. if i != reprs.len() {
  512. panic!("Too few reprs emitted. Wanted {}, got {}", reprs.len(), i);
  513. }
  514. }
  515. macro_rules! send {
  516. ($socket:ident, $repr:expr) =>
  517. (send!($socket, time 0, $repr));
  518. ($socket:ident, $repr:expr, $result:expr) =>
  519. (send!($socket, time 0, $repr, $result));
  520. ($socket:ident, time $time:expr, $repr:expr) =>
  521. (send!($socket, time $time, $repr, Ok(( ))));
  522. ($socket:ident, time $time:expr, $repr:expr, $result:expr) =>
  523. (assert_eq!(send(&mut $socket, Instant::from_millis($time), $repr), $result));
  524. }
  525. macro_rules! recv {
  526. ($socket:ident, $reprs:expr) => ({
  527. recv!($socket, time 0, $reprs);
  528. });
  529. ($socket:ident, time $time:expr, $reprs:expr) => ({
  530. recv(&mut $socket, Instant::from_millis($time), &$reprs);
  531. });
  532. }
  533. #[cfg(feature = "log")]
  534. fn init_logger() {
  535. struct Logger;
  536. static LOGGER: Logger = Logger;
  537. impl log::Log for Logger {
  538. fn enabled(&self, _metadata: &log::Metadata) -> bool {
  539. true
  540. }
  541. fn log(&self, record: &log::Record) {
  542. println!("{}", record.args());
  543. }
  544. fn flush(&self) {}
  545. }
  546. // If it fails, that just means we've already set it to the same value.
  547. let _ = log::set_logger(&LOGGER);
  548. log::set_max_level(log::LevelFilter::Trace);
  549. println!();
  550. }
  551. // =========================================================================================//
  552. // Constants
  553. const TXID: u32 = 0x12345678;
  554. const MY_IP: Ipv4Address = Ipv4Address([192, 168, 1, 42]);
  555. const SERVER_IP: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
  556. const DNS_IP_1: Ipv4Address = Ipv4Address([1, 1, 1, 1]);
  557. const DNS_IP_2: Ipv4Address = Ipv4Address([1, 1, 1, 2]);
  558. const DNS_IP_3: Ipv4Address = Ipv4Address([1, 1, 1, 3]);
  559. const DNS_IPS: [Option<Ipv4Address>; DHCP_MAX_DNS_SERVER_COUNT] =
  560. [Some(DNS_IP_1), Some(DNS_IP_2), Some(DNS_IP_3)];
  561. const MASK_24: Ipv4Address = Ipv4Address([255, 255, 255, 0]);
  562. const MY_MAC: EthernetAddress = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]);
  563. const IP_BROADCAST: Ipv4Repr = Ipv4Repr {
  564. src_addr: Ipv4Address::UNSPECIFIED,
  565. dst_addr: Ipv4Address::BROADCAST,
  566. protocol: IpProtocol::Udp,
  567. payload_len: 0,
  568. hop_limit: 64,
  569. };
  570. const IP_RECV: Ipv4Repr = Ipv4Repr {
  571. src_addr: SERVER_IP,
  572. dst_addr: MY_IP,
  573. protocol: IpProtocol::Udp,
  574. payload_len: 0,
  575. hop_limit: 64,
  576. };
  577. const IP_SEND: Ipv4Repr = Ipv4Repr {
  578. src_addr: MY_IP,
  579. dst_addr: SERVER_IP,
  580. protocol: IpProtocol::Udp,
  581. payload_len: 0,
  582. hop_limit: 64,
  583. };
  584. const UDP_SEND: UdpRepr = UdpRepr {
  585. src_port: 68,
  586. dst_port: 67,
  587. };
  588. const UDP_RECV: UdpRepr = UdpRepr {
  589. src_port: 67,
  590. dst_port: 68,
  591. };
  592. const DHCP_DEFAULT: DhcpRepr = DhcpRepr {
  593. message_type: DhcpMessageType::Unknown(99),
  594. transaction_id: TXID,
  595. client_hardware_address: MY_MAC,
  596. client_ip: Ipv4Address::UNSPECIFIED,
  597. your_ip: Ipv4Address::UNSPECIFIED,
  598. server_ip: Ipv4Address::UNSPECIFIED,
  599. router: None,
  600. subnet_mask: None,
  601. relay_agent_ip: Ipv4Address::UNSPECIFIED,
  602. broadcast: false,
  603. requested_ip: None,
  604. client_identifier: None,
  605. server_identifier: None,
  606. parameter_request_list: None,
  607. dns_servers: None,
  608. max_size: None,
  609. lease_duration: None,
  610. };
  611. const DHCP_DISCOVER: DhcpRepr = DhcpRepr {
  612. message_type: DhcpMessageType::Discover,
  613. client_identifier: Some(MY_MAC),
  614. parameter_request_list: Some(&[1, 3, 6]),
  615. max_size: Some(1432),
  616. ..DHCP_DEFAULT
  617. };
  618. const DHCP_OFFER: DhcpRepr = DhcpRepr {
  619. message_type: DhcpMessageType::Offer,
  620. server_ip: SERVER_IP,
  621. server_identifier: Some(SERVER_IP),
  622. your_ip: MY_IP,
  623. router: Some(SERVER_IP),
  624. subnet_mask: Some(MASK_24),
  625. dns_servers: Some(DNS_IPS),
  626. lease_duration: Some(1000),
  627. ..DHCP_DEFAULT
  628. };
  629. const DHCP_REQUEST: DhcpRepr = DhcpRepr {
  630. message_type: DhcpMessageType::Request,
  631. client_identifier: Some(MY_MAC),
  632. server_identifier: Some(SERVER_IP),
  633. max_size: Some(1432),
  634. requested_ip: Some(MY_IP),
  635. parameter_request_list: Some(&[1, 3, 6]),
  636. ..DHCP_DEFAULT
  637. };
  638. const DHCP_ACK: DhcpRepr = DhcpRepr {
  639. message_type: DhcpMessageType::Ack,
  640. server_ip: SERVER_IP,
  641. server_identifier: Some(SERVER_IP),
  642. your_ip: MY_IP,
  643. router: Some(SERVER_IP),
  644. subnet_mask: Some(MASK_24),
  645. dns_servers: Some(DNS_IPS),
  646. lease_duration: Some(1000),
  647. ..DHCP_DEFAULT
  648. };
  649. const DHCP_RENEW: DhcpRepr = DhcpRepr {
  650. message_type: DhcpMessageType::Request,
  651. client_identifier: Some(MY_MAC),
  652. // NO server_identifier in renew requests, only in first one!
  653. client_ip: MY_IP,
  654. max_size: Some(1432),
  655. requested_ip: None,
  656. parameter_request_list: Some(&[1, 3, 6]),
  657. ..DHCP_DEFAULT
  658. };
  659. // =========================================================================================//
  660. // Tests
  661. fn socket() -> Dhcpv4Socket {
  662. #[cfg(feature = "log")]
  663. init_logger();
  664. let mut s = Dhcpv4Socket::new();
  665. assert_eq!(s.poll(), Some(Event::Deconfigured));
  666. s
  667. }
  668. fn socket_bound() -> Dhcpv4Socket {
  669. let mut s = socket();
  670. s.state = ClientState::Renewing(RenewState {
  671. config: Config {
  672. address: Ipv4Cidr::new(MY_IP, 24),
  673. dns_servers: DNS_IPS,
  674. router: Some(SERVER_IP),
  675. },
  676. server: ServerInfo {
  677. address: SERVER_IP,
  678. identifier: SERVER_IP,
  679. },
  680. renew_at: Instant::from_secs(500),
  681. expires_at: Instant::from_secs(1000),
  682. });
  683. s
  684. }
  685. #[test]
  686. fn test_bind() {
  687. let mut s = socket();
  688. recv!(s, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
  689. assert_eq!(s.poll(), None);
  690. send!(s, (IP_RECV, UDP_RECV, DHCP_OFFER));
  691. assert_eq!(s.poll(), None);
  692. recv!(s, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  693. assert_eq!(s.poll(), None);
  694. send!(s, (IP_RECV, UDP_RECV, DHCP_ACK));
  695. assert_eq!(
  696. s.poll(),
  697. Some(Event::Configured(&Config {
  698. address: Ipv4Cidr::new(MY_IP, 24),
  699. dns_servers: DNS_IPS,
  700. router: Some(SERVER_IP),
  701. }))
  702. );
  703. match &s.state {
  704. ClientState::Renewing(r) => {
  705. assert_eq!(r.renew_at, Instant::from_secs(500));
  706. assert_eq!(r.expires_at, Instant::from_secs(1000));
  707. }
  708. _ => panic!("Invalid state"),
  709. }
  710. }
  711. #[test]
  712. fn test_discover_retransmit() {
  713. let mut s = socket();
  714. recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
  715. recv!(s, time 1_000, []);
  716. recv!(s, time 10_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
  717. recv!(s, time 11_000, []);
  718. recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
  719. // check after retransmits it still works
  720. send!(s, time 20_000, (IP_RECV, UDP_RECV, DHCP_OFFER));
  721. recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  722. }
  723. #[test]
  724. fn test_request_retransmit() {
  725. let mut s = socket();
  726. recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
  727. send!(s, time 0, (IP_RECV, UDP_RECV, DHCP_OFFER));
  728. recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  729. recv!(s, time 1_000, []);
  730. recv!(s, time 5_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  731. recv!(s, time 6_000, []);
  732. recv!(s, time 10_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  733. recv!(s, time 15_000, []);
  734. recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  735. // check after retransmits it still works
  736. send!(s, time 20_000, (IP_RECV, UDP_RECV, DHCP_ACK));
  737. match &s.state {
  738. ClientState::Renewing(r) => {
  739. assert_eq!(r.renew_at, Instant::from_secs(20 + 500));
  740. assert_eq!(r.expires_at, Instant::from_secs(20 + 1000));
  741. }
  742. _ => panic!("Invalid state"),
  743. }
  744. }
  745. #[test]
  746. fn test_request_timeout() {
  747. let mut s = socket();
  748. recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
  749. send!(s, time 0, (IP_RECV, UDP_RECV, DHCP_OFFER));
  750. recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  751. recv!(s, time 5_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  752. recv!(s, time 10_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  753. recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  754. recv!(s, time 30_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  755. // After 5 tries and 70 seconds, it gives up.
  756. // 5 + 5 + 10 + 10 + 20 = 70
  757. recv!(s, time 70_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
  758. // check it still works
  759. send!(s, time 60_000, (IP_RECV, UDP_RECV, DHCP_OFFER));
  760. recv!(s, time 60_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
  761. }
  762. #[test]
  763. fn test_renew() {
  764. let mut s = socket_bound();
  765. recv!(s, []);
  766. assert_eq!(s.poll(), None);
  767. recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
  768. assert_eq!(s.poll(), None);
  769. match &s.state {
  770. ClientState::Renewing(r) => {
  771. // the expiration still hasn't been bumped, because
  772. // we haven't received the ACK yet
  773. assert_eq!(r.expires_at, Instant::from_secs(1000));
  774. }
  775. _ => panic!("Invalid state"),
  776. }
  777. send!(s, time 500_000, (IP_RECV, UDP_RECV, DHCP_ACK));
  778. assert_eq!(s.poll(), None);
  779. match &s.state {
  780. ClientState::Renewing(r) => {
  781. // NOW the expiration gets bumped
  782. assert_eq!(r.renew_at, Instant::from_secs(500 + 500));
  783. assert_eq!(r.expires_at, Instant::from_secs(500 + 1000));
  784. }
  785. _ => panic!("Invalid state"),
  786. }
  787. }
  788. #[test]
  789. fn test_renew_retransmit() {
  790. let mut s = socket_bound();
  791. recv!(s, []);
  792. recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
  793. recv!(s, time 749_000, []);
  794. recv!(s, time 750_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
  795. recv!(s, time 874_000, []);
  796. recv!(s, time 875_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
  797. // check it still works
  798. send!(s, time 875_000, (IP_RECV, UDP_RECV, DHCP_ACK));
  799. match &s.state {
  800. ClientState::Renewing(r) => {
  801. // NOW the expiration gets bumped
  802. assert_eq!(r.renew_at, Instant::from_secs(875 + 500));
  803. assert_eq!(r.expires_at, Instant::from_secs(875 + 1000));
  804. }
  805. _ => panic!("Invalid state"),
  806. }
  807. }
  808. #[test]
  809. fn test_renew_timeout() {
  810. let mut s = socket_bound();
  811. recv!(s, []);
  812. recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
  813. recv!(s, time 999_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
  814. recv!(s, time 1_000_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
  815. match &s.state {
  816. ClientState::Discovering(_) => {}
  817. _ => panic!("Invalid state"),
  818. }
  819. }
  820. }