netlink_packet_route/rtnl/route/nlas/
mod.rs

1// SPDX-License-Identifier: MIT
2
3mod cache_info;
4pub use self::cache_info::*;
5
6mod metrics;
7pub use self::metrics::*;
8
9mod mfc_stats;
10pub use self::mfc_stats::*;
11
12mod mpls_ip_tunnel;
13pub use self::mpls_ip_tunnel::*;
14
15mod next_hops;
16pub use self::next_hops::*;
17
18use anyhow::Context;
19use byteorder::{ByteOrder, NativeEndian};
20
21use crate::constants::*;
22
23use netlink_packet_utils::{
24    nla::{self, DefaultNla, NlaBuffer},
25    parsers::{parse_u16, parse_u32},
26    traits::Parseable,
27    DecodeError,
28};
29
30#[cfg(feature = "rich_nlas")]
31use netlink_packet_utils::traits::Emitable;
32
33/// Netlink attributes for `RTM_NEWROUTE`, `RTM_DELROUTE`,
34/// `RTM_GETROUTE` messages.
35#[derive(Debug, PartialEq, Eq, Clone)]
36#[non_exhaustive]
37pub enum Nla {
38    #[cfg(not(feature = "rich_nlas"))]
39    Metrics(Vec<u8>),
40    #[cfg(feature = "rich_nlas")]
41    Metrics(Metrics),
42    #[cfg(not(feature = "rich_nlas"))]
43    MfcStats(Vec<u8>),
44    #[cfg(feature = "rich_nlas")]
45    MfcStats(MfcStats),
46    #[cfg(not(feature = "rich_nlas"))]
47    MultiPath(Vec<u8>),
48    #[cfg(feature = "rich_nlas")]
49    // See: https://codecave.cc/multipath-routing-in-linux-part-1.html
50    MultiPath(Vec<NextHop>),
51    #[cfg(not(feature = "rich_nlas"))]
52    CacheInfo(Vec<u8>),
53    #[cfg(feature = "rich_nlas")]
54    CacheInfo(CacheInfo),
55    Unspec(Vec<u8>),
56    Destination(Vec<u8>),
57    Source(Vec<u8>),
58    Gateway(Vec<u8>),
59    PrefSource(Vec<u8>),
60    Session(Vec<u8>),
61    MpAlgo(Vec<u8>),
62    Via(Vec<u8>),
63    NewDestination(Vec<u8>),
64    Pref(Vec<u8>),
65    Encap(Vec<u8>),
66    Expires(Vec<u8>),
67    Pad(Vec<u8>),
68    Uid(Vec<u8>),
69    TtlPropagate(Vec<u8>),
70    EncapType(u16),
71    Iif(u32),
72    Oif(u32),
73    Priority(u32),
74    ProtocolInfo(u32),
75    Flow(u32),
76    Table(u32),
77    Mark(u32),
78    Other(DefaultNla),
79}
80
81impl nla::Nla for Nla {
82    #[rustfmt::skip]
83    fn value_len(&self) -> usize {
84        use self::Nla::*;
85        match *self {
86            Unspec(ref bytes)
87                | Destination(ref bytes)
88                | Source(ref bytes)
89                | Gateway(ref bytes)
90                | PrefSource(ref bytes)
91                | Session(ref bytes)
92                | MpAlgo(ref bytes)
93                | Via(ref bytes)
94                | NewDestination(ref bytes)
95                | Pref(ref bytes)
96                | Encap(ref bytes)
97                | Expires(ref bytes)
98                | Pad(ref bytes)
99                | Uid(ref bytes)
100                | TtlPropagate(ref bytes)
101                => bytes.len(),
102
103            #[cfg(not(feature = "rich_nlas"))]
104            CacheInfo(ref bytes)
105                | MfcStats(ref bytes)
106                | Metrics(ref bytes)
107                | MultiPath(ref bytes)
108                => bytes.len(),
109
110            #[cfg(feature = "rich_nlas")]
111            CacheInfo(ref cache_info) => cache_info.buffer_len(),
112            #[cfg(feature = "rich_nlas")]
113            MfcStats(ref stats) => stats.buffer_len(),
114            #[cfg(feature = "rich_nlas")]
115            Metrics(ref metrics) => metrics.buffer_len(),
116            #[cfg(feature = "rich_nlas")]
117            MultiPath(ref next_hops) => next_hops.iter().map(|nh| nh.buffer_len()).sum(),
118
119            EncapType(_) => 2,
120            Iif(_)
121                | Oif(_)
122                | Priority(_)
123                | ProtocolInfo(_)
124                | Flow(_)
125                | Table(_)
126                | Mark(_)
127                => 4,
128
129            Other(ref attr) => attr.value_len(),
130        }
131    }
132
133    #[rustfmt::skip]
134    fn emit_value(&self, buffer: &mut [u8]) {
135        use self::Nla::*;
136        match *self {
137            Unspec(ref bytes)
138                | Destination(ref bytes)
139                | Source(ref bytes)
140                | Gateway(ref bytes)
141                | PrefSource(ref bytes)
142                | Session(ref bytes)
143                | MpAlgo(ref bytes)
144                | Via(ref bytes)
145                | NewDestination(ref bytes)
146                | Pref(ref bytes)
147                | Encap(ref bytes)
148                | Expires(ref bytes)
149                | Pad(ref bytes)
150                | Uid(ref bytes)
151                | TtlPropagate(ref bytes)
152                => buffer.copy_from_slice(bytes.as_slice()),
153
154            #[cfg(not(feature = "rich_nlas"))]
155                MultiPath(ref bytes)
156                | CacheInfo(ref bytes)
157                | MfcStats(ref bytes)
158                | Metrics(ref bytes)
159                => buffer.copy_from_slice(bytes.as_slice()),
160
161            #[cfg(feature = "rich_nlas")]
162            CacheInfo(ref cache_info) => cache_info.emit(buffer),
163            #[cfg(feature = "rich_nlas")]
164            MfcStats(ref stats) => stats.emit(buffer),
165            #[cfg(feature = "rich_nlas")]
166            Metrics(ref metrics) => metrics.emit(buffer),
167            #[cfg(feature = "rich_nlas")]
168            MultiPath(ref next_hops) => {
169                let mut offset = 0;
170                for nh in next_hops {
171                    let len = nh.buffer_len();
172                    nh.emit(&mut buffer[offset..offset+len]);
173                    offset += len
174                }
175            }
176
177            EncapType(value) => NativeEndian::write_u16(buffer, value),
178            Iif(value)
179                | Oif(value)
180                | Priority(value)
181                | ProtocolInfo(value)
182                | Flow(value)
183                | Table(value)
184                | Mark(value)
185                => NativeEndian::write_u32(buffer, value),
186            Other(ref attr) => attr.emit_value(buffer),
187        }
188    }
189
190    fn kind(&self) -> u16 {
191        use self::Nla::*;
192        match *self {
193            Unspec(_) => RTA_UNSPEC,
194            Destination(_) => RTA_DST,
195            Source(_) => RTA_SRC,
196            Iif(_) => RTA_IIF,
197            Oif(_) => RTA_OIF,
198            Gateway(_) => RTA_GATEWAY,
199            Priority(_) => RTA_PRIORITY,
200            PrefSource(_) => RTA_PREFSRC,
201            Metrics(_) => RTA_METRICS,
202            MultiPath(_) => RTA_MULTIPATH,
203            ProtocolInfo(_) => RTA_PROTOINFO,
204            Flow(_) => RTA_FLOW,
205            CacheInfo(_) => RTA_CACHEINFO,
206            Session(_) => RTA_SESSION,
207            MpAlgo(_) => RTA_MP_ALGO,
208            Table(_) => RTA_TABLE,
209            Mark(_) => RTA_MARK,
210            MfcStats(_) => RTA_MFC_STATS,
211            Via(_) => RTA_VIA,
212            NewDestination(_) => RTA_NEWDST,
213            Pref(_) => RTA_PREF,
214            EncapType(_) => RTA_ENCAP_TYPE,
215            Encap(_) => RTA_ENCAP,
216            Expires(_) => RTA_EXPIRES,
217            Pad(_) => RTA_PAD,
218            Uid(_) => RTA_UID,
219            TtlPropagate(_) => RTA_TTL_PROPAGATE,
220            Other(ref attr) => attr.kind(),
221        }
222    }
223}
224
225impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nla {
226    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
227        use self::Nla::*;
228
229        let payload = buf.value();
230        Ok(match buf.kind() {
231            RTA_UNSPEC => Unspec(payload.to_vec()),
232            RTA_DST => Destination(payload.to_vec()),
233            RTA_SRC => Source(payload.to_vec()),
234            RTA_GATEWAY => Gateway(payload.to_vec()),
235            RTA_PREFSRC => PrefSource(payload.to_vec()),
236            RTA_SESSION => Session(payload.to_vec()),
237            RTA_MP_ALGO => MpAlgo(payload.to_vec()),
238            RTA_VIA => Via(payload.to_vec()),
239            RTA_NEWDST => NewDestination(payload.to_vec()),
240            RTA_PREF => Pref(payload.to_vec()),
241            RTA_ENCAP => Encap(payload.to_vec()),
242            RTA_EXPIRES => Expires(payload.to_vec()),
243            RTA_PAD => Pad(payload.to_vec()),
244            RTA_UID => Uid(payload.to_vec()),
245            RTA_TTL_PROPAGATE => TtlPropagate(payload.to_vec()),
246            RTA_ENCAP_TYPE => EncapType(
247                parse_u16(payload).context("invalid RTA_ENCAP_TYPE value")?,
248            ),
249            RTA_IIF => {
250                Iif(parse_u32(payload).context("invalid RTA_IIF value")?)
251            }
252            RTA_OIF => {
253                Oif(parse_u32(payload).context("invalid RTA_OIF value")?)
254            }
255            RTA_PRIORITY => Priority(
256                parse_u32(payload).context("invalid RTA_PRIORITY value")?,
257            ),
258            RTA_PROTOINFO => ProtocolInfo(
259                parse_u32(payload).context("invalid RTA_PROTOINFO value")?,
260            ),
261            RTA_FLOW => {
262                Flow(parse_u32(payload).context("invalid RTA_FLOW value")?)
263            }
264            RTA_TABLE => {
265                Table(parse_u32(payload).context("invalid RTA_TABLE value")?)
266            }
267            RTA_MARK => {
268                Mark(parse_u32(payload).context("invalid RTA_MARK value")?)
269            }
270
271            #[cfg(not(feature = "rich_nlas"))]
272            RTA_CACHEINFO => CacheInfo(payload.to_vec()),
273            #[cfg(feature = "rich_nlas")]
274            RTA_CACHEINFO => CacheInfo(
275                cache_info::CacheInfo::parse(
276                    &CacheInfoBuffer::new_checked(payload)
277                        .context("invalid RTA_CACHEINFO value")?,
278                )
279                .context("invalid RTA_CACHEINFO value")?,
280            ),
281            #[cfg(not(feature = "rich_nlas"))]
282            RTA_MFC_STATS => MfcStats(payload.to_vec()),
283            #[cfg(feature = "rich_nlas")]
284            RTA_MFC_STATS => MfcStats(
285                mfc_stats::MfcStats::parse(
286                    &MfcStatsBuffer::new_checked(payload)
287                        .context("invalid RTA_MFC_STATS value")?,
288                )
289                .context("invalid RTA_MFC_STATS value")?,
290            ),
291            #[cfg(not(feature = "rich_nlas"))]
292            RTA_METRICS => Metrics(payload.to_vec()),
293            #[cfg(feature = "rich_nlas")]
294            RTA_METRICS => Metrics(
295                metrics::Metrics::parse(
296                    &NlaBuffer::new_checked(payload)
297                        .context("invalid RTA_METRICS value")?,
298                )
299                .context("invalid RTA_METRICS value")?,
300            ),
301            #[cfg(not(feature = "rich_nlas"))]
302            RTA_MULTIPATH => MultiPath(payload.to_vec()),
303            #[cfg(feature = "rich_nlas")]
304            RTA_MULTIPATH => {
305                let mut next_hops = vec![];
306                let mut buf = payload;
307                loop {
308                    let nh_buf = NextHopBuffer::new_checked(&buf)
309                        .context("invalid RTA_MULTIPATH value")?;
310                    let len = nh_buf.length() as usize;
311                    let nh = NextHop::parse(&nh_buf)
312                        .context("invalid RTA_MULTIPATH value")?;
313                    next_hops.push(nh);
314                    if buf.len() == len {
315                        break;
316                    }
317                    buf = &buf[len..];
318                }
319                MultiPath(next_hops)
320            }
321            _ => Other(
322                DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?,
323            ),
324        })
325    }
326}