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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// SPDX-License-Identifier: MIT

use anyhow::Context;
use netlink_packet_utils::{
    traits::{Parseable, ParseableParametrized},
    DecodeError,
};

use crate::{
    constants::*, AddressHeader, AddressMessage, AddressMessageBuffer,
    LinkMessage, LinkMessageBuffer, NeighbourMessage, NeighbourMessageBuffer,
    NeighbourTableMessage, NeighbourTableMessageBuffer, NsidMessage,
    NsidMessageBuffer, RouteHeader, RouteMessage, RouteMessageBuffer,
    RtnlMessage, RuleMessage, RuleMessageBuffer, TcMessage, TcMessageBuffer,
};

buffer!(RtnlMessageBuffer);

impl<'a, T: AsRef<[u8]> + ?Sized>
    ParseableParametrized<RtnlMessageBuffer<&'a T>, u16> for RtnlMessage
{
    #[rustfmt::skip]
    fn parse_with_param(buf: &RtnlMessageBuffer<&'a T>, message_type: u16) -> Result<Self, DecodeError> {
        use self::RtnlMessage::*;
        let message = match message_type {

            // Link messages
            RTM_NEWLINK | RTM_GETLINK | RTM_DELLINK | RTM_SETLINK => {
                let msg = match LinkMessageBuffer::new_checked(&buf.inner()) {
                    Ok(buf) => LinkMessage::parse(&buf).context("invalid link message")?,
                    // HACK: iproute2 sends invalid RTM_GETLINK message, where the header is
                    // limited to the interface family (1 byte) and 3 bytes of padding.
                    Err(e) => {
                        if buf.inner().len() == 4 && message_type == RTM_GETLINK {
                            let mut msg = LinkMessage::default();
                            msg.header.interface_family = buf.inner()[0];
                            msg
                        } else {
                            return Err(e);
                        }
                    }
                };
                match message_type {
                    RTM_NEWLINK => NewLink(msg),
                    RTM_GETLINK => GetLink(msg),
                    RTM_DELLINK => DelLink(msg),
                    RTM_SETLINK => SetLink(msg),
                    _ => unreachable!(),
                }
            }

            // Address messages
            RTM_NEWADDR | RTM_GETADDR | RTM_DELADDR => {
                let msg = match AddressMessageBuffer::new_checked(&buf.inner()) {
                    Ok(buf) => AddressMessage::parse(&buf).context("invalid link message")?,
                    // HACK: iproute2 sends invalid RTM_GETADDR message, where the header is
                    // limited to the interface family (1 byte) and 3 bytes of padding.
                    Err(e) => {
                        if buf.inner().len() == 4 && message_type == RTM_GETADDR {
                            let mut msg = AddressMessage {
                                header: AddressHeader::default(),
                                nlas: vec![],
                            };
                            msg.header.family = buf.inner()[0];
                            msg
                        } else {
                            return Err(e);
                        }
                    }
                };
                match message_type {
                    RTM_NEWADDR => NewAddress(msg),
                    RTM_GETADDR => GetAddress(msg),
                    RTM_DELADDR => DelAddress(msg),
                    _ => unreachable!(),
                }
            }

            // Neighbour messages
            RTM_NEWNEIGH | RTM_GETNEIGH | RTM_DELNEIGH => {
                let err = "invalid neighbour message";
                let msg = NeighbourMessage::parse(&NeighbourMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?;
                match message_type {
                    RTM_GETNEIGH => GetNeighbour(msg),
                    RTM_NEWNEIGH => NewNeighbour(msg),
                    RTM_DELNEIGH => DelNeighbour(msg),
                    _ => unreachable!(),
                }
            }

            // Neighbour table messages
            RTM_NEWNEIGHTBL | RTM_GETNEIGHTBL | RTM_SETNEIGHTBL => {
                let err = "invalid neighbour table message";
                let msg = NeighbourTableMessage::parse(&NeighbourTableMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?;
                match message_type {
                    RTM_GETNEIGHTBL => GetNeighbourTable(msg),
                    RTM_NEWNEIGHTBL => NewNeighbourTable(msg),
                    RTM_SETNEIGHTBL => SetNeighbourTable(msg),
                    _ => unreachable!(),
                }
            }

            // Route messages
            RTM_NEWROUTE | RTM_GETROUTE | RTM_DELROUTE => {
                let msg = match RouteMessageBuffer::new_checked(&buf.inner()) {
                    Ok(buf) => RouteMessage::parse(&buf).context("invalid route message")?,
                    // HACK: iproute2 sends invalid RTM_GETROUTE message, where the header is
                    // limited to the interface family (1 byte) and 3 bytes of padding.
                    Err(e) => {
                        // Not only does iproute2 sends invalid messages, it's also inconsistent in
                        // doing so: for link and address messages, the length advertised in the
                        // netlink header includes the 3 bytes of padding but it does not seem to
                        // be the case for the route message, hence the buf.length() == 1 check.
                        if (buf.inner().len() == 4 || buf.inner().len() == 1) && message_type == RTM_GETROUTE {
                            let mut msg = RouteMessage {
                                header: RouteHeader::default(),
                                nlas: vec![],
                            };
                            msg.header.address_family = buf.inner()[0];
                            msg
                        } else {
                            return Err(e);
                        }
                    }
                };
                match message_type {
                    RTM_NEWROUTE => NewRoute(msg),
                    RTM_GETROUTE => GetRoute(msg),
                    RTM_DELROUTE => DelRoute(msg),
                    _ => unreachable!(),
                }
            }

            RTM_NEWRULE | RTM_GETRULE | RTM_DELRULE => {
                let err = "invalid fib rule message";
                let msg = RuleMessage::parse(&RuleMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?;
                match message_type {
                    RTM_NEWRULE => NewRule(msg),
                    RTM_DELRULE => DelRule(msg),
                    RTM_GETRULE => GetRule(msg),
                    _ => unreachable!()
                }
            }
            // TC Messages
            RTM_NEWQDISC | RTM_DELQDISC | RTM_GETQDISC |
            RTM_NEWTCLASS | RTM_DELTCLASS | RTM_GETTCLASS |
            RTM_NEWTFILTER | RTM_DELTFILTER | RTM_GETTFILTER |
            RTM_NEWCHAIN | RTM_DELCHAIN | RTM_GETCHAIN => {
                let err = "invalid tc message";
                let msg = TcMessage::parse(&TcMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?;
                match message_type {
                    RTM_NEWQDISC => NewQueueDiscipline(msg),
                    RTM_DELQDISC => DelQueueDiscipline(msg),
                    RTM_GETQDISC => GetQueueDiscipline(msg),
                    RTM_NEWTCLASS => NewTrafficClass(msg),
                    RTM_DELTCLASS => DelTrafficClass(msg),
                    RTM_GETTCLASS => GetTrafficClass(msg),
                    RTM_NEWTFILTER => NewTrafficFilter(msg),
                    RTM_DELTFILTER => DelTrafficFilter(msg),
                    RTM_GETTFILTER => GetTrafficFilter(msg),
                    RTM_NEWCHAIN => NewTrafficChain(msg),
                    RTM_DELCHAIN => DelTrafficChain(msg),
                    RTM_GETCHAIN => GetTrafficChain(msg),
                    _ => unreachable!(),
                }
            }

            // ND ID Messages
            RTM_NEWNSID | RTM_GETNSID | RTM_DELNSID => {
                let err = "invalid nsid message";
                let msg = NsidMessage::parse(&NsidMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?;
                match message_type {
                    RTM_NEWNSID => NewNsId(msg),
                    RTM_DELNSID => DelNsId(msg),
                    RTM_GETNSID => GetNsId(msg),
                    _ => unreachable!(),
                }
            }

            _ => return Err(format!("Unknown message type: {message_type}").into()),
        };
        Ok(message)
    }
}