Skip to main content

push_packet/events/
copy.rs

1//! Defines events for [`crate::rules::Action::Copy`].
2use std::mem::offset_of;
3
4use aya::maps::ring_buf::RingBufItem;
5use push_packet_common::CopyArgs;
6
7use crate::{cast, rules::RuleId};
8
9/// A packet event captured with [`crate::rules::Action::Copy`]. This will block the ring buffer
10/// until it is [dropped](`Drop`), so it should be consumed quickly. Calling
11/// [`CopyEvent::into_owned`] returns an [`OwnedCopyEvent`], dropping the underlying [`RingBufItem`]
12/// at the cost of a copy.
13#[derive(Debug)]
14pub struct CopyEvent<'a>(RingBufItem<'a>);
15
16impl AsRef<[u8]> for CopyEvent<'_> {
17    fn as_ref(&self) -> &[u8] {
18        self.data()
19    }
20}
21
22impl<'a> From<RingBufItem<'a>> for CopyEvent<'a> {
23    fn from(value: RingBufItem<'a>) -> Self {
24        Self(value)
25    }
26}
27
28fn read_u32(data: &[u8], offset: usize) -> u32 {
29    u32::from_ne_bytes(data[offset..offset + 4].try_into().expect("4 bytes"))
30}
31
32fn parse_take(data: &[u8]) -> Option<u32> {
33    match read_u32(data, offset_of!(CopyArgs, take)) {
34        0 => None,
35        n => Some(n),
36    }
37}
38
39fn parse_rule_id(data: &[u8]) -> RuleId {
40    let rule_id = read_u32(data, offset_of!(CopyArgs, rule_id));
41    RuleId(rule_id)
42}
43
44fn parse_packet_len(data: &[u8]) -> u32 {
45    read_u32(data, offset_of!(CopyArgs, packet_len))
46}
47
48fn parse_data_len(data: &[u8]) -> u32 {
49    parse_take(data).unwrap_or(parse_packet_len(data))
50}
51
52fn parse_data(data: &[u8]) -> &[u8] {
53    let header_len = core::mem::size_of::<CopyArgs>();
54    &data[header_len..(header_len + cast::packet_len_to_usize(parse_data_len(data)))]
55}
56
57impl CopyEvent<'_> {
58    /// If set, returns the number of bytes copied. This may be lower than the originally specified
59    /// value, if the packet is less than the original take.
60    #[must_use]
61    pub fn take(&self) -> Option<u32> {
62        parse_take(&self.0)
63    }
64
65    /// Returns the [`RuleId`] that the packet matched on
66    #[must_use]
67    pub fn rule_id(&self) -> RuleId {
68        parse_rule_id(&self.0)
69    }
70
71    /// Returns the length of the packet data, before take is applied
72    #[must_use]
73    pub fn packet_len(&self) -> u32 {
74        parse_packet_len(&self.0)
75    }
76
77    /// Returns the length of the data, after take has been applied
78    #[must_use]
79    pub fn data_len(&self) -> u32 {
80        parse_data_len(&self.0)
81    }
82
83    /// Returns the raw data copied from the packet
84    #[must_use]
85    pub fn data(&self) -> &[u8] {
86        parse_data(&self.0)
87    }
88
89    /// Converts to an [`OwnedCopyEvent`], dropping the underlying [`RingBufItem`] which frees
90    /// capacity for successive writes.
91    #[must_use]
92    pub fn into_owned(self) -> OwnedCopyEvent {
93        let take = self.take();
94        let rule_id = self.rule_id();
95        let packet_len = self.packet_len();
96        let data = self.data().into();
97        OwnedCopyEvent {
98            take,
99            rule_id,
100            packet_len,
101            data,
102        }
103    }
104}
105
106/// An owned copy event contains a Box containing copied data from the [`RingBufItem`]. This allows
107/// working with the data why freeing up capacity for the [`aya::maps::RingBuf`].
108#[derive(Debug, Clone)]
109pub struct OwnedCopyEvent {
110    take: Option<u32>,
111    rule_id: RuleId,
112    packet_len: u32,
113    data: Box<[u8]>,
114}
115
116impl AsRef<[u8]> for OwnedCopyEvent {
117    fn as_ref(&self) -> &[u8] {
118        self.data()
119    }
120}
121
122impl OwnedCopyEvent {
123    /// If set, returns the number of bytes copied. This may be lower than the originally specified
124    /// value, if the packet is less than the original take.
125    #[must_use]
126    pub fn take(&self) -> Option<u32> {
127        self.take
128    }
129
130    /// Returns the [`RuleId`] that the packet matched on
131    #[must_use]
132    pub fn rule_id(&self) -> RuleId {
133        self.rule_id
134    }
135
136    /// Returns the length of the packet data, before take is applied
137    #[must_use]
138    pub fn packet_len(&self) -> u32 {
139        self.packet_len
140    }
141
142    /// Returns the length of the data, after take has been applied
143    #[must_use]
144    pub fn data_len(&self) -> u32 {
145        self.take.unwrap_or(self.packet_len)
146    }
147
148    /// Returns the raw data copied from the packet
149    #[must_use]
150    pub fn data(&self) -> &[u8] {
151        &self.data
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use crate::{
158        events::copy::{parse_packet_len, parse_rule_id, parse_take},
159        rules::RuleId,
160    };
161
162    fn make_packet(take: u32, rule_id: u32, packet_len: u32, data: &[u8]) -> Vec<u8> {
163        let mut packet = vec![];
164        packet.extend_from_slice(&take.to_ne_bytes());
165        packet.extend_from_slice(&rule_id.to_ne_bytes());
166        packet.extend_from_slice(&packet_len.to_ne_bytes());
167        packet.extend_from_slice(data);
168        packet
169    }
170
171    #[test]
172    fn zero_take_is_none() {
173        let packet = make_packet(0, 0, 100, &[0]);
174        assert_eq!(parse_take(&packet), None);
175    }
176
177    #[test]
178    fn nonzero_take_is_some() {
179        let packet = make_packet(10, 0, 100, &[0]);
180        assert_eq!(parse_take(&packet), Some(10));
181    }
182
183    #[test]
184    fn rule_id_parses() {
185        let packet = make_packet(0, 4, 100, &[0]);
186        assert_eq!(parse_rule_id(&packet), RuleId(4));
187    }
188
189    #[test]
190    fn packet_len_parses() {
191        let packet = make_packet(0, 0, 100, &[0]);
192        assert_eq!(parse_packet_len(&packet), 100);
193    }
194}