netlink_packet_route/rtnl/route/nlas/
next_hops.rs1use 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 pub flags: NextHopFlags,
79 pub hops: u8,
81 pub interface_id: u32,
83 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 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 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}