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;
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}