Skip to main content

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