netlink_packet/netlink/
message.rs

1use crate::constants::*;
2use failure::ResultExt;
3
4use crate::{
5    AckMessage, DecodeError, Emitable, EncodeError, ErrorBuffer, ErrorMessage, NetlinkBuffer,
6    NetlinkHeader, Parseable,
7};
8
9#[cfg(feature = "rtnetlink")]
10use crate::RtnlMessage;
11
12#[cfg(feature = "audit")]
13use crate::AuditMessage;
14
15/// Represent a netlink message.
16///
17/// A netlink message is made of a header (represented by
18/// [`NetlinkHeader`](struct.NetlinkHeader.html)) and message (represented by
19/// [`NetlinkPayload`](enum.NetlinkPayload.html)):
20///
21/// ```no_rust
22/// 0                8                16              24               32
23/// +----------------+----------------+----------------+----------------+
24/// |                 packet length (including header)                  |   \
25/// +----------------+----------------+----------------+----------------+    |
26/// |          message type           |              flags              |    |
27/// +----------------+----------------+----------------+----------------+    | NetlinkHeader
28/// |                           sequence number                         |    |
29/// +----------------+----------------+----------------+----------------+    |
30/// |                   port number (formerly known as PID)             |   /
31/// +----------------+----------------+----------------+----------------+   
32/// |                               payload                             |   \
33/// |                          (variable length)                        |    |  NetlinkPayload
34/// |                                                                   |    |
35/// |                                                                   |   /
36/// +----------------+----------------+----------------+----------------+
37/// ```
38#[derive(Debug, PartialEq, Eq, Clone)]
39pub struct NetlinkMessage {
40    header: NetlinkHeader,
41    payload: NetlinkPayload,
42}
43
44#[derive(Debug, PartialEq, Eq, Clone)]
45pub enum NetlinkPayload {
46    Done,
47    Error(ErrorMessage),
48    Ack(AckMessage),
49    Noop,
50    Overrun(Vec<u8>),
51    #[cfg(feature = "rtnetlink")]
52    Rtnl(RtnlMessage),
53    #[cfg(feature = "audit")]
54    Audit(AuditMessage),
55    #[cfg(not(any(feature = "rtnetlink", feature = "audit")))]
56    #[doc(hidden)]
57    __Default,
58}
59
60impl NetlinkPayload {
61    pub fn message_type(&self) -> u16 {
62        use self::NetlinkPayload::*;
63
64        match self {
65            Noop => NLMSG_NOOP,
66            Done => NLMSG_DONE,
67            Error(_) | Ack(_) => NLMSG_ERROR,
68            Overrun(_) => NLMSG_OVERRUN,
69            #[cfg(feature = "rtnetlink")]
70            Rtnl(ref msg) => msg.message_type(),
71            #[cfg(feature = "audit")]
72            Audit(ref msg) => msg.message_type(),
73            #[cfg(not(any(feature = "rtnetlink", feature = "audit")))]
74            _ => 0,
75        }
76    }
77
78    #[cfg(feature = "rtnetlink")]
79    pub fn is_rtnl(&self) -> bool {
80        if let NetlinkPayload::Rtnl(_) = *self {
81            true
82        } else {
83            false
84        }
85    }
86
87    #[cfg(feature = "audit")]
88    pub fn is_audit(&self) -> bool {
89        if let NetlinkPayload::Audit(_) = *self {
90            true
91        } else {
92            false
93        }
94    }
95
96    pub fn is_done(&self) -> bool {
97        *self == NetlinkPayload::Done
98    }
99
100    pub fn is_noop(&self) -> bool {
101        *self == NetlinkPayload::Noop
102    }
103
104    pub fn is_overrun(&self) -> bool {
105        if let NetlinkPayload::Overrun(_) = *self {
106            true
107        } else {
108            false
109        }
110    }
111
112    pub fn is_error(&self) -> bool {
113        if let NetlinkPayload::Error(_) = *self {
114            true
115        } else {
116            false
117        }
118    }
119
120    pub fn is_ack(&self) -> bool {
121        if let NetlinkPayload::Ack(_) = *self {
122            true
123        } else {
124            false
125        }
126    }
127}
128
129impl From<NetlinkPayload> for NetlinkMessage {
130    fn from(payload: NetlinkPayload) -> Self {
131        NetlinkMessage {
132            header: NetlinkHeader::default(),
133            payload,
134        }
135    }
136}
137
138#[cfg(feature = "rtnetlink")]
139impl From<RtnlMessage> for NetlinkMessage {
140    fn from(msg: RtnlMessage) -> Self {
141        NetlinkMessage::from(NetlinkPayload::Rtnl(msg))
142    }
143}
144
145#[cfg(feature = "audit")]
146impl From<AuditMessage> for NetlinkMessage {
147    fn from(msg: AuditMessage) -> Self {
148        NetlinkMessage::from(NetlinkPayload::Audit(msg))
149    }
150}
151
152impl NetlinkMessage {
153    pub fn new(header: NetlinkHeader, payload: NetlinkPayload) -> Self {
154        NetlinkMessage { header, payload }
155    }
156
157    pub fn into_parts(self) -> (NetlinkHeader, NetlinkPayload) {
158        (self.header, self.payload)
159    }
160
161    pub fn payload(&self) -> &NetlinkPayload {
162        &self.payload
163    }
164
165    pub fn payload_mut(&mut self) -> &mut NetlinkPayload {
166        &mut self.payload
167    }
168
169    pub fn header(&self) -> &NetlinkHeader {
170        &self.header
171    }
172
173    pub fn header_mut(&mut self) -> &mut NetlinkHeader {
174        &mut self.header
175    }
176
177    /// Safely serialize the message. Under the hood, this calls
178    /// [`Emitable::emit()`](trait.Emitable.html#tymethod.emit), but unlike `emit()`, this method
179    /// does not panic if the message is malformed or if the destination buffer is too small.
180    /// Instead, an error is returned.
181    #[allow(clippy::wrong_self_convention)]
182    pub fn to_bytes(&mut self, buffer: &mut [u8]) -> Result<usize, EncodeError> {
183        self.finalize();
184        if self.header().length() as usize > buffer.len() {
185            Err(EncodeError::from("buffer exhausted"))
186        } else {
187            self.emit(buffer);
188            Ok(self.header().length() as usize)
189        }
190    }
191
192    /// Try to parse a message from a buffer
193    pub fn from_bytes(buffer: &[u8]) -> Result<Self, DecodeError> {
194        Ok(NetlinkBuffer::new_checked(&buffer)
195            .context("failed to parse netlink packet")?
196            .parse()
197            .context("failed to parse netlink packet")?)
198    }
199
200    /// Check if the payload is a `NLMSG_DONE` message
201    /// ([`Rtnl::Done`](enum.NetlinkPayload.html#variant.Done))
202    pub fn is_done(&self) -> bool {
203        self.payload().is_done()
204    }
205
206    /// Check if the payload is a `NLMSG_NOOP` message
207    /// ([`Rtnl::Noop`](enum.NetlinkPayload.html#variant.Noop))
208    pub fn is_noop(&self) -> bool {
209        self.payload().is_noop()
210    }
211
212    /// Check if the payload is a `NLMSG_OVERRUN` message
213    /// ([`Rtnl::Overrun`](enum.NetlinkPayload.html#variant.Overrun))
214    pub fn is_overrun(&self) -> bool {
215        self.payload().is_overrun()
216    }
217
218    /// Check if the payload is a `NLMSG_ERROR` message with a negative error code
219    /// ([`Rtnl::Error`](enum.NetlinkPayload.html#variant.Error))
220    pub fn is_error(&self) -> bool {
221        self.payload().is_error()
222    }
223
224    /// Check if the payload is a `NLMSG_ERROR` message with a non-negative error code
225    /// ([`Rtnl::Ack`](enum.NetlinkPayload.html#variant.Ack))
226    pub fn is_ack(&self) -> bool {
227        self.payload().is_ack()
228    }
229
230    #[cfg(feature = "rtnetlink")]
231    pub fn is_rtnl(&self) -> bool {
232        self.payload().is_rtnl()
233    }
234
235    #[cfg(feature = "audit")]
236    pub fn is_audit(&self) -> bool {
237        self.payload().is_audit()
238    }
239
240    /// Ensure the header (`NetlinkHeader`) is consistent with the payload (`NetlinkPayload`):
241    ///
242    /// - compute the payload length and set the header's length field
243    /// - check the payload type and set the header's message type field accordingly
244    ///
245    /// If you are not 100% sure the header is correct, this method should be called before calling
246    /// [`Emitable::emit()`](trait.Emitable.html#tymethod.emit) or
247    /// [`to_bytes()`](#method.to_bytes). `emit()` could panic if the header is inconsistent with
248    /// the rest of the message, and `to_bytes()` would return an error.
249    pub fn finalize(&mut self) {
250        *self.header.length_mut() = self.buffer_len() as u32;
251        *self.header.message_type_mut() = self.payload.message_type();
252    }
253}
254
255impl<'buffer, T: AsRef<[u8]> + 'buffer> Parseable<NetlinkMessage> for NetlinkBuffer<&'buffer T> {
256    fn parse(&self) -> Result<NetlinkMessage, DecodeError> {
257        use self::NetlinkPayload::*;
258        let header = <Self as Parseable<NetlinkHeader>>::parse(self)
259            .context("failed to parse netlink header")?;
260
261        let payload = match header.message_type() {
262            NLMSG_ERROR => {
263                let msg: ErrorMessage = ErrorBuffer::new(&self.payload())
264                    .parse()
265                    .context("failed to parse NLMSG_ERROR")?;
266                if msg.code >= 0 {
267                    Ack(msg as AckMessage)
268                } else {
269                    Error(msg)
270                }
271            }
272            NLMSG_NOOP => Noop,
273            NLMSG_DONE => Done,
274
275            #[cfg(feature = "rtnetlink")]
276            message_type => {
277                NetlinkPayload::Rtnl(RtnlMessage::parse(message_type, &self.payload())?)
278            }
279
280            #[cfg(feature = "audit")]
281            message_type => {
282                NetlinkPayload::Audit(AuditMessage::parse(message_type, &self.payload())?)
283            }
284
285            #[cfg(not(any(feature = "rtnetlink", feature = "audit")))]
286            _ => __Default,
287        };
288        Ok(NetlinkMessage { header, payload })
289    }
290}
291
292impl Emitable for NetlinkMessage {
293    fn buffer_len(&self) -> usize {
294        use self::NetlinkPayload::*;
295        let payload_len = match self.payload {
296            Noop | Done => 0,
297            Overrun(ref bytes) => bytes.len(),
298            Error(ref msg) => msg.buffer_len(),
299            Ack(ref msg) => msg.buffer_len(),
300
301            #[cfg(feature = "rtnetlink")]
302            Rtnl(ref msg) => msg.buffer_len(),
303
304            #[cfg(feature = "audit")]
305            Audit(ref msg) => msg.buffer_len(),
306
307            #[cfg(not(any(feature = "rtnetlink", feature = "audit")))]
308            __Default => 0,
309        };
310
311        self.header.buffer_len() + payload_len
312    }
313
314    fn emit(&self, buffer: &mut [u8]) {
315        use self::NetlinkPayload::*;
316
317        self.header.emit(buffer);
318
319        let buffer = &mut buffer[self.header.buffer_len()..];
320        match self.payload {
321            Noop | Done => {}
322            Overrun(ref bytes) => buffer.copy_from_slice(bytes),
323            Error(ref msg) => msg.emit(buffer),
324            Ack(ref msg) => msg.emit(buffer),
325
326            #[cfg(feature = "rtnetlink")]
327            Rtnl(ref msg) => msg.emit(buffer),
328
329            #[cfg(feature = "audit")]
330            Audit(ref msg) => msg.emit(buffer),
331
332            #[cfg(not(any(feature = "rtnetlink", feature = "audit")))]
333            __Default => {}
334        }
335    }
336}