neli/
iter.rs

1//! Module for iteration over netlink responses
2
3use std::{fmt::Debug, marker::PhantomData};
4
5use crate::{
6    consts::nl::{NlType, NlmF, Nlmsg},
7    err::NlError,
8    nl::{NlPayload, Nlmsghdr},
9    socket::NlSocketHandle,
10    FromBytesWithInput,
11};
12
13/// Define iteration behavior when traversing a stream of netlink
14/// messages.
15#[derive(PartialEq, Eq)]
16pub enum IterationBehavior {
17    /// End iteration of multi-part messages when a DONE message is
18    /// reached.
19    EndMultiOnDone,
20    /// Iterate indefinitely. Mostly useful for multicast
21    /// subscriptions.
22    IterIndefinitely,
23}
24
25/// Iterator over messages in an
26/// [`NlSocketHandle`][crate::socket::NlSocketHandle] type.
27///
28/// This iterator has two high-level options:
29/// * Iterate indefinitely over messages. This is most
30/// useful in the case of subscribing to messages in a
31/// multicast group.
32/// * Iterate until a message is returned with
33/// [`Nlmsg::Done`][crate::consts::nl::Nlmsg::Done] is set.
34/// This is most useful in the case of request-response workflows
35/// where the iterator will parse and iterate through all of the
36/// messages with [`NlmF::Multi`][crate::consts::nl::NlmF::Multi] set
37/// until a message with
38/// [`Nlmsg::Done`][crate::consts::nl::Nlmsg::Done] is
39/// received at which point [`None`] will be returned indicating the
40/// end of the response.
41pub struct NlMessageIter<'a, T, P> {
42    sock_ref: &'a mut NlSocketHandle,
43    next_is_none: Option<bool>,
44    type_: PhantomData<T>,
45    payload: PhantomData<P>,
46}
47
48impl<'a, T, P> NlMessageIter<'a, T, P>
49where
50    T: NlType + Debug,
51    P: FromBytesWithInput<'a, Input = usize> + Debug,
52{
53    /// Construct a new iterator that yields
54    /// [`Nlmsghdr`][crate::nl::Nlmsghdr] structs from the provided
55    /// buffer. `behavior` set to
56    /// [`IterationBehavior::IterIndefinitely`] will treat
57    /// messages as a never-ending stream.
58    /// [`IterationBehavior::EndMultiOnDone`] will cause
59    /// [`NlMessageIter`] to respect the netlink identifiers
60    /// [`NlmF::Multi`][crate::consts::nl::NlmF::Multi] and
61    /// [`Nlmsg::Done`][crate::consts::nl::Nlmsg::Done].
62    ///
63    /// If `behavior` is [`IterationBehavior::EndMultiOnDone`],
64    /// this means that [`NlMessageIter`] will iterate through
65    /// either exactly one message if
66    /// [`NlmF::Multi`][crate::consts::nl::NlmF::Multi] is not
67    /// set, or through all consecutive messages with
68    /// [`NlmF::Multi`][crate::consts::nl::NlmF::Multi] set until
69    /// a terminating message with
70    /// [`Nlmsg::Done`][crate::consts::nl::Nlmsg::Done] is reached
71    /// at which point [`None`] will be returned by the iterator.
72    pub fn new(sock_ref: &'a mut NlSocketHandle, behavior: IterationBehavior) -> Self {
73        NlMessageIter {
74            sock_ref,
75            next_is_none: if behavior == IterationBehavior::IterIndefinitely {
76                None
77            } else {
78                Some(false)
79            },
80            type_: PhantomData,
81            payload: PhantomData,
82        }
83    }
84
85    fn next<TT, PP>(&mut self) -> Option<Result<Nlmsghdr<TT, PP>, NlError<TT, PP>>>
86    where
87        TT: NlType + Debug,
88        PP: for<'c> FromBytesWithInput<'c, Input = usize> + Debug,
89    {
90        if let Some(true) = self.next_is_none {
91            return None;
92        }
93
94        let next_res = self.sock_ref.recv::<TT, PP>();
95        let next = match next_res {
96            Ok(Some(n)) => n,
97            Ok(None) => return None,
98            Err(e) => return Some(Err(e)),
99        };
100
101        if let NlPayload::Ack(_) = next.nl_payload {
102            self.next_is_none = self.next_is_none.map(|_| true);
103        } else if (!next.nl_flags.contains(&NlmF::Multi)
104            || next.nl_type.into() == Nlmsg::Done.into())
105            && !self.sock_ref.needs_ack
106        {
107            self.next_is_none = self.next_is_none.map(|_| true);
108        }
109
110        Some(Ok(next))
111    }
112}
113
114impl<'a, T, P> Iterator for NlMessageIter<'a, T, P>
115where
116    T: NlType + Debug,
117    P: for<'b> FromBytesWithInput<'b, Input = usize> + Debug,
118{
119    type Item = Result<Nlmsghdr<T, P>, NlError<T, P>>;
120
121    fn next(&mut self) -> Option<Self::Item> {
122        NlMessageIter::next::<T, P>(self)
123    }
124}