1#![allow(unreachable_patterns)]
2
3use std::io::{self, ErrorKind};
4use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
5
6use ftth_common::channel::{AsyncWorldClient, AsyncWorldServer};
7use futures::TryStreamExt;
8use tracing::warn;
9use netlink_packet_route::AddressFamily;
10use netlink_packet_route::route::{
11 RouteAddress, RouteAttribute, RouteMessage, RouteNextHop, RouteNextHopFlags, RouteType,
12 RouteVia,
13};
14use rtnetlink::RouteMessageBuilder;
15
16pub(crate) type Client = AsyncWorldClient<RtnlRouteRequest, RtnlRouteResponse>;
17pub(crate) type Server = AsyncWorldServer<RtnlRouteRequest, RtnlRouteResponse>;
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Ipv4Route {
21 pub if_id: Option<u32>,
22 pub gateway: Option<IpAddr>,
23 pub source: Option<Ipv4Addr>,
24 pub metric: Option<u32>,
25 pub table: Option<u32>,
26 pub route: crate::Ipv4Net,
27 pub nexthops: Vec<RouteNextHopInfo>,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct Ipv6Route {
32 pub if_id: Option<u32>,
33 pub gateway: Option<IpAddr>,
34 pub source: Option<Ipv6Addr>,
35 pub metric: Option<u32>,
36 pub table: Option<u32>,
37 pub route: crate::Ipv6Net,
38 pub nexthops: Vec<RouteNextHopInfo>,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct RouteNextHopInfo {
43 pub if_id: Option<u32>,
44 pub gateway: Option<IpAddr>,
45 pub weight: u32,
46 pub flags: RouteNextHopFlags,
47}
48
49#[derive(Debug, Clone, PartialEq)]
50#[non_exhaustive]
51pub enum RtnlRouteRequest {
52 Ipv4RouteList,
53 Ipv6RouteList,
54 Ipv4RouteAdd(Ipv4Route),
55 Ipv4RouteReplace(Ipv4Route),
56 Ipv6RouteAdd(Ipv6Route),
57 Ipv6RouteReplace(Ipv6Route),
58 Ipv4RouteDel(Ipv4Route),
59 Ipv6RouteDel(Ipv6Route),
60 Ipv4RouteGet(Ipv4Addr),
61 Ipv6RouteGet(Ipv6Addr),
62 Ipv4RouteGetByPrefix(crate::Ipv4Net),
63 Ipv6RouteGetByPrefix(crate::Ipv6Net),
64}
65
66#[derive(Debug, Clone, PartialEq)]
67#[non_exhaustive]
68pub enum RtnlRouteResponse {
69 Success,
70 Failed,
71 NotImplemented,
72 NotFound,
73 Ipv4RouteList(Vec<Ipv4Route>),
74 Ipv6RouteList(Vec<Ipv6Route>),
75 Ipv4Route(Ipv4Route),
76 Ipv6Route(Ipv6Route),
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Hash)]
80pub struct RtnlRouteClient {
81 client: Client,
82}
83
84impl RtnlRouteClient {
85 pub(crate) fn new(client: Client) -> Self {
86 Self { client }
87 }
88
89 pub fn ipv4_route_add(&self, route: Ipv4Route) -> io::Result<()> {
90 let res = self
91 .client
92 .send_request(RtnlRouteRequest::Ipv4RouteAdd(route))?;
93 handle_route_status("IPv4 route add", res)
94 }
95
96 pub fn ipv4_route_replace(&self, route: Ipv4Route) -> io::Result<()> {
97 let res = self
98 .client
99 .send_request(RtnlRouteRequest::Ipv4RouteReplace(route))?;
100 handle_route_status("IPv4 route replace", res)
101 }
102
103 pub fn ipv4_route_del(&self, route: Ipv4Route) -> io::Result<()> {
104 let res = self
105 .client
106 .send_request(RtnlRouteRequest::Ipv4RouteDel(route))?;
107 handle_route_status("IPv4 route delete", res)
108 }
109
110 pub fn ipv4_route_list(&self) -> io::Result<Vec<Ipv4Route>> {
111 match self.client.send_request(RtnlRouteRequest::Ipv4RouteList)? {
112 RtnlRouteResponse::Ipv4RouteList(routes) => Ok(routes),
113 other => Err(io::Error::other(format!(
114 "Unexpected response for IPv4 route list: {:?}",
115 other
116 ))),
117 }
118 }
119
120 pub fn ipv4_route_get(&self, destination: Ipv4Addr) -> io::Result<Ipv4Route> {
121 match self
122 .client
123 .send_request(RtnlRouteRequest::Ipv4RouteGet(destination))?
124 {
125 RtnlRouteResponse::Ipv4Route(route) => Ok(route),
126 RtnlRouteResponse::NotFound => {
127 Err(io::Error::new(ErrorKind::NotFound, "Route not found"))
128 }
129 other => Err(io::Error::other(format!(
130 "Unexpected response for IPv4 route get: {:?}",
131 other
132 ))),
133 }
134 }
135
136 pub fn ipv4_route_get_by_prefix(&self, prefix: crate::Ipv4Net) -> io::Result<Ipv4Route> {
137 match self
138 .client
139 .send_request(RtnlRouteRequest::Ipv4RouteGetByPrefix(prefix))?
140 {
141 RtnlRouteResponse::Ipv4Route(route) => Ok(route),
142 RtnlRouteResponse::NotFound => {
143 Err(io::Error::new(ErrorKind::NotFound, "Route not found"))
144 }
145 other => Err(io::Error::other(format!(
146 "Unexpected response for IPv4 route get by prefix: {:?}",
147 other
148 ))),
149 }
150 }
151
152 pub fn ipv6_route_add(&self, route: Ipv6Route) -> io::Result<()> {
153 let res = self
154 .client
155 .send_request(RtnlRouteRequest::Ipv6RouteAdd(route))?;
156 handle_route_status("IPv6 route add", res)
157 }
158
159 pub fn ipv6_route_replace(&self, route: Ipv6Route) -> io::Result<()> {
160 let res = self
161 .client
162 .send_request(RtnlRouteRequest::Ipv6RouteReplace(route))?;
163 handle_route_status("IPv6 route replace", res)
164 }
165
166 pub fn ipv6_route_del(&self, route: Ipv6Route) -> io::Result<()> {
167 let res = self
168 .client
169 .send_request(RtnlRouteRequest::Ipv6RouteDel(route))?;
170 handle_route_status("IPv6 route delete", res)
171 }
172
173 pub fn ipv6_route_list(&self) -> io::Result<Vec<Ipv6Route>> {
174 match self.client.send_request(RtnlRouteRequest::Ipv6RouteList)? {
175 RtnlRouteResponse::Ipv6RouteList(routes) => Ok(routes),
176 other => Err(io::Error::other(format!(
177 "Unexpected response for IPv6 route list: {:?}",
178 other
179 ))),
180 }
181 }
182
183 pub fn ipv6_route_get(&self, destination: Ipv6Addr) -> io::Result<Ipv6Route> {
184 match self
185 .client
186 .send_request(RtnlRouteRequest::Ipv6RouteGet(destination))?
187 {
188 RtnlRouteResponse::Ipv6Route(route) => Ok(route),
189 RtnlRouteResponse::NotFound => {
190 Err(io::Error::new(ErrorKind::NotFound, "Route not found"))
191 }
192 other => Err(io::Error::other(format!(
193 "Unexpected response for IPv6 route get: {:?}",
194 other
195 ))),
196 }
197 }
198
199 pub fn ipv6_route_get_by_prefix(&self, prefix: crate::Ipv6Net) -> io::Result<Ipv6Route> {
200 match self
201 .client
202 .send_request(RtnlRouteRequest::Ipv6RouteGetByPrefix(prefix))?
203 {
204 RtnlRouteResponse::Ipv6Route(route) => Ok(route),
205 RtnlRouteResponse::NotFound => {
206 Err(io::Error::new(ErrorKind::NotFound, "Route not found"))
207 }
208 other => Err(io::Error::other(format!(
209 "Unexpected response for IPv6 route get by prefix: {:?}",
210 other
211 ))),
212 }
213 }
214}
215
216pub(crate) async fn run_server(mut server: Server, handle: rtnetlink::RouteHandle) {
217 while let Some((req, respond)) = server.accept().await {
218 let response = match req {
219 RtnlRouteRequest::Ipv4RouteList => list_routes_v4(&handle).await,
220 RtnlRouteRequest::Ipv6RouteList => list_routes_v6(&handle).await,
221 RtnlRouteRequest::Ipv4RouteAdd(route) => add_route_v4(&handle, route, false).await,
222 RtnlRouteRequest::Ipv4RouteReplace(route) => add_route_v4(&handle, route, true).await,
223 RtnlRouteRequest::Ipv6RouteAdd(route) => add_route_v6(&handle, route, false).await,
224 RtnlRouteRequest::Ipv6RouteReplace(route) => add_route_v6(&handle, route, true).await,
225 RtnlRouteRequest::Ipv4RouteDel(route) => delete_route_v4(&handle, route).await,
226 RtnlRouteRequest::Ipv6RouteDel(route) => delete_route_v6(&handle, route).await,
227 RtnlRouteRequest::Ipv4RouteGet(destination) => get_route_v4(&handle, destination).await,
228 RtnlRouteRequest::Ipv6RouteGet(destination) => get_route_v6(&handle, destination).await,
229 RtnlRouteRequest::Ipv4RouteGetByPrefix(prefix) => {
230 get_route_v4_by_prefix(&handle, prefix).await
231 }
232 RtnlRouteRequest::Ipv6RouteGetByPrefix(prefix) => {
233 get_route_v6_by_prefix(&handle, prefix).await
234 }
235 };
236 respond(response);
237 }
238}
239
240fn handle_route_status(op: &str, response: RtnlRouteResponse) -> io::Result<()> {
241 match response {
242 RtnlRouteResponse::Success => Ok(()),
243 RtnlRouteResponse::NotFound => Err(io::Error::new(
244 ErrorKind::NotFound,
245 format!("{}: route not found", op),
246 )),
247 RtnlRouteResponse::Failed => Err(io::Error::other(format!("{} failed", op))),
248 RtnlRouteResponse::NotImplemented => Err(io::Error::new(
249 ErrorKind::Unsupported,
250 format!("{} not implemented", op),
251 )),
252 other => Err(io::Error::other(format!(
253 "{} returned unexpected response: {:?}",
254 op, other
255 ))),
256 }
257}
258
259async fn list_routes_v4(handle: &rtnetlink::RouteHandle) -> RtnlRouteResponse {
260 let message = RouteMessageBuilder::<Ipv4Addr>::new().build();
261 let stream = handle.get(message).execute();
262 futures::pin_mut!(stream);
263 let mut routes = Vec::new();
264 loop {
265 match stream.try_next().await {
266 Ok(Some(msg)) => {
267 if let Some(route) = decode_ipv4_route(msg) {
268 routes.push(route);
269 }
270 }
271 Ok(None) => break,
272 Err(err) => {
273 warn!("Failed to list IPv4 routes: {}", err);
274 return RtnlRouteResponse::Failed;
275 }
276 }
277 }
278 RtnlRouteResponse::Ipv4RouteList(routes)
279}
280
281async fn list_routes_v6(handle: &rtnetlink::RouteHandle) -> RtnlRouteResponse {
282 let message = RouteMessageBuilder::<Ipv6Addr>::new().build();
283 let stream = handle.get(message).execute();
284 futures::pin_mut!(stream);
285 let mut routes = Vec::new();
286 loop {
287 match stream.try_next().await {
288 Ok(Some(msg)) => {
289 if let Some(route) = decode_ipv6_route(msg) {
290 routes.push(route);
291 }
292 }
293 Ok(None) => break,
294 Err(err) => {
295 warn!("Failed to list IPv6 routes: {}", err);
296 return RtnlRouteResponse::Failed;
297 }
298 }
299 }
300 RtnlRouteResponse::Ipv6RouteList(routes)
301}
302
303async fn add_route_v4(
304 handle: &rtnetlink::RouteHandle,
305 route: Ipv4Route,
306 replace: bool,
307) -> RtnlRouteResponse {
308 let message = build_ipv4_route_message(&route);
309 let request = handle.add(message);
310 let request = if replace { request.replace() } else { request };
311 map_route_result(
312 request.execute().await,
313 if replace {
314 "replace IPv4 route"
315 } else {
316 "add IPv4 route"
317 },
318 )
319}
320
321async fn add_route_v6(
322 handle: &rtnetlink::RouteHandle,
323 route: Ipv6Route,
324 replace: bool,
325) -> RtnlRouteResponse {
326 let message = build_ipv6_route_message(&route);
327 let request = handle.add(message);
328 let request = if replace { request.replace() } else { request };
329 map_route_result(
330 request.execute().await,
331 if replace {
332 "replace IPv6 route"
333 } else {
334 "add IPv6 route"
335 },
336 )
337}
338
339async fn delete_route_v4(handle: &rtnetlink::RouteHandle, route: Ipv4Route) -> RtnlRouteResponse {
340 let message = build_ipv4_route_message(&route);
341 map_route_result(handle.del(message).execute().await, "delete IPv4 route")
342}
343
344async fn delete_route_v6(handle: &rtnetlink::RouteHandle, route: Ipv6Route) -> RtnlRouteResponse {
345 let message = build_ipv6_route_message(&route);
346 map_route_result(handle.del(message).execute().await, "delete IPv6 route")
347}
348
349async fn get_route_v4(handle: &rtnetlink::RouteHandle, destination: Ipv4Addr) -> RtnlRouteResponse {
350 let target = destination;
351 let message = build_route_message_v4(Some(destination), 32);
352 lookup_route_v4(handle, message, move |route| route.route.contains(&target)).await
353}
354
355async fn get_route_v4_by_prefix(
356 handle: &rtnetlink::RouteHandle,
357 prefix: crate::Ipv4Net,
358) -> RtnlRouteResponse {
359 let target = prefix;
360 let message = build_route_message_v4(
361 if prefix.prefix_len() == 0 {
362 None
363 } else {
364 Some(prefix.addr())
365 },
366 prefix.prefix_len(),
367 );
368 lookup_route_v4(handle, message, move |route| route.route == target).await
369}
370
371async fn get_route_v6(handle: &rtnetlink::RouteHandle, destination: Ipv6Addr) -> RtnlRouteResponse {
372 let target = destination;
373 let message = build_route_message_v6(Some(destination), 128);
374 lookup_route_v6(handle, message, move |route| route.route.contains(&target)).await
375}
376
377async fn get_route_v6_by_prefix(
378 handle: &rtnetlink::RouteHandle,
379 prefix: crate::Ipv6Net,
380) -> RtnlRouteResponse {
381 let target = prefix;
382 let message = build_route_message_v6(
383 if prefix.prefix_len() == 0 {
384 None
385 } else {
386 Some(prefix.addr())
387 },
388 prefix.prefix_len(),
389 );
390 lookup_route_v6(handle, message, move |route| route.route == target).await
391}
392
393async fn lookup_route<F>(
394 handle: &rtnetlink::RouteHandle,
395 message: RouteMessage,
396 map: F,
397) -> RtnlRouteResponse
398where
399 F: Fn(RouteMessage) -> Option<RtnlRouteResponse>,
400{
401 let stream = handle.get(message).execute();
402 futures::pin_mut!(stream);
403 loop {
404 match stream.try_next().await {
405 Ok(Some(msg)) => {
406 if let Some(resp) = map(msg) {
407 return resp;
408 }
409 }
410 Ok(None) => return RtnlRouteResponse::NotFound,
411 Err(err) => {
412 warn!("Failed to get route: {}", err);
413 return RtnlRouteResponse::Failed;
414 }
415 }
416 }
417}
418
419async fn lookup_route_v4<F>(
420 handle: &rtnetlink::RouteHandle,
421 message: RouteMessage,
422 predicate: F,
423) -> RtnlRouteResponse
424where
425 F: Fn(&Ipv4Route) -> bool,
426{
427 lookup_route(handle, message, |msg| {
428 if matches!(msg.header.kind, RouteType::Local | RouteType::Broadcast) {
429 return None;
430 }
431 decode_ipv4_route(msg).and_then(|route| {
432 if predicate(&route) {
433 Some(RtnlRouteResponse::Ipv4Route(route))
434 } else {
435 None
436 }
437 })
438 })
439 .await
440}
441
442async fn lookup_route_v6<F>(
443 handle: &rtnetlink::RouteHandle,
444 message: RouteMessage,
445 predicate: F,
446) -> RtnlRouteResponse
447where
448 F: Fn(&Ipv6Route) -> bool,
449{
450 lookup_route(handle, message, |msg| {
451 if matches!(msg.header.kind, RouteType::Local | RouteType::Broadcast) {
452 return None;
453 }
454 decode_ipv6_route(msg).and_then(|route| {
455 if predicate(&route) {
456 Some(RtnlRouteResponse::Ipv6Route(route))
457 } else {
458 None
459 }
460 })
461 })
462 .await
463}
464
465fn build_route_message_v4(destination: Option<Ipv4Addr>, prefix_len: u8) -> RouteMessage {
466 let mut message = RouteMessage::default();
467 message.header.address_family = AddressFamily::Inet;
468 message.header.destination_prefix_length = prefix_len;
469 if let Some(addr) = destination {
470 message
471 .attributes
472 .push(RouteAttribute::Destination(RouteAddress::Inet(addr)));
473 }
474 message
475}
476
477fn build_route_message_v6(destination: Option<Ipv6Addr>, prefix_len: u8) -> RouteMessage {
478 let mut message = RouteMessage::default();
479 message.header.address_family = AddressFamily::Inet6;
480 message.header.destination_prefix_length = prefix_len;
481 if let Some(addr) = destination {
482 message
483 .attributes
484 .push(RouteAttribute::Destination(RouteAddress::Inet6(addr)));
485 }
486 message
487}
488
489fn map_route_result(result: Result<(), rtnetlink::Error>, op: &str) -> RtnlRouteResponse {
490 match result {
491 Ok(()) => RtnlRouteResponse::Success,
492 Err(rtnetlink::Error::NetlinkError(err_msg)) => {
493 let io_err = err_msg.to_io();
494 match io_err.kind() {
495 ErrorKind::NotFound => RtnlRouteResponse::NotFound,
496 ErrorKind::AlreadyExists => {
497 warn!("Route operation failed (already exists): {}", io_err);
498 RtnlRouteResponse::Failed
499 }
500 _ => {
501 warn!("Route operation '{}' failed: {}", op, io_err);
502 RtnlRouteResponse::Failed
503 }
504 }
505 }
506 Err(err) => {
507 warn!("Route operation '{}' failed: {}", op, err);
508 RtnlRouteResponse::Failed
509 }
510 }
511}
512
513fn build_ipv4_route_message(route: &Ipv4Route) -> RouteMessage {
514 let mut builder = RouteMessageBuilder::<Ipv4Addr>::new()
515 .destination_prefix(route.route.addr(), route.route.prefix_len());
516
517 if let Some(if_id) = route.if_id.filter(|id| *id != 0) {
518 builder = builder.output_interface(if_id);
519 }
520
521 if let Some(gw) = route.gateway {
522 match gw {
523 IpAddr::V4(addr) => {
524 builder = builder.gateway(addr);
525 }
526 IpAddr::V6(addr) => {
527 builder
528 .get_mut()
529 .attributes
530 .push(RouteAttribute::Via(RouteVia::Inet6(addr)));
531 }
532 }
533 }
534
535 if let Some(src) = route.source {
536 builder = builder.pref_source(src);
537 }
538
539 if let Some(metric) = route.metric {
540 builder = builder.priority(metric);
541 }
542
543 if let Some(table) = route.table {
544 builder = builder.table_id(table);
545 }
546
547 if !route.nexthops.is_empty() {
548 if let Some(multipath) = build_multipath_v4(&route.nexthops) {
549 builder = builder.multipath(multipath);
550 }
551 }
552
553 builder.build()
554}
555
556fn build_ipv6_route_message(route: &Ipv6Route) -> RouteMessage {
557 let mut builder = RouteMessageBuilder::<Ipv6Addr>::new()
558 .destination_prefix(route.route.addr(), route.route.prefix_len());
559
560 if let Some(if_id) = route.if_id.filter(|id| *id != 0) {
561 builder = builder.output_interface(if_id);
562 }
563
564 if let Some(gw) = route.gateway {
565 match gw {
566 IpAddr::V6(addr) => {
567 builder = builder.gateway(addr);
568 }
569 IpAddr::V4(addr) => {
570 builder
571 .get_mut()
572 .attributes
573 .push(RouteAttribute::Via(RouteVia::Inet(addr)));
574 }
575 }
576 }
577
578 if let Some(src) = route.source {
579 builder = builder.pref_source(src);
580 }
581
582 if let Some(metric) = route.metric {
583 builder = builder.priority(metric);
584 }
585
586 if let Some(table) = route.table {
587 builder = builder.table_id(table);
588 }
589
590 if !route.nexthops.is_empty() {
591 if let Some(multipath) = build_multipath_v6(&route.nexthops) {
592 builder = builder.multipath(multipath);
593 }
594 }
595
596 builder.build()
597}
598
599fn decode_ipv4_route(message: RouteMessage) -> Option<Ipv4Route> {
600 if message.header.address_family != AddressFamily::Inet {
601 return None;
602 }
603
604 let header = message.header;
605 let mut destination = None;
606 let mut gateway: Option<IpAddr> = None;
607 let mut source = None;
608 let mut metric = None;
609 let mut table = table_from_header(header.table);
610 let mut oif = None;
611 let mut nexthops = Vec::new();
612
613 for attr in message.attributes {
614 match attr {
615 RouteAttribute::Destination(RouteAddress::Inet(addr)) => destination = Some(addr),
616 RouteAttribute::Gateway(RouteAddress::Inet(addr)) => gateway = Some(IpAddr::V4(addr)),
617 RouteAttribute::Gateway(RouteAddress::Inet6(addr)) => gateway = Some(IpAddr::V6(addr)),
618 RouteAttribute::Via(RouteVia::Inet(addr)) => gateway = Some(IpAddr::V4(addr)),
619 RouteAttribute::Via(RouteVia::Inet6(addr)) => gateway = Some(IpAddr::V6(addr)),
620 RouteAttribute::PrefSource(RouteAddress::Inet(addr)) => source = Some(addr),
621 RouteAttribute::Priority(value) => metric = Some(value),
622 RouteAttribute::Oif(index) => oif = Some(index),
623 RouteAttribute::Table(value) => table = Some(value),
624 RouteAttribute::MultiPath(paths) => {
625 nexthops.extend(convert_multipath(paths));
626 }
627 _ => {}
628 }
629 }
630
631 let addr = destination.unwrap_or(Ipv4Addr::UNSPECIFIED);
632 let net = crate::Ipv4Net::new(addr, header.destination_prefix_length).ok()?;
633
634 Some(Ipv4Route {
635 if_id: oif.filter(|id| *id != 0),
636 gateway,
637 source,
638 metric,
639 table,
640 route: net,
641 nexthops,
642 })
643}
644
645fn decode_ipv6_route(message: RouteMessage) -> Option<Ipv6Route> {
646 if message.header.address_family != AddressFamily::Inet6 {
647 return None;
648 }
649
650 let header = message.header;
651 let mut destination = None;
652 let mut gateway: Option<IpAddr> = None;
653 let mut source = None;
654 let mut metric = None;
655 let mut table = table_from_header(header.table);
656 let mut oif = None;
657 let mut nexthops = Vec::new();
658
659 for attr in message.attributes {
660 match attr {
661 RouteAttribute::Destination(RouteAddress::Inet6(addr)) => destination = Some(addr),
662 RouteAttribute::Gateway(RouteAddress::Inet(addr)) => gateway = Some(IpAddr::V4(addr)),
663 RouteAttribute::Gateway(RouteAddress::Inet6(addr)) => gateway = Some(IpAddr::V6(addr)),
664 RouteAttribute::Via(RouteVia::Inet(addr)) => gateway = Some(IpAddr::V4(addr)),
665 RouteAttribute::Via(RouteVia::Inet6(addr)) => gateway = Some(IpAddr::V6(addr)),
666 RouteAttribute::PrefSource(RouteAddress::Inet6(addr)) => source = Some(addr),
667 RouteAttribute::Priority(value) => metric = Some(value),
668 RouteAttribute::Oif(index) => oif = Some(index),
669 RouteAttribute::Table(value) => table = Some(value),
670 RouteAttribute::MultiPath(paths) => {
671 nexthops.extend(convert_multipath(paths));
672 }
673 _ => {}
674 }
675 }
676
677 let addr = destination.unwrap_or(Ipv6Addr::UNSPECIFIED);
678 let net = crate::Ipv6Net::new(addr, header.destination_prefix_length).ok()?;
679
680 Some(Ipv6Route {
681 if_id: oif.filter(|id| *id != 0),
682 gateway,
683 source,
684 metric,
685 table,
686 route: net,
687 nexthops,
688 })
689}
690
691fn table_from_header(value: u8) -> Option<u32> {
692 if value == 0 { None } else { Some(value as u32) }
693}
694
695fn convert_multipath(paths: Vec<RouteNextHop>) -> Vec<RouteNextHopInfo> {
696 let mut result = Vec::new();
697 for path in paths {
698 let mut gateway = None;
699 for attr in path.attributes {
700 match attr {
701 RouteAttribute::Gateway(RouteAddress::Inet(addr)) => {
702 gateway = Some(IpAddr::V4(addr))
703 }
704 RouteAttribute::Gateway(RouteAddress::Inet6(addr)) => {
705 gateway = Some(IpAddr::V6(addr))
706 }
707 RouteAttribute::Via(RouteVia::Inet(addr)) => gateway = Some(IpAddr::V4(addr)),
708 RouteAttribute::Via(RouteVia::Inet6(addr)) => gateway = Some(IpAddr::V6(addr)),
709 RouteAttribute::NewDestination(_) => {}
710 _ => {}
711 }
712 }
713
714 let weight = u32::from(path.hops).saturating_add(1);
715 let if_id = match path.interface_index {
716 0 => None,
717 index => Some(index),
718 };
719
720 result.push(RouteNextHopInfo {
721 if_id,
722 gateway,
723 weight,
724 flags: path.flags,
725 });
726 }
727 result
728}
729
730fn build_multipath_v4(entries: &[RouteNextHopInfo]) -> Option<Vec<RouteNextHop>> {
731 let mut nexthops = Vec::new();
732 for entry in entries {
733 let mut route_entry = RouteNextHop::default();
734 route_entry.flags = entry.flags;
735 route_entry.hops = weight_to_hops(entry.weight);
736 route_entry.interface_index = entry.if_id.unwrap_or(0);
737 route_entry.attributes = Vec::new();
738
739 match entry.gateway {
740 Some(IpAddr::V4(addr)) => {
741 route_entry
742 .attributes
743 .push(RouteAttribute::Gateway(RouteAddress::Inet(addr)));
744 }
745 Some(IpAddr::V6(addr)) => {
746 route_entry
747 .attributes
748 .push(RouteAttribute::Via(RouteVia::Inet6(addr)));
749 }
750 None => {}
751 }
752
753 nexthops.push(route_entry);
754 }
755
756 if nexthops.is_empty() {
757 None
758 } else {
759 Some(nexthops)
760 }
761}
762
763fn build_multipath_v6(entries: &[RouteNextHopInfo]) -> Option<Vec<RouteNextHop>> {
764 let mut nexthops = Vec::new();
765 for entry in entries {
766 let mut route_entry = RouteNextHop::default();
767 route_entry.flags = entry.flags;
768 route_entry.hops = weight_to_hops(entry.weight);
769 route_entry.interface_index = entry.if_id.unwrap_or(0);
770 route_entry.attributes = Vec::new();
771
772 match entry.gateway {
773 Some(IpAddr::V6(addr)) => {
774 route_entry
775 .attributes
776 .push(RouteAttribute::Gateway(RouteAddress::Inet6(addr)));
777 }
778 Some(IpAddr::V4(addr)) => {
779 route_entry
780 .attributes
781 .push(RouteAttribute::Via(RouteVia::Inet(addr)));
782 }
783 None => {}
784 }
785
786 nexthops.push(route_entry);
787 }
788
789 if nexthops.is_empty() {
790 None
791 } else {
792 Some(nexthops)
793 }
794}
795
796fn weight_to_hops(weight: u32) -> u8 {
797 weight.saturating_sub(1).min(u8::MAX as u32) as u8
798}