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 log::warn;
8use netlink_packet_route::neighbour::{NeighbourAddress, NeighbourAttribute, NeighbourMessage};
9use netlink_packet_route::{AddressFamily, route::RouteType};
10
11pub use netlink_packet_route::neighbour::{NeighbourFlags, NeighbourState};
12
13pub(crate) type Client = AsyncWorldClient<RtnlNeighborRequest, RtnlNeighborResponse>;
14pub(crate) type Server = AsyncWorldServer<RtnlNeighborRequest, RtnlNeighborResponse>;
15
16#[derive(Debug, Clone, PartialEq)]
17pub struct NeighborEntry {
18    pub if_id: u32,
19    pub destination: IpAddr,
20    pub link_address: Option<Vec<u8>>,
21    pub state: Option<NeighbourState>,
22    pub flags: Option<NeighbourFlags>,
23}
24
25#[derive(Debug, Clone, PartialEq)]
26pub struct NeighborDelete {
27    pub if_id: u32,
28    pub destination: IpAddr,
29    pub link_address: Option<Vec<u8>>,
30    pub state: Option<NeighbourState>,
31    pub flags: Option<NeighbourFlags>,
32}
33
34#[derive(Debug, Clone, PartialEq)]
35#[non_exhaustive]
36pub enum RtnlNeighborRequest {
37    Add(NeighborEntry),
38    Change(NeighborEntry),
39    Delete(NeighborDelete),
40}
41
42#[derive(Debug, Clone, PartialEq)]
43#[non_exhaustive]
44pub enum RtnlNeighborResponse {
45    Success,
46    Failed,
47    NotImplemented,
48    NotFound,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52pub struct RtnlNeighborClient {
53    client: Client,
54}
55
56impl RtnlNeighborClient {
57    pub(crate) fn new(client: Client) -> Self {
58        Self { client }
59    }
60
61    pub fn add(&self, entry: NeighborEntry) -> io::Result<()> {
62        let res = self.client.send_request(RtnlNeighborRequest::Add(entry))?;
63        handle_neighbor_response("Neighbor add", res, false)
64    }
65
66    pub fn change(&self, entry: NeighborEntry) -> io::Result<()> {
67        let res = self
68            .client
69            .send_request(RtnlNeighborRequest::Change(entry))?;
70        handle_neighbor_response("Neighbor change", res, false)
71    }
72
73    pub fn delete(&self, entry: NeighborDelete) -> io::Result<()> {
74        let res = self
75            .client
76            .send_request(RtnlNeighborRequest::Delete(entry))?;
77        handle_neighbor_response("Neighbor delete", res, false)
78    }
79}
80
81pub(crate) async fn run_server(mut server: Server, handle: rtnetlink::NeighbourHandle) {
82    while let Some((req, respond)) = server.accept().await {
83        let response = match req {
84            RtnlNeighborRequest::Add(entry) => add_or_change_neighbor(&handle, entry, false).await,
85            RtnlNeighborRequest::Change(entry) => {
86                add_or_change_neighbor(&handle, entry, true).await
87            }
88            RtnlNeighborRequest::Delete(entry) => delete_neighbor(&handle, entry).await,
89        };
90        respond(response);
91    }
92}
93
94fn handle_neighbor_response(
95    operation: &str,
96    response: RtnlNeighborResponse,
97    allow_not_found: bool,
98) -> io::Result<()> {
99    match response {
100        RtnlNeighborResponse::Success => Ok(()),
101        RtnlNeighborResponse::NotFound if allow_not_found => Ok(()),
102        RtnlNeighborResponse::NotFound => Err(io::Error::new(
103            ErrorKind::NotFound,
104            format!("{}: entry not found", operation),
105        )),
106        RtnlNeighborResponse::Failed => Err(io::Error::other(format!("{} failed", operation))),
107        RtnlNeighborResponse::NotImplemented => Err(io::Error::new(
108            ErrorKind::Unsupported,
109            format!("{} is not implemented", operation),
110        )),
111    }
112}
113
114async fn add_or_change_neighbor(
115    handle: &rtnetlink::NeighbourHandle,
116    entry: NeighborEntry,
117    replace: bool,
118) -> RtnlNeighborResponse {
119    let mut request = handle.add(entry.if_id, entry.destination);
120
121    if let Some(ref link_address) = entry.link_address {
122        request = request.link_local_address(link_address);
123    }
124
125    if let Some(state) = entry.state {
126        request = request.state(state);
127    }
128
129    if let Some(flags) = entry.flags {
130        request = request.flags(flags);
131    }
132
133    if replace {
134        request = request.replace();
135    }
136
137    match request.execute().await {
138        Ok(()) => RtnlNeighborResponse::Success,
139        Err(rtnetlink::Error::NetlinkError(err_msg)) => {
140            let io_err = err_msg.to_io();
141            match io_err.kind() {
142                ErrorKind::NotFound => RtnlNeighborResponse::NotFound,
143                ErrorKind::AlreadyExists => {
144                    warn!("Neighbor operation failed (already exists): {}", io_err);
145                    RtnlNeighborResponse::Failed
146                }
147                _ => {
148                    warn!("Neighbor operation failed: {}", io_err);
149                    RtnlNeighborResponse::Failed
150                }
151            }
152        }
153        Err(err) => {
154            warn!("Neighbor operation failed: {}", err);
155            RtnlNeighborResponse::Failed
156        }
157    }
158}
159
160async fn delete_neighbor(
161    handle: &rtnetlink::NeighbourHandle,
162    entry: NeighborDelete,
163) -> RtnlNeighborResponse {
164    let message = build_delete_message(&entry);
165    match handle.del(message).execute().await {
166        Ok(()) => RtnlNeighborResponse::Success,
167        Err(rtnetlink::Error::NetlinkError(err_msg)) => {
168            let io_err = err_msg.to_io();
169            match io_err.kind() {
170                ErrorKind::NotFound => RtnlNeighborResponse::NotFound,
171                _ => {
172                    warn!("Neighbor delete failed: {}", io_err);
173                    RtnlNeighborResponse::Failed
174                }
175            }
176        }
177        Err(err) => {
178            warn!("Neighbor delete failed: {}", err);
179            RtnlNeighborResponse::Failed
180        }
181    }
182}
183
184fn build_delete_message(entry: &NeighborDelete) -> NeighbourMessage {
185    let mut message = NeighbourMessage::default();
186    message.header.family = match entry.destination {
187        IpAddr::V4(_) => AddressFamily::Inet,
188        IpAddr::V6(_) => AddressFamily::Inet6,
189    };
190    message.header.ifindex = entry.if_id;
191    message.header.kind = RouteType::Unspec;
192
193    if let Some(state) = entry.state {
194        message.header.state = state;
195    }
196
197    if let Some(flags) = entry.flags {
198        message.header.flags = flags;
199    }
200
201    let destination = match entry.destination {
202        IpAddr::V4(addr) => NeighbourAddress::Inet(addr),
203        IpAddr::V6(addr) => NeighbourAddress::Inet6(addr),
204    };
205
206    message
207        .attributes
208        .push(NeighbourAttribute::Destination(destination));
209
210    if let Some(ref link_address) = entry.link_address {
211        message
212            .attributes
213            .push(NeighbourAttribute::LinkLocalAddress(link_address.clone()));
214    }
215
216    message
217}