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