1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// SPDX-License-Identifier: MIT

use crate::{
    constants::*,
    traits::{Emitable, Parseable},
    DecodeError,
    RouteMessageBuffer,
    ROUTE_HEADER_LEN,
};

bitflags! {
    /// Flags that can be set in a `RTM_GETROUTE` ([`RtnlMessage::GetRoute`]) message.
    pub struct RouteFlags: u32 {
        /// If the route changes, notify the user via rtnetlink
        const RTM_F_NOTIFY = RTM_F_NOTIFY;
        /// This route is cloned. Cloned routes are routes coming from the cache instead of the
        /// FIB. For IPv4, the cache was removed in Linux 3.6 (see [IPv4 route lookup on Linux] for
        /// more information about IPv4 routing)
        ///
        /// [IPv4 route lookup on Linux]: https://vincent.bernat.ch/en/blog/2017-ipv4-route-lookup-linux
        const RTM_F_CLONED = RTM_F_CLONED;
        /// Multipath equalizer (not yet implemented)
        const RTM_F_EQUALIZE = RTM_F_EQUALIZE;
        /// Prefix addresses
        const RTM_F_PREFIX = RTM_F_PREFIX;
        /// Show the table from which the lookup result comes. Note that before commit
        /// `c36ba6603a11`, Linux would always hardcode [`RouteMessageHeader.table`] (known as
        /// `rtmsg.rtm_table` in the kernel) to `RT_TABLE_MAIN`.
        ///
        /// [`RouteMessageHeader.table`]: ../struct.RouteMessageHeader.html#structfield.table
        const RTM_F_LOOKUP_TABLE = RTM_F_LOOKUP_TABLE;
        /// Return the full FIB lookup match (see commit `b61798130f1be5bff08712308126c2d7ebe390ef`)
        const RTM_F_FIB_MATCH = RTM_F_FIB_MATCH;
    }
}

impl Default for RouteFlags {
    fn default() -> Self {
        Self::empty()
    }
}

/// High level representation of `RTM_GETROUTE`, `RTM_ADDROUTE`, `RTM_DELROUTE`
/// messages headers.
///
/// These headers have the following structure:
///
/// ```no_rust
/// 0                8                16              24               32
/// +----------------+----------------+----------------+----------------+
/// | address family | dest. length   | source length  |      tos       |
/// +----------------+----------------+----------------+----------------+
/// |     table      |   protocol     |      scope     | type (kind)    |
/// +----------------+----------------+----------------+----------------+
/// |                               flags                               |
/// +----------------+----------------+----------------+----------------+
/// ```
///
/// # Example
///
/// ```rust
/// extern crate netlink_packet_route;
/// use netlink_packet_route::{constants::*, RouteFlags, RouteHeader};
///
/// fn main() {
///     let mut hdr = RouteHeader::default();
///     assert_eq!(hdr.address_family, 0u8);
///     assert_eq!(hdr.destination_prefix_length, 0u8);
///     assert_eq!(hdr.source_prefix_length, 0u8);
///     assert_eq!(hdr.tos, 0u8);
///     assert_eq!(hdr.table, RT_TABLE_UNSPEC);
///     assert_eq!(hdr.protocol, RTPROT_UNSPEC);
///     assert_eq!(hdr.scope, RT_SCOPE_UNIVERSE);
///     assert_eq!(hdr.kind, RTN_UNSPEC);
///     assert_eq!(hdr.flags.bits(), 0u32);
///
///     // set some values
///     hdr.destination_prefix_length = 8;
///     hdr.table = RT_TABLE_MAIN;
///     hdr.protocol = RTPROT_KERNEL;
///     hdr.scope = RT_SCOPE_NOWHERE;
///
///     // ...
/// }
/// ```
#[derive(Debug, PartialEq, Eq, Hash, Clone, Default)]
pub struct RouteHeader {
    /// Address family of the route: either [`AF_INET`] for IPv4 prefixes, or [`AF_INET6`] for IPv6
    /// prefixes.
    pub address_family: u8,
    /// Prefix length of the destination subnet.
    ///
    /// Note that setting
    pub destination_prefix_length: u8,
    /// Prefix length of the source address.
    ///
    /// There could be multiple addresses from which a certain network is reachable. To decide which
    /// source address to use to reach and address in that network, the kernel rely on the route's
    /// source address for this destination.
    ///
    /// For instance, interface `if1` could have two addresses `10.0.0.1/24` and `10.0.0.128/24`,
    /// and we could have the following routes:
    ///
    /// ```no_rust
    /// 10.0.0.10/32 dev if1 scope link src 10.0.0.1
    /// 10.0.0.11/32 dev if1 scope link src 10.0.0.1
    /// 10.0.0.12/32 dev if1 scope link src 10.0.0.1
    /// 10.0.0.0/24 dev if1 scope link src 10.0.0.128
    /// ```
    ///
    /// It means that for `10.0.0.10`, `10.0.0.11` and `10.0.0.12` the packets will be sent with
    /// `10.0.0.1` as source address, while for the rest of the `10.0.0.0/24` subnet, the source
    /// address will be `10.0.0.128`
    pub source_prefix_length: u8,
    /// TOS filter
    pub tos: u8,
    /// Routing table ID. It can be one of the `RT_TABLE_*` constants or a custom table number
    /// between 1 and 251 (included). Note that Linux supports routing table with an ID greater than
    /// 255, in which case this attribute will be set to [`RT_TABLE_COMPAT`] and an [`Nla::Table`]
    /// netlink attribute will be present in the message.
    pub table: u8,
    /// Protocol from which the route was learnt. It should be set to one of the `RTPROT_*`
    /// constants.
    pub protocol: u8,
    /// The scope of the area where the addresses in the destination subnet are valid. Predefined
    /// scope values are the `RT_SCOPE_*` constants.
    pub scope: u8,
    /// Route type. It should be set to one of the `RTN_*` constants.
    pub kind: u8,
    /// Flags when querying the kernel with a `RTM_GETROUTE` message. See [`RouteFlags`].
    pub flags: RouteFlags,
}

impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<RouteMessageBuffer<&'a T>> for RouteHeader {
    fn parse(buf: &RouteMessageBuffer<&'a T>) -> Result<Self, DecodeError> {
        Ok(RouteHeader {
            address_family: buf.address_family(),
            destination_prefix_length: buf.destination_prefix_length(),
            source_prefix_length: buf.source_prefix_length(),
            tos: buf.tos(),
            table: buf.table(),
            protocol: buf.protocol(),
            scope: buf.scope(),
            kind: buf.kind(),
            flags: RouteFlags::from_bits_truncate(buf.flags()),
        })
    }
}

impl Emitable for RouteHeader {
    fn buffer_len(&self) -> usize {
        ROUTE_HEADER_LEN
    }

    fn emit(&self, buffer: &mut [u8]) {
        let mut buffer = RouteMessageBuffer::new(buffer);
        buffer.set_address_family(self.address_family);
        buffer.set_destination_prefix_length(self.destination_prefix_length);
        buffer.set_source_prefix_length(self.source_prefix_length);
        buffer.set_tos(self.tos);
        buffer.set_table(self.table);
        buffer.set_protocol(self.protocol);
        buffer.set_scope(self.scope);
        buffer.set_kind(self.kind);
        buffer.set_flags(self.flags.bits());
    }
}