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}