netlink_packet_route/rtnl/link/
message.rs

1// SPDX-License-Identifier: MIT
2
3use anyhow::Context;
4use netlink_packet_utils::{
5    traits::{Emitable, Parseable, ParseableParametrized},
6    DecodeError,
7};
8
9use crate::{nlas::link::Nla, LinkHeader, LinkMessageBuffer};
10
11#[derive(Debug, PartialEq, Eq, Clone, Default)]
12#[non_exhaustive]
13pub struct LinkMessage {
14    pub header: LinkHeader,
15    pub nlas: Vec<Nla>,
16}
17
18impl Emitable for LinkMessage {
19    fn buffer_len(&self) -> usize {
20        self.header.buffer_len() + self.nlas.as_slice().buffer_len()
21    }
22
23    fn emit(&self, buffer: &mut [u8]) {
24        self.header.emit(buffer);
25        self.nlas
26            .as_slice()
27            .emit(&mut buffer[self.header.buffer_len()..]);
28    }
29}
30
31impl<'a, T: AsRef<[u8]> + 'a> Parseable<LinkMessageBuffer<&'a T>>
32    for LinkMessage
33{
34    fn parse(buf: &LinkMessageBuffer<&'a T>) -> Result<Self, DecodeError> {
35        let header = LinkHeader::parse(buf)
36            .context("failed to parse link message header")?;
37        let interface_family = header.interface_family;
38        let nlas = Vec::<Nla>::parse_with_param(buf, interface_family)
39            .context("failed to parse link message NLAs")?;
40        Ok(LinkMessage { header, nlas })
41    }
42}
43
44impl<'a, T: AsRef<[u8]> + 'a>
45    ParseableParametrized<LinkMessageBuffer<&'a T>, u16> for Vec<Nla>
46{
47    fn parse_with_param(
48        buf: &LinkMessageBuffer<&'a T>,
49        family: u16,
50    ) -> Result<Self, DecodeError> {
51        let mut nlas = vec![];
52        for nla_buf in buf.nlas() {
53            nlas.push(Nla::parse_with_param(&nla_buf?, family)?);
54        }
55        Ok(nlas)
56    }
57}
58
59impl<'a, T: AsRef<[u8]> + 'a>
60    ParseableParametrized<LinkMessageBuffer<&'a T>, u8> for Vec<Nla>
61{
62    fn parse_with_param(
63        buf: &LinkMessageBuffer<&'a T>,
64        family: u8,
65    ) -> Result<Self, DecodeError> {
66        Vec::<Nla>::parse_with_param(buf, u16::from(family))
67    }
68}
69
70#[cfg(test)]
71mod test {
72    use crate::{
73        constants::*,
74        nlas::link::{Nla, State},
75        LinkHeader, LinkMessage, LinkMessageBuffer,
76    };
77
78    use netlink_packet_utils::traits::{Emitable, ParseableParametrized};
79
80    #[rustfmt::skip]
81    static HEADER: [u8; 96] = [
82        0x00, // interface family
83        0x00, // reserved
84        0x04, 0x03, // link layer type 772 = loopback
85        0x01, 0x00, 0x00, 0x00, // interface index = 1
86        // Note: in the wireshark capture, the thrid byte is 0x01
87        // but that does not correpond to any of the IFF_ flags...
88        0x49, 0x00, 0x00, 0x00, // device flags: UP, LOOPBACK, RUNNING, LOWERUP
89        0x00, 0x00, 0x00, 0x00, // reserved 2 (aka device change flag)
90
91        // nlas
92        0x07, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x00, // device name L=7,T=3,V=lo
93        0x00, // padding
94        0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, // TxQueue length L=8,T=13,V=1000
95        0x05, 0x00, 0x10, 0x00, 0x00, // OperState L=5,T=16,V=0 (unknown)
96        0x00, 0x00, 0x00, // padding
97        0x05, 0x00, 0x11, 0x00, 0x00, // Link mode L=5,T=17,V=0
98        0x00, 0x00, 0x00, // padding
99        0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, // MTU L=8,T=4,V=65536
100        0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, // Group L=8,T=27,V=9
101        0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, // Promiscuity L=8,T=30,V=0
102        0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, // Number of Tx Queues L=8,T=31,V=1
103        0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, // Maximum GSO segment count L=8,T=40,V=65536
104        0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, // Maximum GSO size L=8,T=41,V=65536
105    ];
106
107    #[test]
108    fn packet_header_read() {
109        let packet = LinkMessageBuffer::new(&HEADER[0..16]);
110        assert_eq!(packet.interface_family(), 0);
111        assert_eq!(packet.reserved_1(), 0);
112        assert_eq!(packet.link_layer_type(), ARPHRD_LOOPBACK);
113        assert_eq!(packet.link_index(), 1);
114        assert_eq!(packet.flags(), IFF_UP | IFF_LOOPBACK | IFF_RUNNING);
115        assert_eq!(packet.change_mask(), 0);
116    }
117
118    #[test]
119    fn packet_header_build() {
120        let mut buf = vec![0xff; 16];
121        {
122            let mut packet = LinkMessageBuffer::new(&mut buf);
123            packet.set_interface_family(0);
124            packet.set_reserved_1(0);
125            packet.set_link_layer_type(ARPHRD_LOOPBACK);
126            packet.set_link_index(1);
127            packet.set_flags(IFF_UP | IFF_LOOPBACK | IFF_RUNNING);
128            packet.set_change_mask(0);
129        }
130        assert_eq!(&buf[..], &HEADER[0..16]);
131    }
132
133    #[test]
134    fn packet_nlas_read() {
135        let packet = LinkMessageBuffer::new(&HEADER[..]);
136        assert_eq!(packet.nlas().count(), 10);
137        let mut nlas = packet.nlas();
138
139        // device name L=7,T=3,V=lo
140        let nla = nlas.next().unwrap().unwrap();
141        nla.check_buffer_length().unwrap();
142        assert_eq!(nla.length(), 7);
143        assert_eq!(nla.kind(), 3);
144        assert_eq!(nla.value(), &[0x6c, 0x6f, 0x00]);
145        let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap();
146        assert_eq!(parsed, Nla::IfName(String::from("lo")));
147
148        // TxQueue length L=8,T=13,V=1000
149        let nla = nlas.next().unwrap().unwrap();
150        nla.check_buffer_length().unwrap();
151        assert_eq!(nla.length(), 8);
152        assert_eq!(nla.kind(), 13);
153        assert_eq!(nla.value(), &[0xe8, 0x03, 0x00, 0x00]);
154        let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap();
155        assert_eq!(parsed, Nla::TxQueueLen(1000));
156
157        // OperState L=5,T=16,V=0 (unknown)
158        let nla = nlas.next().unwrap().unwrap();
159        nla.check_buffer_length().unwrap();
160        assert_eq!(nla.length(), 5);
161        assert_eq!(nla.kind(), 16);
162        assert_eq!(nla.value(), &[0x00]);
163        let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap();
164        assert_eq!(parsed, Nla::OperState(State::Unknown));
165
166        // Link mode L=5,T=17,V=0
167        let nla = nlas.next().unwrap().unwrap();
168        nla.check_buffer_length().unwrap();
169        assert_eq!(nla.length(), 5);
170        assert_eq!(nla.kind(), 17);
171        assert_eq!(nla.value(), &[0x00]);
172        let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap();
173        assert_eq!(parsed, Nla::Mode(0));
174
175        // MTU L=8,T=4,V=65536
176        let nla = nlas.next().unwrap().unwrap();
177        nla.check_buffer_length().unwrap();
178        assert_eq!(nla.length(), 8);
179        assert_eq!(nla.kind(), 4);
180        assert_eq!(nla.value(), &[0x00, 0x00, 0x01, 0x00]);
181        let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap();
182        assert_eq!(parsed, Nla::Mtu(65_536));
183
184        // 0x00, 0x00, 0x00, 0x00,
185        // Group L=8,T=27,V=9
186        let nla = nlas.next().unwrap().unwrap();
187        nla.check_buffer_length().unwrap();
188        assert_eq!(nla.length(), 8);
189        assert_eq!(nla.kind(), 27);
190        assert_eq!(nla.value(), &[0x00, 0x00, 0x00, 0x00]);
191        let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap();
192        assert_eq!(parsed, Nla::Group(0));
193
194        // Promiscuity L=8,T=30,V=0
195        let nla = nlas.next().unwrap().unwrap();
196        nla.check_buffer_length().unwrap();
197        assert_eq!(nla.length(), 8);
198        assert_eq!(nla.kind(), 30);
199        assert_eq!(nla.value(), &[0x00, 0x00, 0x00, 0x00]);
200        let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap();
201        assert_eq!(parsed, Nla::Promiscuity(0));
202
203        // Number of Tx Queues L=8,T=31,V=1
204        // 0x01, 0x00, 0x00, 0x00
205        let nla = nlas.next().unwrap().unwrap();
206        nla.check_buffer_length().unwrap();
207        assert_eq!(nla.length(), 8);
208        assert_eq!(nla.kind(), 31);
209        assert_eq!(nla.value(), &[0x01, 0x00, 0x00, 0x00]);
210        let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap();
211        assert_eq!(parsed, Nla::NumTxQueues(1));
212    }
213
214    #[test]
215    fn emit() {
216        let header = LinkHeader {
217            link_layer_type: ARPHRD_LOOPBACK,
218            index: 1,
219            flags: IFF_UP | IFF_LOOPBACK | IFF_RUNNING | IFF_LOWER_UP,
220            ..Default::default()
221        };
222
223        let nlas = vec![
224            Nla::IfName("lo".into()),
225            Nla::TxQueueLen(1000),
226            Nla::OperState(State::Unknown),
227            Nla::Mode(0),
228            Nla::Mtu(0x1_0000),
229            Nla::Group(0),
230            Nla::Promiscuity(0),
231            Nla::NumTxQueues(1),
232            Nla::GsoMaxSegs(0xffff),
233            Nla::GsoMaxSize(0x1_0000),
234        ];
235
236        let packet = LinkMessage { header, nlas };
237
238        let mut buf = [0; 96];
239
240        assert_eq!(packet.buffer_len(), 96);
241        packet.emit(&mut buf[..]);
242    }
243}