netlink/
messaging.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//! A module for managing message passing between Netlink and its clients.
6
7use futures::Stream;
8use netlink_packet_core::{
9    NetlinkBuffer, NetlinkDeserializable, NetlinkHeader, NetlinkMessage, NetlinkPayload,
10    NetlinkSerializable,
11};
12use netlink_packet_route::RouteNetlinkMessageParseError;
13use netlink_packet_utils::nla::NlaError;
14use netlink_packet_utils::{DecodeError, Parseable};
15use std::fmt::Debug;
16
17use crate::multicast_groups::ModernGroup;
18use crate::netlink_packet;
19use crate::netlink_packet::errno::Errno;
20
21/// A type capable of sending messages, `M`, from Netlink to a client.
22pub trait Sender<M>: Clone + Send + Sync {
23    /// Sends the given message to the client.
24    ///
25    /// If the message is a multicast, `group` will hold a `Some`; `None` for
26    /// unicast messages.
27    ///
28    /// Implementors must ensure this call does not block.
29    fn send(&mut self, message: NetlinkMessage<M>, group: Option<ModernGroup>);
30}
31
32/// A type capable of receiving messages, `M`, from a client to Netlink.
33///
34/// [`Stream`] already provides a sufficient interface for this purpose.
35pub trait Receiver<M, C>:
36    Stream<Item: UnvalidatedNetlinkMessage<Message = M, Credentials = C>> + Send
37where
38    M: Send + MessageWithPermission,
39    C: Send,
40{
41}
42
43/// Blanket implementation allows any [`Stream`] to be used as a [`Receiver`].
44impl<M, C, S> Receiver<M, C> for S
45where
46    M: Send + MessageWithPermission,
47    C: Send,
48    S: Stream<Item: UnvalidatedNetlinkMessage<Message = M, Credentials = C>> + Send,
49{
50}
51
52/// A permission that is required from the sender when processing a netlink
53/// request.
54pub enum Permission {
55    /// GET messages in NETLINK_ROUTE.
56    NetlinkRouteRead,
57
58    /// SET/NEW/DELETE messages in NETLINK_ROUTE.
59    NetlinkRouteWrite,
60}
61
62/// An object responsible to validating permissions for netlink clients.
63pub trait AccessControl<C>: Clone {
64    /// Returns true if a client with the specified credentials has the
65    /// specified permission.
66    fn grant_assess(&self, creds: &C, permission: Permission) -> Result<(), Errno>;
67}
68
69/// A message that may require special permissions from the sender to be
70/// processed.
71pub trait MessageWithPermission {
72    /// Returns the permission that's required in order to process this message.
73    fn permission(&self) -> Permission;
74}
75
76/// An error observed when parsing a netlink message.
77#[derive(Debug)]
78pub struct ParseError {
79    /// The error encountered during parsing.
80    pub error: DecodeError,
81    /// The header on the original message.
82    ///
83    /// If a header was not able to be extracted from the original message,
84    /// `None` is set.
85    pub header: Option<NetlinkHeader>,
86}
87
88/// A trait abstracting netlink messages that may already be parsed or still
89/// need to go through parsing.
90pub trait MaybeParsedNetlinkMessage {
91    /// The inner message type potentially contained by this message.
92    type Message: MessageWithPermission;
93
94    /// Parses the message, returning an owned [`NetlinkMessage`] on success.
95    fn try_into_parsed(self) -> Result<NetlinkMessage<Self::Message>, ParseError>;
96}
97
98impl<M: MessageWithPermission> MaybeParsedNetlinkMessage for NetlinkMessage<M> {
99    type Message = M;
100    fn try_into_parsed(self) -> Result<NetlinkMessage<M>, ParseError> {
101        Ok(self)
102    }
103}
104
105/// An unparsed netlink message backed by the bytes in `B`.
106pub struct UnparsedNetlinkMessage<B, M> {
107    data: B,
108    _marker: std::marker::PhantomData<M>,
109}
110
111impl<B, M> UnparsedNetlinkMessage<B, M> {
112    /// Creates a new `UnparsedNetlinkMessage`.
113    pub fn new(data: B) -> Self {
114        Self { data, _marker: std::marker::PhantomData }
115    }
116}
117
118impl<M, B> MaybeParsedNetlinkMessage for UnparsedNetlinkMessage<B, M>
119where
120    B: AsRef<[u8]>,
121    M: NetlinkDeserializable + MessageWithPermission,
122    M::Error: Into<DecodeError>,
123{
124    type Message = M;
125
126    fn try_into_parsed(self) -> Result<NetlinkMessage<M>, ParseError> {
127        let Self { data, _marker } = self;
128        let data = data.as_ref();
129        let netlink_buffer =
130            NetlinkBuffer::new(&data).map_err(|error| ParseError { error, header: None })?;
131        NetlinkMessage::<M>::parse(&netlink_buffer).map_err(|error| ParseError {
132            error,
133            // Silently drop the parsing error here, the error from parsing the
134            // NetlinkMessage itself should be enough.
135            header: NetlinkHeader::parse(&netlink_buffer).ok(),
136        })
137    }
138}
139
140/// The outcome of validating a netlink message.
141#[derive(Debug)]
142#[allow(missing_docs)]
143pub enum ValidationError {
144    /// The message failed to parse.
145    Parse(ParseError),
146    /// The provided credentials are insufficient for the requested operation.
147    Permission { header: NetlinkHeader, error: Errno },
148}
149
150// TODO(https://fxbug.dev/450959280): Move this close to the netlink crates, so
151// we have more control of the error types more locally,
152fn nla_error_to_errno(error: &NlaError) -> Errno {
153    match error {
154        NlaError::BufferTooSmall { .. }
155        | NlaError::LengthMismatch { .. }
156        | NlaError::InvalidLength { .. } => Errno::EINVAL,
157    }
158}
159
160// TODO(https://fxbug.dev/450959280): Move this close to the netlink crates, so
161// we have more control of the error types more locally,
162fn route_netlink_error_to_errno(error: &RouteNetlinkMessageParseError) -> Errno {
163    match error {
164        RouteNetlinkMessageParseError::ParseBuffer(decode_error)
165        | RouteNetlinkMessageParseError::InvalidLinkMessage(decode_error) => {
166            decode_error_to_errno(decode_error)
167        }
168        RouteNetlinkMessageParseError::InvalidRouteMessage(_)
169        | RouteNetlinkMessageParseError::InvalidAddrMessage(_)
170        | RouteNetlinkMessageParseError::InvalidPrefixMessage(_)
171        | RouteNetlinkMessageParseError::InvalidFibRuleMessage(_)
172        | RouteNetlinkMessageParseError::InvalidTcMessage(_)
173        | RouteNetlinkMessageParseError::InvalidNsidMessage(_)
174        | RouteNetlinkMessageParseError::InvalidNeighbourMessage(_)
175        | RouteNetlinkMessageParseError::InvalidNeighbourTableMessage(_)
176        | RouteNetlinkMessageParseError::InvalidNeighbourDiscoveryUserOptionMessage(_) => {
177            Errno::EINVAL
178        }
179        RouteNetlinkMessageParseError::UnknownMessageType(_) => Errno::ENOTSUP,
180    }
181}
182
183// TODO(https://fxbug.dev/450959280): Move this close to the netlink crates, so
184// we have more control of the error types more locally,
185fn decode_error_to_errno(error: &DecodeError) -> Errno {
186    match error {
187        DecodeError::InvalidMACAddress
188        | DecodeError::InvalidIPAddress
189        | DecodeError::Utf8Error(_)
190        | DecodeError::InvalidU8
191        | DecodeError::InvalidU16
192        | DecodeError::InvalidU32
193        | DecodeError::InvalidU64
194        | DecodeError::InvalidU128
195        | DecodeError::InvalidI32
196        | DecodeError::InvalidBufferLength { .. } => Errno::EINVAL,
197        DecodeError::Nla(nla_error) => nla_error_to_errno(nla_error),
198        DecodeError::Other(error) => {
199            if let Some(error) = error.downcast_ref::<RouteNetlinkMessageParseError>() {
200                return route_netlink_error_to_errno(error);
201            }
202            if let Some(error) = error.downcast_ref::<netlink_packet_utils::DecodeError>() {
203                return decode_error_to_errno(error);
204            }
205            Errno::EINVAL
206        }
207        DecodeError::FailedToParseNlMsgError(error)
208        | DecodeError::FailedToParseNlMsgDone(error)
209        | DecodeError::FailedToParseMessageWithType { message_type: _, source: error }
210        | DecodeError::FailedToParseNetlinkHeader(error) => decode_error_to_errno(error),
211    }
212}
213
214impl ValidationError {
215    /// Creates an error message response from this error, if one can be
216    /// created.
217    pub fn into_error_message<M: NetlinkSerializable>(self) -> Option<NetlinkMessage<M>> {
218        match self {
219            ValidationError::Parse(ParseError { error, header }) => {
220                // If we couldn't parse at least a header, we can't respond.
221                let header = header?;
222                // NB: Decode error from netlink_core doesn't quite have enough
223                // granularity here for us to be able to tell not supported from
224                // supported, but malformed.
225                Some(netlink_packet::new_error(Err(decode_error_to_errno(&error)), header))
226            }
227            ValidationError::Permission { header, error } => {
228                Some(netlink_packet::new_error(Err(error), header))
229            }
230        }
231    }
232}
233
234/// Encapsulates `NetlinkMessage` with the credentials of the sender.
235#[derive(Clone, Debug)]
236pub struct NetlinkMessageWithCreds<M, C> {
237    message: M,
238    creds: C,
239}
240
241impl<M, C> NetlinkMessageWithCreds<M, C> {
242    /// Creates a new instance.
243    pub fn new(message: M, creds: C) -> Self {
244        Self { message, creds }
245    }
246}
247
248/// A trait abstracting a yet unvalidated netlink message.
249///
250/// This trait provides storage-abstraction for [`NetlinkMessageWithCreds`].
251pub trait UnvalidatedNetlinkMessage {
252    /// The message type in this unvalidated message.
253    type Message;
254    /// The credentials type required for validation.
255    type Credentials;
256
257    /// Validates permission using the specified `AccessControl` and returns
258    /// the parsed message. If the permission is not granted then return an
259    /// error that should be sent back to the client.
260    fn validate_creds_and_get_message<PS: AccessControl<Self::Credentials>>(
261        self,
262        access_control: &PS,
263    ) -> Result<NetlinkMessage<Self::Message>, ValidationError>;
264}
265
266impl<M, C> UnvalidatedNetlinkMessage for NetlinkMessageWithCreds<M, C>
267where
268    M: MaybeParsedNetlinkMessage,
269    M::Message: MessageWithPermission,
270{
271    type Message = M::Message;
272    type Credentials = C;
273
274    fn validate_creds_and_get_message<PS: AccessControl<C>>(
275        self,
276        access_control: &PS,
277    ) -> Result<NetlinkMessage<M::Message>, ValidationError> {
278        let Self { message, creds } = self;
279        let message = message.try_into_parsed().map_err(ValidationError::Parse)?;
280        let permission = match &message.payload {
281            NetlinkPayload::InnerMessage(msg) => msg.permission(),
282            NetlinkPayload::Done(_)
283            | NetlinkPayload::Error(_)
284            | NetlinkPayload::Noop
285            | NetlinkPayload::Overrun(_) => return Ok(message),
286        };
287
288        access_control
289            .grant_assess(&creds, permission)
290            .map_err(|error| ValidationError::Permission { header: message.header, error })?;
291        Ok(message)
292    }
293}
294
295/// A type capable of providing a concrete types used in Netlink.
296pub trait NetlinkContext {
297    /// The type used to represent process credentials.
298    type Creds: Clone + Send + Debug;
299
300    /// The type of [`Sender`] provided.
301    type Sender<M: Clone + NetlinkSerializable + Send>: Sender<M>;
302
303    /// The type of [`Receiver`] provided.
304    type Receiver<M: Send + MessageWithPermission + NetlinkDeserializable<Error: Into<DecodeError>>>: Receiver<M, Self::Creds>;
305
306    /// The type of an object that validates access to netlink operations.
307    type AccessControl<'a>: AccessControl<Self::Creds>;
308}
309
310#[cfg(test)]
311pub(crate) mod testutil {
312    use super::*;
313    use crate::mpsc;
314    use futures::{FutureExt as _, StreamExt as _};
315    use netlink_packet_core::NetlinkSerializable;
316
317    #[derive(Clone, Debug, PartialEq, Eq)]
318    pub(crate) struct SentMessage<M> {
319        pub message: NetlinkMessage<M>,
320        pub group: Option<ModernGroup>,
321    }
322
323    impl<M> SentMessage<M> {
324        pub(crate) fn unicast(message: NetlinkMessage<M>) -> Self {
325            Self { message, group: None }
326        }
327
328        pub(crate) fn multicast(message: NetlinkMessage<M>, group: ModernGroup) -> Self {
329            Self { message, group: Some(group) }
330        }
331    }
332
333    #[derive(Clone, Debug)]
334    pub(crate) struct FakeSender<M> {
335        sender: futures::channel::mpsc::UnboundedSender<SentMessage<M>>,
336    }
337
338    impl<M: Clone + Send + NetlinkSerializable> Sender<M> for FakeSender<M> {
339        fn send(&mut self, message: NetlinkMessage<M>, group: Option<ModernGroup>) {
340            self.sender
341                .unbounded_send(SentMessage { message, group })
342                .expect("unable to send message");
343        }
344    }
345
346    pub(crate) struct FakeSenderSink<M> {
347        receiver: futures::channel::mpsc::UnboundedReceiver<SentMessage<M>>,
348    }
349
350    impl<M> FakeSenderSink<M> {
351        pub(crate) fn take_messages(&mut self) -> Vec<SentMessage<M>> {
352            let mut messages = Vec::new();
353            while let Some(msg_opt) = self.receiver.next().now_or_never() {
354                match msg_opt {
355                    Some(msg) => messages.push(msg),
356                    None => return messages, // Stream closed.
357                };
358            }
359            // All receiver messages that were ready were added.
360            messages
361        }
362
363        pub(crate) async fn next_message(&mut self) -> SentMessage<M> {
364            self.receiver.next().await.expect("receiver unexpectedly closed")
365        }
366    }
367
368    pub(crate) fn fake_sender_with_sink<M>() -> (FakeSender<M>, FakeSenderSink<M>) {
369        let (sender, receiver) = futures::channel::mpsc::unbounded();
370        (FakeSender { sender }, FakeSenderSink { receiver })
371    }
372
373    #[derive(Default, Debug, Clone)]
374    pub(crate) struct FakeCreds {
375        error: Option<Errno>,
376    }
377
378    impl FakeCreds {
379        pub fn with_error(error: Errno) -> Self {
380            FakeCreds { error: Some(error) }
381        }
382    }
383
384    #[derive(Default, Clone)]
385    pub(crate) struct FakeAccessControl {}
386
387    impl AccessControl<FakeCreds> for FakeAccessControl {
388        fn grant_assess(&self, creds: &FakeCreds, _perm: Permission) -> Result<(), Errno> {
389            if let Some(ref error) = creds.error { Err(*error) } else { Ok(()) }
390        }
391    }
392
393    pub(crate) struct TestNetlinkContext;
394
395    impl NetlinkContext for TestNetlinkContext {
396        type Creds = FakeCreds;
397        type Sender<M: Clone + NetlinkSerializable + Send> = FakeSender<M>;
398        type Receiver<
399            M: Send + MessageWithPermission + NetlinkDeserializable<Error: Into<DecodeError>>,
400        > = mpsc::Receiver<NetlinkMessageWithCreds<NetlinkMessage<M>, Self::Creds>>;
401        type AccessControl<'a> = FakeAccessControl;
402    }
403}