1use 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::Serializer;
21use packet::serialize::InnerPacketBuilder;
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
30const 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#[derive(argh::FromArgs)]
62struct Args {
63 #[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 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 match server.borrow_mut().enable() {
128 Ok(None) => unreachable!("server can't be enabled already"),
129 Ok(Some(socket_collection)) => {
130 let () = 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 let () = socket.set_reuse_port(true)?;
182 let () = socket.bind_device(Some(name.as_bytes()))?;
183 info!("socket bound to device {}", name);
184 let () = socket.set_broadcast(true)?;
185 let () = 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
212struct 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 fn new(server: S) -> Self {
236 Self { abort_handle: None, server }
237 }
238
239 fn disable(&mut self) {
246 if let Some(abort_handle) = self.abort_handle.take() {
247 let () = abort_handle.abort();
248 }
249 }
250
251 fn enable(&mut self) -> Result<Option<ServerSocketCollection<S::Socket>>, zx::Status> {
264 if self.abort_handle.is_some() {
265 return Ok(None);
267 }
268 let params = self.server.try_validate_parameters()?;
269 let (abort_handle, abort_registration) = futures::future::AbortHandle::new_pair();
272
273 let sockets = S::create_sockets(params).map_err(|e| {
274 let () = match e.raw_os_error() {
275 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 fn enabled(&self) -> bool {
296 self.abort_handle.is_some()
297 }
298
299 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
316struct MessageHandler<'a, S: SocketServerDispatcher> {
318 server: &'a RefCell<ServerDispatcherRuntime<S>>,
319}
320
321impl<'a, S: SocketServerDispatcher> MessageHandler<'a, S> {
322 fn new(server: &'a RefCell<ServerDispatcherRuntime<S>>) -> Self {
324 Self { server }
325 }
326
327 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 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 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 let dst_ip: net_types::ip::Ipv4Addr = (*dst.ip()).into();
445 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 .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 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()
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 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 let socket = socket2::Socket::new(
494 socket2::Domain::PACKET,
495 socket2::Type::DGRAM,
496 None, )
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 error!("Server encountered an error: {:?}. Stopping server.", error);
545 let () = 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 let () = 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 let () = 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 .map(|_socket_sink: C| ())
644}
645
646#[cfg(test)]
647mod tests {
648 use super::*;
649 use dhcpv4::configuration::ServerParameters;
650 use fuchsia_async as fasync;
651 use futures::FutureExt;
652 use futures::sink::drain;
653 use net_declare::{fidl_ip_v4, std_ip_v4};
654
655 #[derive(Debug, Eq, PartialEq)]
656 struct CannedSocket {
657 name: String,
658 src: Ipv4Addr,
659 }
660
661 struct CannedDispatcher {
662 params: Option<ServerParameters>,
663 mock_leases: u32,
664 }
665
666 impl CannedDispatcher {
667 fn new() -> Self {
668 Self { params: None, mock_leases: 0 }
669 }
670 }
671
672 impl SocketServerDispatcher for CannedDispatcher {
673 type Socket = CannedSocket;
674
675 fn create_socket(name: &str, src: Ipv4Addr) -> std::io::Result<Self::Socket> {
676 let name = name.to_string();
677 Ok(CannedSocket { name, src })
678 }
679
680 fn dispatch_message(&mut self, mut msg: Message) -> Result<ServerAction, ServerError> {
681 msg.op = dhcpv4::protocol::OpCode::BOOTREPLY;
682 Ok(ServerAction::SendResponse(msg, ResponseTarget::Broadcast))
683 }
684
685 fn create_sockets(
686 params: &configuration::ServerParameters,
687 ) -> std::io::Result<Vec<SocketWithId<Self::Socket>>> {
688 let configuration::ServerParameters { bound_device_names, .. } = params;
689 bound_device_names
690 .iter()
691 .map(String::as_str)
692 .enumerate()
693 .map(|(iface_id, name)| {
694 let iface_id = std::convert::TryInto::try_into(iface_id).map_err(|e| {
695 std::io::Error::new(
696 std::io::ErrorKind::InvalidInput,
697 format!("interface id {} out of range: {}", iface_id, e),
698 )
699 })?;
700 let socket = Self::create_socket(name, Ipv4Addr::UNSPECIFIED)?;
701 Ok(SocketWithId { socket, iface_id })
702 })
703 .collect()
704 }
705 }
706
707 impl ServerDispatcher for CannedDispatcher {
708 fn try_validate_parameters(&self) -> Result<&ServerParameters, zx::Status> {
709 self.params.as_ref().ok_or(zx::Status::INVALID_ARGS)
710 }
711
712 fn dispatch_get_option(
713 &self,
714 _code: fidl_fuchsia_net_dhcp::OptionCode,
715 ) -> Result<fidl_fuchsia_net_dhcp::Option_, zx::Status> {
716 Ok(fidl_fuchsia_net_dhcp::Option_::SubnetMask(fidl_ip_v4!("0.0.0.0")))
717 }
718 fn dispatch_get_parameter(
719 &self,
720 _name: fidl_fuchsia_net_dhcp::ParameterName,
721 ) -> Result<fidl_fuchsia_net_dhcp::Parameter, zx::Status> {
722 Ok(fidl_fuchsia_net_dhcp::Parameter::Lease(fidl_fuchsia_net_dhcp::LeaseLength {
723 default: None,
724 max: None,
725 ..Default::default()
726 }))
727 }
728 fn dispatch_set_option(
729 &mut self,
730 _value: fidl_fuchsia_net_dhcp::Option_,
731 ) -> Result<(), zx::Status> {
732 Ok(())
733 }
734 fn dispatch_set_parameter(
735 &mut self,
736 _value: fidl_fuchsia_net_dhcp::Parameter,
737 ) -> Result<(), zx::Status> {
738 Ok(())
739 }
740 fn dispatch_list_options(&self) -> Result<Vec<fidl_fuchsia_net_dhcp::Option_>, zx::Status> {
741 Ok(vec![])
742 }
743 fn dispatch_list_parameters(
744 &self,
745 ) -> Result<Vec<fidl_fuchsia_net_dhcp::Parameter>, zx::Status> {
746 Ok(vec![])
747 }
748 fn dispatch_reset_options(&mut self) -> Result<(), zx::Status> {
749 Ok(())
750 }
751 fn dispatch_reset_parameters(
752 &mut self,
753 _defaults: &dhcpv4::configuration::ServerParameters,
754 ) -> Result<(), zx::Status> {
755 Ok(())
756 }
757 fn dispatch_clear_leases(&mut self) -> Result<(), zx::Status> {
758 self.mock_leases = 0;
759 Ok(())
760 }
761 }
762
763 const DEFAULT_DEVICE_NAME: &str = "foo13";
764
765 fn default_params() -> dhcpv4::configuration::ServerParameters {
766 dhcpv4::configuration::ServerParameters {
767 server_ips: vec![std_ip_v4!("192.168.0.1")],
768 lease_length: dhcpv4::configuration::LeaseLength {
769 default_seconds: 86400,
770 max_seconds: 86400,
771 },
772 managed_addrs: dhcpv4::configuration::ManagedAddresses {
773 mask: dhcpv4::configuration::SubnetMask::new(prefix_length_v4!(25)),
774 pool_range_start: std_ip_v4!("192.168.0.0"),
775 pool_range_stop: std_ip_v4!("192.168.0.0"),
776 },
777 permitted_macs: dhcpv4::configuration::PermittedMacs(vec![]),
778 static_assignments: dhcpv4::configuration::StaticAssignments(HashMap::new()),
779 arp_probe: false,
780 bound_device_names: vec![DEFAULT_DEVICE_NAME.to_string()],
781 }
782 }
783
784 async fn run_with_server<T, F, Fut>(f: F) -> T
785 where
786 F: Fn(fidl_fuchsia_net_dhcp::Server_Proxy) -> Fut,
787 Fut: Future<Output = T>,
788 {
789 let (proxy, stream) =
790 fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcp::Server_Marker>();
791 let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
792
793 let defaults = default_params();
794 futures::select! {
795 res = f(proxy).fuse() => res,
796 res = run_server(stream, &server, &defaults, drain()).fuse() => {
797 unreachable!("server finished before request: {:?}", res)
798 },
799 }
800 }
801
802 #[fasync::run_singlethreaded(test)]
803 async fn get_option_with_subnet_mask_returns_subnet_mask() {
804 run_with_server(|proxy| async move {
805 assert_eq!(
806 proxy
807 .get_option(fidl_fuchsia_net_dhcp::OptionCode::SubnetMask)
808 .await
809 .expect("get_option failed"),
810 Ok(fidl_fuchsia_net_dhcp::Option_::SubnetMask(fidl_ip_v4!("0.0.0.0")))
811 );
812 })
813 .await
814 }
815
816 #[fasync::run_until_stalled(test)]
817 async fn get_parameter_with_lease_length_returns_lease_length() {
818 run_with_server(|proxy| async move {
819 assert_eq!(
820 proxy
821 .get_parameter(fidl_fuchsia_net_dhcp::ParameterName::LeaseLength)
822 .await
823 .expect("get_parameter failed"),
824 Ok(fidl_fuchsia_net_dhcp::Parameter::Lease(fidl_fuchsia_net_dhcp::LeaseLength {
825 default: None,
826 max: None,
827 ..Default::default()
828 }))
829 );
830 })
831 .await
832 }
833
834 #[fasync::run_singlethreaded(test)]
835 async fn set_option_with_subnet_mask_returns_unit() {
836 run_with_server(|proxy| async move {
837 assert_eq!(
838 proxy
839 .set_option(&fidl_fuchsia_net_dhcp::Option_::SubnetMask(fidl_ip_v4!("0.0.0.0")))
840 .await
841 .expect("set_option failed"),
842 Ok(())
843 );
844 })
845 .await
846 }
847
848 #[fasync::run_singlethreaded(test)]
849 async fn set_parameter_with_lease_length_returns_unit() {
850 run_with_server(|proxy| async move {
851 assert_eq!(
852 proxy
853 .set_parameter(&fidl_fuchsia_net_dhcp::Parameter::Lease(
854 fidl_fuchsia_net_dhcp::LeaseLength {
855 default: None,
856 max: None,
857 ..Default::default()
858 },
859 ))
860 .await
861 .expect("set_parameter failed"),
862 Ok(())
863 );
864 })
865 .await
866 }
867
868 #[fasync::run_singlethreaded(test)]
869 async fn list_options_returns_empty_vec() {
870 run_with_server(|proxy| async move {
871 assert_eq!(proxy.list_options().await.expect("list_options failed"), Ok(Vec::new()));
872 })
873 .await
874 }
875
876 #[fasync::run_singlethreaded(test)]
877 async fn list_parameters_returns_empty_vec() {
878 run_with_server(|proxy| async move {
879 assert_eq!(
880 proxy.list_parameters().await.expect("list_parameters failed"),
881 Ok(Vec::new())
882 );
883 })
884 .await
885 }
886
887 #[fasync::run_singlethreaded(test)]
888 async fn reset_options_returns_unit() {
889 run_with_server(|proxy| async move {
890 assert_eq!(proxy.reset_options().await.expect("reset_options failed"), Ok(()));
891 })
892 .await
893 }
894
895 #[fasync::run_singlethreaded(test)]
896 async fn reset_parameters_returns_unit() {
897 run_with_server(|proxy| async move {
898 assert_eq!(proxy.reset_parameters().await.expect("reset_parameters failed"), Ok(()));
899 })
900 .await
901 }
902
903 #[fasync::run_singlethreaded(test)]
904 async fn clear_leases_returns_unit() {
905 run_with_server(|proxy| async move {
906 assert_eq!(proxy.clear_leases().await.expect("clear_leases failed"), Ok(()));
907 })
908 .await
909 }
910
911 #[fasync::run_singlethreaded(test)]
912 async fn start_stop_server() {
913 let (proxy, stream) =
914 fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcp::Server_Marker>();
915 let (socket_sink, mut socket_stream) =
916 futures::channel::mpsc::channel::<ServerSocketCollection<CannedSocket>>(1);
917
918 let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
919 server.borrow_mut().params = Some(default_params());
921
922 let defaults = default_params();
923
924 server.borrow_mut().mock_leases = 1;
926
927 let test_fut = async {
928 for () in std::iter::repeat(()).take(3) {
929 assert!(
930 !proxy.is_serving().await.expect("query server status request"),
931 "server should not be serving"
932 );
933
934 let () = proxy
935 .start_serving()
936 .await
937 .expect("start_serving failed")
938 .map_err(zx::Status::from_raw)
939 .expect("start_serving returned an error");
940
941 let ServerSocketCollection { sockets, abort_registration } =
942 socket_stream.next().await.expect("Socket stream ended unexpectedly");
943
944 assert_eq!(
946 sockets,
947 vec![SocketWithId {
948 socket: CannedSocket {
949 name: DEFAULT_DEVICE_NAME.to_string(),
950 src: Ipv4Addr::UNSPECIFIED
951 },
952 iface_id: 0
953 }]
954 );
955
956 let dummy_fut = futures::future::Abortable::new(
959 futures::future::pending::<()>(),
960 abort_registration,
961 );
962
963 assert!(
964 proxy.is_serving().await.expect("query server status request"),
965 "server should be serving"
966 );
967
968 let () = proxy.stop_serving().await.expect("stop_serving failed");
969
970 assert_eq!(dummy_fut.await, Err(futures::future::Aborted {}));
972 assert_eq!(server.borrow().mock_leases, 1);
974
975 assert!(
976 !proxy.is_serving().await.expect("query server status request"),
977 "server should no longer be serving"
978 );
979 }
980 };
981
982 let () = futures::select! {
983 res = test_fut.fuse() => res,
984 res = run_server(stream, &server, &defaults, socket_sink).fuse() => {
985 unreachable!("server finished before request: {:?}", res)
986 },
987 };
988 }
989
990 #[fasync::run_singlethreaded(test)]
991 async fn start_server_fails_on_bad_params() {
992 let (proxy, stream) =
993 fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcp::Server_Marker>();
994 let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
995
996 let defaults = default_params();
997 let res = futures::select! {
998 res = proxy.start_serving().fuse() => res.expect("start_serving failed"),
999 res = run_server(stream, &server, &defaults, drain()).fuse() => {
1000 unreachable!("server finished before request: {:?}", res)
1001 },
1002 }
1003 .map_err(zx::Status::from_raw);
1004
1005 assert_eq!(res, Err(zx::Status::INVALID_ARGS));
1007 assert!(server.borrow().abort_handle.is_none());
1009 }
1010
1011 #[fasync::run_singlethreaded(test)]
1012 async fn start_server_fails_on_missing_interface_names() {
1013 let (proxy, stream) =
1014 fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcp::Server_Marker>();
1015 let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
1016
1017 let defaults = dhcpv4::configuration::ServerParameters {
1018 bound_device_names: Vec::new(),
1019 ..default_params()
1020 };
1021 server.borrow_mut().params = Some(defaults.clone());
1022
1023 let res = futures::select! {
1024 res = proxy.start_serving().fuse() => res.expect("start_serving failed"),
1025 res = run_server(stream, &server, &defaults, drain()).fuse() => {
1026 unreachable!("server finished before request: {:?}", res)
1027 },
1028 }
1029 .map_err(zx::Status::from_raw);
1030
1031 assert_eq!(res, Err(zx::Status::INVALID_ARGS));
1033 assert!(server.borrow().abort_handle.is_none());
1035 }
1036
1037 #[fasync::run_singlethreaded(test)]
1038 async fn disallow_change_parameters_if_enabled() {
1039 let (proxy, stream) =
1040 fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcp::Server_Marker>();
1041
1042 let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
1043 server.borrow_mut().params = Some(default_params());
1045
1046 let defaults = default_params();
1047
1048 let test_fut = async {
1049 let () = proxy
1050 .start_serving()
1051 .await
1052 .expect("start_serving failed")
1053 .map_err(zx::Status::from_raw)
1054 .expect("start_serving returned an error");
1055
1056 assert_eq!(
1058 proxy
1059 .set_parameter(&fidl_fuchsia_net_dhcp::Parameter::Lease(
1060 fidl_fuchsia_net_dhcp::LeaseLength {
1061 default: None,
1062 max: None,
1063 ..Default::default()
1064 },
1065 ))
1066 .await
1067 .expect("set_parameter FIDL failure")
1068 .map_err(zx::Status::from_raw),
1069 Err(zx::Status::BAD_STATE)
1070 );
1071
1072 assert_eq!(
1074 proxy
1075 .reset_parameters()
1076 .await
1077 .expect("reset_parameters FIDL failure")
1078 .map_err(zx::Status::from_raw),
1079 Err(zx::Status::BAD_STATE)
1080 );
1081 };
1082
1083 let () = futures::select! {
1084 res = test_fut.fuse() => res,
1085 res = run_server(stream, &server, &defaults, drain()).fuse() => {
1086 unreachable!("server finished before request: {:?}", res)
1087 },
1088 };
1089 }
1090
1091 #[test]
1094 fn handle_failed_parse() {
1095 let server = RefCell::new(ServerDispatcherRuntime::new(CannedDispatcher::new()));
1096 let mut handler = MessageHandler::new(&server);
1097 assert_matches::assert_matches!(
1098 handler.handle_from_sender(
1099 &[0xFF, 0x00, 0xBA, 0x03],
1100 std::net::SocketAddrV4::new(Ipv4Addr::UNSPECIFIED.into(), 0),
1101 ),
1102 Ok(None)
1103 );
1104 }
1105}