proc_connector/iter.rs
1//! Netlink message iterator for multi-part messages.
2//!
3//! This module contains the `NetlinkMessageIter` struct for iterating
4//! over multiple netlink messages packed into a single receive buffer.
5
6use crate::consts::*;
7use crate::error::{Error, Result};
8use crate::parse::parse_netlink_message;
9use crate::proc_event::ProcEvent;
10
11/// Iterator over multiple netlink messages packed into a single receive buffer.
12///
13/// The netlink protocol can deliver multiple messages in one `recv` call
14/// (multi-part messages). This iterator handles walking through them.
15pub struct NetlinkMessageIter<'a> {
16 buf: &'a [u8],
17 pos: usize,
18 len: usize,
19}
20
21impl<'a> NetlinkMessageIter<'a> {
22 /// Create a new iterator over `len` bytes starting at `buf`.
23 pub fn new(buf: &'a [u8], len: usize) -> Self {
24 NetlinkMessageIter { buf, pos: 0, len }
25 }
26}
27
28impl<'a> Iterator for NetlinkMessageIter<'a> {
29 type Item = Result<Option<ProcEvent>>;
30
31 fn next(&mut self) -> Option<Self::Item> {
32 if self.pos >= self.len {
33 return None;
34 }
35
36 let remaining = self.len - self.pos;
37 if remaining < SIZE_NLMSGHDR {
38 return Some(Err(Error::Truncated));
39 }
40
41 let nlmsg_len = read_u32(&self.buf[self.pos..], 0) as usize;
42 if nlmsg_len < SIZE_NLMSGHDR || nlmsg_len > remaining {
43 return Some(Err(Error::Truncated));
44 }
45
46 let msg_slice = &self.buf[self.pos..self.pos + nlmsg_len];
47 let nlmsg_type = read_u16(msg_slice, 4);
48
49 // Check for end of multi-part message.
50 // Kernel connector protocol uses NLMSG_DONE as the message type for
51 // ALL data messages (including proc events). A true multi-part DONE
52 // has no payload (nlmsg_len == SIZE_NLMSGHDR), while connector data
53 // messages have a cn_msg payload (nlmsg_len > SIZE_NLMSGHDR).
54 if nlmsg_type == NLMSG_DONE && nlmsg_len == SIZE_NLMSGHDR {
55 self.pos = self.len; // consume all remaining
56 return None; // Done is not an event, stop iteration
57 }
58
59 // Parse this single message
60 let result = parse_netlink_message(msg_slice, nlmsg_len);
61
62 // Advance position (aligned)
63 self.pos += nlmsg_align(nlmsg_len);
64
65 Some(result)
66 }
67}
68
69// ---------------------------------------------------------------------------
70// Wire format helpers (private)
71// ---------------------------------------------------------------------------
72
73/// Read a `u32` from a byte slice at a given offset (native endian).
74#[inline]
75fn read_u32(buf: &[u8], off: usize) -> u32 {
76 let arr: [u8; 4] = buf[off..off + 4].try_into().unwrap();
77 u32::from_ne_bytes(arr)
78}
79
80/// Read a `u16` from a byte slice at a given offset (native endian).
81#[inline]
82fn read_u16(buf: &[u8], off: usize) -> u16 {
83 let arr: [u8; 2] = buf[off..off + 2].try_into().unwrap();
84 u16::from_ne_bytes(arr)
85}