netlink_packet_route/rtnl/
buffer.rs

1// SPDX-License-Identifier: MIT
2
3use crate::{
4    constants::*,
5    traits::{Parseable, ParseableParametrized},
6    AddressHeader,
7    AddressMessage,
8    AddressMessageBuffer,
9    DecodeError,
10    LinkMessage,
11    LinkMessageBuffer,
12    NeighbourMessage,
13    NeighbourMessageBuffer,
14    NeighbourTableMessage,
15    NeighbourTableMessageBuffer,
16    NsidMessage,
17    NsidMessageBuffer,
18    RouteHeader,
19    RouteMessage,
20    RouteMessageBuffer,
21    RtnlMessage,
22    RuleMessage,
23    RuleMessageBuffer,
24    TcMessage,
25    TcMessageBuffer,
26};
27use anyhow::Context;
28
29buffer!(RtnlMessageBuffer);
30
31impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized<RtnlMessageBuffer<&'a T>, u16>
32    for RtnlMessage
33{
34    #[rustfmt::skip]
35    fn parse_with_param(buf: &RtnlMessageBuffer<&'a T>, message_type: u16) -> Result<Self, DecodeError> {
36        use self::RtnlMessage::*;
37        let message = match message_type {
38
39            // Link messages
40            RTM_NEWLINK | RTM_GETLINK | RTM_DELLINK | RTM_SETLINK => {
41                let msg = match LinkMessageBuffer::new_checked(&buf.inner()) {
42                    Ok(buf) => LinkMessage::parse(&buf).context("invalid link message")?,
43                    // HACK: iproute2 sends invalid RTM_GETLINK message, where the header is
44                    // limited to the interface family (1 byte) and 3 bytes of padding.
45                    Err(e) => {
46                        if buf.inner().len() == 4 && message_type == RTM_GETLINK {
47                            let mut msg = LinkMessage::default();
48                            msg.header.interface_family = buf.inner()[0];
49                            msg
50                        } else {
51                            return Err(e);
52                        }
53                    }
54                };
55                match message_type {
56                    RTM_NEWLINK => NewLink(msg),
57                    RTM_GETLINK => GetLink(msg),
58                    RTM_DELLINK => DelLink(msg),
59                    RTM_SETLINK => SetLink(msg),
60                    _ => unreachable!(),
61                }
62            }
63
64            // Address messages
65            RTM_NEWADDR | RTM_GETADDR | RTM_DELADDR => {
66                let msg = match AddressMessageBuffer::new_checked(&buf.inner()) {
67                    Ok(buf) => AddressMessage::parse(&buf).context("invalid link message")?,
68                    // HACK: iproute2 sends invalid RTM_GETADDR message, where the header is
69                    // limited to the interface family (1 byte) and 3 bytes of padding.
70                    Err(e) => {
71                        if buf.inner().len() == 4 && message_type == RTM_GETADDR {
72                            let mut msg = AddressMessage {
73                                header: AddressHeader::default(),
74                                nlas: vec![],
75                            };
76                            msg.header.family = buf.inner()[0];
77                            msg
78                        } else {
79                            return Err(e);
80                        }
81                    }
82                };
83                match message_type {
84                    RTM_NEWADDR => NewAddress(msg),
85                    RTM_GETADDR => GetAddress(msg),
86                    RTM_DELADDR => DelAddress(msg),
87                    _ => unreachable!(),
88                }
89            }
90
91            // Neighbour messages
92            RTM_NEWNEIGH | RTM_GETNEIGH | RTM_DELNEIGH => {
93                let err = "invalid neighbour message";
94                let msg = NeighbourMessage::parse(&NeighbourMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?;
95                match message_type {
96                    RTM_GETNEIGH => GetNeighbour(msg),
97                    RTM_NEWNEIGH => NewNeighbour(msg),
98                    RTM_DELNEIGH => DelNeighbour(msg),
99                    _ => unreachable!(),
100                }
101            }
102
103            // Neighbour table messages
104            RTM_NEWNEIGHTBL | RTM_GETNEIGHTBL | RTM_SETNEIGHTBL => {
105                let err = "invalid neighbour table message";
106                let msg = NeighbourTableMessage::parse(&NeighbourTableMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?;
107                match message_type {
108                    RTM_GETNEIGHTBL => GetNeighbourTable(msg),
109                    RTM_NEWNEIGHTBL => NewNeighbourTable(msg),
110                    RTM_SETNEIGHTBL => SetNeighbourTable(msg),
111                    _ => unreachable!(),
112                }
113            }
114
115            // Route messages
116            RTM_NEWROUTE | RTM_GETROUTE | RTM_DELROUTE => {
117                let msg = match RouteMessageBuffer::new_checked(&buf.inner()) {
118                    Ok(buf) => RouteMessage::parse(&buf).context("invalid route message")?,
119                    // HACK: iproute2 sends invalid RTM_GETROUTE message, where the header is
120                    // limited to the interface family (1 byte) and 3 bytes of padding.
121                    Err(e) => {
122                        // Not only does iproute2 sends invalid messages, it's also inconsistent in
123                        // doing so: for link and address messages, the length advertised in the
124                        // netlink header includes the 3 bytes of padding but it does not seem to
125                        // be the case for the route message, hence the buf.length() == 1 check.
126                        if (buf.inner().len() == 4 || buf.inner().len() == 1) && message_type == RTM_GETROUTE {
127                            let mut msg = RouteMessage {
128                                header: RouteHeader::default(),
129                                nlas: vec![],
130                            };
131                            msg.header.address_family = buf.inner()[0];
132                            msg
133                        } else {
134                            return Err(e);
135                        }
136                    }
137                };
138                match message_type {
139                    RTM_NEWROUTE => NewRoute(msg),
140                    RTM_GETROUTE => GetRoute(msg),
141                    RTM_DELROUTE => DelRoute(msg),
142                    _ => unreachable!(),
143                }
144            }
145
146            RTM_NEWRULE | RTM_GETRULE | RTM_DELRULE => {
147                let err = "invalid fib rule message";
148                let msg = RuleMessage::parse(&RuleMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?;
149                match message_type {
150                    RTM_NEWRULE => NewRule(msg),
151                    RTM_DELRULE => DelRule(msg),
152                    RTM_GETRULE => GetRule(msg),
153                    _ => unreachable!()
154                }
155            }
156            // TC Messages
157            RTM_NEWQDISC | RTM_DELQDISC | RTM_GETQDISC |
158            RTM_NEWTCLASS | RTM_DELTCLASS | RTM_GETTCLASS |
159            RTM_NEWTFILTER | RTM_DELTFILTER | RTM_GETTFILTER |
160            RTM_NEWCHAIN | RTM_DELCHAIN | RTM_GETCHAIN => {
161                let err = "invalid tc message";
162                let msg = TcMessage::parse(&TcMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?;
163                match message_type {
164                    RTM_NEWQDISC => NewQueueDiscipline(msg),
165                    RTM_DELQDISC => DelQueueDiscipline(msg),
166                    RTM_GETQDISC => GetQueueDiscipline(msg),
167                    RTM_NEWTCLASS => NewTrafficClass(msg),
168                    RTM_DELTCLASS => DelTrafficClass(msg),
169                    RTM_GETTCLASS => GetTrafficClass(msg),
170                    RTM_NEWTFILTER => NewTrafficFilter(msg),
171                    RTM_DELTFILTER => DelTrafficFilter(msg),
172                    RTM_GETTFILTER => GetTrafficFilter(msg),
173                    RTM_NEWCHAIN => NewTrafficChain(msg),
174                    RTM_DELCHAIN => DelTrafficChain(msg),
175                    RTM_GETCHAIN => GetTrafficChain(msg),
176                    _ => unreachable!(),
177                }
178            }
179
180            // ND ID Messages
181            RTM_NEWNSID | RTM_GETNSID | RTM_DELNSID => {
182                let err = "invalid nsid message";
183                let msg = NsidMessage::parse(&NsidMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?;
184                match message_type {
185                    RTM_NEWNSID => NewNsId(msg),
186                    RTM_DELNSID => DelNsId(msg),
187                    RTM_GETNSID => GetNsId(msg),
188                    _ => unreachable!(),
189                }
190            }
191
192            _ => return Err(format!("Unknown message type: {}", message_type).into()),
193        };
194        Ok(message)
195    }
196}