Skip to main content

dhcpv4_server/
main.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Context as _, Error};
6use dhcpv4::configuration;
7use dhcpv4::protocol::{CLIENT_PORT, Message, SERVER_PORT};
8use dhcpv4::server::{
9    DEFAULT_STASH_ID, DataStore, ResponseTarget, Server, ServerAction, ServerDispatcher,
10    ServerError,
11};
12use dhcpv4::stash::Stash;
13use fuchsia_async::net::UdpSocket;
14use fuchsia_async::{self as fasync};
15use fuchsia_component::server::{ServiceFs, ServiceFsDir};
16use futures::{Future, SinkExt as _, StreamExt as _, TryFutureExt as _, TryStreamExt as _};
17use log::{debug, error, info, warn};
18use net_declare::net::prefix_length_v4;
19use net_types::ethernet::Mac;
20use packet::serialize::InnerPacketBuilder;
21use packet::{NestableSerializer as _, NoOpSerializationContext, Serializer};
22use packet_formats::ipv4::Ipv4PacketBuilder;
23use packet_formats::udp::UdpPacketBuilder;
24use sockaddr::IntoSockAddr as _;
25use std::cell::RefCell;
26use std::collections::HashMap;
27use std::convert::{Infallible, TryInto as _};
28use std::net::{IpAddr, Ipv4Addr, SocketAddr};
29
30/// A buffer size in excess of the maximum allowable DHCP message size.
31const BUF_SZ: usize = 1024;
32
33enum IncomingService {
34    Server(fidl_fuchsia_net_dhcp::Server_RequestStream),
35}
36
37const DEFAULT_LEASE_DURATION_SECONDS: u32 = 24 * 60 * 60;
38
39fn default_parameters() -> configuration::ServerParameters {
40    configuration::ServerParameters {
41        server_ips: vec![],
42        lease_length: dhcpv4::configuration::LeaseLength {
43            default_seconds: DEFAULT_LEASE_DURATION_SECONDS,
44            max_seconds: DEFAULT_LEASE_DURATION_SECONDS,
45        },
46        managed_addrs: dhcpv4::configuration::ManagedAddresses {
47            mask: configuration::SubnetMask::new(prefix_length_v4!(0)),
48            pool_range_start: Ipv4Addr::UNSPECIFIED,
49            pool_range_stop: Ipv4Addr::UNSPECIFIED,
50        },
51        permitted_macs: dhcpv4::configuration::PermittedMacs(vec![]),
52        static_assignments: dhcpv4::configuration::StaticAssignments(
53            std::collections::hash_map::HashMap::new(),
54        ),
55        arp_probe: false,
56        bound_device_names: vec![],
57    }
58}
59
60/// dhcpd is the Fuchsia DHCPv4 server.
61#[derive(argh::FromArgs)]
62struct Args {
63    /// enables storage of dhcpd lease and configuration state to persistent storage
64    #[argh(switch)]
65    persistent: bool,
66}
67
68#[fuchsia::main()]
69pub async fn main() -> Result<(), Error> {
70    info!("starting");
71
72    let Args { persistent } = argh::from_env();
73    info!("persistent={}", persistent);
74    if persistent {
75        let stash = Stash::new(DEFAULT_STASH_ID).context("failed to instantiate stash")?;
76        // The server parameters and the client records must be consistent with one another in
77        // order to ensure correct server operation. The records cannot be consistent with default
78        // parameters, so if parameters fail to load from the stash, then the records should
79        // default to empty.
80        let (params, options, records) = match stash.load_parameters().await {
81            Ok(params) => {
82                let options = stash.load_options().await.unwrap_or_else(|e| {
83                    warn!("failed to load options from stash: {:?}", e);
84                    HashMap::new()
85                });
86                let records = stash.load_client_records().await.unwrap_or_else(|e| {
87                    warn!("failed to load client records from stash: {:?}", e);
88                    HashMap::new()
89                });
90                (params, options, records)
91            }
92            Err(e) => {
93                warn!("failed to load parameters from stash: {:?}", e);
94                (default_parameters(), HashMap::new(), HashMap::new())
95            }
96        };
97        let server = match Server::new_from_state(stash.clone(), params, options, records) {
98            Ok(v) => v,
99            Err(e) => {
100                warn!("failed to create server from persistent state: {}", e);
101                Server::new(Some(stash), default_parameters())
102            }
103        };
104        Ok(run(server).await?)
105    } else {
106        Ok(run(Server::<Stash>::new(None, default_parameters())).await?)
107    }
108}
109
110async fn run<DS: DataStore>(server: Server<DS>) -> Result<(), Error> {
111    let server = RefCell::new(ServerDispatcherRuntime::new(server));
112
113    let mut fs = ServiceFs::new_local();
114    let _: &mut ServiceFsDir<'_, _> = fs.dir("svc").add_fidl_service(IncomingService::Server);
115    let _: &mut ServiceFs<_> = fs
116        .take_and_serve_directory_handle()
117        .context("service fs failed to take and serve directory handle")?;
118
119    let (mut socket_sink, socket_stream) =
120        futures::channel::mpsc::channel::<ServerSocketCollection<UdpSocket>>(1);
121
122    // Attempt to enable the server on startup.
123    // NOTE(brunodalbo): Enabling the server on startup should be an explicit
124    // configuration loaded from default configs and stash. For now, just mimic
125    // existing behavior and try to enable. It'll fail if we don't have a valid
126    // configuration from stash/config.
127    match server.borrow_mut().enable() {
128        Ok(None) => unreachable!("server can't be enabled already"),
129        Ok(Some(socket_collection)) => {
130            // Sending here should never fail; we just created the stream above.
131            socket_sink.try_send(socket_collection)?;
132        }
133        Err(e @ zx::Status::INVALID_ARGS) => {
134            info!("server not configured for serving leases: {:?}", e)
135        }
136        Err(e) => warn!("could not enable server on startup: {:?}", e),
137    }
138
139    let admin_fut =
140        fs.then(futures::future::ok).try_for_each_concurrent(None, |incoming_service| async {
141            match incoming_service {
142                IncomingService::Server(stream) => {
143                    run_server(stream, &server, &default_parameters(), socket_sink.clone())
144                        .inspect_err(|e| warn!("run_server failed: {:?}", e))
145                        .await?;
146                    Ok(())
147                }
148            }
149        });
150
151    let server_fut = define_running_server_fut(&server, socket_stream);
152
153    info!("running");
154    let ((), ()) = futures::try_join!(server_fut, admin_fut)?;
155
156    Ok(())
157}
158
159trait SocketServerDispatcher: ServerDispatcher {
160    type Socket;
161
162    fn create_socket(name: &str, src: Ipv4Addr) -> std::io::Result<Self::Socket>;
163    fn dispatch_message(&mut self, msg: Message) -> Result<ServerAction, ServerError>;
164    fn create_sockets(
165        params: &configuration::ServerParameters,
166    ) -> std::io::Result<Vec<SocketWithId<Self::Socket>>>;
167}
168
169impl<DS: DataStore> SocketServerDispatcher for Server<DS> {
170    type Socket = UdpSocket;
171
172    fn create_socket(name: &str, src: Ipv4Addr) -> std::io::Result<Self::Socket> {
173        let socket = socket2::Socket::new(
174            socket2::Domain::IPV4,
175            socket2::Type::DGRAM,
176            Some(socket2::Protocol::UDP),
177        )?;
178        // Since dhcpd may listen to multiple interfaces, we must enable
179        // SO_REUSEPORT so that binding the same (address, port) pair to each
180        // interface can still succeed.
181        socket.set_reuse_port(true)?;
182        socket.bind_device(Some(name.as_bytes()))?;
183        info!("socket bound to device {}", name);
184        socket.set_broadcast(true)?;
185        socket.bind(&SocketAddr::new(IpAddr::V4(src), SERVER_PORT.into()).into())?;
186        Ok(UdpSocket::from_socket(socket.into())?)
187    }
188
189    fn dispatch_message(&mut self, msg: Message) -> Result<ServerAction, ServerError> {
190        self.dispatch(msg)
191    }
192
193    fn create_sockets(
194        params: &configuration::ServerParameters,
195    ) -> std::io::Result<Vec<SocketWithId<Self::Socket>>> {
196        let configuration::ServerParameters { bound_device_names, .. } = params;
197        bound_device_names
198            .iter()
199            .map(|name| {
200                let iface_id =
201                    fuchsia_nix::net::if_::if_nametoindex(name.as_str()).map_err(|e| {
202                        let e: std::io::Error = e.into();
203                        e
204                    })?;
205                let socket = Self::create_socket(name, Ipv4Addr::UNSPECIFIED)?;
206                Ok(SocketWithId { socket, iface_id: iface_id.into() })
207            })
208            .collect()
209    }
210}
211
212/// A wrapper around a [`ServerDispatcher`] that keeps information about the
213/// server status through a [`futures::future::AbortHandle`].
214struct ServerDispatcherRuntime<S> {
215    abort_handle: Option<futures::future::AbortHandle>,
216    server: S,
217}
218
219impl<S> std::ops::Deref for ServerDispatcherRuntime<S> {
220    type Target = S;
221
222    fn deref(&self) -> &Self::Target {
223        &self.server
224    }
225}
226
227impl<S> std::ops::DerefMut for ServerDispatcherRuntime<S> {
228    fn deref_mut(&mut self) -> &mut Self::Target {
229        &mut self.server
230    }
231}
232
233impl<S: SocketServerDispatcher> ServerDispatcherRuntime<S> {
234    /// Creates a new runtime with `server`.
235    fn new(server: S) -> Self {
236        Self { abort_handle: None, server }
237    }
238
239    /// Disables the server.
240    ///
241    /// `disable` will cancel the previous
242    /// [`futures::future::AbortRegistration`] returned by `enable`.
243    ///
244    /// If the server is already disabled, `disable` is a no-op.
245    fn disable(&mut self) {
246        if let Some(abort_handle) = self.abort_handle.take() {
247            abort_handle.abort();
248        }
249    }
250
251    /// Enables the server.
252    ///
253    /// Attempts to enable the server, returning a new
254    /// [`ServerSocketCollection`] on success. The returned collection contains
255    /// the list of sockets where the server can listen on and an abort
256    /// registration that is used to cancel the future that listen on the
257    /// sockets when [`ServerDispatcherRuntime::disable`] is called.
258    ///
259    /// Returns an error if the server couldn't be started or if the closure
260    /// fails, maintaining the server in the disabled state.
261    ///
262    /// If the server is already enabled, `enable` returns `Ok(None)`.
263    fn enable(&mut self) -> Result<Option<ServerSocketCollection<S::Socket>>, zx::Status> {
264        if self.abort_handle.is_some() {
265            // Server already running.
266            return Ok(None);
267        }
268        let params = self.server.try_validate_parameters()?;
269        // Provide the closure with an AbortRegistration and a ref to
270        // parameters.
271        let (abort_handle, abort_registration) = futures::future::AbortHandle::new_pair();
272
273        let sockets = S::create_sockets(params).map_err(|e| {
274            match e.raw_os_error() {
275                // A short-lived SoftAP interface may be, and frequently is, torn down prior to the
276                // full instantiation of its associated dhcpd component. Consequently, binding to
277                // the SoftAP interface name will fail with ENODEV. However, such a failure is
278                // normal and expected under those circumstances.
279                Some(libc::ENODEV) => {
280                    warn!("Failed to create server sockets: {}", e)
281                }
282                Some(_) | None => error!("Failed to create server sockets: {}", e),
283            };
284            zx::Status::IO
285        })?;
286        if sockets.is_empty() {
287            error!("No sockets to run server on");
288            return Err(zx::Status::INVALID_ARGS);
289        }
290        self.abort_handle = Some(abort_handle);
291        Ok(Some(ServerSocketCollection { sockets, abort_registration }))
292    }
293
294    /// Returns `true` if the server is enabled.
295    fn enabled(&self) -> bool {
296        self.abort_handle.is_some()
297    }
298
299    /// Runs the closure `f` only if the server is currently disabled.
300    ///
301    /// Returns `BAD_STATE` error otherwise.
302    fn if_disabled<R, F: FnOnce(&mut S) -> Result<R, zx::Status>>(
303        &mut self,
304        f: F,
305    ) -> Result<R, zx::Status> {
306        if self.abort_handle.is_none() { f(&mut self.server) } else { Err(zx::Status::BAD_STATE) }
307    }
308}
309
310#[derive(Debug, PartialEq)]
311struct SocketWithId<S> {
312    socket: S,
313    iface_id: u64,
314}
315
316/// Helper struct to handle buffer data from sockets.
317struct MessageHandler<'a, S: SocketServerDispatcher> {
318    server: &'a RefCell<ServerDispatcherRuntime<S>>,
319}
320
321impl<'a, S: SocketServerDispatcher> MessageHandler<'a, S> {
322    /// Creates a new `MessageHandler` for `server`.
323    fn new(server: &'a RefCell<ServerDispatcherRuntime<S>>) -> Self {
324        Self { server }
325    }
326
327    /// Handles `buf` from `sender`.
328    ///
329    /// Returns `Ok(Some(sock, msg, dst))` if `msg` must be sent to `dst`
330    /// over `sock`.
331    ///
332    /// Returns `Ok(None)` if no action is required and the handler is ready to
333    /// receive more messages.
334    ///
335    /// Returns `Err` if an unrecoverable error occurs and the server must stop
336    /// serving.
337    fn handle_from_sender(
338        &mut self,
339        buf: &[u8],
340        mut sender: std::net::SocketAddrV4,
341    ) -> Result<Option<(std::net::SocketAddrV4, Message, Option<Mac>)>, Error> {
342        let msg = match Message::from_buffer(buf) {
343            Ok(msg) => {
344                debug!("parsed message from {}: {:?}", sender, msg);
345                msg
346            }
347            Err(e) => {
348                warn!("failed to parse message from {}: {}", sender, e);
349                return Ok(None);
350            }
351        };
352
353        let typ = msg.get_dhcp_type();
354        if sender.ip().is_unspecified() {
355            info!("processing {:?} from {}", typ, msg.chaddr);
356        } else {
357            info!("processing {:?} from {}", typ, sender);
358        }
359
360        // This call should not block because the server is single-threaded.
361        let result = self.server.borrow_mut().dispatch_message(msg);
362        match result {
363            Err(e) => {
364                warn!("error processing client message: {:?}", e);
365                Ok(None)
366            }
367            Ok(ServerAction::AddressRelease(addr)) => {
368                info!("released address: {}", addr);
369                Ok(None)
370            }
371            Ok(ServerAction::AddressDecline(addr)) => {
372                info!("allocated address: {}", addr);
373                Ok(None)
374            }
375            Ok(ServerAction::SendResponse(message, dst)) => {
376                debug!("generated response: {:?}", message);
377
378                let typ = message.get_dhcp_type();
379                // Check if server returned an explicit destination ip.
380                let (addr, chaddr) = match dst {
381                    ResponseTarget::Broadcast => {
382                        info!("sending {:?} to {}", typ, Ipv4Addr::BROADCAST);
383                        (Ipv4Addr::BROADCAST, None)
384                    }
385                    ResponseTarget::Unicast(addr, None) => {
386                        info!("sending {:?} to {}", typ, addr);
387                        (addr, None)
388                    }
389                    ResponseTarget::Unicast(addr, Some(chaddr)) => {
390                        info!("sending {:?} to ip {} chaddr {}", typ, addr, chaddr);
391                        (addr, Some(chaddr))
392                    }
393                };
394                sender.set_ip(addr);
395                Ok(Some((sender, message, chaddr)))
396            }
397        }
398    }
399}
400
401async fn define_msg_handling_loop_future<DS: DataStore>(
402    sock: SocketWithId<<Server<DS> as SocketServerDispatcher>::Socket>,
403    server: &RefCell<ServerDispatcherRuntime<Server<DS>>>,
404) -> Result<Infallible, Error> {
405    let SocketWithId { socket, iface_id } = sock;
406    let mut handler = MessageHandler::new(server);
407    let mut buf = vec![0u8; BUF_SZ];
408    loop {
409        let (received, sender) =
410            socket.recv_from(&mut buf).await.context("failed to read from socket")?;
411        let sender = match sender {
412            std::net::SocketAddr::V4(sender) => sender,
413            std::net::SocketAddr::V6(sender) => {
414                return Err(anyhow::anyhow!(
415                    "IPv4 socket received datagram from IPv6 sender: {}",
416                    sender
417                ));
418            }
419        };
420        if let Some((dst, msg, chaddr)) = handler
421            .handle_from_sender(&buf[..received], sender)
422            .context("failed to handle buffer")?
423        {
424            let chaddr = if let Some(chaddr) = chaddr {
425                chaddr
426            } else {
427                let response = msg.serialize();
428                let sent = socket
429                    .send_to(&response, SocketAddr::V4(dst))
430                    .await
431                    .context("unable to send response")?;
432                if sent != response.len() {
433                    return Err(anyhow::anyhow!(
434                        "sent {} bytes for a message of size {}",
435                        sent,
436                        response.len()
437                    ));
438                }
439                info!("response sent to {}: {} bytes", dst, sent);
440                continue;
441            };
442            // Packet sockets are necessary here because the on-device Netstack does
443            // not yet have a relation linking the `chaddr` MAC address to an IP address.
444            let dst_ip: net_types::ip::Ipv4Addr = (*dst.ip()).into();
445            // Prefer the ServerIdentifier if set; otherwise use the socket's local address.
446            let src_ip: net_types::ip::Ipv4Addr = msg
447                .options
448                .iter()
449                .find_map(|opt| match opt {
450                    dhcpv4::protocol::DhcpOption::ServerIdentifier(addr) => Some(addr.clone()),
451                    _ => None,
452                })
453                // TODO(https://fxbug.dev/42056628): Eliminate this panic.
454                .expect("expect server identifier is always present")
455                .into();
456            let response = msg.serialize();
457            let udp_builder = UdpPacketBuilder::new(src_ip, dst_ip, Some(SERVER_PORT), CLIENT_PORT);
458            // Use the default TTL shared across UNIX systems.
459            const TTL: u8 = 64;
460            let ipv4_builder = Ipv4PacketBuilder::new(
461                src_ip,
462                dst_ip,
463                TTL,
464                packet_formats::ip::Ipv4Proto::Proto(packet_formats::ip::IpProto::Udp),
465            );
466            let packet = response
467                .into_serializer()
468                .wrap_in(udp_builder)
469                .wrap_in(ipv4_builder)
470                .serialize_vec_outer(&mut NoOpSerializationContext)
471                .expect("serialize packet failed")
472                .unwrap_b();
473
474            let mut sll_addr = [0; 8];
475            (&mut sll_addr[..chaddr.bytes().len()]).copy_from_slice(&chaddr.bytes());
476            let sockaddr_ll = libc::sockaddr_ll {
477                sll_family: libc::AF_PACKET.try_into().expect("convert sll_family failed"),
478                sll_ifindex: iface_id.try_into().expect("convert sll_ifindex failed"),
479                // Network order is big endian.
480                sll_protocol: u16::try_from(libc::ETH_P_IP)
481                    .expect("convert ETH_P_IP failed")
482                    .to_be(),
483                sll_halen: chaddr.bytes().len().try_into().expect("convert chaddr size failed"),
484                sll_addr: sll_addr,
485                sll_hatype: 0,
486                sll_pkttype: 0,
487            };
488
489            // Create the packet socket without binding to a protocol so that
490            // the packet socket is not registered for RX. This desirable since
491            // the socket is only used to send packets and receiving packets
492            // on a packet socket is not free.
493            let socket = socket2::Socket::new(
494                socket2::Domain::PACKET,
495                socket2::Type::DGRAM,
496                None, /* protocol */
497            )
498            .context("create packet socket failed")?;
499
500            let socket = fasync::net::DatagramSocket::new_from_socket(socket)
501                .context("failed to wrap into fuchsia-async DatagramSocket")?;
502
503            let sent = socket
504                .send_to(packet.as_ref(), sockaddr_ll.into_sockaddr())
505                .await
506                .context("unable to send response")?;
507            if sent != packet.as_ref().len() {
508                return Err(anyhow::anyhow!(
509                    "sent {} bytes for a packet of size {}",
510                    sent,
511                    packet.as_ref().len()
512                ));
513            }
514            info!("response sent to {}: {} bytes", dst, sent);
515        }
516    }
517}
518
519fn define_running_server_fut<'a, S, DS>(
520    server: &'a RefCell<ServerDispatcherRuntime<Server<DS>>>,
521    socket_stream: S,
522) -> impl Future<Output = Result<(), Error>> + 'a
523where
524    S: futures::Stream<
525            Item = ServerSocketCollection<<Server<Stash> as SocketServerDispatcher>::Socket>,
526        > + 'static,
527    DS: DataStore,
528{
529    socket_stream.map(Ok).try_for_each(move |socket_collection| async move {
530        let ServerSocketCollection { sockets, abort_registration } = socket_collection;
531        let msg_loops = futures::future::try_join_all(
532            sockets.into_iter().map(|sock| define_msg_handling_loop_future(sock, server)),
533        );
534
535        info!("Server starting");
536        match futures::future::Abortable::new(msg_loops, abort_registration).await {
537            Ok(Ok(v)) => {
538                let _: Vec<Infallible> = v;
539                Err(anyhow::anyhow!("Server futures finished unexpectedly"))
540            }
541            Ok(Err(error)) => {
542                // There was an error handling the server sockets. Disable the
543                // server.
544                error!("Server encountered an error: {:?}. Stopping server.", error);
545                server.borrow_mut().disable();
546                Ok(())
547            }
548            Err(futures::future::Aborted {}) => {
549                info!("Server stopped");
550                Ok(())
551            }
552        }
553    })
554}
555
556struct ServerSocketCollection<S> {
557    sockets: Vec<SocketWithId<S>>,
558    abort_registration: futures::future::AbortRegistration,
559}
560
561async fn run_server<S, C>(
562    stream: fidl_fuchsia_net_dhcp::Server_RequestStream,
563    server: &RefCell<ServerDispatcherRuntime<S>>,
564    default_params: &dhcpv4::configuration::ServerParameters,
565    socket_sink: C,
566) -> Result<(), fidl::Error>
567where
568    S: SocketServerDispatcher,
569    C: futures::sink::Sink<ServerSocketCollection<S::Socket>> + Unpin,
570    C::Error: std::fmt::Debug,
571{
572    stream
573        .try_fold(socket_sink, |mut socket_sink, request| async move {
574            match request {
575                fidl_fuchsia_net_dhcp::Server_Request::StartServing { responder } => {
576                    responder.send(
577                        match server.borrow_mut().enable() {
578                            Ok(Some(socket_collection)) => {
579                                socket_sink.send(socket_collection).await.map_err(|e| {
580                                    error!("Failed to send sockets to sink: {:?}", e);
581                                    // Disable the server again to keep a consistent state.
582                                    server.borrow_mut().disable();
583                                    zx::Status::INTERNAL
584                                })
585                            }
586                            Ok(None) => {
587                                info!("Server already running");
588                                Ok(())
589                            }
590                            Err(status) => Err(status),
591                        }
592                        .map_err(zx::Status::into_raw),
593                    )
594                }
595                fidl_fuchsia_net_dhcp::Server_Request::StopServing { responder } => {
596                    server.borrow_mut().disable();
597                    responder.send()
598                }
599                fidl_fuchsia_net_dhcp::Server_Request::IsServing { responder } => {
600                    responder.send(server.borrow().enabled())
601                }
602                fidl_fuchsia_net_dhcp::Server_Request::GetOption { code: c, responder: r } => r
603                    .send(
604                        server.borrow().dispatch_get_option(c).as_ref().map_err(|e| e.into_raw()),
605                    ),
606                fidl_fuchsia_net_dhcp::Server_Request::GetParameter { name: n, responder: r } => {
607                    let response = server.borrow().dispatch_get_parameter(n);
608                    r.send(response.as_ref().map_err(|e| e.into_raw()))
609                }
610                fidl_fuchsia_net_dhcp::Server_Request::SetOption { value: v, responder: r } => {
611                    r.send(server.borrow_mut().dispatch_set_option(v).map_err(|e| e.into_raw()))
612                }
613                fidl_fuchsia_net_dhcp::Server_Request::SetParameter { value: v, responder: r } => r
614                    .send(
615                        server
616                            .borrow_mut()
617                            .if_disabled(|s| s.dispatch_set_parameter(v))
618                            .map_err(|e| e.into_raw()),
619                    ),
620                fidl_fuchsia_net_dhcp::Server_Request::ListOptions { responder: r } => r.send(
621                    server.borrow().dispatch_list_options().as_deref().map_err(|e| e.into_raw()),
622                ),
623                fidl_fuchsia_net_dhcp::Server_Request::ListParameters { responder: r } => r.send(
624                    server.borrow().dispatch_list_parameters().as_deref().map_err(|e| e.into_raw()),
625                ),
626                fidl_fuchsia_net_dhcp::Server_Request::ResetOptions { responder: r } => {
627                    r.send(server.borrow_mut().dispatch_reset_options().map_err(|e| e.into_raw()))
628                }
629                fidl_fuchsia_net_dhcp::Server_Request::ResetParameters { responder: r } => r.send(
630                    server
631                        .borrow_mut()
632                        .if_disabled(|s| s.dispatch_reset_parameters(&default_params))
633                        .map_err(|e| e.into_raw()),
634                ),
635                fidl_fuchsia_net_dhcp::Server_Request::ClearLeases { responder: r } => r.send(
636                    server.borrow_mut().dispatch_clear_leases().map_err(zx::Status::into_raw),
637                ),
638            }
639            .map(|()| socket_sink)
640        })
641        .await
642        // Discard the socket sink.
643        .map(|_socket_sink: C| ())
644}
645
646#[cfg(test)]
647mod tests {
648    use super::*;
649    use dhcpv4::configuration::ServerParameters;
650    use futures::FutureExt;
651    use futures::sink::drain;
652    use net_declare::{fidl_ip_v4, std_ip_v4};
653
654    #[derive(Debug, Eq, PartialEq)]
655    struct CannedSocket {
656        name: String,
657        src: Ipv4Addr,
658    }
659
660    struct CannedDispatcher {
661        params: Option<ServerParameters>,
662        mock_leases: u32,
663    }
664
665    impl CannedDispatcher {
666        fn new() -> Self {
667            Self { params: None, mock_leases: 0 }
668        }
669    }
670
671    impl SocketServerDispatcher for CannedDispatcher {
672        type Socket = CannedSocket;
673
674        fn create_socket(name: &str, src: Ipv4Addr) -> std::io::Result<Self::Socket> {
675            let name = name.to_string();
676            Ok(CannedSocket { name, src })
677        }
678
679        fn dispatch_message(&mut self, mut msg: Message) -> Result<ServerAction, ServerError> {
680            msg.op = dhcpv4::protocol::OpCode::BOOTREPLY;
681            Ok(ServerAction::SendResponse(msg, ResponseTarget::Broadcast))
682        }
683
684        fn create_sockets(
685            params: &configuration::ServerParameters,
686        ) -> std::io::Result<Vec<SocketWithId<Self::Socket>>> {
687            let configuration::ServerParameters { bound_device_names, .. } = params;
688            bound_device_names
689                .iter()
690                .map(String::as_str)
691                .enumerate()
692                .map(|(iface_id, name)| {
693                    let iface_id = std::convert::TryInto::try_into(iface_id).map_err(|e| {
694                        std::io::Error::new(
695                            std::io::ErrorKind::InvalidInput,
696                            format!("interface id {} out of range: {}", iface_id, e),
697                        )
698                    })?;
699                    let socket = Self::create_socket(name, Ipv4Addr::UNSPECIFIED)?;
700                    Ok(SocketWithId { socket, iface_id })
701                })
702                .collect()
703        }
704    }
705
706    impl ServerDispatcher for CannedDispatcher {
707        fn try_validate_parameters(&self) -> Result<&ServerParameters, zx::Status> {
708            self.params.as_ref().ok_or(zx::Status::INVALID_ARGS)
709        }
710
711        fn dispatch_get_option(
712            &self,
713            _code: fidl_fuchsia_net_dhcp::OptionCode,
714        ) -> Result<fidl_fuchsia_net_dhcp::Option_, zx::Status> {
715            Ok(fidl_fuchsia_net_dhcp::Option_::SubnetMask(fidl_ip_v4!("0.0.0.0")))
716        }
717        fn dispatch_get_parameter(
718            &self,
719            _name: fidl_fuchsia_net_dhcp::ParameterName,
720        ) -> Result<fidl_fuchsia_net_dhcp::Parameter, zx::Status> {
721            Ok(fidl_fuchsia_net_dhcp::Parameter::Lease(fidl_fuchsia_net_dhcp::LeaseLength {
722                default: None,
723                max: None,
724                ..Default::default()
725            }))
726        }
727        fn dispatch_set_option(
728            &mut self,
729            _value: fidl_fuchsia_net_dhcp::Option_,
730        ) -> Result<(), zx::Status> {
731            Ok(())
732        }
733        fn dispatch_set_parameter(
734            &mut self,
735            _value: fidl_fuchsia_net_dhcp::Parameter,
736        ) -> Result<(), zx::Status> {
737            Ok(())
738        }
739        fn dispatch_list_options(&self) -> Result<Vec<fidl_fuchsia_net_dhcp::Option_>, zx::Status> {
740            Ok(vec![])
741        }
742        fn dispatch_list_parameters(
743            &self,
744        ) -> Result<Vec<fidl_fuchsia_net_dhcp::Parameter>, zx::Status> {
745            Ok(vec![])
746        }
747        fn dispatch_reset_options(&mut self) -> Result<(), zx::Status> {
748            Ok(())
749        }
750        fn dispatch_reset_parameters(
751            &mut self,
752            _defaults: &dhcpv4::configuration::ServerParameters,
753        ) -> Result<(), zx::Status> {
754            Ok(())
755        }
756        fn dispatch_clear_leases(&mut self) -> Result<(), zx::Status> {
757            self.mock_leases = 0;
758            Ok(())
759        }
760    }
761
762    const DEFAULT_DEVICE_NAME: &str = "foo13";
763
764    fn default_params() -> dhcpv4::configuration::ServerParameters {
765        dhcpv4::configuration::ServerParameters {
766            server_ips: vec![std_ip_v4!("192.168.0.1")],
767            lease_length: dhcpv4::configuration::LeaseLength {
768                default_seconds: 86400,
769                max_seconds: 86400,
770            },
771            managed_addrs: dhcpv4::configuration::ManagedAddresses {
772                mask: dhcpv4::configuration::SubnetMask::new(prefix_length_v4!(25)),
773                pool_range_start: std_ip_v4!("192.168.0.0"),
774                pool_range_stop: std_ip_v4!("192.168.0.0"),
775            },
776            permitted_macs: dhcpv4::configuration::PermittedMacs(vec![]),
777            static_assignments: dhcpv4::configuration::StaticAssignments(HashMap::new()),
778            arp_probe: false,
779            bound_device_names: vec![DEFAULT_DEVICE_NAME.to_string()],
780        }
781    }
782
783    async fn run_with_server<T, F, Fut>(f: F) -> T
784    where
785        F: Fn(fidl_fuchsia_net_dhcp::Server_Proxy) -> Fut,
786        Fut: Future<Output = T>,
787    {
788        let (proxy, stream) =
789            fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcp::Server_Marker>();
790        let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
791
792        let defaults = default_params();
793        futures::select! {
794            res = f(proxy).fuse() => res,
795            res = run_server(stream, &server, &defaults, drain()).fuse() => {
796                unreachable!("server finished before request: {:?}", res)
797            },
798        }
799    }
800
801    #[fuchsia::test(logging = false)]
802    async fn get_option_with_subnet_mask_returns_subnet_mask() {
803        run_with_server(|proxy| async move {
804            assert_eq!(
805                proxy
806                    .get_option(fidl_fuchsia_net_dhcp::OptionCode::SubnetMask)
807                    .await
808                    .expect("get_option failed"),
809                Ok(fidl_fuchsia_net_dhcp::Option_::SubnetMask(fidl_ip_v4!("0.0.0.0")))
810            );
811        })
812        .await
813    }
814
815    #[fuchsia::test(allow_stalls = false)]
816    async fn get_parameter_with_lease_length_returns_lease_length() {
817        run_with_server(|proxy| async move {
818            assert_eq!(
819                proxy
820                    .get_parameter(fidl_fuchsia_net_dhcp::ParameterName::LeaseLength)
821                    .await
822                    .expect("get_parameter failed"),
823                Ok(fidl_fuchsia_net_dhcp::Parameter::Lease(fidl_fuchsia_net_dhcp::LeaseLength {
824                    default: None,
825                    max: None,
826                    ..Default::default()
827                }))
828            );
829        })
830        .await
831    }
832
833    #[fuchsia::test(logging = false)]
834    async fn set_option_with_subnet_mask_returns_unit() {
835        run_with_server(|proxy| async move {
836            assert_eq!(
837                proxy
838                    .set_option(&fidl_fuchsia_net_dhcp::Option_::SubnetMask(fidl_ip_v4!("0.0.0.0")))
839                    .await
840                    .expect("set_option failed"),
841                Ok(())
842            );
843        })
844        .await
845    }
846
847    #[fuchsia::test(logging = false)]
848    async fn set_parameter_with_lease_length_returns_unit() {
849        run_with_server(|proxy| async move {
850            assert_eq!(
851                proxy
852                    .set_parameter(&fidl_fuchsia_net_dhcp::Parameter::Lease(
853                        fidl_fuchsia_net_dhcp::LeaseLength {
854                            default: None,
855                            max: None,
856                            ..Default::default()
857                        },
858                    ))
859                    .await
860                    .expect("set_parameter failed"),
861                Ok(())
862            );
863        })
864        .await
865    }
866
867    #[fuchsia::test(logging = false)]
868    async fn list_options_returns_empty_vec() {
869        run_with_server(|proxy| async move {
870            assert_eq!(proxy.list_options().await.expect("list_options failed"), Ok(Vec::new()));
871        })
872        .await
873    }
874
875    #[fuchsia::test(logging = false)]
876    async fn list_parameters_returns_empty_vec() {
877        run_with_server(|proxy| async move {
878            assert_eq!(
879                proxy.list_parameters().await.expect("list_parameters failed"),
880                Ok(Vec::new())
881            );
882        })
883        .await
884    }
885
886    #[fuchsia::test(logging = false)]
887    async fn reset_options_returns_unit() {
888        run_with_server(|proxy| async move {
889            assert_eq!(proxy.reset_options().await.expect("reset_options failed"), Ok(()));
890        })
891        .await
892    }
893
894    #[fuchsia::test(logging = false)]
895    async fn reset_parameters_returns_unit() {
896        run_with_server(|proxy| async move {
897            assert_eq!(proxy.reset_parameters().await.expect("reset_parameters failed"), Ok(()));
898        })
899        .await
900    }
901
902    #[fuchsia::test(logging = false)]
903    async fn clear_leases_returns_unit() {
904        run_with_server(|proxy| async move {
905            assert_eq!(proxy.clear_leases().await.expect("clear_leases failed"), Ok(()));
906        })
907        .await
908    }
909
910    #[fuchsia::test(logging = false)]
911    async fn start_stop_server() {
912        let (proxy, stream) =
913            fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcp::Server_Marker>();
914        let (socket_sink, mut socket_stream) =
915            futures::channel::mpsc::channel::<ServerSocketCollection<CannedSocket>>(1);
916
917        let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
918        // Set default parameters to the server so we can create sockets.
919        server.borrow_mut().params = Some(default_params());
920
921        let defaults = default_params();
922
923        // Set mock leases that should not change when the server is disabled.
924        server.borrow_mut().mock_leases = 1;
925
926        let test_fut = async {
927            for () in std::iter::repeat(()).take(3) {
928                assert!(
929                    !proxy.is_serving().await.expect("query server status request"),
930                    "server should not be serving"
931                );
932
933                proxy
934                    .start_serving()
935                    .await
936                    .expect("start_serving failed")
937                    .map_err(zx::Status::from_raw)
938                    .expect("start_serving returned an error");
939
940                let ServerSocketCollection { sockets, abort_registration } =
941                    socket_stream.next().await.expect("Socket stream ended unexpectedly");
942
943                // Assert that the sockets that would be created are correct.
944                assert_eq!(
945                    sockets,
946                    vec![SocketWithId {
947                        socket: CannedSocket {
948                            name: DEFAULT_DEVICE_NAME.to_string(),
949                            src: Ipv4Addr::UNSPECIFIED
950                        },
951                        iface_id: 0
952                    }]
953                );
954
955                // Create a dummy future that should be aborted when we disable the
956                // server.
957                let dummy_fut = futures::future::Abortable::new(
958                    futures::future::pending::<()>(),
959                    abort_registration,
960                );
961
962                assert!(
963                    proxy.is_serving().await.expect("query server status request"),
964                    "server should be serving"
965                );
966
967                proxy.stop_serving().await.expect("stop_serving failed");
968
969                // Dummy future was aborted.
970                assert_eq!(dummy_fut.await, Err(futures::future::Aborted {}));
971                // Leases were not cleared.
972                assert_eq!(server.borrow().mock_leases, 1);
973
974                assert!(
975                    !proxy.is_serving().await.expect("query server status request"),
976                    "server should no longer be serving"
977                );
978            }
979        };
980
981        futures::select! {
982            res = test_fut.fuse() => res,
983            res = run_server(stream, &server, &defaults, socket_sink).fuse() => {
984                unreachable!("server finished before request: {:?}", res)
985            },
986        };
987    }
988
989    #[fuchsia::test(logging = false)]
990    async fn start_server_fails_on_bad_params() {
991        let (proxy, stream) =
992            fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcp::Server_Marker>();
993        let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
994
995        let defaults = default_params();
996        let res = futures::select! {
997            res = proxy.start_serving().fuse() => res.expect("start_serving failed"),
998            res = run_server(stream, &server, &defaults, drain()).fuse() => {
999                unreachable!("server finished before request: {:?}", res)
1000            },
1001        }
1002        .map_err(zx::Status::from_raw);
1003
1004        // Must have failed to start the server.
1005        assert_eq!(res, Err(zx::Status::INVALID_ARGS));
1006        // No abort handler must've been set.
1007        assert!(server.borrow().abort_handle.is_none());
1008    }
1009
1010    #[fuchsia::test(logging = false)]
1011    async fn start_server_fails_on_missing_interface_names() {
1012        let (proxy, stream) =
1013            fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcp::Server_Marker>();
1014        let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
1015
1016        let defaults = dhcpv4::configuration::ServerParameters {
1017            bound_device_names: Vec::new(),
1018            ..default_params()
1019        };
1020        server.borrow_mut().params = Some(defaults.clone());
1021
1022        let res = futures::select! {
1023            res = proxy.start_serving().fuse() => res.expect("start_serving failed"),
1024            res = run_server(stream, &server, &defaults, drain()).fuse() => {
1025                unreachable!("server finished before request: {:?}", res)
1026            },
1027        }
1028        .map_err(zx::Status::from_raw);
1029
1030        // Must have failed to start the server.
1031        assert_eq!(res, Err(zx::Status::INVALID_ARGS));
1032        // No abort handler must've been set.
1033        assert!(server.borrow().abort_handle.is_none());
1034    }
1035
1036    #[fuchsia::test(logging = false)]
1037    async fn disallow_change_parameters_if_enabled() {
1038        let (proxy, stream) =
1039            fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcp::Server_Marker>();
1040
1041        let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
1042        // Set default parameters to the server so we can create sockets.
1043        server.borrow_mut().params = Some(default_params());
1044
1045        let defaults = default_params();
1046
1047        let test_fut = async {
1048            proxy
1049                .start_serving()
1050                .await
1051                .expect("start_serving failed")
1052                .map_err(zx::Status::from_raw)
1053                .expect("start_serving returned an error");
1054
1055            // SetParameter disallowed when the server is enabled.
1056            assert_eq!(
1057                proxy
1058                    .set_parameter(&fidl_fuchsia_net_dhcp::Parameter::Lease(
1059                        fidl_fuchsia_net_dhcp::LeaseLength {
1060                            default: None,
1061                            max: None,
1062                            ..Default::default()
1063                        },
1064                    ))
1065                    .await
1066                    .expect("set_parameter FIDL failure")
1067                    .map_err(zx::Status::from_raw),
1068                Err(zx::Status::BAD_STATE)
1069            );
1070
1071            // ResetParameters disallowed when the server is enabled.
1072            assert_eq!(
1073                proxy
1074                    .reset_parameters()
1075                    .await
1076                    .expect("reset_parameters FIDL failure")
1077                    .map_err(zx::Status::from_raw),
1078                Err(zx::Status::BAD_STATE)
1079            );
1080        };
1081
1082        futures::select! {
1083            res = test_fut.fuse() => res,
1084            res = run_server(stream, &server, &defaults, drain()).fuse() => {
1085                unreachable!("server finished before request: {:?}", res)
1086            },
1087        };
1088    }
1089
1090    /// Test that a malformed message does not cause MessageHandler to return an
1091    /// error.
1092    #[test]
1093    fn handle_failed_parse() {
1094        let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
1095        let mut handler = MessageHandler::new(&server);
1096        assert_matches::assert_matches!(
1097            handler.handle_from_sender(
1098                &[0xFF, 0x00, 0xBA, 0x03],
1099                std::net::SocketAddrV4::new(Ipv4Addr::UNSPECIFIED.into(), 0),
1100            ),
1101            Ok(None)
1102        );
1103    }
1104}