1#![allow(unreachable_patterns)]
2
3use std::io::{self, ErrorKind};
4use std::net::IpAddr;
5
6use ftth_common::channel::{AsyncWorldClient, AsyncWorldServer};
7use futures::TryStreamExt;
8
9use tracing::warn;
10use netlink_packet_route::neighbour::{NeighbourAddress, NeighbourAttribute, NeighbourMessage};
11use netlink_packet_route::{AddressFamily, route::RouteType};
12
13pub use netlink_packet_route::neighbour::{NeighbourFlags, NeighbourState};
14
15pub(crate) type Client = AsyncWorldClient<RtnlNeighborRequest, RtnlNeighborResponse>;
16pub(crate) type Server = AsyncWorldServer<RtnlNeighborRequest, RtnlNeighborResponse>;
17
18#[derive(Debug, Clone, PartialEq)]
19pub struct NeighborEntry {
20 pub if_id: u32,
21 pub destination: IpAddr,
22 pub link_address: Option<Vec<u8>>,
23 pub state: Option<NeighbourState>,
24 pub flags: Option<NeighbourFlags>,
25}
26
27#[derive(Debug, Clone, PartialEq)]
28pub struct NeighborDelete {
29 pub if_id: u32,
30 pub destination: IpAddr,
31 pub link_address: Option<Vec<u8>>,
32 pub state: Option<NeighbourState>,
33 pub flags: Option<NeighbourFlags>,
34}
35
36#[derive(Debug, Clone, PartialEq)]
37#[non_exhaustive]
38pub enum RtnlNeighborRequest {
39 Add(NeighborEntry),
40 Change(NeighborEntry),
41 Delete(NeighborDelete),
42 List {
43 if_id: Option<u32>,
44 },
45 Get {
46 destination: IpAddr,
47 if_id: Option<u32>,
48 },
49}
50
51#[derive(Debug, Clone, PartialEq)]
52#[non_exhaustive]
53pub enum RtnlNeighborResponse {
54 Success,
55 Failed,
56 NotImplemented,
57 NotFound,
58 Neighbors(Vec<NeighborEntry>),
59 Neighbor(NeighborEntry),
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, Hash)]
63pub struct RtnlNeighborClient {
64 client: Client,
65}
66
67impl RtnlNeighborClient {
68 pub(crate) fn new(client: Client) -> Self {
69 Self { client }
70 }
71
72 pub fn add(&self, entry: NeighborEntry) -> io::Result<()> {
73 let res = self.client.send_request(RtnlNeighborRequest::Add(entry))?;
74 handle_neighbor_response("Neighbor add", res, false)
75 }
76
77 pub fn change(&self, entry: NeighborEntry) -> io::Result<()> {
78 let res = self
79 .client
80 .send_request(RtnlNeighborRequest::Change(entry))?;
81 handle_neighbor_response("Neighbor change", res, false)
82 }
83
84 pub fn delete(&self, entry: NeighborDelete) -> io::Result<()> {
85 let res = self
86 .client
87 .send_request(RtnlNeighborRequest::Delete(entry))?;
88 handle_neighbor_response("Neighbor delete", res, false)
89 }
90
91 pub fn list(&self, if_id: Option<u32>) -> io::Result<Vec<NeighborEntry>> {
92 match self
93 .client
94 .send_request(RtnlNeighborRequest::List { if_id })?
95 {
96 RtnlNeighborResponse::Neighbors(entries) => Ok(entries),
97 other => Err(io::Error::other(format!(
98 "Unexpected response for neighbor list: {:?}",
99 other
100 ))),
101 }
102 }
103
104 pub fn get(&self, destination: IpAddr, if_id: Option<u32>) -> io::Result<NeighborEntry> {
105 match self
106 .client
107 .send_request(RtnlNeighborRequest::Get { destination, if_id })?
108 {
109 RtnlNeighborResponse::Neighbor(entry) => Ok(entry),
110 RtnlNeighborResponse::NotFound => {
111 Err(io::Error::new(ErrorKind::NotFound, "Neighbor not found"))
112 }
113 other => Err(io::Error::other(format!(
114 "Unexpected response for neighbor get: {:?}",
115 other
116 ))),
117 }
118 }
119}
120
121pub(crate) async fn run_server(mut server: Server, handle: rtnetlink::NeighbourHandle) {
122 while let Some((req, respond)) = server.accept().await {
123 let response = match req {
124 RtnlNeighborRequest::Add(entry) => add_or_change_neighbor(&handle, entry, false).await,
125 RtnlNeighborRequest::Change(entry) => {
126 add_or_change_neighbor(&handle, entry, true).await
127 }
128 RtnlNeighborRequest::Delete(entry) => delete_neighbor(&handle, entry).await,
129 RtnlNeighborRequest::List { if_id } => list_neighbors(&handle, if_id).await,
130 RtnlNeighborRequest::Get { destination, if_id } => {
131 get_neighbor(&handle, destination, if_id).await
132 }
133 };
134 respond(response);
135 }
136}
137
138fn handle_neighbor_response(
139 operation: &str,
140 response: RtnlNeighborResponse,
141 allow_not_found: bool,
142) -> io::Result<()> {
143 match response {
144 RtnlNeighborResponse::Success => Ok(()),
145 RtnlNeighborResponse::NotFound if allow_not_found => Ok(()),
146 RtnlNeighborResponse::NotFound => Err(io::Error::new(
147 ErrorKind::NotFound,
148 format!("{}: entry not found", operation),
149 )),
150 RtnlNeighborResponse::Failed => Err(io::Error::other(format!("{} failed", operation))),
151 RtnlNeighborResponse::NotImplemented => Err(io::Error::new(
152 ErrorKind::Unsupported,
153 format!("{} is not implemented", operation),
154 )),
155 other => Err(io::Error::other(format!(
156 "{} returned unexpected response: {:?}",
157 operation, other
158 ))),
159 }
160}
161
162async fn add_or_change_neighbor(
163 handle: &rtnetlink::NeighbourHandle,
164 entry: NeighborEntry,
165 replace: bool,
166) -> RtnlNeighborResponse {
167 let mut request = handle.add(entry.if_id, entry.destination);
168
169 if let Some(ref link_address) = entry.link_address {
170 request = request.link_local_address(link_address);
171 }
172
173 if let Some(state) = entry.state {
174 request = request.state(state);
175 }
176
177 if let Some(flags) = entry.flags {
178 request = request.flags(flags);
179 }
180
181 if replace {
182 request = request.replace();
183 }
184
185 match request.execute().await {
186 Ok(()) => RtnlNeighborResponse::Success,
187 Err(rtnetlink::Error::NetlinkError(err_msg)) => {
188 let io_err = err_msg.to_io();
189 match io_err.kind() {
190 ErrorKind::NotFound => RtnlNeighborResponse::NotFound,
191 ErrorKind::AlreadyExists => {
192 warn!("Neighbor operation failed (already exists): {}", io_err);
193 RtnlNeighborResponse::Failed
194 }
195 _ => {
196 warn!("Neighbor operation failed: {}", io_err);
197 RtnlNeighborResponse::Failed
198 }
199 }
200 }
201 Err(err) => {
202 warn!("Neighbor operation failed: {}", err);
203 RtnlNeighborResponse::Failed
204 }
205 }
206}
207
208async fn delete_neighbor(
209 handle: &rtnetlink::NeighbourHandle,
210 entry: NeighborDelete,
211) -> RtnlNeighborResponse {
212 let message = build_delete_message(&entry);
213 match handle.del(message).execute().await {
214 Ok(()) => RtnlNeighborResponse::Success,
215 Err(rtnetlink::Error::NetlinkError(err_msg)) => {
216 let io_err = err_msg.to_io();
217 match io_err.kind() {
218 ErrorKind::NotFound => RtnlNeighborResponse::NotFound,
219 _ => {
220 warn!("Neighbor delete failed: {}", io_err);
221 RtnlNeighborResponse::Failed
222 }
223 }
224 }
225 Err(err) => {
226 warn!("Neighbor delete failed: {}", err);
227 RtnlNeighborResponse::Failed
228 }
229 }
230}
231
232fn build_delete_message(entry: &NeighborDelete) -> NeighbourMessage {
233 let mut message = NeighbourMessage::default();
234 message.header.family = match entry.destination {
235 IpAddr::V4(_) => AddressFamily::Inet,
236 IpAddr::V6(_) => AddressFamily::Inet6,
237 };
238 message.header.ifindex = entry.if_id;
239 message.header.kind = RouteType::Unspec;
240
241 if let Some(state) = entry.state {
242 message.header.state = state;
243 }
244
245 if let Some(flags) = entry.flags {
246 message.header.flags = flags;
247 }
248
249 let destination = match entry.destination {
250 IpAddr::V4(addr) => NeighbourAddress::Inet(addr),
251 IpAddr::V6(addr) => NeighbourAddress::Inet6(addr),
252 };
253
254 message
255 .attributes
256 .push(NeighbourAttribute::Destination(destination));
257
258 if let Some(ref link_address) = entry.link_address {
259 message
260 .attributes
261 .push(NeighbourAttribute::LinkLocalAddress(link_address.clone()));
262 }
263
264 message
265}
266
267async fn list_neighbors(
268 handle: &rtnetlink::NeighbourHandle,
269 if_id: Option<u32>,
270) -> RtnlNeighborResponse {
271 match fetch_neighbors(handle).await {
272 Ok(entries) => {
273 let filtered: Vec<_> = entries
274 .into_iter()
275 .filter(|entry| if_id.map_or(true, |id| entry.if_id == id))
276 .collect();
277 RtnlNeighborResponse::Neighbors(filtered)
278 }
279 Err(err) => {
280 warn!("Neighbor list failed: {}", err);
281 RtnlNeighborResponse::Failed
282 }
283 }
284}
285
286fn neighbor_from_message(message: NeighbourMessage) -> Option<NeighborEntry> {
287 let NeighbourMessage {
288 header, attributes, ..
289 } = message;
290
291 let mut destination_attr = None;
292 let mut link_address = None;
293
294 for attr in attributes {
295 match attr {
296 NeighbourAttribute::Destination(addr) => destination_attr = Some(addr),
297 NeighbourAttribute::LinkLocalAddress(addr) => link_address = Some(addr),
298 _ => {}
299 }
300 }
301
302 let destination_attr = destination_attr?;
303 let destination = match destination_attr {
304 NeighbourAddress::Inet(addr) => IpAddr::V4(addr),
305 NeighbourAddress::Inet6(addr) => IpAddr::V6(addr),
306 NeighbourAddress::Other(_) => return None,
307 _ => return None,
308 };
309
310 let state = match header.state {
311 NeighbourState::None => None,
312 other => Some(other),
313 };
314
315 let flags = if header.flags.is_empty() {
316 None
317 } else {
318 Some(header.flags)
319 };
320
321 Some(NeighborEntry {
322 if_id: header.ifindex,
323 destination,
324 link_address,
325 state,
326 flags,
327 })
328}
329
330async fn get_neighbor(
331 handle: &rtnetlink::NeighbourHandle,
332 destination: IpAddr,
333 if_id: Option<u32>,
334) -> RtnlNeighborResponse {
335 match fetch_neighbors(handle).await {
336 Ok(entries) => {
337 let neighbor = entries.into_iter().find(|entry| {
338 if entry.destination != destination {
339 return false;
340 }
341 if let Some(index) = if_id {
342 if entry.if_id != index {
343 return false;
344 }
345 }
346 true
347 });
348 match neighbor {
349 Some(entry) => RtnlNeighborResponse::Neighbor(entry),
350 None => RtnlNeighborResponse::NotFound,
351 }
352 }
353 Err(err) => {
354 warn!("Neighbor get failed: {}", err);
355 RtnlNeighborResponse::Failed
356 }
357 }
358}
359
360async fn fetch_neighbors(
361 handle: &rtnetlink::NeighbourHandle,
362) -> Result<Vec<NeighborEntry>, rtnetlink::Error> {
363 let messages = handle.get().execute().try_collect::<Vec<_>>().await?;
364 let mut entries = Vec::new();
365 for message in messages {
366 if let Some(entry) = neighbor_from_message(message) {
367 entries.push(entry);
368 }
369 }
370 Ok(entries)
371}