netlink_packet_route/rtnl/link/nlas/
link_xdp.rs

1// SPDX-License-Identifier: MIT
2
3use std::{convert::TryFrom, mem::size_of, os::fd::RawFd};
4
5use anyhow::Context;
6use byteorder::{ByteOrder, NativeEndian};
7use netlink_packet_utils::{
8    nla::{DefaultNla, Nla, NlaBuffer, NlasIterator},
9    parsers::{parse_i32, parse_u32},
10    DecodeError, Parseable,
11};
12
13use crate::constants::*;
14
15#[non_exhaustive]
16#[derive(Debug, PartialEq, Eq, Clone)]
17pub enum Xdp {
18    Fd(RawFd),
19    Attached(XdpAttached),
20    Flags(u32),
21    ProgId(u32),
22    DrvProgId(u32),
23    SkbProgId(u32),
24    HwProgId(u32),
25    ExpectedFd(u32),
26    Other(DefaultNla),
27}
28
29impl Nla for Xdp {
30    #[rustfmt::skip]
31    fn value_len(&self) -> usize {
32        use self::Xdp::*;
33        match self {
34            Fd(_) => size_of::<RawFd>(),
35            Attached(_) => size_of::<u8>(),
36            Flags(_) => size_of::<u32>(),
37            ProgId(_) => size_of::<u32>(),
38            DrvProgId(_) => size_of::<u32>(),
39            SkbProgId(_) => size_of::<u32>(),
40            HwProgId(_) => size_of::<u32>(),
41            ExpectedFd(_) => size_of::<u32>(),
42            Other(nla) => nla.value_len()
43        }
44    }
45
46    #[rustfmt::skip]
47    fn emit_value(&self, buffer: &mut [u8]) {
48        use self::Xdp::*;
49        match self {
50            Fd(ref value) => NativeEndian::write_i32(buffer, *value),
51            Attached(ref value) => buffer[0] = value.as_u8(),
52            Flags(ref value) => NativeEndian::write_u32(buffer, *value),
53            ProgId(ref value) => NativeEndian::write_u32(buffer, *value),
54            DrvProgId(ref value) => NativeEndian::write_u32(buffer, *value),
55            SkbProgId(ref value) => NativeEndian::write_u32(buffer, *value),
56            HwProgId(ref value) => NativeEndian::write_u32(buffer, *value),
57            ExpectedFd(ref value) => NativeEndian::write_u32(buffer, *value),
58            Other(ref nla) => nla.emit_value(buffer)
59        }
60    }
61
62    fn kind(&self) -> u16 {
63        use self::Xdp::*;
64        match self {
65            Fd(_) => IFLA_XDP_FD as u16,
66            Attached(_) => IFLA_XDP_ATTACHED as u16,
67            Flags(_) => IFLA_XDP_FLAGS as u16,
68            ProgId(_) => IFLA_XDP_PROG_ID as u16,
69            DrvProgId(_) => IFLA_XDP_DRV_PROG_ID as u16,
70            SkbProgId(_) => IFLA_XDP_SKB_PROG_ID as u16,
71            HwProgId(_) => IFLA_XDP_HW_PROG_ID as u16,
72            ExpectedFd(_) => IFLA_XDP_EXPECTED_FD as u16,
73            Other(nla) => nla.kind(),
74        }
75    }
76}
77
78pub(crate) struct VecXdp(pub(crate) Vec<Xdp>);
79
80// These NLAs are nested, meaning they are NLAs that contain NLAs. These NLAs
81// can contain more nested NLAs nla->type     // IFLA_XDP
82// nla->len
83// nla->data[]   // <- You are here == Vec<Xdp>
84//  nla->data[0].type   <- nla.kind()
85//  nla->data[0].len
86impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for VecXdp {
87    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
88        let mut res = Vec::new();
89        let nlas = NlasIterator::new(buf.into_inner());
90        for nla in nlas {
91            let nla = nla?;
92            match nla.kind() as u32 {
93                IFLA_XDP_FD => res.push(Xdp::Fd(
94                    parse_i32(nla.value())
95                        .context("invalid IFLA_XDP_FD value")?,
96                )),
97                IFLA_XDP_ATTACHED => res.push(Xdp::Attached(
98                    XdpAttached::try_from(nla.value()[0])
99                        .context("invalid IFLA_XDP_ATTACHED value")?,
100                )),
101                IFLA_XDP_FLAGS => res.push(Xdp::Flags(
102                    parse_u32(nla.value())
103                        .context("invalid IFLA_XDP_FLAGS value")?,
104                )),
105                IFLA_XDP_PROG_ID => res.push(Xdp::ProgId(
106                    parse_u32(nla.value())
107                        .context("invalid IFLA_XDP_PROG_ID value")?,
108                )),
109                IFLA_XDP_DRV_PROG_ID => res.push(Xdp::DrvProgId(
110                    parse_u32(nla.value())
111                        .context("invalid IFLA_XDP_PROG_ID value")?,
112                )),
113                IFLA_XDP_SKB_PROG_ID => res.push(Xdp::SkbProgId(
114                    parse_u32(nla.value())
115                        .context("invalid IFLA_XDP_PROG_ID value")?,
116                )),
117                IFLA_XDP_HW_PROG_ID => res.push(Xdp::HwProgId(
118                    parse_u32(nla.value())
119                        .context("invalid IFLA_XDP_PROG_ID value")?,
120                )),
121                IFLA_XDP_EXPECTED_FD => res.push(Xdp::ExpectedFd(
122                    parse_u32(nla.value())
123                        .context("invalid IFLA_XDP_PROG_ID value")?,
124                )),
125                _ => res
126                    .push(Xdp::Other(DefaultNla::parse(&nla).context(
127                        format!("unknown NLA type {}", nla.kind()),
128                    )?)),
129            }
130        }
131        Ok(VecXdp(res))
132    }
133}
134
135#[non_exhaustive]
136#[derive(Debug, PartialEq, Eq, Clone, Copy)]
137pub enum XdpAttached {
138    /// XDP_ATTACHED_NONE
139    None,
140    /// XDP_ATTACHED_DRV
141    Driver,
142    /// XDP_ATTACHED_SKB
143    SocketBuffer,
144    /// XDP_ATTACHED_HW
145    Hardware,
146    /// XDP_ATTACHED_MULTI
147    Multiple,
148    /// This crate is unaware of the attachment type the kernel is reporting
149    Other(u8),
150}
151
152impl TryFrom<u8> for XdpAttached {
153    type Error = DecodeError;
154
155    fn try_from(value: u8) -> Result<Self, Self::Error> {
156        match value {
157            XDP_ATTACHED_NONE => Ok(XdpAttached::None),
158            XDP_ATTACHED_DRV => Ok(XdpAttached::Driver),
159            XDP_ATTACHED_SKB => Ok(XdpAttached::SocketBuffer),
160            XDP_ATTACHED_HW => Ok(XdpAttached::Hardware),
161            XDP_ATTACHED_MULTI => Ok(XdpAttached::Multiple),
162            _ => Ok(XdpAttached::Other(value)),
163        }
164    }
165}
166
167impl XdpAttached {
168    fn as_u8(&self) -> u8 {
169        match self {
170            XdpAttached::None => XDP_ATTACHED_NONE,
171            XdpAttached::Driver => XDP_ATTACHED_DRV,
172            XdpAttached::SocketBuffer => XDP_ATTACHED_SKB,
173            XdpAttached::Hardware => XDP_ATTACHED_HW,
174            XdpAttached::Multiple => XDP_ATTACHED_MULTI,
175            XdpAttached::Other(other) => *other,
176        }
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use netlink_packet_utils::Emitable;
183
184    use super::*;
185
186    #[rustfmt::skip]
187    static ATTACHED: [u8; 48] = [
188        0x05, 0x00, // length = 5
189        0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED
190        0x00, 0x00, // none = XDP_ATTACHED_NONE
191        0x00, 0x00, // padding
192        0x05, 0x00, // length = 5
193        0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED
194        0x01, 0x00, // driver = XDP_ATTACHED_DRV
195        0x00, 0x00, // padding
196        0x05, 0x00, // length = 5
197        0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED
198        0x02, 0x00, // skb = XDP_ATTACHED_SKB
199        0x00, 0x00, // padding
200        0x05, 0x00, // length = 5
201        0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED
202        0x03, 0x00, // hw = XDP_ATTACHED_HW
203        0x00, 0x00, // padding
204        0x05, 0x00, // length = 5
205        0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED
206        0x04, 0x00, // multi = XDP_ATTACHED_MULTI
207        0x00, 0x00, // padding
208        0x05, 0x00, // length = 5
209        0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED
210        0xfc, 0x00, // other = random number = 252
211        0x00, 0x00, // padding
212    ];
213
214    #[test]
215    fn parse_xdp_attached() {
216        let nla = NlaBuffer::new_checked(&ATTACHED[..]).unwrap();
217        let parsed = VecXdp::parse(&nla).unwrap().0;
218        let expected = vec![
219            Xdp::Attached(XdpAttached::None),
220            Xdp::Attached(XdpAttached::Driver),
221            Xdp::Attached(XdpAttached::SocketBuffer),
222            Xdp::Attached(XdpAttached::Hardware),
223            Xdp::Attached(XdpAttached::Multiple),
224            Xdp::Attached(XdpAttached::Other(252)),
225        ];
226        assert_eq!(expected, parsed);
227    }
228
229    #[test]
230    fn emit_xdp_attached() {
231        // None
232        let nlas = vec![Xdp::Attached(XdpAttached::None)];
233        assert_eq!(nlas.as_slice().buffer_len(), 8);
234
235        let mut vec = vec![0xff; 8];
236        nlas.as_slice().emit(&mut vec);
237        assert_eq!(&vec[..], &ATTACHED[..8]);
238
239        // Driver
240        let nlas = vec![Xdp::Attached(XdpAttached::Driver)];
241        assert_eq!(nlas.as_slice().buffer_len(), 8);
242
243        let mut vec = vec![0xff; 8];
244        nlas.as_slice().emit(&mut vec);
245        assert_eq!(&vec[..], &ATTACHED[8..16]);
246
247        // SocketBuffer/skb
248        let nlas = vec![Xdp::Attached(XdpAttached::SocketBuffer)];
249        assert_eq!(nlas.as_slice().buffer_len(), 8);
250
251        let mut vec = vec![0xff; 8];
252        nlas.as_slice().emit(&mut vec);
253        assert_eq!(&vec[..], &ATTACHED[16..24]);
254
255        // Hardware
256        let nlas = vec![Xdp::Attached(XdpAttached::Hardware)];
257        assert_eq!(nlas.as_slice().buffer_len(), 8);
258
259        let mut vec = vec![0xff; 8];
260        nlas.as_slice().emit(&mut vec);
261        assert_eq!(&vec[..], &ATTACHED[24..32]);
262
263        // Multiple
264        let nlas = vec![Xdp::Attached(XdpAttached::Multiple)];
265        assert_eq!(nlas.as_slice().buffer_len(), 8);
266
267        let mut vec = vec![0xff; 8];
268        nlas.as_slice().emit(&mut vec);
269        assert_eq!(&vec[..], &ATTACHED[32..40]);
270
271        // Multiple
272        let nlas = vec![Xdp::Attached(XdpAttached::Other(252))];
273        assert_eq!(nlas.as_slice().buffer_len(), 8);
274
275        let mut vec = vec![0xff; 8];
276        nlas.as_slice().emit(&mut vec);
277        assert_eq!(&vec[..], &ATTACHED[40..48]);
278    }
279
280    #[rustfmt::skip]
281    static XDP: [u8; 72] = [
282        0x08, 0x00, // length = 8
283        0x01, 0x00, // type = 1 = IFLA_XDP_FD
284        0xA0, 0x74, 0x00, 0x00, // 29856
285        0x08, 0x00, // length = 8
286        0x03, 0x00, // type = 3 = IFLA_XDP_FLAGS
287        0x00, 0x00, 0x00, 0x00, // empty
288        0x08, 0x00, // length = 8
289        0x04, 0x00, // type = 4 = IFLA_XDP_PROG_ID
290        0x67, 0x00, 0x00, 0x00, // 103
291        0x08, 0x00, // length = 8
292        0x05, 0x00, // type = 5 = IFLA_XDP_DRV_PROG_ID
293        0x65, 0x00, 0x00, 0x00, // 101
294        0x08, 0x00, // length = 8
295        0x06, 0x00, // type = 6 = IFLA_XDP_DRV_SKB_ID
296        0x65, 0x00, 0x00, 0x00, // 101
297        0x08, 0x00, // length = 8
298        0x07, 0x00, // type = 7 = IFLA_XDP_DRV_HW_ID
299        0x65, 0x00, 0x00, 0x00, // 101
300        0x08, 0x00, // length = 8
301        0x08, 0x00, // type = 8 = IFLA_XDP_DRV_EXPECTED_FD
302        0xA1, 0x74, 0x00, 0x00, // 29857
303        0x08, 0x00, // length = 8
304        0xfc, 0x00, // type = 252 = random number/unknown type
305        0xA1, 0x74, 0x00, 0x00, // 29857
306        0x06, 0x00, // length = 6
307        0xfb, 0x00, // type = 251 = random number/unknown type
308        0xaa, 0xab, // 29857
309        0x00, 0x00, // padding
310    ];
311
312    #[test]
313    fn parse_xdp() {
314        let nla = NlaBuffer::new_checked(&XDP[..]).unwrap();
315        let parsed = VecXdp::parse(&nla).unwrap().0;
316        let expected = vec![
317            Xdp::Fd(29856),
318            Xdp::Flags(0),
319            Xdp::ProgId(103),
320            Xdp::DrvProgId(101),
321            Xdp::SkbProgId(101),
322            Xdp::HwProgId(101),
323            Xdp::ExpectedFd(29857),
324            Xdp::Other(
325                DefaultNla::parse(&NlaBuffer::new(&XDP[56..64])).unwrap(),
326            ),
327            Xdp::Other(DefaultNla::parse(&NlaBuffer::new(&XDP[64..])).unwrap()),
328        ];
329        assert_eq!(expected, parsed);
330    }
331
332    #[test]
333    fn emit_xdp() {
334        let nlas = vec![
335            Xdp::Fd(29856),
336            Xdp::Flags(0),
337            Xdp::ProgId(103),
338            Xdp::DrvProgId(101),
339            Xdp::SkbProgId(101),
340            Xdp::HwProgId(101),
341            Xdp::ExpectedFd(29857),
342            Xdp::Other(
343                DefaultNla::parse(&NlaBuffer::new(&XDP[56..64])).unwrap(),
344            ),
345            Xdp::Other(DefaultNla::parse(&NlaBuffer::new(&XDP[64..])).unwrap()),
346        ];
347        assert_eq!(nlas.as_slice().buffer_len(), XDP.len());
348
349        let mut vec = vec![0xff; XDP.len()];
350        nlas.as_slice().emit(&mut vec);
351        assert_eq!(&vec[..], &XDP[..]);
352    }
353}