rtnetlink/route/
builder.rs

1// SPDX-License-Identifier: MIT
2
3use std::{
4    marker::PhantomData,
5    net::{IpAddr, Ipv4Addr, Ipv6Addr},
6};
7
8#[cfg(not(target_os = "android"))]
9use netlink_packet_route::route::{
10    MplsLabel, RouteLwEnCapType, RouteLwTunnelEncap, RouteMplsIpTunnel,
11};
12use netlink_packet_route::{
13    route::{
14        RouteAddress, RouteAttribute, RouteFlags, RouteHeader, RouteMessage,
15        RouteNextHop, RouteNextHopFlags, RouteProtocol, RouteScope, RouteType,
16        RouteVia,
17    },
18    AddressFamily,
19};
20
21#[derive(Debug, Clone)]
22pub struct RouteMessageBuilder<T = IpAddr> {
23    message: RouteMessage,
24    _phantom: PhantomData<T>,
25}
26
27#[derive(Debug, Clone)]
28pub struct RouteNextHopBuilder {
29    address_family: AddressFamily,
30    nexthop: RouteNextHop,
31}
32
33impl<T> RouteMessageBuilder<T> {
34    /// Create default RouteMessage with header set to:
35    ///  * route: [RouteHeader::RT_TABLE_MAIN]
36    ///  * protocol: [RouteProtocol::Static]
37    ///  * scope: [RouteScope::Universe]
38    ///  * kind: [RouteType::Unicast]
39    ///
40    /// For using this message in querying routes, these settings
41    /// are ignored unless `NETLINK_GET_STRICT_CHK` been enabled.
42    fn new_no_address_family() -> Self {
43        let mut message = RouteMessage::default();
44        message.header.table = RouteHeader::RT_TABLE_MAIN;
45        message.header.protocol = RouteProtocol::Static;
46        message.header.scope = RouteScope::Universe;
47        message.header.kind = RouteType::Unicast;
48        Self {
49            message,
50            _phantom: Default::default(),
51        }
52    }
53
54    /// Sets the input interface index.
55    pub fn input_interface(mut self, index: u32) -> Self {
56        self.message.attributes.push(RouteAttribute::Iif(index));
57        self
58    }
59
60    /// Sets the output interface index.
61    pub fn output_interface(mut self, index: u32) -> Self {
62        self.message.attributes.push(RouteAttribute::Oif(index));
63        self
64    }
65
66    /// Sets the output MPLS encapsulation labels.
67    #[cfg(not(target_os = "android"))]
68    pub fn output_mpls(mut self, labels: Vec<MplsLabel>) -> Self {
69        if labels.is_empty() {
70            return self;
71        }
72        if self.message.header.address_family == AddressFamily::Mpls {
73            self.message
74                .attributes
75                .push(RouteAttribute::NewDestination(labels));
76        } else {
77            self.message
78                .attributes
79                .push(RouteAttribute::EncapType(RouteLwEnCapType::Mpls));
80            let encap = RouteLwTunnelEncap::Mpls(
81                RouteMplsIpTunnel::Destination(labels),
82            );
83            self.message
84                .attributes
85                .push(RouteAttribute::Encap(vec![encap]));
86        }
87        self
88    }
89
90    /// Sets multiple nexthop entries for the route.
91    pub fn multipath(mut self, nexthops: Vec<RouteNextHop>) -> Self {
92        self.message
93            .attributes
94            .push(RouteAttribute::MultiPath(nexthops));
95        self
96    }
97
98    /// Sets the route priority (metric)
99    pub fn priority(mut self, priority: u32) -> Self {
100        self.message
101            .attributes
102            .push(RouteAttribute::Priority(priority));
103        self
104    }
105
106    /// Sets the route table ID.
107    ///
108    /// Default is main route table.
109    pub fn table_id(mut self, table: u32) -> Self {
110        if table > 255 {
111            self.message.attributes.push(RouteAttribute::Table(table));
112        } else {
113            self.message.header.table = table as u8;
114        }
115        self
116    }
117
118    #[cfg(not(target_os = "android"))]
119    /// Sets mark value on route.
120    pub fn mark(mut self, mark: u32) -> Self {
121        self.message.attributes.push(RouteAttribute::Mark(mark));
122        self
123    }
124
125    /// Sets the route protocol.
126    ///
127    /// Default is static route protocol.
128    pub fn protocol(mut self, protocol: RouteProtocol) -> Self {
129        self.message.header.protocol = protocol;
130        self
131    }
132
133    /// Sets the route scope.
134    ///
135    /// Default is universe route scope.
136    pub fn scope(mut self, scope: RouteScope) -> Self {
137        self.message.header.scope = scope;
138        self
139    }
140
141    /// Sets the route kind.
142    ///
143    /// Default is unicast route kind.
144    pub fn kind(mut self, kind: RouteType) -> Self {
145        self.message.header.kind = kind;
146        self
147    }
148
149    /// Marks the next hop as directly reachable (on-link).
150    ///
151    /// Indicates that the next hop is reachable without passing through a
152    /// connected subnet.
153    pub fn onlink(mut self) -> Self {
154        self.message.header.flags.insert(RouteFlags::Onlink);
155        self
156    }
157
158    /// Return a mutable reference to the request message.
159    pub fn get_mut(&mut self) -> &mut RouteMessage {
160        &mut self.message
161    }
162
163    pub fn build(self) -> RouteMessage {
164        self.message
165    }
166}
167
168impl RouteMessageBuilder<Ipv4Addr> {
169    /// Create default RouteMessage with header set to:
170    ///  * route: [RouteHeader::RT_TABLE_MAIN]
171    ///  * protocol: [RouteProtocol::Static]
172    ///  * scope: [RouteScope::Universe]
173    ///  * kind: [RouteType::Unicast]
174    ///  * address_family: [AddressFamily::Inet4]
175    ///
176    /// For using this message in querying routes, these settings
177    /// are ignored unless `NETLINK_GET_STRICT_CHK` been enabled.
178    pub fn new() -> Self {
179        let mut builder = Self::new_no_address_family();
180        builder.get_mut().header.address_family = AddressFamily::Inet;
181        builder
182    }
183
184    /// Sets the source address prefix.
185    pub fn source_prefix(mut self, addr: Ipv4Addr, prefix_length: u8) -> Self {
186        self.message.header.source_prefix_length = prefix_length;
187        self.message
188            .attributes
189            .push(RouteAttribute::Source(RouteAddress::Inet(addr)));
190        self
191    }
192
193    /// Sets the preferred source address.
194    pub fn pref_source(mut self, addr: Ipv4Addr) -> Self {
195        self.message
196            .attributes
197            .push(RouteAttribute::PrefSource(RouteAddress::Inet(addr)));
198        self
199    }
200
201    /// Sets the destination address prefix.
202    pub fn destination_prefix(
203        mut self,
204        addr: Ipv4Addr,
205        prefix_length: u8,
206    ) -> Self {
207        self.message.header.destination_prefix_length = prefix_length;
208        self.message
209            .attributes
210            .push(RouteAttribute::Destination(RouteAddress::Inet(addr)));
211        self
212    }
213
214    /// Sets the gateway (via) address.
215    pub fn gateway(mut self, addr: Ipv4Addr) -> Self {
216        self.message
217            .attributes
218            .push(RouteAttribute::Gateway(RouteAddress::Inet(addr)));
219        self
220    }
221
222    /// Sets the IPv6 gateway (via) address.
223    pub fn via(mut self, addr: Ipv6Addr) -> Self {
224        self.message
225            .attributes
226            .push(RouteAttribute::Via(RouteVia::Inet6(addr)));
227        self
228    }
229}
230
231impl Default for RouteMessageBuilder<Ipv4Addr> {
232    fn default() -> Self {
233        Self::new()
234    }
235}
236
237impl RouteMessageBuilder<Ipv6Addr> {
238    /// Create default RouteMessage with header set to:
239    ///  * route: [RouteHeader::RT_TABLE_MAIN]
240    ///  * protocol: [RouteProtocol::Static]
241    ///  * scope: [RouteScope::Universe]
242    ///  * kind: [RouteType::Unicast]
243    ///  * address_family: [AddressFamily::Inet6]
244    ///
245    /// For using this message in querying routes, these settings
246    /// are ignored unless `NETLINK_GET_STRICT_CHK` been enabled.
247    pub fn new() -> Self {
248        let mut builder = Self::new_no_address_family();
249        builder.get_mut().header.address_family = AddressFamily::Inet6;
250        builder
251    }
252
253    /// Sets the source address prefix.
254    pub fn source_prefix(mut self, addr: Ipv6Addr, prefix_length: u8) -> Self {
255        self.message.header.source_prefix_length = prefix_length;
256        self.message
257            .attributes
258            .push(RouteAttribute::Source(RouteAddress::Inet6(addr)));
259        self
260    }
261
262    /// Sets the preferred source address.
263    pub fn pref_source(mut self, addr: Ipv6Addr) -> Self {
264        self.message
265            .attributes
266            .push(RouteAttribute::PrefSource(RouteAddress::Inet6(addr)));
267        self
268    }
269
270    /// Sets the destination address prefix.
271    pub fn destination_prefix(
272        mut self,
273        addr: Ipv6Addr,
274        prefix_length: u8,
275    ) -> Self {
276        self.message.header.destination_prefix_length = prefix_length;
277        self.message
278            .attributes
279            .push(RouteAttribute::Destination(RouteAddress::Inet6(addr)));
280        self
281    }
282
283    /// Sets the gateway (via) address.
284    pub fn gateway(mut self, addr: Ipv6Addr) -> Self {
285        self.message
286            .attributes
287            .push(RouteAttribute::Gateway(RouteAddress::Inet6(addr)));
288        self
289    }
290}
291
292impl Default for RouteMessageBuilder<Ipv6Addr> {
293    fn default() -> Self {
294        Self::new()
295    }
296}
297
298#[derive(Debug, thiserror::Error)]
299pub enum InvalidRouteMessage {
300    #[error("invalid address family {:?}", _0)]
301    AddressFamily(AddressFamily),
302
303    #[error("invalid gateway {}", _0)]
304    Gateway(IpAddr),
305
306    #[error("invalid preferred source {}", _0)]
307    PrefSource(IpAddr),
308
309    #[error("invalid source prefix {}/{}", _0, _1)]
310    SourcePrefix(IpAddr, u8),
311
312    #[error("invalid destination prefix {}/{}", _0, _1)]
313    DestinationPrefix(IpAddr, u8),
314}
315
316impl RouteMessageBuilder<IpAddr> {
317    pub fn new() -> Self {
318        Self::new_no_address_family()
319    }
320
321    /// Sets the source address prefix.
322    pub fn source_prefix(
323        mut self,
324        addr: IpAddr,
325        prefix_length: u8,
326    ) -> Result<Self, InvalidRouteMessage> {
327        self.set_address_family_from_ip_addr(addr);
328        match self.message.header.address_family {
329            AddressFamily::Inet => {
330                if addr.is_ipv6() || prefix_length > 32 {
331                    return Err(InvalidRouteMessage::SourcePrefix(
332                        addr,
333                        prefix_length,
334                    ));
335                }
336            }
337            AddressFamily::Inet6 => {
338                if addr.is_ipv4() || prefix_length > 128 {
339                    return Err(InvalidRouteMessage::SourcePrefix(
340                        addr,
341                        prefix_length,
342                    ));
343                }
344            }
345            af => return Err(InvalidRouteMessage::AddressFamily(af)),
346        };
347        self.message
348            .attributes
349            .push(RouteAttribute::Source(addr.into()));
350        self.message.header.source_prefix_length = prefix_length;
351        Ok(self)
352    }
353
354    /// Sets the preferred source address.
355    pub fn pref_source(
356        mut self,
357        addr: IpAddr,
358    ) -> Result<Self, InvalidRouteMessage> {
359        self.set_address_family_from_ip_addr(addr);
360        match self.message.header.address_family {
361            AddressFamily::Inet => {
362                if addr.is_ipv6() {
363                    return Err(InvalidRouteMessage::PrefSource(addr));
364                };
365            }
366            AddressFamily::Inet6 => {
367                if addr.is_ipv4() {
368                    return Err(InvalidRouteMessage::PrefSource(addr));
369                };
370            }
371            af => {
372                return Err(InvalidRouteMessage::AddressFamily(af));
373            }
374        }
375        self.message
376            .attributes
377            .push(RouteAttribute::PrefSource(addr.into()));
378        Ok(self)
379    }
380
381    /// Sets the destination address prefix.
382    pub fn destination_prefix(
383        mut self,
384        addr: IpAddr,
385        prefix_length: u8,
386    ) -> Result<Self, InvalidRouteMessage> {
387        self.set_address_family_from_ip_addr(addr);
388        match self.message.header.address_family {
389            AddressFamily::Inet => {
390                if addr.is_ipv6() || prefix_length > 32 {
391                    return Err(InvalidRouteMessage::DestinationPrefix(
392                        addr,
393                        prefix_length,
394                    ));
395                }
396            }
397            AddressFamily::Inet6 => {
398                if addr.is_ipv4() || prefix_length > 128 {
399                    return Err(InvalidRouteMessage::DestinationPrefix(
400                        addr,
401                        prefix_length,
402                    ));
403                }
404            }
405            af => {
406                return Err(InvalidRouteMessage::AddressFamily(af));
407            }
408        };
409        self.message.header.destination_prefix_length = prefix_length;
410        self.message
411            .attributes
412            .push(RouteAttribute::Destination(addr.into()));
413        Ok(self)
414    }
415
416    /// Sets the gateway (via) address.
417    pub fn gateway(
418        mut self,
419        addr: IpAddr,
420    ) -> Result<Self, InvalidRouteMessage> {
421        let attr = match (self.message.header.address_family, addr) {
422            (AddressFamily::Inet, addr @ IpAddr::V4(_))
423            | (AddressFamily::Inet6, addr @ IpAddr::V6(_)) => {
424                RouteAttribute::Gateway(addr.into())
425            }
426            (AddressFamily::Inet, IpAddr::V6(v6)) => {
427                RouteAttribute::Via(RouteVia::Inet6(v6))
428            }
429            (af, _) => return Err(InvalidRouteMessage::AddressFamily(af)),
430        };
431        self.message.attributes.push(attr);
432        Ok(self)
433    }
434
435    /// If it is not set already, set the address family based on the
436    /// given IP address. This is a noop is the address family is
437    /// already set.
438    fn set_address_family_from_ip_addr(&mut self, addr: IpAddr) {
439        if self.message.header.address_family != AddressFamily::Unspec {
440            return;
441        }
442        if addr.is_ipv4() {
443            self.message.header.address_family = AddressFamily::Inet;
444        } else {
445            self.message.header.address_family = AddressFamily::Inet6;
446        }
447    }
448}
449
450impl Default for RouteMessageBuilder<IpAddr> {
451    fn default() -> Self {
452        Self::new()
453    }
454}
455
456#[cfg(not(target_os = "android"))]
457impl RouteMessageBuilder<MplsLabel> {
458    /// Create default RouteMessage with header set to:
459    ///  * route: [RouteHeader::RT_TABLE_MAIN]
460    ///  * protocol: [RouteProtocol::Static]
461    ///  * scope: [RouteScope::Universe]
462    ///  * kind: [RouteType::Unicast]
463    ///  * address_family: [AddressFamily::Mpls]
464    ///
465    /// For using this message in querying routes, these settings
466    /// are ignored unless `NETLINK_GET_STRICT_CHK` been enabled.
467    pub fn new() -> Self {
468        let mut builder = Self::new_no_address_family();
469        builder.get_mut().header.address_family = AddressFamily::Mpls;
470        builder
471    }
472
473    #[cfg(not(target_os = "android"))]
474    /// Sets the destination MPLS label.
475    pub fn label(mut self, label: MplsLabel) -> Self {
476        self.message.header.address_family = AddressFamily::Mpls;
477        self.message.header.destination_prefix_length = 20;
478        self.message
479            .attributes
480            .push(RouteAttribute::Destination(RouteAddress::Mpls(label)));
481        self
482    }
483
484    /// Sets the gateway (via) address.
485    pub fn via(mut self, addr: IpAddr) -> Self {
486        self.message
487            .attributes
488            .push(RouteAttribute::Via(addr.into()));
489        self
490    }
491}
492#[cfg(not(target_os = "android"))]
493impl Default for RouteMessageBuilder<MplsLabel> {
494    fn default() -> Self {
495        Self::new()
496    }
497}
498
499impl RouteNextHopBuilder {
500    /// Create default RouteNexthop for a route with the given address family.
501    pub fn new(address_family: AddressFamily) -> Self {
502        Self {
503            address_family,
504            nexthop: Default::default(),
505        }
506    }
507
508    /// Create IPv4 RouteNexthop
509    pub fn new_ipv4() -> Self {
510        Self {
511            address_family: AddressFamily::Inet,
512            nexthop: Default::default(),
513        }
514    }
515
516    /// Create IPv6 RouteNexthop
517    pub fn new_ipv6() -> Self {
518        Self {
519            address_family: AddressFamily::Inet6,
520            nexthop: Default::default(),
521        }
522    }
523
524    /// Sets the nexthop interface index.
525    pub fn interface(mut self, index: u32) -> Self {
526        self.nexthop.interface_index = index;
527        self
528    }
529
530    /// Sets the nexthop (via) address.
531    pub fn via(mut self, addr: IpAddr) -> Result<Self, InvalidRouteMessage> {
532        let attr = match (self.address_family, addr) {
533            (AddressFamily::Inet, addr @ IpAddr::V4(_))
534            | (AddressFamily::Inet6, addr @ IpAddr::V6(_)) => {
535                RouteAttribute::Gateway(addr.into())
536            }
537            (AddressFamily::Inet, IpAddr::V6(v6)) => {
538                RouteAttribute::Via(RouteVia::Inet6(v6))
539            }
540            #[cfg(not(target_os = "android"))]
541            (AddressFamily::Mpls, _) => RouteAttribute::Via(addr.into()),
542            (af, _) => return Err(InvalidRouteMessage::AddressFamily(af)),
543        };
544        self.nexthop.attributes.push(attr);
545        Ok(self)
546    }
547
548    /// Marks the nexthop as directly reachable (on-link).
549    ///
550    /// Indicates that the nexthop is reachable without passing through a
551    /// connected subnet.
552    pub fn onlink(mut self) -> Self {
553        self.nexthop.flags.insert(RouteNextHopFlags::Onlink);
554        self
555    }
556
557    /// Sets the nexthop MPLS encapsulation labels.
558    #[cfg(not(target_os = "android"))]
559    pub fn mpls(mut self, labels: Vec<MplsLabel>) -> Self {
560        if labels.is_empty() {
561            return self;
562        }
563        if self.address_family == AddressFamily::Mpls {
564            self.nexthop
565                .attributes
566                .push(RouteAttribute::NewDestination(labels));
567        } else {
568            self.nexthop
569                .attributes
570                .push(RouteAttribute::EncapType(RouteLwEnCapType::Mpls));
571            let encap = RouteLwTunnelEncap::Mpls(
572                RouteMplsIpTunnel::Destination(labels),
573            );
574            self.nexthop
575                .attributes
576                .push(RouteAttribute::Encap(vec![encap]));
577        }
578        self
579    }
580
581    /// Set the nexthop weight
582    ///
583    /// Equal to `weight` property in `ip route`, but please be advised the
584    /// number shown in `ip route` command has plus 1. Meaning kernel has
585    /// `weight 0`, but `ip route` shows as `weight 1`. This function is using
586    /// kernel number from range of 0 to 255.
587    pub fn weight(mut self, weight: u8) -> Self {
588        self.nexthop.hops = weight;
589        self
590    }
591
592    /// Set flags for next hop
593    pub fn flags(mut self, flags: RouteNextHopFlags) -> Self {
594        self.nexthop.flags = flags;
595        self
596    }
597
598    pub fn build(self) -> RouteNextHop {
599        self.nexthop
600    }
601}