neli/
genl.rs

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