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}