Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting default route #63

Open
pseusys opened this issue May 3, 2024 · 5 comments
Open

Getting default route #63

pseusys opened this issue May 3, 2024 · 5 comments

Comments

@pseusys
Copy link
Contributor

pseusys commented May 3, 2024

I'm interested in finding the default route among all the routes present. Is there any way to do that?
Does combination of absence of Destination and existence of Gateway attribute mean the route is default?
Maybe there is an option to implement default route request in an easier manner?

@daniel-noland
Copy link
Contributor

daniel-noland commented May 10, 2024

This is a good question with a slightly tricky answer.

To find the default route for a given ip address family (i.e. IPv4 or IPv6), in a given routing table:

  1. pick a routing table (in Linux this is almost always table 254 unless you are using VRFs or something)
  2. find all routes in the routing table with .header.destination_prefix_length == 0 (that is, routes which are either 0.0.0.0/0 for IPv4 or ::/0 for IPv6
  3. take the route with the lowest priority among those.

I have written up an example of how to do this for all routing tables here

// SPDX-License-Identifier: MIT

use std::collections::BTreeMap;

use futures::stream::TryStreamExt;
use netlink_packet_route::route::{RouteAttribute, RouteMessage};

use rtnetlink::{new_connection, IpVersion};

type RouteTableId = u8;
type RoutePriority = Option<u32>;

#[tokio::main]
async fn main() -> Result<(), ()> {
    let (connection, handle, _) = new_connection().unwrap();
    tokio::spawn(connection);

    let mut default_routes: BTreeMap<
        RouteTableId,
        BTreeMap<RoutePriority, RouteMessage>,
    > = BTreeMap::new();

    // Change to `IpVersion::V6` and this will work for the IPv6 routing tables
    let mut all_routes = handle.clone().route().get(IpVersion::V4).execute();

    while let Ok(Some(route)) = all_routes.try_next().await {
        if route.header.destination_prefix_length != 0 {
            continue;
        }
        let prio = route.attributes.iter().find_map(|attr| match attr {
            RouteAttribute::Priority(prio) => Some(*prio),
            _ => None,
        });
        if let Some(prio_map) = default_routes.get_mut(&route.header.table) {
            prio_map.insert(prio, route);
        } else {
            let mut prio_map = BTreeMap::new();
            let table_id = route.header.table;
            prio_map.insert(prio, route);
            default_routes.insert(table_id, prio_map);
        }
    }

    for (table, prio_map) in default_routes.iter() {
        println!("Table: {}", table);
        for (_, route) in prio_map.iter() {
            println!("\t{route:?}");
        }
    }

    Ok(())
}

On my machine this returns

Table: 254
        RouteMessage { header: RouteHeader { address_family: Inet, destination_prefix_length: 0, source_prefix_length: 0, tos: 0, table: 254, protocol: Dhcp, scope: Universe, kind: Unicast, flags: RouteFlags(0x0) }, attributes: [Table(254), Priority(100), PrefSource(Inet(192.168.5.113)), Gateway(Inet(192.168.5.1)), Oif(2)] }
        RouteMessage { header: RouteHeader { address_family: Inet, destination_prefix_length: 0, source_prefix_length: 0, tos: 0, table: 254, protocol: Dhcp, scope: Universe, kind: Unicast, flags: RouteFlags(0x0) }, attributes: [Table(254), Priority(600), PrefSource(Inet(192.168.5.117)), Gateway(Inet(192.168.5.1)), Oif(4)] }

Which says that in the default routing table (254), I have two default routes assigned at different priorities.

One is my wired network (interface Oif(2)) at priority 100 and the other is my wifi chip (interface Oif(4)) at priority 600.

I will see about getting this included as an example in this repo.

We may also want a convenience function to do this.

@daniel-noland
Copy link
Contributor

Oh, and to be really sure you have found the correct route, you would want to exclude routes which are not unicast (linux supports local routes and multicast routes as well as (kinda sorta) the very confusing anycast route type (which I have never seen anyone use).

daniel-noland added a commit to daniel-noland/rtnetlink that referenced this issue May 12, 2024
This example was created in response to [issue rust-netlink#63](rust-netlink#63)
daniel-noland added a commit to daniel-noland/rtnetlink that referenced this issue May 12, 2024
This example was created in response to [issue rust-netlink#63](rust-netlink#63)
@pseusys
Copy link
Contributor Author

pseusys commented May 13, 2024

Maybe a function should be added for getting default route? It seems to be a quite common usage, maybe it would be better to avoid boiler-plate code?..

@daniel-noland
Copy link
Contributor

I agree. I will try that as a distinct PR tho

@pseusys
Copy link
Contributor Author

pseusys commented May 14, 2024

That would be great! Let me know if I could help you with that!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants