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}