netlink_rust/core/
attribute.rs

1use std::ffi::{CStr, CString};
2use std::mem;
3use std::str;
4
5use crate::core::hardware_address::HardwareAddress;
6use crate::core::message::netlink_padding;
7use crate::core::pack::{NativePack, NativeUnpack};
8use crate::errors::{NetlinkError, NetlinkErrorKind, Result};
9
10/// Parsing an array of nested attributes
11///
12/// Each chunk of attributes has a size and an index, the size is the size of
13/// the chunk including the header
14///
15/// ```text
16/// ---------------------------------------------------------------
17/// | size | index | attributes ... | size | index | attributes ...
18/// ---------------------------------------------------------------
19///    u16    u16    u8 * (size - 4)
20/// ```
21pub fn nested_attribute_array(data: &[u8]) -> Vec<Vec<Attribute>> {
22    let vs = mem::size_of::<u16>();
23    let mut attrs = vec![];
24    let mut d = &data[..];
25    while d.len() > (vs * 2) {
26        let size = u16::unpack(&d).unwrap();
27        let _index = u16::unpack(&d[vs..]).unwrap();
28        if d.len() > size as usize {
29            let (_, attributes) = Attribute::unpack_all(&d[(vs * 2)..size as usize]);
30            attrs.push(attributes);
31        } else {
32            break;
33        }
34        d = &d[size as usize..];
35    }
36    attrs
37}
38
39/// Netlink attribute
40///
41/// ```text
42/// | length | identifier |        data        | padding |
43/// |--------|------------|--------------------|---------|
44/// |   u16  |     u16    |  u8 * (length - 4) |         |
45/// ```
46///
47/// The data is 4 byte aligned.
48#[derive(Clone)]
49pub struct Attribute {
50    /// Attribute identifier
51    pub identifier: u16,
52    /// Attribute data
53    data: Vec<u8>,
54}
55
56impl Attribute {
57    const HEADER_SIZE: usize = 4;
58
59    /// Unpack all attributes in the byte slice
60    pub fn unpack_all(data: &[u8]) -> (usize, Vec<Attribute>) {
61        let mut pos = 0usize;
62        let mut attrs = vec![];
63        loop {
64            match Attribute::unpack_with_size(&data[pos..]) {
65                Ok(r) => {
66                    attrs.push(r.1);
67                    pos += r.0;
68                }
69                Err(_) => {
70                    break;
71                }
72            }
73        }
74        (pos, attrs)
75    }
76
77    /// Create a new string attribute with provided identifier
78    pub fn new_bytes<ID: Into<u16>>(identifier: ID, value: &[u8]) -> Attribute {
79        Attribute {
80            identifier: identifier.into(),
81            data: value.to_vec(),
82        }
83    }
84
85    /// Create a new string attribute with provided identifier
86    pub fn new_string_with_nul<ID: Into<u16>>(identifier: ID, value: &str) -> Attribute {
87        let c_string = CString::new(value).unwrap();
88        Attribute {
89            identifier: identifier.into(),
90            data: c_string.into_bytes_with_nul(),
91        }
92    }
93
94    /// Create a new string attribute with provided identifier
95    pub fn new_string<ID: Into<u16>>(identifier: ID, value: &str) -> Attribute {
96        let string = CString::new(value).unwrap();
97        Attribute {
98            identifier: identifier.into(),
99            data: string.into_bytes(),
100        }
101    }
102
103    /// Create a new attribute from a type that can be packed into a byte slice
104    pub fn new<ID: Into<u16>, V: NativePack>(identifier: ID, value: V) -> Attribute {
105        let mut data = vec![0u8; mem::size_of::<V>()];
106        value.pack_unchecked(&mut data);
107        Attribute {
108            identifier: identifier.into(),
109            data: data,
110        }
111    }
112
113    /// Get the length of the data
114    pub fn len(&self) -> u16 {
115        self.data.len() as u16
116    }
117    /// Get the length of the data and header
118    pub fn total_len(&self) -> usize {
119        self.data.len() + Attribute::HEADER_SIZE
120    }
121    /// Unpack the underlying data into a u8
122    pub fn as_u8(&self) -> Result<u8> {
123        u8::unpack(&self.data)
124    }
125    /// Unpack the underlying data into a u16
126    pub fn as_u16(&self) -> Result<u16> {
127        u16::unpack(&self.data)
128    }
129    /// Unpack the underlying data into a u32
130    pub fn as_u32(&self) -> Result<u32> {
131        u32::unpack(&self.data)
132    }
133    /// Unpack the underlying data into a u64
134    pub fn as_u64(&self) -> Result<u64> {
135        u64::unpack(&self.data)
136    }
137    /// Unpack the underlying data into a i8
138    pub fn as_i8(&self) -> Result<i8> {
139        i8::unpack(&self.data)
140    }
141    /// Unpack the underlying data into a i16
142    pub fn as_i16(&self) -> Result<i16> {
143        i16::unpack(&self.data)
144    }
145    /// Unpack the underlying data into a i32
146    pub fn as_i32(&self) -> Result<i32> {
147        i32::unpack(&self.data)
148    }
149    /// Unpack the underlying data into a i64
150    pub fn as_i64(&self) -> Result<i64> {
151        i64::unpack(&self.data)
152    }
153    /// Unpack the underlying data into a String
154    pub fn as_string(&self) -> Result<String> {
155        match CStr::from_bytes_with_nul(&self.data) {
156            Ok(bytes) => {
157                let s = bytes.to_str()?;
158                Ok(String::from(s))
159            }
160            Err(_) => {
161                let s = String::from_utf8(self.data.clone())?;
162                Ok(s)
163            }
164        }
165    }
166    /// Unpack the underlying data into a HardwareAddress
167    pub fn as_hardware_address(&self) -> Result<HardwareAddress> {
168        HardwareAddress::unpack(&self.data)
169    }
170    /// Get a clone of the underlying data
171    pub fn as_bytes(&self) -> Vec<u8> {
172        self.data.clone()
173    }
174}
175
176impl NativePack for Attribute {
177    fn pack_size(&self) -> usize {
178        self.total_len()
179    }
180    fn pack<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8]> {
181        let length = self.total_len() as u16;
182        let slice = length.pack(buffer)?;
183        let slice = self.identifier.pack(slice)?;
184        let slice = self.data.pack(slice)?;
185        let padding = netlink_padding(self.data.len());
186        Ok(&mut slice[padding..])
187    }
188    fn pack_unchecked(&self, buffer: &mut [u8]) {
189        let length = self.total_len() as u16;
190        length.pack_unchecked(buffer);
191        self.identifier.pack_unchecked(&mut buffer[2..]);
192        self.data.pack_unchecked(&mut buffer[4..]);
193    }
194}
195
196impl NativeUnpack for Attribute {
197    fn unpack_with_size(buffer: &[u8]) -> Result<(usize, Self)> {
198        if buffer.len() < Attribute::HEADER_SIZE {
199            return Err(NetlinkError::new(NetlinkErrorKind::NotEnoughData).into());
200        }
201        let length = u16::unpack_unchecked(buffer) as usize;
202        let identifier = u16::unpack_unchecked(&buffer[2..]);
203
204        let padding = netlink_padding(length);
205        if buffer.len() < (length + padding) {
206            return Err(NetlinkError::new(NetlinkErrorKind::NotEnoughData).into());
207        }
208        let attr_data = (&buffer[4..length]).to_vec();
209        Ok((
210            length + padding,
211            Attribute {
212                identifier: identifier,
213                data: attr_data,
214            },
215        ))
216    }
217    fn unpack_unchecked(buffer: &[u8]) -> Self {
218        let length = u16::unpack_unchecked(buffer) as usize;
219        let identifier = u16::unpack_unchecked(&buffer[2..]);
220        let attr_data = (&buffer[4..length]).to_vec();
221        Attribute {
222            identifier: identifier,
223            data: attr_data,
224        }
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231
232    #[test]
233    fn unpack_attribute() {
234        let data = [
235            0x07, 0x00, // size
236            0x00, 0x10, // identifier
237            0x11, 0xaa, 0x55, // data
238            0xee, // padding
239        ];
240        let (used, attr) = Attribute::unpack_with_size(&data).unwrap();
241        assert_eq!(used, 8);
242        assert_eq!(attr.data.len(), 3usize);
243        assert_eq!(attr.identifier, 0x1000u16);
244        assert_eq!(attr.data[0], 0x11);
245        assert_eq!(attr.data[1], 0xaa);
246        assert_eq!(attr.data[2], 0x55);
247
248        let data = [
249            0x08, 0x00, // size
250            0x00, 0x10, // identifier
251            0x11, 0xaa, 0x55, 0xee, // data
252        ];
253        let (used, attr) = Attribute::unpack_with_size(&data).unwrap();
254        assert_eq!(used, 8);
255        assert_eq!(attr.data.len(), 4usize);
256        assert_eq!(attr.identifier, 0x1000u16);
257        assert_eq!(attr.data[0], 0x11);
258        assert_eq!(attr.data[1], 0xaa);
259        assert_eq!(attr.data[2], 0x55);
260        assert_eq!(attr.data[3], 0xee);
261    }
262
263    #[test]
264    fn unpack_attributes() {
265        let data = [
266            0x07, 0x00, // size
267            0x00, 0x10, // identifier
268            0x11, 0xaa, 0x55, // data
269            0xee, // padding
270            0x08, 0x00, // size
271            0x00, 0x10, // identifier
272            0x11, 0xaa, 0x55, 0xee, // data
273        ];
274        let (used, attrs) = Attribute::unpack_all(&data);
275        assert_eq!(used, 16usize);
276        assert_eq!(attrs.len(), 2usize);
277
278        assert_eq!(attrs[0].data.len(), 3usize);
279        assert_eq!(attrs[0].identifier, 0x1000u16);
280        assert_eq!(attrs[0].data[0], 0x11);
281        assert_eq!(attrs[0].data[1], 0xaa);
282        assert_eq!(attrs[0].data[2], 0x55);
283
284        assert_eq!(attrs[1].data.len(), 4usize);
285        assert_eq!(attrs[1].identifier, 0x1000u16);
286        assert_eq!(attrs[1].data[0], 0x11);
287        assert_eq!(attrs[1].data[1], 0xaa);
288        assert_eq!(attrs[1].data[2], 0x55);
289        assert_eq!(attrs[1].data[3], 0xee);
290    }
291
292    #[test]
293    fn pack_attribute() {
294        let data = [
295            0x07, 0x00, // size
296            0x55, 0x81, // identifier
297            0x11, 0xaa, 0x55, // data
298            0x00, // padding
299        ];
300        let attr = Attribute {
301            identifier: 0x8155,
302            data: vec![0x11, 0xaa, 0x55],
303        };
304        let mut buffer = [0u8; 32];
305        {
306            let slice = attr.pack(&mut buffer).unwrap();
307            assert_eq!(slice.len(), 24);
308        }
309        assert_eq!(&buffer[0..8], data);
310    }
311}