netlink_packet_route/rtnl/
buffer.rs

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