netlink_packet_route/rtnl/route/nlas/
next_hops.rs

1// SPDX-License-Identifier: MIT
2
3use anyhow::Context;
4use std::net::IpAddr;
5
6use crate::{constants, route::nlas::Nla};
7
8use netlink_packet_utils::{
9    nla::{NlaBuffer, NlasIterator},
10    parsers::parse_ip,
11    traits::{Emitable, Parseable},
12    DecodeError,
13};
14
15bitflags! {
16    #[non_exhaustive]
17pub struct NextHopFlags: u8 {
18        const RTNH_F_DEAD = constants::RTNH_F_DEAD;
19        const RTNH_F_PERVASIVE = constants::RTNH_F_PERVASIVE;
20        const RTNH_F_ONLINK = constants::RTNH_F_ONLINK;
21        const RTNH_F_OFFLOAD = constants::RTNH_F_OFFLOAD;
22        const RTNH_F_LINKDOWN = constants::RTNH_F_LINKDOWN;
23        const RTNH_F_UNRESOLVED = constants::RTNH_F_UNRESOLVED;
24    }
25}
26
27const PAYLOAD_OFFSET: usize = 8;
28
29buffer!(NextHopBuffer {
30    length: (u16, 0..2),
31    flags: (u8, 2),
32    hops: (u8, 3),
33    interface_id: (u32, 4..8),
34    payload: (slice, PAYLOAD_OFFSET..),
35});
36
37impl<T: AsRef<[u8]>> NextHopBuffer<T> {
38    pub fn new_checked(buffer: T) -> Result<Self, DecodeError> {
39        let packet = Self::new(buffer);
40        packet.check_buffer_length()?;
41        Ok(packet)
42    }
43
44    fn check_buffer_length(&self) -> Result<(), DecodeError> {
45        let len = self.buffer.as_ref().len();
46        if len < PAYLOAD_OFFSET {
47            return Err(format!(
48                "invalid NextHopBuffer: length {len} < {PAYLOAD_OFFSET}"
49            )
50            .into());
51        }
52        if len < self.length() as usize {
53            return Err(format!(
54                "invalid NextHopBuffer: length {} < {}",
55                len,
56                8 + self.length()
57            )
58            .into());
59        }
60        Ok(())
61    }
62}
63
64impl<'a, T: AsRef<[u8]> + ?Sized> NextHopBuffer<&'a T> {
65    pub fn nlas(
66        &self,
67    ) -> impl Iterator<Item = Result<NlaBuffer<&'a [u8]>, DecodeError>> {
68        NlasIterator::new(
69            &self.payload()[..(self.length() as usize - PAYLOAD_OFFSET)],
70        )
71    }
72}
73
74#[derive(Debug, Clone, Eq, PartialEq)]
75#[non_exhaustive]
76pub struct NextHop {
77    /// Next-hop flags (see [`NextHopFlags`])
78    pub flags: NextHopFlags,
79    /// Next-hop priority
80    pub hops: u8,
81    /// Interface index for the next-hop
82    pub interface_id: u32,
83    /// Attributes
84    pub nlas: Vec<Nla>,
85}
86
87impl<'a, T: AsRef<[u8]>> Parseable<NextHopBuffer<&'a T>> for NextHop {
88    fn parse(buf: &NextHopBuffer<&T>) -> Result<NextHop, DecodeError> {
89        let nlas = Vec::<Nla>::parse(
90            &NextHopBuffer::new_checked(buf.buffer)
91                .context("cannot parse route attributes in next-hop")?,
92        )
93        .context("cannot parse route attributes in next-hop")?;
94        Ok(NextHop {
95            flags: NextHopFlags::from_bits_truncate(buf.flags()),
96            hops: buf.hops(),
97            interface_id: buf.interface_id(),
98            nlas,
99        })
100    }
101}
102
103impl<'a, T: AsRef<[u8]> + 'a> Parseable<NextHopBuffer<&'a T>> for Vec<Nla> {
104    fn parse(buf: &NextHopBuffer<&'a T>) -> Result<Self, DecodeError> {
105        let mut nlas = vec![];
106        for nla_buf in buf.nlas() {
107            nlas.push(Nla::parse(&nla_buf?)?);
108        }
109        Ok(nlas)
110    }
111}
112
113impl Emitable for NextHop {
114    fn buffer_len(&self) -> usize {
115        // len, flags, hops and interface id fields
116        PAYLOAD_OFFSET + self.nlas.as_slice().buffer_len()
117    }
118
119    fn emit(&self, buffer: &mut [u8]) {
120        let mut nh_buffer = NextHopBuffer::new(buffer);
121        nh_buffer.set_length(self.buffer_len() as u16);
122        nh_buffer.set_flags(self.flags.bits());
123        nh_buffer.set_hops(self.hops);
124        nh_buffer.set_interface_id(self.interface_id);
125        self.nlas.as_slice().emit(nh_buffer.payload_mut())
126    }
127}
128
129impl NextHop {
130    /// Gateway address (it is actually encoded as an `RTA_GATEWAY` nla)
131    pub fn gateway(&self) -> Option<IpAddr> {
132        self.nlas.iter().find_map(|nla| {
133            if let Nla::Gateway(ip) = nla {
134                parse_ip(ip).ok()
135            } else {
136                None
137            }
138        })
139    }
140}