Skip to main content

ftth_rtnl/
neighbor.rs

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}