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