Skip to main content

netlink/
lib.rs

1// Copyright 2023 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
5//! An implementation of Linux's Netlink API for Fuchsia.
6//!
7//! Netlink is a socket-based API provided by Linux that user space applications
8//! can use to interact with the kernel. The API is split up into several
9//! protocol families each offering different functionality. This crate targets
10//! the implementation of families related to networking.
11
12#![warn(missing_docs, unused)]
13
14mod client;
15pub mod interfaces;
16pub(crate) mod logging;
17pub mod messaging;
18pub mod multicast_groups;
19mod nduseropt;
20pub mod neighbors;
21mod netlink_packet;
22pub mod protocol_family;
23pub(crate) mod route_eventloop;
24pub(crate) mod route_tables;
25pub mod routes;
26mod rules;
27pub(crate) mod util;
28
29use std::num::NonZeroU64;
30
31use fidl_fuchsia_net_interfaces as fnet_interfaces;
32use fidl_fuchsia_net_ndp as fnet_ndp;
33use fidl_fuchsia_net_neighbor as fnet_neighbor;
34use fidl_fuchsia_net_root as fnet_root;
35use fidl_fuchsia_net_routes as fnet_routes;
36use fidl_fuchsia_net_routes_admin as fnet_routes_admin;
37use fidl_fuchsia_net_routes_ext as fnet_routes_ext;
38use fidl_fuchsia_net_sockets as fnet_sockets;
39use fuchsia_component::client::connect_to_protocol;
40use futures::StreamExt as _;
41use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
42use futures::channel::oneshot;
43use net_types::ip::{Ipv4, Ipv6};
44use netlink_packet_route::RouteNetlinkMessage;
45use netlink_packet_sock_diag::{SockDiagRequest, SockDiagResponse};
46use protocol_family::route::NetlinkRouteNotifiedGroup;
47
48use crate::client::{AsyncWorkItem, ClientIdGenerator, ClientTable, InternalClient};
49use crate::logging::{log_debug, log_warn};
50use crate::messaging::{NetlinkContext, UnvalidatedNetlinkMessage as _, ValidationError};
51pub use crate::netlink_packet::errno::Errno;
52use crate::protocol_family::route::{
53    NetlinkRoute, NetlinkRouteClient, NetlinkRouteRequestHandler, RouteAsyncWork,
54};
55use crate::protocol_family::sock_diag::{
56    NetlinkSockDiag, NetlinkSockDiagClient, NetlinkSockDiagRequestHandler, SockDiagEventLoop,
57};
58use crate::protocol_family::{NetlinkFamilyRequestHandler as _, ProtocolFamily};
59use crate::route_eventloop::RouteEventLoop;
60
61/// The tag added to all logs generated by this crate.
62pub const NETLINK_LOG_TAG: &'static str = "netlink";
63
64/// Selects the interface for the sysctl.
65#[derive(Debug, Clone, Copy)]
66pub enum SysctlInterfaceSelector {
67    /// "all" interfaces.
68    ///
69    /// This is supposed to change all interfaces' settings, but this is a
70    /// lie for most of the sysctls, they have no effect at all when written.
71    All,
72    /// "default" interface, all interface created after this write will inherit the value.
73    Default,
74    /// The id of the interface to change.
75    Id(NonZeroU64),
76}
77
78/// The implementation of the Netlink protocol suite.
79pub struct Netlink<C: NetlinkContext> {
80    /// Generator of new Client IDs.
81    id_generator: ClientIdGenerator,
82    /// Sender to attach new `NETLINK_ROUTE` clients to the Netlink worker.
83    route_client_sender: UnboundedSender<ClientWithReceiver<C, NetlinkRoute>>,
84    /// Sender to send other async work items to the Netlink worker.
85    route_async_work_sink: mpsc::UnboundedSender<AsyncWorkItem<NetlinkRoute>>,
86    /// Sender to attach new `NETLINK_SOCK_DIAG` clients to the Netlink worker.
87    sock_diag_client_sender: UnboundedSender<ClientWithReceiver<C, NetlinkSockDiag>>,
88    /// Sender to send other async work items to the Netlink worker.
89    sock_diag_async_work_sink: mpsc::UnboundedSender<AsyncWorkItem<NetlinkSockDiag>>,
90}
91
92impl<C: NetlinkContext> Netlink<C> {
93    /// Returns a newly instantiated [`Netlink`] and parameters used to start the
94    /// asynchronous worker.
95    ///
96    /// Caller is expected to run the worker by calling `run_netlink_worker()`.
97    pub fn new<H: interfaces::InterfacesHandler>(
98        interfaces_handler: H,
99    ) -> (Self, NetlinkWorkerParams<H, C>) {
100        let (route_client_sender, route_client_receiver) = mpsc::unbounded();
101        let (route_async_work_sink, async_work_receiver) = mpsc::unbounded();
102        let (sock_diag_client_sender, sock_diag_client_receiver) = mpsc::unbounded();
103        let (sock_diag_async_work_sink, sock_diag_async_work_receiver) = mpsc::unbounded();
104        (
105            Netlink {
106                id_generator: ClientIdGenerator::default(),
107                route_client_sender,
108                sock_diag_client_sender,
109                route_async_work_sink,
110                sock_diag_async_work_sink,
111            },
112            NetlinkWorkerParams {
113                interfaces_handler,
114                route_client_receiver,
115                route_async_work_receiver: async_work_receiver,
116                sock_diag_client_receiver,
117                sock_diag_async_work_receiver,
118            },
119        )
120    }
121
122    /// Writes the accept_ra_rt_table sysctl for the selected interface.
123    pub fn write_accept_ra_rt_table(
124        &self,
125        interface: SysctlInterfaceSelector,
126        value: i32,
127    ) -> Result<(), SysctlError> {
128        let (responder, receiver) = oneshot_sync::channel();
129        self.route_async_work_sink
130            .unbounded_send(AsyncWorkItem::Inner(RouteAsyncWork::SetAcceptRaRtTable {
131                interface,
132                value: value.into(),
133                responder,
134            }))
135            .map_err(|_| SysctlError::Disconnected)?;
136        receiver.receive().map_err(|_| SysctlError::Disconnected)?
137    }
138
139    /// Reads the accept_ra_rt_table sysctl for the selected interface.
140    pub fn read_accept_ra_rt_table(
141        &self,
142        interface: SysctlInterfaceSelector,
143    ) -> Result<i32, SysctlError> {
144        let (responder, receiver) = oneshot_sync::channel();
145        self.route_async_work_sink
146            .unbounded_send(AsyncWorkItem::Inner(RouteAsyncWork::GetAcceptRaRtTable {
147                interface,
148                responder,
149            }))
150            .map_err(|_| SysctlError::Disconnected)?;
151        Ok(receiver.receive().map_err(|_| SysctlError::Disconnected)??.into())
152    }
153
154    /// Creates a new client of the `NETLINK_ROUTE` protocol family.
155    ///
156    /// `sender` is used by Netlink to send messages to the client.
157    /// `receiver` is used by Netlink to receive messages from the client.
158    ///
159    /// Closing the `receiver` will close this client, disconnecting `sender`.
160    pub fn new_route_client(
161        &self,
162        sender: C::Sender<RouteNetlinkMessage>,
163        receiver: C::Receiver<RouteNetlinkMessage>,
164    ) -> Result<NetlinkRouteClient, NewClientError> {
165        let Netlink {
166            id_generator,
167            route_client_sender,
168            route_async_work_sink,
169            sock_diag_client_sender: _,
170            sock_diag_async_work_sink: _,
171        } = self;
172        let (external_client, internal_client) = client::new_client_pair::<NetlinkRoute, _>(
173            id_generator.new_id(),
174            sender,
175            route_async_work_sink.clone(),
176        );
177        route_client_sender
178            .unbounded_send(ClientWithReceiver { client: internal_client, receiver })
179            .map_err(|e| {
180                // Sending on an `UnboundedSender` can never fail with `is_full()`.
181                debug_assert!(e.is_disconnected());
182                NewClientError::Disconnected
183            })?;
184        Ok(NetlinkRouteClient(external_client))
185    }
186
187    /// Creates a new client of the `NETLINK_SOCK_DIAG` protocol family.
188    ///
189    /// `sender` is used by Netlink to send messages to the client.
190    /// `receiver` is used by Netlink to receive messages from the client.
191    ///
192    /// Closing the `receiver` will close this client, disconnecting `sender`.
193    pub fn new_sock_diag_client(
194        &self,
195        sender: C::Sender<SockDiagResponse>,
196        receiver: C::Receiver<SockDiagRequest>,
197    ) -> Result<NetlinkSockDiagClient, NewClientError> {
198        let Netlink {
199            id_generator,
200            route_client_sender: _,
201            route_async_work_sink: _,
202            sock_diag_client_sender,
203            sock_diag_async_work_sink,
204        } = self;
205        let (external_client, internal_client) = client::new_client_pair::<NetlinkSockDiag, _>(
206            id_generator.new_id(),
207            sender,
208            sock_diag_async_work_sink.clone(),
209        );
210        sock_diag_client_sender
211            .unbounded_send(ClientWithReceiver { client: internal_client, receiver })
212            .map_err(|e| {
213                // Sending on an `UnboundedSender` can never fail with `is_full()`.
214                debug_assert!(e.is_disconnected());
215                NewClientError::Disconnected
216            })?;
217        Ok(NetlinkSockDiagClient(external_client))
218    }
219}
220
221/// A wrapper to hold an [`InternalClient`], and its [`Receiver`] of requests.
222struct ClientWithReceiver<C: NetlinkContext, F: ProtocolFamily> {
223    client: InternalClient<F, C::Sender<F::Response>>,
224    receiver: C::Receiver<F::Request>,
225}
226
227/// The possible error types when instantiating a new client.
228#[derive(Debug)]
229pub enum NewClientError {
230    /// The [`Netlink`] is disconnected from its associated worker, perhaps as a
231    /// result of dropping the worker.
232    Disconnected,
233}
234
235/// The possible error types when trying to access a sysctl.
236#[derive(Debug)]
237pub enum SysctlError {
238    /// The [`Netlink`] is disconnected from its associated worker.
239    Disconnected,
240    /// The interface went away.
241    NoInterface,
242    /// The written value requests for an unsupported operation.
243    Unsupported,
244}
245
246/// Parameters used to start the Netlink asynchronous worker.
247pub struct NetlinkWorkerParams<H, C: NetlinkContext> {
248    interfaces_handler: H,
249    /// Receiver of newly created `NETLINK_ROUTE` clients.
250    route_client_receiver: UnboundedReceiver<ClientWithReceiver<C, NetlinkRoute>>,
251    route_async_work_receiver:
252        futures::channel::mpsc::UnboundedReceiver<AsyncWorkItem<NetlinkRoute>>,
253    /// Receiver of newly created `NETLINK_SOCK_DIAG` clients.
254    sock_diag_client_receiver: UnboundedReceiver<ClientWithReceiver<C, NetlinkSockDiag>>,
255    sock_diag_async_work_receiver:
256        futures::channel::mpsc::UnboundedReceiver<AsyncWorkItem<NetlinkSockDiag>>,
257}
258
259/// All of the protocols that the netlink worker connects to.
260#[allow(missing_docs)]
261pub struct NetlinkWorkerDiscoverableProtocols {
262    pub root_interfaces: fnet_root::InterfacesProxy,
263    pub interfaces_state: fnet_interfaces::StateProxy,
264    pub v4_routes_state: fnet_routes::StateV4Proxy,
265    pub v6_routes_state: fnet_routes::StateV6Proxy,
266    pub v4_main_route_table: fnet_routes_admin::RouteTableV4Proxy,
267    pub v6_main_route_table: fnet_routes_admin::RouteTableV6Proxy,
268    pub v4_route_table_provider: fnet_routes_admin::RouteTableProviderV4Proxy,
269    pub v6_route_table_provider: fnet_routes_admin::RouteTableProviderV6Proxy,
270    pub v4_rule_table: fnet_routes_admin::RuleTableV4Proxy,
271    pub v6_rule_table: fnet_routes_admin::RuleTableV6Proxy,
272    pub ndp_option_watcher_provider: fnet_ndp::RouterAdvertisementOptionWatcherProviderProxy,
273    pub socket_diagnostics: fnet_sockets::DiagnosticsProxy,
274    pub socket_control: fnet_sockets::ControlProxy,
275    pub neighbors_view: fnet_neighbor::ViewProxy,
276    pub neighbors_controller: fnet_neighbor::ControllerProxy,
277}
278
279impl NetlinkWorkerDiscoverableProtocols {
280    fn from_environment() -> Self {
281        let root_interfaces = connect_to_protocol::<fnet_root::InterfacesMarker>()
282            .expect("connect to fuchsia.net.root.Interfaces");
283        let interfaces_state = connect_to_protocol::<fnet_interfaces::StateMarker>()
284            .expect("connect to fuchsia.net.interfaces.State");
285        let v4_routes_state =
286            connect_to_protocol::<<Ipv4 as fnet_routes_ext::FidlRouteIpExt>::StateMarker>()
287                .expect("connect to fuchsia.net.routes.StateV4");
288        let v6_routes_state =
289            connect_to_protocol::<<Ipv6 as fnet_routes_ext::FidlRouteIpExt>::StateMarker>()
290                .expect("connect to fuchsia.net.routes.StateV6");
291        let v4_main_route_table = connect_to_protocol::<
292            <Ipv4 as fnet_routes_ext::admin::FidlRouteAdminIpExt>::RouteTableMarker,
293        >()
294        .expect("connect to fuchsia.net.routes.admin.RouteTableV4");
295        let v6_main_route_table = connect_to_protocol::<
296            <Ipv6 as fnet_routes_ext::admin::FidlRouteAdminIpExt>::RouteTableMarker,
297        >()
298        .expect("connect to fuchsia.net.routes.admin.RouteTableV6");
299        let v4_route_table_provider = connect_to_protocol::<
300            <Ipv4 as fnet_routes_ext::admin::FidlRouteAdminIpExt>::RouteTableProviderMarker,
301        >()
302        .expect("connect to fuchsia.net.routes.admin.RouteTableProviderV4");
303        let v6_route_table_provider = connect_to_protocol::<
304            <Ipv6 as fnet_routes_ext::admin::FidlRouteAdminIpExt>::RouteTableProviderMarker,
305        >()
306        .expect("connect to fuchsia.net.routes.admin.RouteTableProviderV6");
307        let v4_rule_table = connect_to_protocol::<
308            <Ipv4 as fnet_routes_ext::rules::FidlRuleAdminIpExt>::RuleTableMarker,
309        >()
310        .expect("connect to fuchsia.net.routes.admin.RuleTableV4");
311        let v6_rule_table = connect_to_protocol::<
312            <Ipv6 as fnet_routes_ext::rules::FidlRuleAdminIpExt>::RuleTableMarker,
313        >()
314        .expect("connect to fuchsia.net.routes.admin.RuleTableV6");
315        let ndp_option_watcher_provider =
316            connect_to_protocol::<fnet_ndp::RouterAdvertisementOptionWatcherProviderMarker>()
317                .expect("connect to fuchsia.net.ndp.RouterAdvertisementOptionWatcherProvider");
318        let socket_diagnostics = connect_to_protocol::<fnet_sockets::DiagnosticsMarker>()
319            .expect("connect to fuchsia.net.sockets.Diagnostics");
320        let socket_control = connect_to_protocol::<fnet_sockets::ControlMarker>()
321            .expect("connect to fuchsia.net.sockets.Control");
322        let neighbors_view = connect_to_protocol::<fnet_neighbor::ViewMarker>()
323            .expect("connect to fuchsia.net.neighbor.View");
324        let neighbors_controller = connect_to_protocol::<fnet_neighbor::ControllerMarker>()
325            .expect("connect to fuchsia.net.neighbor.Controller");
326
327        Self {
328            root_interfaces,
329            interfaces_state,
330            v4_routes_state,
331            v6_routes_state,
332            v4_main_route_table,
333            v6_main_route_table,
334            v4_route_table_provider,
335            v6_route_table_provider,
336            v4_rule_table,
337            v6_rule_table,
338            ndp_option_watcher_provider,
339            socket_diagnostics,
340            socket_control,
341            neighbors_view,
342            neighbors_controller,
343        }
344    }
345}
346
347/// The worker encompassing all asynchronous Netlink work.
348///
349/// The worker is never expected to complete.
350///
351/// `protocols` is taken as a closure because we need to avoid creating asynchronous FIDL proxies
352/// until an executor is running, so it's helpful to defer creation until the event loop starts
353/// running.
354///
355/// # Panics
356///
357/// Panics if a non-recoverable error is encountered by the worker. For example,
358/// a FIDL error on one of the FIDL connections with the netstack.
359pub async fn run_netlink_worker<H: interfaces::InterfacesHandler, C: NetlinkContext>(
360    params: NetlinkWorkerParams<H, C>,
361    access_control: C::AccessControl<'_>,
362) {
363    run_netlink_worker_with_protocols(
364        params,
365        NetlinkWorkerDiscoverableProtocols::from_environment(),
366        None,
367        access_control,
368    )
369    .await;
370}
371
372/// Same as `run_netlink_worker()`, but allows to pass custom
373/// `NetlinkWorkerDiscoverableProtocols`.
374pub async fn run_netlink_worker_with_protocols<
375    H: interfaces::InterfacesHandler,
376    C: NetlinkContext,
377>(
378    params: NetlinkWorkerParams<H, C>,
379    protocols: NetlinkWorkerDiscoverableProtocols,
380    on_route_initialized: Option<oneshot::Sender<()>>,
381    access_control: C::AccessControl<'_>,
382) {
383    let NetlinkWorkerParams {
384        interfaces_handler,
385        route_client_receiver,
386        route_async_work_receiver,
387        sock_diag_client_receiver,
388        sock_diag_async_work_receiver,
389    } = params;
390
391    let NetlinkWorkerDiscoverableProtocols {
392        root_interfaces,
393        interfaces_state,
394        v4_routes_state,
395        v6_routes_state,
396        v4_main_route_table,
397        v6_main_route_table,
398        v4_route_table_provider,
399        v6_route_table_provider,
400        v4_rule_table,
401        v6_rule_table,
402        ndp_option_watcher_provider,
403        socket_diagnostics,
404        socket_control,
405        neighbors_view,
406        neighbors_controller,
407    } = protocols;
408
409    let route_clients = ClientTable::default();
410    let (route_request_sink, route_request_stream) = mpsc::channel(1);
411
412    let route_event_loop = {
413        let route_clients = route_clients.clone();
414        async move {
415            let event_loop: RouteEventLoop<H, C::Sender<_>> = RouteEventLoop {
416                interfaces_proxy: root_interfaces,
417                interfaces_state_proxy: interfaces_state,
418                v4_routes_state,
419                v6_routes_state,
420                v4_main_route_table,
421                v6_main_route_table,
422                v4_route_table_provider,
423                v6_route_table_provider,
424                v4_rule_table,
425                v6_rule_table,
426                ndp_option_watcher_provider,
427                neighbors_view,
428                neighbors_controller,
429                route_clients,
430                request_stream: route_request_stream,
431                interfaces_handler,
432                async_work_receiver: route_async_work_receiver,
433            };
434
435            event_loop.run(on_route_initialized).await;
436        }
437    };
438
439    let route_client_receiver_loop = {
440        let access_control = access_control.clone();
441        async move {
442            // Accept new NETLINK_ROUTE clients.
443            connect_new_clients::<C, NetlinkRoute>(
444                route_clients,
445                route_client_receiver,
446                NetlinkRouteRequestHandler { unified_request_sink: route_request_sink },
447                access_control,
448            )
449            .await;
450            panic!("route_client_receiver stream unexpectedly finished");
451        }
452    };
453
454    let sock_diag_clients = ClientTable::default();
455    let (sock_diag_request_sink, sock_diag_request_stream) = mpsc::channel(1);
456
457    let sock_diag_event_loop = {
458        let sock_diag_clients = sock_diag_clients.clone();
459        async move {
460            SockDiagEventLoop::new(
461                socket_diagnostics,
462                socket_control,
463                sock_diag_request_stream,
464                sock_diag_async_work_receiver,
465                sock_diag_clients,
466            )
467            .run()
468            .await;
469        }
470    };
471
472    let sock_diag_client_receiver_loop = async move {
473        // Accept new NETLINK_SOCK_DIAG clients.
474        connect_new_clients::<C, NetlinkSockDiag>(
475            sock_diag_clients,
476            sock_diag_client_receiver,
477            NetlinkSockDiagRequestHandler { sock_diag_request_sink },
478            access_control,
479        )
480        .await;
481        panic!("sock_diag_client_receiver stream unexpectedly finished");
482    };
483
484    futures::future::join4(
485        route_event_loop,
486        route_client_receiver_loop,
487        sock_diag_event_loop,
488        sock_diag_client_receiver_loop,
489    )
490    .await;
491}
492
493/// Receives clients from the given receiver, adding them to the given table.
494///
495/// A "Request Handler" Task will be spawned for each received client. The given
496/// `request_handler_impl` defines how the requests will be handled.
497async fn connect_new_clients<C: NetlinkContext, F: ProtocolFamily>(
498    client_table: ClientTable<F, C::Sender<F::Response>>,
499    client_receiver: UnboundedReceiver<ClientWithReceiver<C, F>>,
500    request_handler_impl: F::RequestHandler<C::Sender<F::Response>>,
501    access_control: C::AccessControl<'_>,
502) {
503    client_receiver
504        // Drive each client concurrently with `for_each_concurrent`.
505        .for_each_concurrent(None, async |ClientWithReceiver { client, receiver }| {
506            client_table.add_client(client.clone());
507            let client = run_client_request_handler::<C, F>(
508                client,
509                receiver,
510                request_handler_impl.clone(),
511                access_control.clone(),
512            )
513            .await;
514            client_table.remove_client(client);
515        })
516        .await;
517}
518
519/// Reads messages from the `receiver` and handles them using the `handler`.
520///
521/// The task terminates when the underlying `Receiver` closes, yielding the
522/// original client.
523async fn run_client_request_handler<C: NetlinkContext, F: ProtocolFamily>(
524    client: InternalClient<F, C::Sender<F::Response>>,
525    receiver: C::Receiver<F::Request>,
526    handler: F::RequestHandler<C::Sender<F::Response>>,
527    access_control: C::AccessControl<'_>,
528) -> InternalClient<F, C::Sender<F::Response>> {
529    // State needed to handle an individual request, that is cycled through the
530    // `fold` combinator below.
531    struct FoldState<C, H, P> {
532        client: C,
533        handler: H,
534        access_control: P,
535    }
536
537    // Use `fold` for two reasons. First, it processes requests serially,
538    // ensuring requests are handled in order. Second, it allows us to
539    // "hand-off" the client/handler from one request to the other, avoiding
540    // copies for each request.
541    let FoldState { client, handler: _, access_control: _ } = receiver
542        .fold(
543            FoldState { client, handler, access_control },
544            |FoldState { mut client, mut handler, access_control }, req| async {
545                match req.validate_creds_and_get_message(&access_control) {
546                    Ok(req) => {
547                        log_debug!("{} Received request: {:?}", client, req);
548                        handler.handle_request(req, &mut client).await
549                    }
550                    Err(e) => {
551                        match &e {
552                            ValidationError::Parse(e) => {
553                                log_warn!("{client} failed to parse netlink message: {e:?}");
554                            }
555                            p @ ValidationError::Permission { .. } => {
556                                log_debug!("{client} permission check failed {p:?}")
557                            }
558                        }
559                        if let Some(rsp) = e.into_error_message() {
560                            client.send_unicast(rsp)
561                        }
562                    }
563                }
564                FoldState { client, handler, access_control }
565            },
566        )
567        .await;
568
569    client
570}
571
572#[cfg(test)]
573mod tests {
574    use super::*;
575    use fuchsia_async as fasync;
576    use futures::FutureExt as _;
577
578    use assert_matches::assert_matches;
579    use netlink_packet_core::{ErrorMessage, NetlinkPayload};
580    use std::num::NonZeroI32;
581    use std::pin::pin;
582
583    use crate::messaging::NetlinkMessageWithCreds;
584    use crate::messaging::testutil::{FakeCreds, SentMessage, TestNetlinkContext};
585    use crate::protocol_family::testutil::{
586        FakeNetlinkRequestHandler, FakeProtocolFamily, new_fake_netlink_message,
587        new_fake_netlink_message_with_creds,
588    };
589
590    #[fasync::run_singlethreaded(test)]
591    async fn test_run_client_request_handler() {
592        let (mut req_sender, req_receiver) = mpsc::channel(0);
593        let (mut client_sink, client, async_work_drain_task) =
594            crate::client::testutil::new_fake_client::<FakeProtocolFamily>(
595                crate::client::testutil::CLIENT_ID_1,
596                std::iter::empty(),
597            );
598        let join_handle = fasync::Task::spawn(async_work_drain_task);
599
600        {
601            let mut client_task = pin!(
602                run_client_request_handler::<TestNetlinkContext, FakeProtocolFamily>(
603                    client,
604                    req_receiver,
605                    FakeNetlinkRequestHandler,
606                    Default::default()
607                )
608                .fuse()
609            );
610
611            assert_matches!((&mut client_task).now_or_never(), None);
612            assert_eq!(&client_sink.take_messages()[..], &[]);
613
614            // Send a message and expect to see the response on the `client_sink`.
615            // NB: Use the sender's channel size as a synchronization method; If a
616            // second message could be sent, the first *must* have been handled.
617            req_sender
618                .try_send(new_fake_netlink_message_with_creds())
619                .expect("should send without error");
620            let mut could_send_fut =
621                pin!(futures::future::poll_fn(|ctx| req_sender.poll_ready(ctx)).fuse());
622            futures::select!(
623                res = could_send_fut => res.expect("should be able to send without error"),
624                _client = client_task => panic!("client task unexpectedly finished"),
625            );
626            assert_eq!(
627                &client_sink.take_messages()[..],
628                &[SentMessage::unicast(new_fake_netlink_message())]
629            );
630
631            // Close the sender, and expect the Task to exit.
632            req_sender.close_channel();
633            let _client = client_task.await;
634            assert_eq!(&client_sink.take_messages()[..], &[]);
635        }
636        join_handle.await;
637    }
638
639    #[fasync::run_singlethreaded(test)]
640    async fn test_connect_new_clients() {
641        let client_table = ClientTable::default();
642        let scope = fasync::Scope::new();
643        let (client_sender, client_receiver) = futures::channel::mpsc::unbounded();
644        let mut client_acceptor_fut = Box::pin(
645            connect_new_clients::<TestNetlinkContext, FakeProtocolFamily>(
646                client_table.clone(),
647                client_receiver,
648                FakeNetlinkRequestHandler,
649                Default::default(),
650            )
651            .fuse(),
652        );
653
654        assert_eq!((&mut client_acceptor_fut).now_or_never(), None);
655
656        // Connect Client 1.
657        let (mut _client_sink1, client1, async_work_drain_task) =
658            crate::client::testutil::new_fake_client::<FakeProtocolFamily>(
659                crate::client::testutil::CLIENT_ID_1,
660                std::iter::empty(),
661            );
662        let _join_handle = scope.spawn(async_work_drain_task);
663        let (mut req_sender1, req_receiver1) = mpsc::channel(0);
664        client_sender
665            .unbounded_send(ClientWithReceiver { client: client1, receiver: req_receiver1 })
666            .expect("should send without error");
667
668        // Connect Client 2.
669        let (mut client_sink2, client2, async_work_drain_task) =
670            crate::client::testutil::new_fake_client::<FakeProtocolFamily>(
671                crate::client::testutil::CLIENT_ID_2,
672                std::iter::empty(),
673            );
674        let _join_handle = scope.spawn(async_work_drain_task);
675        let (mut req_sender2, req_receiver2) = mpsc::channel(0);
676        client_sender
677            .unbounded_send(ClientWithReceiver { client: client2, receiver: req_receiver2 })
678            .expect("should send without error");
679
680        // Send a request to Client 2, and verify it's handled despite Client 1
681        // being open (e.g. concurrent handling of requests across clients).
682        // NB: Use the sender's channel size as a synchronization method; If a
683        // second message could be sent, the first *must* have been handled.
684        req_sender2
685            .try_send(new_fake_netlink_message_with_creds())
686            .expect("should send without error");
687        let mut could_send_fut =
688            pin!(futures::future::poll_fn(|ctx| req_sender2.poll_ready(ctx)).fuse());
689        futures::select!(
690            res = could_send_fut => res.expect("should be able to send without error"),
691            () = client_acceptor_fut => panic!("client acceptor unexpectedly finished"),
692        );
693        assert_eq!(
694            &client_table.client_ids()[..],
695            [client::testutil::CLIENT_ID_1, client::testutil::CLIENT_ID_2]
696        );
697        assert_eq!(
698            &client_sink2.take_messages()[..],
699            &[SentMessage::unicast(new_fake_netlink_message())]
700        );
701
702        // Close the two clients, and verify the acceptor fut is still pending.
703        req_sender1.close_channel();
704        req_sender2.close_channel();
705        assert_eq!((&mut client_acceptor_fut).now_or_never(), None);
706
707        // Close the client_sender, and verify the acceptor fut finishes.
708        client_sender.close_channel();
709        client_acceptor_fut.await;
710
711        // Confirm the clients have been cleaned up from the client table.
712        assert_eq!(&client_table.client_ids()[..], []);
713
714        drop(client_table);
715        scope.join().await;
716    }
717
718    #[fasync::run_singlethreaded(test)]
719    async fn test_permissions() {
720        let client_table = ClientTable::default();
721        let scope = fasync::Scope::new();
722        let (client_sender, client_receiver) = futures::channel::mpsc::unbounded();
723        let mut client_acceptor_fut = Box::pin(
724            connect_new_clients::<TestNetlinkContext, FakeProtocolFamily>(
725                client_table.clone(),
726                client_receiver,
727                FakeNetlinkRequestHandler,
728                Default::default(),
729            )
730            .fuse(),
731        );
732        assert_eq!((&mut client_acceptor_fut).now_or_never(), None);
733
734        let (mut client_sink, client, async_work_drain_task) =
735            crate::client::testutil::new_fake_client::<FakeProtocolFamily>(
736                crate::client::testutil::CLIENT_ID_1,
737                std::iter::empty(),
738            );
739        let _join_handle = scope.spawn(async_work_drain_task);
740        let (mut req_sender, req_receiver) = mpsc::channel(0);
741        client_sender
742            .unbounded_send(ClientWithReceiver { client, receiver: req_receiver })
743            .expect("should send without error");
744
745        let message = NetlinkMessageWithCreds::new(
746            new_fake_netlink_message(),
747            FakeCreds::with_error(Errno::new(libc::EPERM).unwrap()),
748        );
749        req_sender.try_send(message).expect("should send without error");
750
751        let response = futures::select!(
752            res = client_sink.next_message().fuse() => res,
753            () = client_acceptor_fut => panic!("client acceptor unexpectedly finished"),
754        );
755
756        assert_matches!(
757            response.message.payload,
758            NetlinkPayload::Error(ErrorMessage { code: Some(error_code), .. }) => {
759              assert_eq!(error_code , NonZeroI32::new(-libc::EPERM).unwrap());
760            }
761        );
762    }
763}