netlink_packet_route/route/
attribute.rs

1// SPDX-License-Identifier: MIT
2
3use netlink_packet_core::{
4    emit_u32, emit_u64, parse_u16, parse_u32, parse_u64, parse_u8, DecodeError,
5    DefaultNla, Emitable, ErrorContext, Nla, NlaBuffer, Parseable,
6    ParseableParametrized,
7};
8
9use super::{
10    super::AddressFamily, lwtunnel::VecRouteLwTunnelEncap,
11    metrics::VecRouteMetric, mpls::VecMplsLabel, MplsLabel, RouteAddress,
12    RouteCacheInfo, RouteCacheInfoBuffer, RouteLwEnCapType, RouteLwTunnelEncap,
13    RouteMetric, RouteMfcStats, RouteMfcStatsBuffer, RouteMplsTtlPropagation,
14    RouteNextHop, RouteNextHopBuffer, RoutePreference, RouteRealm, RouteType,
15    RouteVia, RouteViaBuffer,
16};
17
18const RTA_DST: u16 = 1;
19const RTA_SRC: u16 = 2;
20const RTA_IIF: u16 = 3;
21const RTA_OIF: u16 = 4;
22const RTA_GATEWAY: u16 = 5;
23const RTA_PRIORITY: u16 = 6;
24const RTA_PREFSRC: u16 = 7;
25const RTA_METRICS: u16 = 8;
26const RTA_MULTIPATH: u16 = 9;
27// const RTA_PROTOINFO: u16 = 10; // linux kernel said `no longer used`
28const RTA_FLOW: u16 = 11;
29const RTA_CACHEINFO: u16 = 12;
30// const RTA_SESSION: u16 = 13; // linux kernel said `no longer used`
31// const RTA_MP_ALGO: u16 = 14; // linux kernel said `no longer used`
32const RTA_TABLE: u16 = 15;
33const RTA_MARK: u16 = 16;
34const RTA_MFC_STATS: u16 = 17;
35const RTA_VIA: u16 = 18;
36const RTA_NEWDST: u16 = 19;
37const RTA_PREF: u16 = 20;
38pub(crate) const RTA_ENCAP_TYPE: u16 = 21;
39const RTA_ENCAP: u16 = 22;
40const RTA_EXPIRES: u16 = 23;
41const RTA_UID: u16 = 25;
42const RTA_TTL_PROPAGATE: u16 = 26;
43// TODO
44// const RTA_IP_PROTO:u16 = 27;
45// const RTA_SPORT:u16 = 28;
46// const RTA_DPORT:u16 = 29;
47// const RTA_NH_ID:u16 = 30;
48
49/// Netlink attributes for `RTM_NEWROUTE`, `RTM_DELROUTE`,
50/// `RTM_GETROUTE` netlink messages.
51#[derive(Debug, PartialEq, Eq, Clone)]
52#[non_exhaustive]
53pub enum RouteAttribute {
54    Metrics(Vec<RouteMetric>),
55    MfcStats(RouteMfcStats),
56    MultiPath(Vec<RouteNextHop>),
57    CacheInfo(RouteCacheInfo),
58    Destination(RouteAddress),
59    Source(RouteAddress),
60    Gateway(RouteAddress),
61    PrefSource(RouteAddress),
62    Via(RouteVia),
63    /// Only for MPLS for destination label(u32) to forward the packet with
64    NewDestination(Vec<MplsLabel>),
65    Preference(RoutePreference),
66    EncapType(RouteLwEnCapType),
67    Encap(Vec<RouteLwTunnelEncap>),
68    // The RTA_EXPIRES holds different data type in kernel 6.5.8.
69    // For non-multipath route, it is u32 and only used for modifying routes.
70    // For multipath route, it is u64 for querying only.
71    /// This is only for non-multicast route
72    Expires(u32),
73    /// This is only for multicast route
74    MulticastExpires(u64),
75    Uid(u32),
76    TtlPropagate(RouteMplsTtlPropagation),
77    Iif(u32),
78    Oif(u32),
79    Priority(u32),
80    /// IPv4 Realm
81    Realm(RouteRealm),
82    Table(u32),
83    Mark(u32),
84    Other(DefaultNla),
85}
86
87impl Nla for RouteAttribute {
88    fn value_len(&self) -> usize {
89        match self {
90            Self::Destination(addr)
91            | Self::PrefSource(addr)
92            | Self::Gateway(addr)
93            | Self::Source(addr) => addr.buffer_len(),
94            Self::Via(v) => v.buffer_len(),
95            Self::NewDestination(v) => VecMplsLabel(v.clone()).buffer_len(),
96            Self::Encap(v) => v.as_slice().buffer_len(),
97            Self::TtlPropagate(_) => 1,
98            Self::CacheInfo(cache_info) => cache_info.buffer_len(),
99            Self::MfcStats(stats) => stats.buffer_len(),
100            Self::Metrics(metrics) => metrics.as_slice().buffer_len(),
101            Self::MultiPath(next_hops) => {
102                next_hops.iter().map(|nh| nh.buffer_len()).sum()
103            }
104            Self::Preference(_) => 1,
105            Self::EncapType(v) => v.buffer_len(),
106            Self::Realm(v) => v.buffer_len(),
107            Self::Uid(_)
108            | Self::Expires(_)
109            | Self::Iif(_)
110            | Self::Oif(_)
111            | Self::Priority(_)
112            | Self::Table(_)
113            | Self::Mark(_) => 4,
114            Self::MulticastExpires(_) => 8,
115            Self::Other(attr) => attr.value_len(),
116        }
117    }
118
119    fn emit_value(&self, buffer: &mut [u8]) {
120        match self {
121            Self::Destination(addr)
122            | Self::PrefSource(addr)
123            | Self::Source(addr)
124            | Self::Gateway(addr) => addr.emit(buffer),
125            Self::Via(v) => v.emit(buffer),
126            Self::NewDestination(v) => VecMplsLabel(v.to_vec()).emit(buffer),
127
128            Self::Encap(nlas) => nlas.as_slice().emit(buffer),
129            Self::TtlPropagate(v) => buffer[0] = u8::from(*v),
130            Self::Preference(p) => buffer[0] = (*p).into(),
131            Self::CacheInfo(cache_info) => cache_info.emit(buffer),
132            Self::MfcStats(stats) => stats.emit(buffer),
133            Self::Metrics(metrics) => metrics.as_slice().emit(buffer),
134            Self::MultiPath(next_hops) => {
135                let mut offset = 0;
136                for nh in next_hops {
137                    let len = nh.buffer_len();
138                    nh.emit(&mut buffer[offset..offset + len]);
139                    offset += len
140                }
141            }
142            Self::EncapType(v) => v.emit(buffer),
143            Self::Uid(value)
144            | Self::Expires(value)
145            | Self::Iif(value)
146            | Self::Oif(value)
147            | Self::Priority(value)
148            | Self::Table(value)
149            | Self::Mark(value) => emit_u32(buffer, *value).unwrap(),
150            Self::Realm(v) => v.emit(buffer),
151            Self::MulticastExpires(value) => emit_u64(buffer, *value).unwrap(),
152            Self::Other(attr) => attr.emit_value(buffer),
153        }
154    }
155
156    fn kind(&self) -> u16 {
157        match self {
158            Self::Destination(_) => RTA_DST,
159            Self::Source(_) => RTA_SRC,
160            Self::Iif(_) => RTA_IIF,
161            Self::Oif(_) => RTA_OIF,
162            Self::Gateway(_) => RTA_GATEWAY,
163            Self::Priority(_) => RTA_PRIORITY,
164            Self::PrefSource(_) => RTA_PREFSRC,
165            Self::Metrics(_) => RTA_METRICS,
166            Self::MultiPath(_) => RTA_MULTIPATH,
167            Self::Realm(_) => RTA_FLOW,
168            Self::CacheInfo(_) => RTA_CACHEINFO,
169            Self::Table(_) => RTA_TABLE,
170            Self::Mark(_) => RTA_MARK,
171            Self::MfcStats(_) => RTA_MFC_STATS,
172            Self::Via(_) => RTA_VIA,
173            Self::NewDestination(_) => RTA_NEWDST,
174            Self::Preference(_) => RTA_PREF,
175            Self::EncapType(_) => RTA_ENCAP_TYPE,
176            Self::Encap(_) => RTA_ENCAP,
177            Self::Expires(_) => RTA_EXPIRES,
178            Self::MulticastExpires(_) => RTA_EXPIRES,
179            Self::Uid(_) => RTA_UID,
180            Self::TtlPropagate(_) => RTA_TTL_PROPAGATE,
181            Self::Other(ref attr) => attr.kind(),
182        }
183    }
184
185    fn is_nested(&self) -> bool {
186        if let Self::Encap(encap) = self {
187            encap
188                .iter()
189                .any(|e| matches!(e, RouteLwTunnelEncap::Seg6(_)))
190        } else {
191            false
192        }
193    }
194}
195
196impl<'a, T: AsRef<[u8]> + ?Sized>
197    ParseableParametrized<
198        NlaBuffer<&'a T>,
199        (AddressFamily, RouteType, RouteLwEnCapType),
200    > for RouteAttribute
201{
202    fn parse_with_param(
203        buf: &NlaBuffer<&'a T>,
204        (address_family, route_type, encap_type): (
205            AddressFamily,
206            RouteType,
207            RouteLwEnCapType,
208        ),
209    ) -> Result<Self, DecodeError> {
210        let payload = buf.value();
211        Ok(match buf.kind() {
212            RTA_DST => {
213                Self::Destination(RouteAddress::parse(address_family, payload)?)
214            }
215            RTA_SRC => {
216                Self::Source(RouteAddress::parse(address_family, payload)?)
217            }
218            RTA_GATEWAY => {
219                Self::Gateway(RouteAddress::parse(address_family, payload)?)
220            }
221            RTA_PREFSRC => {
222                Self::PrefSource(RouteAddress::parse(address_family, payload)?)
223            }
224            RTA_VIA => Self::Via(
225                RouteVia::parse(
226                    &RouteViaBuffer::new_checked(payload).context(format!(
227                        "Invalid RTA_VIA value {payload:?}"
228                    ))?,
229                )
230                .context(format!("Invalid RTA_VIA value {payload:?}"))?,
231            ),
232            RTA_NEWDST => Self::NewDestination(
233                VecMplsLabel::parse(payload)
234                    .context(format!("Invalid RTA_NEWDST value {payload:?}"))?
235                    .0,
236            ),
237
238            RTA_PREF => Self::Preference(parse_u8(payload)?.into()),
239            RTA_ENCAP => Self::Encap(
240                VecRouteLwTunnelEncap::parse_with_param(buf, encap_type)?.0,
241            ),
242            RTA_EXPIRES => {
243                if route_type == RouteType::Multicast {
244                    Self::MulticastExpires(parse_u64(payload).context(
245                        format!(
246                            "invalid RTA_EXPIRES (multicast) value {payload:?}"
247                        ),
248                    )?)
249                } else {
250                    Self::Expires(parse_u32(payload).context(format!(
251                        "invalid RTA_EXPIRES value {payload:?}"
252                    ))?)
253                }
254            }
255            RTA_UID => Self::Uid(
256                parse_u32(payload)
257                    .context(format!("invalid RTA_UID value {payload:?}"))?,
258            ),
259            RTA_TTL_PROPAGATE => Self::TtlPropagate(
260                RouteMplsTtlPropagation::from(parse_u8(payload).context(
261                    format!("invalid RTA_TTL_PROPAGATE {payload:?}"),
262                )?),
263            ),
264            RTA_ENCAP_TYPE => Self::EncapType(RouteLwEnCapType::from(
265                parse_u16(payload).context("invalid RTA_ENCAP_TYPE value")?,
266            )),
267            RTA_IIF => {
268                Self::Iif(parse_u32(payload).context("invalid RTA_IIF value")?)
269            }
270            RTA_OIF => {
271                Self::Oif(parse_u32(payload).context("invalid RTA_OIF value")?)
272            }
273            RTA_PRIORITY => Self::Priority(
274                parse_u32(payload).context("invalid RTA_PRIORITY value")?,
275            ),
276            RTA_FLOW => Self::Realm(
277                RouteRealm::parse(payload).context("invalid RTA_FLOW value")?,
278            ),
279            RTA_TABLE => Self::Table(
280                parse_u32(payload).context("invalid RTA_TABLE value")?,
281            ),
282            RTA_MARK => Self::Mark(
283                parse_u32(payload).context("invalid RTA_MARK value")?,
284            ),
285
286            RTA_CACHEINFO => Self::CacheInfo(
287                RouteCacheInfo::parse(
288                    &RouteCacheInfoBuffer::new_checked(payload)
289                        .context("invalid RTA_CACHEINFO value")?,
290                )
291                .context("invalid RTA_CACHEINFO value")?,
292            ),
293            RTA_MFC_STATS => Self::MfcStats(
294                RouteMfcStats::parse(
295                    &RouteMfcStatsBuffer::new_checked(payload)
296                        .context("invalid RTA_MFC_STATS value")?,
297                )
298                .context("invalid RTA_MFC_STATS value")?,
299            ),
300            RTA_METRICS => Self::Metrics(
301                VecRouteMetric::parse(payload)
302                    .context("invalid RTA_METRICS value")?
303                    .0,
304            ),
305            RTA_MULTIPATH => {
306                let mut next_hops = vec![];
307                let mut buf = payload;
308                loop {
309                    let nh_buf = RouteNextHopBuffer::new_checked(&buf)
310                        .context("invalid RTA_MULTIPATH value")?;
311                    let len = nh_buf.length() as usize;
312                    let nh = RouteNextHop::parse_with_param(
313                        &nh_buf,
314                        (address_family, route_type, encap_type),
315                    )
316                    .context("invalid RTA_MULTIPATH value")?;
317                    next_hops.push(nh);
318                    if buf.len() == len {
319                        break;
320                    }
321                    buf = &buf[len..];
322                }
323                Self::MultiPath(next_hops)
324            }
325            _ => Self::Other(
326                DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?,
327            ),
328        })
329    }
330}