ftth_rtnl/
address.rs

1#![allow(unreachable_patterns)]
2
3use std::io::{self, ErrorKind};
4use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
5
6use futures::TryStreamExt;
7
8use ftth_common::channel::{AsyncWorldClient, AsyncWorldServer};
9use netlink_packet_route::{
10    AddressFamily,
11    address::{AddressAttribute, AddressMessage},
12};
13
14pub(crate) type Client = AsyncWorldClient<RtnlAddressRequest, RtnlAddressResponse>;
15pub(crate) type Server = AsyncWorldServer<RtnlAddressRequest, RtnlAddressResponse>;
16
17#[derive(Debug, Clone, PartialEq)]
18#[non_exhaustive]
19pub enum RtnlAddressRequest {
20    Ipv4AddrsGet { if_id: u32 },
21    Ipv6AddrsGet { if_id: u32 },
22    Ipv4AddrSet { prefix: crate::Ipv4Net, if_id: u32 },
23    Ipv6AddrSet { prefix: crate::Ipv6Net, if_id: u32 },
24    Ipv4AddrDel { prefix: crate::Ipv4Net, if_id: u32 },
25    Ipv6AddrDel { prefix: crate::Ipv6Net, if_id: u32 },
26}
27
28#[derive(Debug, Clone, PartialEq)]
29#[non_exhaustive]
30pub enum RtnlAddressResponse {
31    Success,
32    Failed,
33    NotImplemented,
34    NotFound,
35    Ipv4Addrs(Vec<Ipv4Addr>),
36    Ipv6Addrs(Vec<Ipv6Addr>),
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash)]
40pub struct RtnlAddressClient {
41    client: Client,
42}
43
44impl RtnlAddressClient {
45    pub(crate) fn new(client: Client) -> Self {
46        Self { client }
47    }
48
49    pub fn ipv4_addrs_get(&self, if_id: Option<u32>) -> std::io::Result<Vec<Ipv4Addr>> {
50        let res = self.client.send_request(RtnlAddressRequest::Ipv4AddrsGet {
51            if_id: if_id.unwrap_or(0),
52        })?;
53        match res {
54            RtnlAddressResponse::Ipv4Addrs(addrs) => {
55                return Ok(addrs);
56            }
57            _ => {}
58        }
59        Err(std::io::Error::other("Failed to get IPv4 addresses"))
60    }
61
62    pub fn ipv6_addrs_get(&self, if_id: Option<u32>) -> std::io::Result<Vec<Ipv6Addr>> {
63        let res = self.client.send_request(RtnlAddressRequest::Ipv6AddrsGet {
64            if_id: if_id.unwrap_or(0),
65        })?;
66        match res {
67            RtnlAddressResponse::Ipv6Addrs(addrs) => {
68                return Ok(addrs);
69            }
70            _ => {}
71        }
72        Err(std::io::Error::other("Failed to get IPv6 addresses"))
73    }
74
75    pub fn ipv4_addr_set(&self, if_id: u32, prefix: crate::Ipv4Net) -> io::Result<()> {
76        let res = self
77            .client
78            .send_request(RtnlAddressRequest::Ipv4AddrSet { prefix, if_id })?;
79        handle_basic_response("IPv4 address set", res, false)
80    }
81
82    pub fn ipv6_addr_set(&self, if_id: u32, prefix: crate::Ipv6Net) -> io::Result<()> {
83        let res = self
84            .client
85            .send_request(RtnlAddressRequest::Ipv6AddrSet { prefix, if_id })?;
86        handle_basic_response("IPv6 address set", res, false)
87    }
88
89    pub fn ipv4_addr_del(&self, if_id: u32, prefix: crate::Ipv4Net) -> io::Result<()> {
90        let res = self
91            .client
92            .send_request(RtnlAddressRequest::Ipv4AddrDel { prefix, if_id })?;
93        handle_basic_response("IPv4 address delete", res, true)
94    }
95
96    pub fn ipv6_addr_del(&self, if_id: u32, prefix: crate::Ipv6Net) -> io::Result<()> {
97        let res = self
98            .client
99            .send_request(RtnlAddressRequest::Ipv6AddrDel { prefix, if_id })?;
100        handle_basic_response("IPv6 address delete", res, true)
101    }
102}
103
104fn build_ipv4_address_message(prefix: &crate::Ipv4Net, if_id: u32) -> AddressMessage {
105    let mut message = AddressMessage::default();
106    message.header.family = AddressFamily::Inet;
107    message.header.index = if_id;
108    message.header.prefix_len = prefix.prefix_len();
109
110    let addr = prefix.addr();
111    if !addr.is_multicast() {
112        message
113            .attributes
114            .push(AddressAttribute::Address(addr.into()));
115        message
116            .attributes
117            .push(AddressAttribute::Local(addr.into()));
118
119        let broadcast = if prefix.prefix_len() == 32 {
120            addr
121        } else {
122            let host_bits = 0xffff_ffff_u32 >> u32::from(prefix.prefix_len());
123            let ip_addr = u32::from(addr);
124            Ipv4Addr::from(ip_addr | host_bits)
125        };
126        message
127            .attributes
128            .push(AddressAttribute::Broadcast(broadcast));
129    }
130
131    message
132}
133
134fn build_ipv6_address_message(prefix: &crate::Ipv6Net, if_id: u32) -> AddressMessage {
135    let mut message = AddressMessage::default();
136    message.header.family = AddressFamily::Inet6;
137    message.header.index = if_id;
138    message.header.prefix_len = prefix.prefix_len();
139
140    let addr = prefix.addr();
141    if addr.is_multicast() {
142        message.attributes.push(AddressAttribute::Multicast(addr));
143    } else {
144        message
145            .attributes
146            .push(AddressAttribute::Address(addr.into()));
147        message
148            .attributes
149            .push(AddressAttribute::Local(addr.into()));
150    }
151
152    message
153}
154
155fn handle_basic_response(
156    operation: &str,
157    response: RtnlAddressResponse,
158    is_delete: bool,
159) -> io::Result<()> {
160    match response {
161        RtnlAddressResponse::Success => Ok(()),
162        RtnlAddressResponse::Failed => {
163            Err(io::Error::other(format!("{} request failed", operation)))
164        }
165        RtnlAddressResponse::NotImplemented => Err(io::Error::new(
166            ErrorKind::Unsupported,
167            format!("{} request is not implemented", operation),
168        )),
169        RtnlAddressResponse::NotFound => Err(io::Error::new(
170            ErrorKind::NotFound,
171            if is_delete {
172                format!("{} target not found", operation)
173            } else {
174                format!("{} not found", operation)
175            },
176        )),
177        unexpected => Err(io::Error::other(format!(
178            "{} returned unexpected response: {:?}",
179            operation, unexpected
180        ))),
181    }
182}
183
184pub(crate) async fn run_server(mut server: Server, handle: rtnetlink::AddressHandle) {
185    while let Some((req, respond)) = server.accept().await {
186        match req {
187            RtnlAddressRequest::Ipv4AddrsGet { if_id } => {
188                let if_index = if_id;
189                let mut addrs = Vec::new();
190                let mut req = handle.get();
191                if if_index != 0 {
192                    req = req.set_link_index_filter(if_index);
193                }
194                let response = req.execute();
195
196                futures::pin_mut!(response);
197                while let Ok(Some(response)) = response.try_next().await {
198                    if response.header.family != netlink_packet_route::AddressFamily::Inet {
199                        continue;
200                    }
201                    for addr in response.attributes.iter() {
202                        if let netlink_packet_route::address::AddressAttribute::Address(
203                            std::net::IpAddr::V4(addr),
204                        ) = addr
205                        {
206                            addrs.push(*addr);
207                        }
208                    }
209                }
210                respond(RtnlAddressResponse::Ipv4Addrs(addrs));
211            }
212            RtnlAddressRequest::Ipv6AddrsGet { if_id } => {
213                let if_index = if_id;
214                let mut addrs = Vec::new();
215                let mut req = handle.get();
216                if if_index != 0 {
217                    req = req.set_link_index_filter(if_index);
218                }
219                let response = req.execute();
220
221                futures::pin_mut!(response);
222                while let Ok(Some(response)) = response.try_next().await {
223                    if response.header.family != netlink_packet_route::AddressFamily::Inet6 {
224                        continue;
225                    }
226                    for addr in response.attributes.iter() {
227                        if let netlink_packet_route::address::AddressAttribute::Address(
228                            std::net::IpAddr::V6(addr),
229                        ) = addr
230                        {
231                            addrs.push(*addr);
232                        }
233                    }
234                }
235                respond(RtnlAddressResponse::Ipv6Addrs(addrs));
236            }
237            RtnlAddressRequest::Ipv4AddrSet { prefix, if_id } => {
238                if if_id == 0 {
239                    respond(RtnlAddressResponse::Failed);
240                    continue;
241                }
242
243                let addr = prefix.addr();
244                let prefix_len = prefix.prefix_len();
245                let result = handle
246                    .add(if_id, IpAddr::V4(addr), prefix_len)
247                    .execute()
248                    .await;
249
250                match result {
251                    Ok(()) => respond(RtnlAddressResponse::Success),
252                    Err(rtnetlink::Error::NetlinkError(err_msg))
253                        if err_msg.to_io().kind() == ErrorKind::AlreadyExists =>
254                    {
255                        respond(RtnlAddressResponse::Success);
256                    }
257                    Err(err) => {
258                        log::warn!(
259                            "Failed to add IPv4 address {}/{} on ifindex {}: {}",
260                            addr,
261                            prefix_len,
262                            if_id,
263                            err,
264                        );
265                        respond(RtnlAddressResponse::Failed);
266                    }
267                }
268            }
269            RtnlAddressRequest::Ipv6AddrSet { prefix, if_id } => {
270                if if_id == 0 {
271                    respond(RtnlAddressResponse::Failed);
272                    continue;
273                }
274
275                let addr = prefix.addr();
276                let prefix_len = prefix.prefix_len();
277                let result = handle
278                    .add(if_id, IpAddr::V6(addr), prefix_len)
279                    .execute()
280                    .await;
281
282                match result {
283                    Ok(()) => respond(RtnlAddressResponse::Success),
284                    Err(rtnetlink::Error::NetlinkError(err_msg))
285                        if err_msg.to_io().kind() == ErrorKind::AlreadyExists =>
286                    {
287                        respond(RtnlAddressResponse::Success);
288                    }
289                    Err(err) => {
290                        log::warn!(
291                            "Failed to add IPv6 address {}/{} on ifindex {}: {}",
292                            addr,
293                            prefix_len,
294                            if_id,
295                            err,
296                        );
297                        respond(RtnlAddressResponse::Failed);
298                    }
299                }
300            }
301            RtnlAddressRequest::Ipv4AddrDel { prefix, if_id } => {
302                if if_id == 0 {
303                    respond(RtnlAddressResponse::Failed);
304                    continue;
305                }
306
307                let addr = prefix.addr();
308                let prefix_len = prefix.prefix_len();
309                let message = build_ipv4_address_message(&prefix, if_id);
310
311                let result = handle.del(message).execute().await;
312
313                match result {
314                    Ok(()) => respond(RtnlAddressResponse::Success),
315                    Err(rtnetlink::Error::NetlinkError(err_msg)) => {
316                        let io_err = err_msg.to_io();
317                        if matches!(
318                            io_err.kind(),
319                            ErrorKind::AddrNotAvailable | ErrorKind::NotFound
320                        ) {
321                            respond(RtnlAddressResponse::NotFound);
322                        } else {
323                            log::warn!(
324                                "Failed to delete IPv4 address {}/{} on ifindex {}: {}",
325                                addr,
326                                prefix_len,
327                                if_id,
328                                err_msg,
329                            );
330                            respond(RtnlAddressResponse::Failed);
331                        }
332                    }
333                    Err(err) => {
334                        log::warn!(
335                            "Failed to delete IPv4 address {}/{} on ifindex {}: {}",
336                            addr,
337                            prefix_len,
338                            if_id,
339                            err,
340                        );
341                        respond(RtnlAddressResponse::Failed);
342                    }
343                }
344            }
345            RtnlAddressRequest::Ipv6AddrDel { prefix, if_id } => {
346                if if_id == 0 {
347                    respond(RtnlAddressResponse::Failed);
348                    continue;
349                }
350
351                let addr = prefix.addr();
352                let prefix_len = prefix.prefix_len();
353                let message = build_ipv6_address_message(&prefix, if_id);
354
355                let result = handle.del(message).execute().await;
356
357                match result {
358                    Ok(()) => respond(RtnlAddressResponse::Success),
359                    Err(rtnetlink::Error::NetlinkError(err_msg)) => {
360                        let io_err = err_msg.to_io();
361                        if matches!(
362                            io_err.kind(),
363                            ErrorKind::AddrNotAvailable | ErrorKind::NotFound
364                        ) {
365                            respond(RtnlAddressResponse::NotFound);
366                        } else {
367                            log::warn!(
368                                "Failed to delete IPv6 address {}/{} on ifindex {}: {}",
369                                addr,
370                                prefix_len,
371                                if_id,
372                                err_msg,
373                            );
374                            respond(RtnlAddressResponse::Failed);
375                        }
376                    }
377                    Err(err) => {
378                        log::warn!(
379                            "Failed to delete IPv6 address {}/{} on ifindex {}: {}",
380                            addr,
381                            prefix_len,
382                            if_id,
383                            err,
384                        );
385                        respond(RtnlAddressResponse::Failed);
386                    }
387                }
388            }
389            _ => respond(RtnlAddressResponse::NotImplemented),
390        }
391    }
392}