clientv4.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. use crate::{Error, Result};
  2. use crate::wire::{IpVersion, IpProtocol, IpEndpoint, IpAddress,
  3. Ipv4Cidr, Ipv4Address, Ipv4Packet, Ipv4Repr,
  4. UdpPacket, UdpRepr,
  5. DhcpPacket, DhcpRepr, DhcpMessageType};
  6. use crate::wire::dhcpv4::{field as dhcpv4_field, Packet as Dhcpv4Packet};
  7. use crate::socket::{SocketSet, SocketHandle, RawSocket, RawSocketBuffer};
  8. use crate::phy::{Device, ChecksumCapabilities};
  9. use crate::iface::Interface;
  10. use crate::time::{Instant, Duration};
  11. use super::{UDP_SERVER_PORT, UDP_CLIENT_PORT};
  12. const DISCOVER_TIMEOUT: u64 = 10;
  13. const REQUEST_TIMEOUT: u64 = 1;
  14. const REQUEST_RETRIES: u16 = 15;
  15. const DEFAULT_RENEW_INTERVAL: u32 = 60;
  16. const PARAMETER_REQUEST_LIST: &[u8] = &[
  17. dhcpv4_field::OPT_SUBNET_MASK,
  18. dhcpv4_field::OPT_ROUTER,
  19. dhcpv4_field::OPT_DOMAIN_NAME_SERVER,
  20. ];
  21. /// IPv4 configuration data returned by `client.poll()`
  22. #[derive(Debug)]
  23. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  24. pub struct Config {
  25. pub address: Option<Ipv4Cidr>,
  26. pub router: Option<Ipv4Address>,
  27. pub dns_servers: [Option<Ipv4Address>; 3],
  28. }
  29. #[derive(Debug)]
  30. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  31. struct RequestState {
  32. retry: u16,
  33. endpoint_ip: Ipv4Address,
  34. server_identifier: Ipv4Address,
  35. requested_ip: Ipv4Address,
  36. }
  37. #[derive(Debug)]
  38. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  39. struct RenewState {
  40. endpoint_ip: Ipv4Address,
  41. server_identifier: Ipv4Address,
  42. }
  43. #[derive(Debug)]
  44. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  45. enum ClientState {
  46. /// Discovering the DHCP server
  47. Discovering,
  48. /// Requesting an address
  49. Requesting(RequestState),
  50. /// Having an address, refresh it periodically
  51. Renew(RenewState),
  52. }
  53. pub struct Client {
  54. state: ClientState,
  55. raw_handle: SocketHandle,
  56. /// When to send next request
  57. next_egress: Instant,
  58. /// When any existing DHCP address will expire.
  59. lease_expiration: Option<Instant>,
  60. transaction_id: u32,
  61. }
  62. /// DHCP client with a RawSocket.
  63. ///
  64. /// To provide memory for the dynamic IP address, configure your
  65. /// `Interface` with one of `ip_addrs` and the `ipv4_gateway` being
  66. /// `Ipv4Address::UNSPECIFIED`. You must also assign this `0.0.0.0/0`
  67. /// while the client's state is `Discovering`. Hence, the `poll()`
  68. /// method returns a corresponding `Config` struct in this case.
  69. ///
  70. /// You must call `dhcp_client.poll()` after `iface.poll()` to send
  71. /// and receive DHCP packets.
  72. impl Client {
  73. /// # Usage
  74. /// ```rust
  75. /// use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata};
  76. /// use smoltcp::dhcp::Dhcpv4Client;
  77. /// use smoltcp::time::Instant;
  78. ///
  79. /// let mut sockets = SocketSet::new(vec![]);
  80. /// let dhcp_rx_buffer = RawSocketBuffer::new(
  81. /// [RawPacketMetadata::EMPTY; 1],
  82. /// vec![0; 600]
  83. /// );
  84. /// let dhcp_tx_buffer = RawSocketBuffer::new(
  85. /// [RawPacketMetadata::EMPTY; 1],
  86. /// vec![0; 600]
  87. /// );
  88. /// let mut dhcp = Dhcpv4Client::new(
  89. /// &mut sockets,
  90. /// dhcp_rx_buffer, dhcp_tx_buffer,
  91. /// Instant::now()
  92. /// );
  93. /// ```
  94. pub fn new<'a>(sockets: &mut SocketSet<'a>, rx_buffer: RawSocketBuffer<'a>, tx_buffer: RawSocketBuffer<'a>, now: Instant) -> Self
  95. {
  96. let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer);
  97. let raw_handle = sockets.add(raw_socket);
  98. Client {
  99. state: ClientState::Discovering,
  100. raw_handle,
  101. next_egress: now,
  102. transaction_id: 1,
  103. lease_expiration: None,
  104. }
  105. }
  106. /// When to send next packet
  107. ///
  108. /// Useful for suspending execution after polling.
  109. pub fn next_poll(&self, now: Instant) -> Duration {
  110. self.next_egress - now
  111. }
  112. /// Process incoming packets on the contained RawSocket, and send
  113. /// DHCP requests when timeouts are ready.
  114. ///
  115. /// Applying the obtained network configuration is left to the
  116. /// user.
  117. ///
  118. /// A Config can be returned from any valid DHCP reply. The client
  119. /// performs no bookkeeping on configuration or their changes.
  120. pub fn poll<DeviceT>(&mut self,
  121. iface: &mut Interface<DeviceT>, sockets: &mut SocketSet,
  122. now: Instant
  123. ) -> Result<Option<Config>>
  124. where
  125. DeviceT: for<'d> Device<'d>,
  126. {
  127. let checksum_caps = iface.device().capabilities().checksum;
  128. let mut raw_socket = sockets.get::<RawSocket>(self.raw_handle);
  129. // Process incoming
  130. let config = {
  131. match raw_socket.recv()
  132. .and_then(|packet| parse_udp(packet, &checksum_caps)) {
  133. Ok((IpEndpoint {
  134. addr: IpAddress::Ipv4(src_ip),
  135. port: UDP_SERVER_PORT,
  136. }, IpEndpoint {
  137. addr: _,
  138. port: UDP_CLIENT_PORT,
  139. }, payload)) =>
  140. self.ingress(iface, now, payload, &src_ip),
  141. Ok(_) =>
  142. return Err(Error::Unrecognized),
  143. Err(Error::Exhausted) =>
  144. None,
  145. Err(e) =>
  146. return Err(e),
  147. }
  148. };
  149. if config.is_some() {
  150. // Return a new config immediately so that addresses can
  151. // be configured that are required by egress().
  152. Ok(config)
  153. } else {
  154. // Send requests
  155. if raw_socket.can_send() && now >= self.next_egress {
  156. self.egress(iface, &mut *raw_socket, &checksum_caps, now)
  157. } else {
  158. Ok(None)
  159. }
  160. }
  161. }
  162. fn ingress<DeviceT>(&mut self,
  163. iface: &mut Interface<DeviceT>, now: Instant,
  164. data: &[u8], src_ip: &Ipv4Address
  165. ) -> Option<Config>
  166. where
  167. DeviceT: for<'d> Device<'d>,
  168. {
  169. let dhcp_packet = match DhcpPacket::new_checked(data) {
  170. Ok(dhcp_packet) => dhcp_packet,
  171. Err(e) => {
  172. net_debug!("DHCP invalid pkt from {}: {:?}", src_ip, e);
  173. return None;
  174. }
  175. };
  176. let dhcp_repr = match DhcpRepr::parse(&dhcp_packet) {
  177. Ok(dhcp_repr) => dhcp_repr,
  178. Err(e) => {
  179. net_debug!("DHCP error parsing pkt from {}: {:?}", src_ip, e);
  180. return None;
  181. }
  182. };
  183. let mac = iface.ethernet_addr();
  184. if dhcp_repr.client_hardware_address != mac { return None }
  185. if dhcp_repr.transaction_id != self.transaction_id { return None }
  186. let server_identifier = match dhcp_repr.server_identifier {
  187. Some(server_identifier) => server_identifier,
  188. None => return None,
  189. };
  190. net_debug!("DHCP recv {:?} from {} ({})", dhcp_repr.message_type, src_ip, server_identifier);
  191. // once we receive the ack, we can pass the config to the user
  192. let config = if dhcp_repr.message_type == DhcpMessageType::Ack {
  193. let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_RENEW_INTERVAL * 2);
  194. self.lease_expiration = Some(now + Duration::from_secs(lease_duration.into()));
  195. // RFC 2131 indicates clients should renew a lease halfway through its expiration.
  196. self.next_egress = now + Duration::from_secs((lease_duration / 2).into());
  197. let address = dhcp_repr.subnet_mask
  198. .and_then(|mask| IpAddress::Ipv4(mask).to_prefix_len())
  199. .map(|prefix_len| Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len));
  200. let router = dhcp_repr.router;
  201. let dns_servers = dhcp_repr.dns_servers
  202. .unwrap_or([None; 3]);
  203. Some(Config { address, router, dns_servers })
  204. } else {
  205. None
  206. };
  207. match self.state {
  208. ClientState::Discovering
  209. if dhcp_repr.message_type == DhcpMessageType::Offer =>
  210. {
  211. self.next_egress = now;
  212. let r_state = RequestState {
  213. retry: 0,
  214. endpoint_ip: *src_ip,
  215. server_identifier,
  216. requested_ip: dhcp_repr.your_ip // use the offered ip
  217. };
  218. Some(ClientState::Requesting(r_state))
  219. }
  220. ClientState::Requesting(ref r_state)
  221. if dhcp_repr.message_type == DhcpMessageType::Ack &&
  222. server_identifier == r_state.server_identifier =>
  223. {
  224. let p_state = RenewState {
  225. endpoint_ip: *src_ip,
  226. server_identifier,
  227. };
  228. Some(ClientState::Renew(p_state))
  229. }
  230. _ => None
  231. }.map(|new_state| self.state = new_state);
  232. config
  233. }
  234. fn egress<DeviceT: for<'d> Device<'d>>(&mut self, iface: &mut Interface<DeviceT>, raw_socket: &mut RawSocket, checksum_caps: &ChecksumCapabilities, now: Instant) -> Result<Option<Config>> {
  235. // Reset after maximum amount of retries
  236. let retries_exceeded = match self.state {
  237. ClientState::Requesting(ref mut r_state) if r_state.retry >= REQUEST_RETRIES => {
  238. net_debug!("DHCP request retries exceeded, restarting discovery");
  239. true
  240. }
  241. _ => false
  242. };
  243. let lease_expired = self.lease_expiration.map_or(false, |expiration| now >= expiration);
  244. if lease_expired || retries_exceeded {
  245. self.reset(now);
  246. // Return a config now so that user code assigns the
  247. // 0.0.0.0/0 address, which will be used sending a DHCP
  248. // discovery packet in the next call to egress().
  249. return Ok(Some(Config {
  250. address: Some(Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)),
  251. router: None,
  252. dns_servers: [None; 3],
  253. }));
  254. }
  255. // Prepare sending next packet
  256. self.transaction_id += 1;
  257. let mac = iface.ethernet_addr();
  258. let mut dhcp_repr = DhcpRepr {
  259. message_type: DhcpMessageType::Discover,
  260. transaction_id: self.transaction_id,
  261. client_hardware_address: mac,
  262. client_ip: Ipv4Address::UNSPECIFIED,
  263. your_ip: Ipv4Address::UNSPECIFIED,
  264. server_ip: Ipv4Address::UNSPECIFIED,
  265. router: None,
  266. subnet_mask: None,
  267. relay_agent_ip: Ipv4Address::UNSPECIFIED,
  268. broadcast: true,
  269. requested_ip: None,
  270. client_identifier: Some(mac),
  271. server_identifier: None,
  272. parameter_request_list: Some(PARAMETER_REQUEST_LIST),
  273. max_size: Some(raw_socket.payload_recv_capacity() as u16),
  274. lease_duration: None,
  275. dns_servers: None,
  276. };
  277. let mut send_packet = |iface, endpoint, dhcp_repr| {
  278. send_packet(iface, raw_socket, &endpoint, &dhcp_repr, checksum_caps)
  279. .map(|()| None)
  280. };
  281. match self.state {
  282. ClientState::Discovering => {
  283. self.next_egress = now + Duration::from_secs(DISCOVER_TIMEOUT);
  284. let endpoint = IpEndpoint {
  285. addr: Ipv4Address::BROADCAST.into(),
  286. port: UDP_SERVER_PORT,
  287. };
  288. net_trace!("DHCP send discover to {}: {:?}", endpoint, dhcp_repr);
  289. send_packet(iface, endpoint, dhcp_repr)
  290. }
  291. ClientState::Requesting(ref mut r_state) => {
  292. r_state.retry += 1;
  293. self.next_egress = now + Duration::from_secs(REQUEST_TIMEOUT);
  294. let endpoint = IpEndpoint {
  295. addr: Ipv4Address::BROADCAST.into(),
  296. port: UDP_SERVER_PORT,
  297. };
  298. dhcp_repr.message_type = DhcpMessageType::Request;
  299. dhcp_repr.broadcast = false;
  300. dhcp_repr.requested_ip = Some(r_state.requested_ip);
  301. dhcp_repr.server_identifier = Some(r_state.server_identifier);
  302. net_trace!("DHCP send request to {} = {:?}", endpoint, dhcp_repr);
  303. send_packet(iface, endpoint, dhcp_repr)
  304. }
  305. ClientState::Renew(ref mut p_state) => {
  306. self.next_egress = now + Duration::from_secs(DEFAULT_RENEW_INTERVAL.into());
  307. let endpoint = IpEndpoint {
  308. addr: p_state.endpoint_ip.into(),
  309. port: UDP_SERVER_PORT,
  310. };
  311. let client_ip = iface.ipv4_addr().unwrap_or(Ipv4Address::UNSPECIFIED);
  312. dhcp_repr.message_type = DhcpMessageType::Request;
  313. dhcp_repr.client_ip = client_ip;
  314. dhcp_repr.broadcast = false;
  315. net_trace!("DHCP send renew to {}: {:?}", endpoint, dhcp_repr);
  316. send_packet(iface, endpoint, dhcp_repr)
  317. }
  318. }
  319. }
  320. /// Reset state and restart discovery phase.
  321. ///
  322. /// Use this to speed up acquisition of an address in a new
  323. /// network if a link was down and it is now back up.
  324. ///
  325. /// You *must* configure a `0.0.0.0` address on your interface
  326. /// before the next call to `poll()`!
  327. pub fn reset(&mut self, now: Instant) {
  328. net_trace!("DHCP reset");
  329. self.state = ClientState::Discovering;
  330. self.next_egress = now;
  331. self.lease_expiration = None;
  332. }
  333. }
  334. fn send_packet<DeviceT: for<'d> Device<'d>>(iface: &mut Interface<DeviceT>, raw_socket: &mut RawSocket, endpoint: &IpEndpoint, dhcp_repr: &DhcpRepr, checksum_caps: &ChecksumCapabilities) -> Result<()> {
  335. let udp_repr = UdpRepr {
  336. src_port: UDP_CLIENT_PORT,
  337. dst_port: endpoint.port,
  338. };
  339. let src_addr = iface.ipv4_addr().unwrap();
  340. let dst_addr = match endpoint.addr {
  341. IpAddress::Ipv4(addr) => addr,
  342. _ => return Err(Error::Illegal),
  343. };
  344. let ipv4_repr = Ipv4Repr {
  345. src_addr,
  346. dst_addr,
  347. protocol: IpProtocol::Udp,
  348. payload_len: udp_repr.header_len() + dhcp_repr.buffer_len(),
  349. hop_limit: 64,
  350. };
  351. let mut packet = raw_socket.send(
  352. ipv4_repr.buffer_len() + udp_repr.header_len() + dhcp_repr.buffer_len()
  353. )?;
  354. {
  355. let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut packet);
  356. ipv4_repr.emit(&mut ipv4_packet, &checksum_caps);
  357. }
  358. {
  359. let mut udp_packet = UdpPacket::new_unchecked(
  360. &mut packet[ipv4_repr.buffer_len()..]
  361. );
  362. udp_repr.emit(&mut udp_packet,
  363. &src_addr.into(), &dst_addr.into(),
  364. dhcp_repr.buffer_len(),
  365. |buf| dhcp_repr.emit(&mut Dhcpv4Packet::new_unchecked(buf)).unwrap(),
  366. checksum_caps);
  367. }
  368. Ok(())
  369. }
  370. fn parse_udp<'a>(data: &'a [u8], checksum_caps: &ChecksumCapabilities) -> Result<(IpEndpoint, IpEndpoint, &'a [u8])> {
  371. let ipv4_packet = Ipv4Packet::new_checked(data)?;
  372. let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps)?;
  373. let udp_packet = UdpPacket::new_checked(ipv4_packet.payload())?;
  374. let udp_repr = UdpRepr::parse(
  375. &udp_packet,
  376. &ipv4_repr.src_addr.into(), &ipv4_repr.dst_addr.into(),
  377. checksum_caps
  378. )?;
  379. let src = IpEndpoint {
  380. addr: ipv4_repr.src_addr.into(),
  381. port: udp_repr.src_port,
  382. };
  383. let dst = IpEndpoint {
  384. addr: ipv4_repr.dst_addr.into(),
  385. port: udp_repr.dst_port,
  386. };
  387. let data = udp_packet.payload();
  388. Ok((src, dst, data))
  389. }