xdp_util/
router.rs

1//! # IPv4 Routing and Neighbor Table Cache
2//!
3//! ## Purpose
4//!
5//! This module provides an efficient, cached interface to the system's IPv4 routing and
6//! neighbor (ARP) tables for a specific network interface. It enables fast next-hop
7//! resolution (gateway IP and destination MAC address) for outgoing packets.
8//!
9//! ## How it works
10//!
11//! It uses the functions from the `netlink` module to fetch routes and neighbors from the
12//! kernel. It caches this data in efficient in-memory structures: a `PrefixMap` (a prefix
13//! trie) for routes, enabling fast longest-prefix-match lookups, and a `HashMap` for
14//! neighbors. The `route` method performs lookups against this cache to find the next hop.
15//!
16//! ## Main components
17//!
18//! - `Router`: The main struct that holds the cached routing and neighbor tables.
19//! - `route()`: Performs a next-hop lookup for a given destination IP address.
20//! - `refresh()`: Updates the cache by re-querying the kernel's tables via netlink.
21//! - `NextHop`: A struct representing the result of a successful route lookup.
22
23pub use crate::netlink::{Ipv4Route, Neighbor, get_ipv4_routes, get_neighbors};
24use ipnet::Ipv4Net;
25use prefix_trie::PrefixMap;
26use std::collections::HashMap;
27use std::io;
28use std::net::Ipv4Addr;
29
30/// Manages a cached view of the system's IPv4 routing and neighbor tables.
31///
32/// It holds the routing table in a prefix trie for efficient longest-prefix-match
33/// lookups and the neighbor (ARP) table in a hash map.
34#[derive(Debug)]
35pub struct Router {
36    /// The network interface index this router is bound to.
37    pub if_index: u32,
38    /// A cache of neighbor (ARP) entries, mapping IP addresses to `Neighbor` structs.
39    pub neighbors: HashMap<Ipv4Addr, Neighbor>,
40    /// A prefix trie (`PrefixMap`) for efficient longest-prefix-match lookups on routes.
41    pub routes: PrefixMap<Ipv4Net, Ipv4Route>,
42}
43
44impl Router {
45    /// Creates a new `Router` for a specific network interface.
46    ///
47    /// # Arguments
48    /// * `if_index` - The index of the network interface to manage routes for.
49    pub fn new(if_index: u32) -> Self {
50        Router {
51            if_index,
52            routes: PrefixMap::new(),
53            neighbors: HashMap::new(),
54        }
55    }
56
57    /// Finds the next-hop information for a given destination IPv4 address.
58    ///
59    /// # How it works
60    ///
61    /// It first performs a longest-prefix-match (LPM) lookup in the cached `routes` table.
62    /// If a route is found, it determines the next-hop IP (either the gateway or the destination itself).
63    /// It then looks up the MAC address for that next-hop IP in the `neighbors` cache.
64    /// If no route is found via LPM, it attempts a direct lookup in the neighbor cache for the destination IP.
65    /// Returns a `NextHop` struct containing the next-hop IP and MAC address if successful.
66    pub fn route(&mut self, dest_ip: &Ipv4Addr) -> Option<NextHop> {
67        let dest_net = Ipv4Net::from(*dest_ip);
68        if let Some((_, route)) = self.routes.get_lpm(&dest_net) {
69            let ip = route.gateway.as_ref().unwrap_or(dest_ip);
70            if let Some(neighbour) = self.neighbors.get(ip) {
71                return Some(NextHop {
72                    ip_addr: *ip,
73                    mac_addr: Some(neighbour.mac),
74                });
75            }
76        };
77        if let Some(neighbour) = self.neighbors.get(dest_ip) {
78            return Some(NextHop {
79                ip_addr: *dest_ip,
80                mac_addr: Some(neighbour.mac),
81            });
82        }
83        None
84    }
85
86    /// Refreshes the router's cached tables from the kernel.
87    ///
88    /// # How it works
89    ///
90    /// It calls `get_ipv4_routes` and `get_neighbors` from the `netlink` module to fetch
91    /// the latest data from the kernel for the router's interface. It then rebuilds the
92    /// internal `routes` prefix map and `neighbors` hash map with the new data.
93    pub fn refresh(&mut self) -> Result<(), io::Error> {
94        let mut routes = get_ipv4_routes(Some(self.if_index))?;
95        let neighbors = get_neighbors(Some(self.if_index))?;
96        let mut prefix_map = PrefixMap::new();
97        for route in routes.drain(..) {
98            let dest_net = Ipv4Net::new(route.destination, route.dest_prefix).map_err(|_| {
99                io::Error::new(io::ErrorKind::InvalidData, "Invalid destination prefix")
100            })?;
101            if route.gateway.is_some() {
102                prefix_map.insert(dest_net, route);
103            }
104        }
105        self.neighbors = neighbors.into_iter().map(|n| (n.ip, n)).collect();
106        self.routes = prefix_map;
107        Ok(())
108    }
109}
110
111/// Represents the next hop for an outgoing packet.
112#[derive(Clone, Debug)]
113pub struct NextHop {
114    /// The IP address of the next hop (either the final destination or a gateway).
115    pub ip_addr: Ipv4Addr,
116    /// The MAC address of the next hop, if resolved.
117    pub mac_addr: Option<[u8; 6]>,
118}