neli/
genl.rs

1//! This module contains generic netlink parsing data structures.
2//! This is all handled by the [`Genlmsghdr`][crate::genl::Genlmsghdr]
3//! header struct which contains all of the information needed for
4//! the generic netlink layer.
5//!
6//! # Design decisions
7//!
8//! The generic netlink `attrs` field has been changed to a
9//! [`GenlBuffer`][crate::types::GenlBuffer] of
10//! [`Nlattr`][crate::genl::Nlattr]s instead of the
11//! original [`Vec<u8>`][Vec] to allow simpler parsing at the top
12//! level when one [`Nlattr`][crate::genl::Nlattr] structure is not
13//! nested within another, a use case that is instead handled using
14//! [`AttrHandle`][crate::attr::AttrHandle].
15
16use crate as neli;
17
18use std::io::Cursor;
19
20use crate::{
21    attr::{AttrHandle, AttrHandleMut, Attribute},
22    consts::genl::{Cmd, NlAttrType},
23    err::{DeError, SerError},
24    types::{Buffer, GenlBuffer},
25    FromBytes, FromBytesWithInput, Header, Size, ToBytes, TypeSize,
26};
27
28/// Struct indicating that no user header is in the generic netlink packet.
29#[derive(Debug, PartialEq, Eq, Size, ToBytes, FromBytes)]
30pub struct NoUserHeader;
31
32impl TypeSize for NoUserHeader {
33    fn type_size() -> usize {
34        0
35    }
36}
37
38/// Struct representing generic netlink header and payload
39#[derive(Debug, PartialEq, Eq, Size, ToBytes, FromBytesWithInput, Header)]
40#[neli(to_bytes_bound = "C: Cmd")]
41#[neli(to_bytes_bound = "T: NlAttrType")]
42#[neli(from_bytes_bound = "C: Cmd + TypeSize")]
43#[neli(from_bytes_bound = "T: NlAttrType")]
44#[neli(header_bound = "C: TypeSize")]
45#[neli(from_bytes_bound = "H: TypeSize + FromBytes")]
46#[neli(header_bound = "H: TypeSize")]
47pub struct Genlmsghdr<C, T, H = NoUserHeader> {
48    /// Generic netlink message command
49    pub cmd: C,
50    /// Version of generic netlink family protocol
51    pub version: u8,
52    reserved: u16,
53    /// User specific header to send with netlink packet; defaults to an empty type
54    /// to maintain backwards compatibility
55    pub header: H,
56    /// Attributes included in generic netlink message
57    #[neli(input = "input - Self::header_size()")]
58    attrs: GenlBuffer<T, Buffer>,
59}
60
61impl<C, T> Genlmsghdr<C, T>
62where
63    C: Cmd,
64    T: NlAttrType,
65{
66    /// Create new generic netlink packet
67    pub fn new(cmd: C, version: u8, attrs: GenlBuffer<T, Buffer>) -> Self {
68        Genlmsghdr {
69            cmd,
70            version,
71            reserved: 0,
72            header: NoUserHeader,
73            attrs,
74        }
75    }
76
77    /// Get handle for attribute parsing and traversal
78    pub fn get_attr_handle(&self) -> AttrHandle<GenlBuffer<T, Buffer>, Nlattr<T, Buffer>> {
79        self.attrs.get_attr_handle()
80    }
81
82    /// Get handle for attribute mutable and traversal
83    pub fn get_attr_handle_mut(
84        &mut self,
85    ) -> AttrHandleMut<GenlBuffer<T, Buffer>, Nlattr<T, Buffer>> {
86        self.attrs.get_attr_handle_mut()
87    }
88}
89
90impl<C, T, H> Genlmsghdr<C, T, H> {
91    /// Create a new netlink struct with a user header
92    pub fn new_with_user_header(
93        cmd: C,
94        version: u8,
95        header: H,
96        attrs: GenlBuffer<T, Buffer>,
97    ) -> Self {
98        Genlmsghdr {
99            cmd,
100            version,
101            reserved: 0,
102            header,
103            attrs,
104        }
105    }
106}
107
108/// The infomation packed into `nla_type` field of `nlattr`
109/// for the C data structure.
110#[derive(Debug, PartialEq, Eq, Clone)]
111pub struct AttrType<T> {
112    /// If true, the payload contains nested attributes.
113    pub nla_nested: bool,
114    /// If true, the payload is in net work byte order.
115    pub nla_network_order: bool,
116    /// Enum representing the type of the attribute payload
117    pub nla_type: T,
118}
119
120impl<T> Size for AttrType<T>
121where
122    T: Size,
123{
124    fn unpadded_size(&self) -> usize {
125        self.nla_type.unpadded_size()
126    }
127}
128
129impl<T> TypeSize for AttrType<T>
130where
131    T: TypeSize,
132{
133    fn type_size() -> usize {
134        T::type_size()
135    }
136}
137
138impl<T> ToBytes for AttrType<T>
139where
140    T: NlAttrType,
141{
142    fn to_bytes(&self, buffer: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
143        let int: u16 = self.into();
144        int.to_bytes(buffer)
145    }
146}
147
148impl<'lt, T> FromBytes<'lt> for AttrType<T>
149where
150    T: NlAttrType,
151{
152    fn from_bytes(buffer: &mut Cursor<&'lt [u8]>) -> Result<Self, DeError> {
153        let int = u16::from_bytes(buffer)?;
154        Ok(AttrType::from(int))
155    }
156}
157
158impl<T> From<AttrType<T>> for u16
159where
160    T: NlAttrType,
161{
162    fn from(v: AttrType<T>) -> Self {
163        let mut int: u16 = v.nla_type.into();
164        int |= u16::from(v.nla_nested) << 15;
165        int |= u16::from(v.nla_network_order) << 14;
166        int
167    }
168}
169
170impl<'a, T> From<&'a AttrType<T>> for u16
171where
172    T: NlAttrType,
173{
174    fn from(v: &'a AttrType<T>) -> Self {
175        let mut int: u16 = v.nla_type.into();
176        int |= u16::from(v.nla_nested) << 15;
177        int |= u16::from(v.nla_network_order) << 14;
178        int
179    }
180}
181
182impl<T> From<u16> for AttrType<T>
183where
184    T: NlAttrType,
185{
186    fn from(int: u16) -> Self {
187        AttrType {
188            nla_nested: (int & 1 << 15) == (1 << 15),
189            nla_network_order: (int & 1 << 14) == (1 << 14),
190            nla_type: T::from(!(3 << 14) & int),
191        }
192    }
193}
194
195/// Struct representing netlink attributes and payloads
196#[derive(Debug, PartialEq, Eq, Size, FromBytes, ToBytes, Header)]
197#[neli(from_bytes_bound = "T: NlAttrType")]
198#[neli(from_bytes_bound = "P: FromBytesWithInput<Input = usize>")]
199#[neli(to_bytes_bound = "T: NlAttrType")]
200#[neli(header_bound = "T: TypeSize")]
201#[neli(padding)]
202pub struct Nlattr<T, P> {
203    /// Length of the attribute header and payload together
204    pub nla_len: u16,
205    /// Type information for the netlink attribute
206    pub nla_type: AttrType<T>,
207    /// Payload of the attribute - either parsed or a binary buffer
208    #[neli(input = "nla_len as usize - Self::header_size()")]
209    pub nla_payload: P,
210}
211
212impl<T> Nlattr<T, Buffer>
213where
214    T: NlAttrType,
215{
216    /// Create a new `Nlattr` with parameters for setting bitflags
217    /// in the header.
218    pub fn new<P>(
219        nla_nested: bool,
220        nla_network_order: bool,
221        nla_type: T,
222        nla_payload: P,
223    ) -> Result<Self, SerError>
224    where
225        P: Size + ToBytes,
226    {
227        let mut attr = Nlattr {
228            nla_len: Self::header_size() as u16,
229            nla_type: AttrType {
230                nla_nested,
231                nla_network_order,
232                nla_type,
233            },
234            nla_payload: Buffer::new(),
235        };
236        attr.set_payload(&nla_payload)?;
237        Ok(attr)
238    }
239
240    /// Add a nested attribute to the end of the payload.
241    pub fn add_nested_attribute<TT, P>(&mut self, attr: &Nlattr<TT, P>) -> Result<(), SerError>
242    where
243        TT: NlAttrType,
244        P: ToBytes,
245    {
246        let mut buffer = Cursor::new(Vec::new());
247        attr.to_bytes(&mut buffer)?;
248
249        self.nla_payload.extend_from_slice(buffer.get_ref());
250        self.nla_len += buffer.get_ref().len() as u16;
251        Ok(())
252    }
253
254    /// Return an `AttrHandle` for attributes nested in the given attribute payload
255    pub fn get_attr_handle<R>(&self) -> Result<GenlAttrHandle<R>, DeError>
256    where
257        R: NlAttrType,
258    {
259        Ok(AttrHandle::new(GenlBuffer::from_bytes_with_input(
260            &mut Cursor::new(self.nla_payload.as_ref()),
261            self.nla_payload.unpadded_size(),
262        )?))
263    }
264
265    /// Return a mutable `AttrHandle` for attributes nested in the given attribute payload
266    pub fn get_attr_handle_mut<R>(&mut self) -> Result<GenlAttrHandleMut<R>, DeError>
267    where
268        R: NlAttrType,
269    {
270        Ok(AttrHandleMut::new(GenlBuffer::from_bytes_with_input(
271            &mut Cursor::new(self.nla_payload.as_ref()),
272            self.nla_payload.unpadded_size(),
273        )?))
274    }
275}
276
277impl<T> Attribute<T> for Nlattr<T, Buffer>
278where
279    T: NlAttrType,
280{
281    fn payload(&self) -> &Buffer {
282        &self.nla_payload
283    }
284
285    fn set_payload<P>(&mut self, payload: &P) -> Result<(), SerError>
286    where
287        P: Size + ToBytes,
288    {
289        let mut buffer = Cursor::new(Vec::new());
290        payload.to_bytes(&mut buffer)?;
291
292        // Update Nlattr with new length
293        self.nla_len -= self.nla_payload.unpadded_size() as u16;
294        self.nla_len += buffer.get_ref().len() as u16;
295
296        self.nla_payload = Buffer::from(buffer.into_inner());
297
298        Ok(())
299    }
300}
301
302type GenlAttrHandle<'a, T> = AttrHandle<'a, GenlBuffer<T, Buffer>, Nlattr<T, Buffer>>;
303type GenlAttrHandleMut<'a, T> = AttrHandleMut<'a, GenlBuffer<T, Buffer>, Nlattr<T, Buffer>>;
304
305impl<'a, T> AttrHandle<'a, GenlBuffer<T, Buffer>, Nlattr<T, Buffer>>
306where
307    T: NlAttrType,
308{
309    /// Get the payload of an attribute as a handle for parsing
310    /// nested attributes
311    pub fn get_nested_attributes<S>(&mut self, subattr: T) -> Result<GenlAttrHandle<S>, DeError>
312    where
313        S: NlAttrType,
314    {
315        let attr = self
316            .get_attribute(subattr)
317            .ok_or_else(|| DeError::new("Couldn't find specified attribute"))?;
318        Ok(AttrHandle::new(GenlBuffer::from_bytes_with_input(
319            &mut Cursor::new(attr.nla_payload.as_ref()),
320            attr.nla_payload.unpadded_size(),
321        )?))
322    }
323
324    /// Get nested attributes from a parsed handle
325    pub fn get_attribute(&self, t: T) -> Option<&Nlattr<T, Buffer>> {
326        self.get_attrs()
327            .iter()
328            .find(|item| item.nla_type.nla_type == t)
329    }
330
331    /// Parse binary payload as a type that implements [`FromBytes`].
332    pub fn get_attr_payload_as<'b, R>(&'b self, attr: T) -> Result<R, DeError>
333    where
334        R: FromBytes<'b>,
335    {
336        match self.get_attribute(attr) {
337            Some(a) => a.get_payload_as::<R>(),
338            _ => Err(DeError::new("Failed to find specified attribute")),
339        }
340    }
341
342    /// Parse binary payload as a type that implements
343    /// [`FromBytesWithInput`]
344    pub fn get_attr_payload_as_with_len<'b, R>(&'b self, attr: T) -> Result<R, DeError>
345    where
346        R: FromBytesWithInput<'b, Input = usize>,
347    {
348        match self.get_attribute(attr) {
349            Some(a) => a.get_payload_as_with_len::<R>(),
350            _ => Err(DeError::new("Failed to find specified attribute")),
351        }
352    }
353}