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