ftth_rtnl/
route.rs

1#![allow(unreachable_patterns)]
2
3use std::io::{self, ErrorKind};
4use std::net::{Ipv4Addr, Ipv6Addr};
5
6use ftth_common::channel::{AsyncWorldClient, AsyncWorldServer};
7use futures::TryStreamExt;
8use log::warn;
9use netlink_packet_route::AddressFamily;
10use netlink_packet_route::route::{RouteAddress, RouteAttribute, RouteMessage};
11use rtnetlink::RouteMessageBuilder;
12
13pub(crate) type Client = AsyncWorldClient<RtnlRouteRequest, RtnlRouteResponse>;
14pub(crate) type Server = AsyncWorldServer<RtnlRouteRequest, RtnlRouteResponse>;
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Ipv4Route {
18    pub if_id: Option<u32>,
19    pub gateway: Option<Ipv4Addr>,
20    pub source: Option<Ipv4Addr>,
21    pub metric: Option<u32>,
22    pub table: Option<u32>,
23    pub route: crate::Ipv4Net,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct Ipv6Route {
28    pub if_id: Option<u32>,
29    pub gateway: Option<Ipv6Addr>,
30    pub source: Option<Ipv6Addr>,
31    pub metric: Option<u32>,
32    pub table: Option<u32>,
33    pub route: crate::Ipv6Net,
34}
35
36#[derive(Debug, Clone, PartialEq)]
37#[non_exhaustive]
38pub enum RtnlRouteRequest {
39    Ipv4RouteList,
40    Ipv6RouteList,
41    Ipv4RouteAdd(Ipv4Route),
42    Ipv4RouteReplace(Ipv4Route),
43    Ipv6RouteAdd(Ipv6Route),
44    Ipv6RouteReplace(Ipv6Route),
45    Ipv4RouteDel(Ipv4Route),
46    Ipv6RouteDel(Ipv6Route),
47    Ipv4RouteGet(Ipv4Addr),
48    Ipv6RouteGet(Ipv6Addr),
49}
50
51#[derive(Debug, Clone, PartialEq)]
52#[non_exhaustive]
53pub enum RtnlRouteResponse {
54    Success,
55    Failed,
56    NotImplemented,
57    NotFound,
58    Ipv4RouteList(Vec<Ipv4Route>),
59    Ipv6RouteList(Vec<Ipv6Route>),
60    Ipv4Route(Ipv4Route),
61    Ipv6Route(Ipv6Route),
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Hash)]
65pub struct RtnlRouteClient {
66    client: Client,
67}
68
69impl RtnlRouteClient {
70    pub(crate) fn new(client: Client) -> Self {
71        Self { client }
72    }
73
74    pub fn ipv4_route_add(&self, route: Ipv4Route) -> io::Result<()> {
75        let res = self
76            .client
77            .send_request(RtnlRouteRequest::Ipv4RouteAdd(route))?;
78        handle_route_status("IPv4 route add", res)
79    }
80
81    pub fn ipv4_route_replace(&self, route: Ipv4Route) -> io::Result<()> {
82        let res = self
83            .client
84            .send_request(RtnlRouteRequest::Ipv4RouteReplace(route))?;
85        handle_route_status("IPv4 route replace", res)
86    }
87
88    pub fn ipv4_route_del(&self, route: Ipv4Route) -> io::Result<()> {
89        let res = self
90            .client
91            .send_request(RtnlRouteRequest::Ipv4RouteDel(route))?;
92        handle_route_status("IPv4 route delete", res)
93    }
94
95    pub fn ipv4_route_list(&self) -> io::Result<Vec<Ipv4Route>> {
96        match self.client.send_request(RtnlRouteRequest::Ipv4RouteList)? {
97            RtnlRouteResponse::Ipv4RouteList(routes) => Ok(routes),
98            other => Err(io::Error::other(format!(
99                "Unexpected response for IPv4 route list: {:?}",
100                other
101            ))),
102        }
103    }
104
105    pub fn ipv4_route_get(&self, destination: Ipv4Addr) -> io::Result<Ipv4Route> {
106        match self
107            .client
108            .send_request(RtnlRouteRequest::Ipv4RouteGet(destination))?
109        {
110            RtnlRouteResponse::Ipv4Route(route) => Ok(route),
111            RtnlRouteResponse::NotFound => {
112                Err(io::Error::new(ErrorKind::NotFound, "Route not found"))
113            }
114            other => Err(io::Error::other(format!(
115                "Unexpected response for IPv4 route get: {:?}",
116                other
117            ))),
118        }
119    }
120
121    pub fn ipv6_route_add(&self, route: Ipv6Route) -> io::Result<()> {
122        let res = self
123            .client
124            .send_request(RtnlRouteRequest::Ipv6RouteAdd(route))?;
125        handle_route_status("IPv6 route add", res)
126    }
127
128    pub fn ipv6_route_replace(&self, route: Ipv6Route) -> io::Result<()> {
129        let res = self
130            .client
131            .send_request(RtnlRouteRequest::Ipv6RouteReplace(route))?;
132        handle_route_status("IPv6 route replace", res)
133    }
134
135    pub fn ipv6_route_del(&self, route: Ipv6Route) -> io::Result<()> {
136        let res = self
137            .client
138            .send_request(RtnlRouteRequest::Ipv6RouteDel(route))?;
139        handle_route_status("IPv6 route delete", res)
140    }
141
142    pub fn ipv6_route_list(&self) -> io::Result<Vec<Ipv6Route>> {
143        match self.client.send_request(RtnlRouteRequest::Ipv6RouteList)? {
144            RtnlRouteResponse::Ipv6RouteList(routes) => Ok(routes),
145            other => Err(io::Error::other(format!(
146                "Unexpected response for IPv6 route list: {:?}",
147                other
148            ))),
149        }
150    }
151
152    pub fn ipv6_route_get(&self, destination: Ipv6Addr) -> io::Result<Ipv6Route> {
153        match self
154            .client
155            .send_request(RtnlRouteRequest::Ipv6RouteGet(destination))?
156        {
157            RtnlRouteResponse::Ipv6Route(route) => Ok(route),
158            RtnlRouteResponse::NotFound => {
159                Err(io::Error::new(ErrorKind::NotFound, "Route not found"))
160            }
161            other => Err(io::Error::other(format!(
162                "Unexpected response for IPv6 route get: {:?}",
163                other
164            ))),
165        }
166    }
167}
168
169pub(crate) async fn run_server(mut server: Server, handle: rtnetlink::RouteHandle) {
170    while let Some((req, respond)) = server.accept().await {
171        let response = match req {
172            RtnlRouteRequest::Ipv4RouteList => list_routes_v4(&handle).await,
173            RtnlRouteRequest::Ipv6RouteList => list_routes_v6(&handle).await,
174            RtnlRouteRequest::Ipv4RouteAdd(route) => add_route_v4(&handle, route, false).await,
175            RtnlRouteRequest::Ipv4RouteReplace(route) => add_route_v4(&handle, route, true).await,
176            RtnlRouteRequest::Ipv6RouteAdd(route) => add_route_v6(&handle, route, false).await,
177            RtnlRouteRequest::Ipv6RouteReplace(route) => add_route_v6(&handle, route, true).await,
178            RtnlRouteRequest::Ipv4RouteDel(route) => delete_route_v4(&handle, route).await,
179            RtnlRouteRequest::Ipv6RouteDel(route) => delete_route_v6(&handle, route).await,
180            RtnlRouteRequest::Ipv4RouteGet(destination) => get_route_v4(&handle, destination).await,
181            RtnlRouteRequest::Ipv6RouteGet(destination) => get_route_v6(&handle, destination).await,
182        };
183        respond(response);
184    }
185}
186
187fn handle_route_status(op: &str, response: RtnlRouteResponse) -> io::Result<()> {
188    match response {
189        RtnlRouteResponse::Success => Ok(()),
190        RtnlRouteResponse::NotFound => Err(io::Error::new(
191            ErrorKind::NotFound,
192            format!("{}: route not found", op),
193        )),
194        RtnlRouteResponse::Failed => Err(io::Error::other(format!("{} failed", op))),
195        RtnlRouteResponse::NotImplemented => Err(io::Error::new(
196            ErrorKind::Unsupported,
197            format!("{} not implemented", op),
198        )),
199        other => Err(io::Error::other(format!(
200            "{} returned unexpected response: {:?}",
201            op, other
202        ))),
203    }
204}
205
206async fn list_routes_v4(handle: &rtnetlink::RouteHandle) -> RtnlRouteResponse {
207    let message = RouteMessageBuilder::<Ipv4Addr>::new().build();
208    let stream = handle.get(message).execute();
209    futures::pin_mut!(stream);
210    let mut routes = Vec::new();
211    loop {
212        match stream.try_next().await {
213            Ok(Some(msg)) => {
214                if let Some(route) = decode_ipv4_route(msg) {
215                    routes.push(route);
216                }
217            }
218            Ok(None) => break,
219            Err(err) => {
220                warn!("Failed to list IPv4 routes: {}", err);
221                return RtnlRouteResponse::Failed;
222            }
223        }
224    }
225    RtnlRouteResponse::Ipv4RouteList(routes)
226}
227
228async fn list_routes_v6(handle: &rtnetlink::RouteHandle) -> RtnlRouteResponse {
229    let message = RouteMessageBuilder::<Ipv6Addr>::new().build();
230    let stream = handle.get(message).execute();
231    futures::pin_mut!(stream);
232    let mut routes = Vec::new();
233    loop {
234        match stream.try_next().await {
235            Ok(Some(msg)) => {
236                if let Some(route) = decode_ipv6_route(msg) {
237                    routes.push(route);
238                }
239            }
240            Ok(None) => break,
241            Err(err) => {
242                warn!("Failed to list IPv6 routes: {}", err);
243                return RtnlRouteResponse::Failed;
244            }
245        }
246    }
247    RtnlRouteResponse::Ipv6RouteList(routes)
248}
249
250async fn add_route_v4(
251    handle: &rtnetlink::RouteHandle,
252    route: Ipv4Route,
253    replace: bool,
254) -> RtnlRouteResponse {
255    let message = build_ipv4_route_message(&route);
256    let request = handle.add(message);
257    let request = if replace { request.replace() } else { request };
258    map_route_result(
259        request.execute().await,
260        if replace {
261            "replace IPv4 route"
262        } else {
263            "add IPv4 route"
264        },
265    )
266}
267
268async fn add_route_v6(
269    handle: &rtnetlink::RouteHandle,
270    route: Ipv6Route,
271    replace: bool,
272) -> RtnlRouteResponse {
273    let message = build_ipv6_route_message(&route);
274    let request = handle.add(message);
275    let request = if replace { request.replace() } else { request };
276    map_route_result(
277        request.execute().await,
278        if replace {
279            "replace IPv6 route"
280        } else {
281            "add IPv6 route"
282        },
283    )
284}
285
286async fn delete_route_v4(handle: &rtnetlink::RouteHandle, route: Ipv4Route) -> RtnlRouteResponse {
287    let message = build_ipv4_route_message(&route);
288    map_route_result(handle.del(message).execute().await, "delete IPv4 route")
289}
290
291async fn delete_route_v6(handle: &rtnetlink::RouteHandle, route: Ipv6Route) -> RtnlRouteResponse {
292    let message = build_ipv6_route_message(&route);
293    map_route_result(handle.del(message).execute().await, "delete IPv6 route")
294}
295
296async fn get_route_v4(handle: &rtnetlink::RouteHandle, destination: Ipv4Addr) -> RtnlRouteResponse {
297    let message = RouteMessageBuilder::<Ipv4Addr>::new()
298        .destination_prefix(destination, 32)
299        .build();
300    let stream = handle.get(message).execute();
301    futures::pin_mut!(stream);
302    match stream.try_next().await {
303        Ok(Some(msg)) => decode_ipv4_route(msg)
304            .map(RtnlRouteResponse::Ipv4Route)
305            .unwrap_or(RtnlRouteResponse::NotFound),
306        Ok(None) => RtnlRouteResponse::NotFound,
307        Err(err) => {
308            warn!("Failed to get IPv4 route: {}", err);
309            RtnlRouteResponse::Failed
310        }
311    }
312}
313
314async fn get_route_v6(handle: &rtnetlink::RouteHandle, destination: Ipv6Addr) -> RtnlRouteResponse {
315    let message = RouteMessageBuilder::<Ipv6Addr>::new()
316        .destination_prefix(destination, 128)
317        .build();
318    let stream = handle.get(message).execute();
319    futures::pin_mut!(stream);
320    match stream.try_next().await {
321        Ok(Some(msg)) => decode_ipv6_route(msg)
322            .map(RtnlRouteResponse::Ipv6Route)
323            .unwrap_or(RtnlRouteResponse::NotFound),
324        Ok(None) => RtnlRouteResponse::NotFound,
325        Err(err) => {
326            warn!("Failed to get IPv6 route: {}", err);
327            RtnlRouteResponse::Failed
328        }
329    }
330}
331
332fn map_route_result(result: Result<(), rtnetlink::Error>, op: &str) -> RtnlRouteResponse {
333    match result {
334        Ok(()) => RtnlRouteResponse::Success,
335        Err(rtnetlink::Error::NetlinkError(err_msg)) => {
336            let io_err = err_msg.to_io();
337            match io_err.kind() {
338                ErrorKind::NotFound => RtnlRouteResponse::NotFound,
339                ErrorKind::AlreadyExists => {
340                    warn!("Route operation failed (already exists): {}", io_err);
341                    RtnlRouteResponse::Failed
342                }
343                _ => {
344                    warn!("Route operation '{}' failed: {}", op, io_err);
345                    RtnlRouteResponse::Failed
346                }
347            }
348        }
349        Err(err) => {
350            warn!("Route operation '{}' failed: {}", op, err);
351            RtnlRouteResponse::Failed
352        }
353    }
354}
355
356fn build_ipv4_route_message(route: &Ipv4Route) -> RouteMessage {
357    let mut builder = RouteMessageBuilder::<Ipv4Addr>::new()
358        .destination_prefix(route.route.addr(), route.route.prefix_len());
359
360    if let Some(if_id) = route.if_id.filter(|id| *id != 0) {
361        builder = builder.output_interface(if_id);
362    }
363
364    if let Some(gw) = route.gateway {
365        builder = builder.gateway(gw);
366    }
367
368    if let Some(src) = route.source {
369        builder = builder.pref_source(src);
370    }
371
372    if let Some(metric) = route.metric {
373        builder = builder.priority(metric);
374    }
375
376    if let Some(table) = route.table {
377        builder = builder.table_id(table);
378    }
379
380    builder.build()
381}
382
383fn build_ipv6_route_message(route: &Ipv6Route) -> RouteMessage {
384    let mut builder = RouteMessageBuilder::<Ipv6Addr>::new()
385        .destination_prefix(route.route.addr(), route.route.prefix_len());
386
387    if let Some(if_id) = route.if_id.filter(|id| *id != 0) {
388        builder = builder.output_interface(if_id);
389    }
390
391    if let Some(gw) = route.gateway {
392        builder = builder.gateway(gw);
393    }
394
395    if let Some(src) = route.source {
396        builder = builder.pref_source(src);
397    }
398
399    if let Some(metric) = route.metric {
400        builder = builder.priority(metric);
401    }
402
403    if let Some(table) = route.table {
404        builder = builder.table_id(table);
405    }
406
407    builder.build()
408}
409
410fn decode_ipv4_route(message: RouteMessage) -> Option<Ipv4Route> {
411    if message.header.address_family != AddressFamily::Inet {
412        return None;
413    }
414
415    let header = message.header;
416    let mut destination = None;
417    let mut gateway = None;
418    let mut source = None;
419    let mut metric = None;
420    let mut table = table_from_header(header.table);
421    let mut oif = None;
422
423    for attr in message.attributes {
424        match attr {
425            RouteAttribute::Destination(RouteAddress::Inet(addr)) => destination = Some(addr),
426            RouteAttribute::Gateway(RouteAddress::Inet(addr)) => gateway = Some(addr),
427            RouteAttribute::PrefSource(RouteAddress::Inet(addr)) => source = Some(addr),
428            RouteAttribute::Priority(value) => metric = Some(value),
429            RouteAttribute::Oif(index) => oif = Some(index),
430            RouteAttribute::Table(value) => table = Some(value),
431            _ => {}
432        }
433    }
434
435    let addr = destination.unwrap_or(Ipv4Addr::UNSPECIFIED);
436    let net = crate::Ipv4Net::new(addr, header.destination_prefix_length).ok()?;
437
438    Some(Ipv4Route {
439        if_id: oif.filter(|id| *id != 0),
440        gateway,
441        source,
442        metric,
443        table,
444        route: net,
445    })
446}
447
448fn decode_ipv6_route(message: RouteMessage) -> Option<Ipv6Route> {
449    if message.header.address_family != AddressFamily::Inet6 {
450        return None;
451    }
452
453    let header = message.header;
454    let mut destination = None;
455    let mut gateway = None;
456    let mut source = None;
457    let mut metric = None;
458    let mut table = table_from_header(header.table);
459    let mut oif = None;
460
461    for attr in message.attributes {
462        match attr {
463            RouteAttribute::Destination(RouteAddress::Inet6(addr)) => destination = Some(addr),
464            RouteAttribute::Gateway(RouteAddress::Inet6(addr)) => gateway = Some(addr),
465            RouteAttribute::PrefSource(RouteAddress::Inet6(addr)) => source = Some(addr),
466            RouteAttribute::Priority(value) => metric = Some(value),
467            RouteAttribute::Oif(index) => oif = Some(index),
468            RouteAttribute::Table(value) => table = Some(value),
469            _ => {}
470        }
471    }
472
473    let addr = destination.unwrap_or(Ipv6Addr::UNSPECIFIED);
474    let net = crate::Ipv6Net::new(addr, header.destination_prefix_length).ok()?;
475
476    Some(Ipv6Route {
477        if_id: oif.filter(|id| *id != 0),
478        gateway,
479        source,
480        metric,
481        table,
482        route: net,
483    })
484}
485
486fn table_from_header(value: u8) -> Option<u32> {
487    if value == 0 { None } else { Some(value as u32) }
488}