Skip to content

Commit 08de373

Browse files
committed
rtnl: cleanup & allow unreachable next hop
1 parent 68d40f4 commit 08de373

File tree

1 file changed

+61
-35
lines changed

1 file changed

+61
-35
lines changed

src/kernel/rtnl.rs

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ use crate::net::{Afi, IpPrefix};
44
use crate::util::grace;
55
use clap::Args;
66
use futures::channel::mpsc::UnboundedReceiver;
7-
use futures::{try_join, StreamExt, TryStreamExt};
7+
use futures::{try_join, StreamExt};
8+
use libc::ENETUNREACH;
89
use rtnetlink::packet_core::{NetlinkMessage, NetlinkPayload};
910
use rtnetlink::packet_route::address::{AddressAttribute, AddressMessage};
10-
use rtnetlink::packet_route::route::{RouteAddress, RouteAttribute, RouteMessage, RouteVia};
11+
use rtnetlink::packet_route::route::{RouteAddress, RouteAttribute, RouteMessage, RouteType, RouteVia};
1112
use rtnetlink::packet_route::rule::{RuleAction, RuleAttribute, RuleMessage};
1213
use rtnetlink::packet_route::{AddressFamily, RouteNetlinkMessage};
1314
use rtnetlink::{Handle, RouteMessageBuilder};
@@ -28,7 +29,7 @@ pub struct RtNetlink<K: Kernel> {
2829
args: RtNetlinkArgs,
2930
handle: Handle,
3031
msgs: UnboundedReceiver<(NetlinkMessage<RouteNetlinkMessage>, rtnetlink::sys::SocketAddr)>,
31-
routes: BTreeMap<K::Handle, (IpPrefix, IpAddr, u32, Vec<RouteAttribute>)>,
32+
routes: BTreeMap<K::Handle, RouteEntry>,
3233
rules: BTreeMap<u32, BTreeSet<IpPrefix>>,
3334
timer: Interval,
3435
}
@@ -80,7 +81,11 @@ impl<K: Kernel> RtNetlink<K> {
8081
.expect("destination prefix should be valid")
8182
.table_id(table_id)
8283
.build();
83-
msg.attributes.extend(attrs.iter().cloned());
84+
if let Some(attrs) = &attrs {
85+
msg.attributes.extend(attrs.iter().cloned());
86+
} else {
87+
msg.header.kind = RouteType::Unreachable;
88+
};
8489
if let Err(error) = self.handle.route().add(msg).execute().await {
8590
if table_created {
8691
self.rules.remove(&table_id);
@@ -90,7 +95,7 @@ impl<K: Kernel> RtNetlink<K> {
9095
}
9196

9297
// ...and finally insert to our own database
93-
self.routes.insert(id, (prefix, next_hop, table_id, attrs));
98+
self.routes.insert(id, RouteEntry { prefix, next_hop, table_id, attrs });
9499

95100
Ok(table_id)
96101
}
@@ -111,7 +116,7 @@ impl<K: Kernel> RtNetlink<K> {
111116
}
112117

113118
pub async fn del(&mut self, id: &K::Handle) {
114-
let Some((prefix, _, table_id, _)) = self.routes.remove(id) else {
119+
let Some(RouteEntry { prefix, table_id, .. }) = self.routes.remove(id) else {
115120
return;
116121
};
117122
self.del_route(table_id, prefix).await;
@@ -156,6 +161,10 @@ impl<K: Kernel> RtNetlink<K> {
156161
);
157162
}
158163

164+
pub fn is_empty(&self) -> bool {
165+
self.routes.is_empty()
166+
}
167+
159168
// route & addr change: check if next hop in changed prefix
160169
// rule change: full update for AF
161170
// link change: full update
@@ -219,67 +228,76 @@ impl<K: Kernel> RtNetlink<K> {
219228
}
220229
}
221230

222-
pub fn is_empty(&self) -> bool {
223-
self.routes.is_empty()
224-
}
225-
226231
async fn process_prefix(&mut self, prefix: IpPrefix) -> Result<()> {
227-
Self::process_iter(&self.handle, self.routes.values_mut().filter(|x| prefix.contains(x.1))).await
232+
Self::process_iter(
233+
&self.handle,
234+
self.routes.values_mut().filter(|x| prefix.contains(x.next_hop)),
235+
)
236+
.await
228237
}
238+
229239
async fn process_all(&mut self) -> Result<()> {
230240
Self::process_iter(&self.handle, self.routes.values_mut()).await
231241
}
232-
async fn process_iter(
233-
handle: &Handle,
234-
iter: impl Iterator<Item = &mut (IpPrefix, IpAddr, u32, Vec<RouteAttribute>)>,
235-
) -> Result<()> {
242+
243+
async fn process_iter(handle: &Handle, iter: impl Iterator<Item = &mut RouteEntry>) -> Result<()> {
236244
// TODO: remove route if next hop becomes unreachable
237-
for (prefix, next_hop, table_id, attrs) in iter {
238-
let new_attrs = Self::get_route2(handle, prefix.afi(), *next_hop).await?;
245+
for RouteEntry { prefix, next_hop, table_id, attrs } in iter {
246+
let new_attrs = Self::get_route_from_handle(handle, prefix.afi(), *next_hop).await?;
239247
if *attrs != new_attrs {
240248
*attrs = new_attrs.clone();
241249
let mut msg = RouteMessageBuilder::<IpAddr>::new()
242250
.destination_prefix(prefix.prefix(), prefix.len())
243251
.expect("destination prefix should be valid")
244252
.table_id(*table_id)
245253
.build();
246-
msg.attributes.extend(new_attrs);
254+
if let Some(attrs) = &attrs {
255+
msg.attributes.extend(attrs.iter().cloned());
256+
} else {
257+
msg.header.kind = RouteType::Unreachable;
258+
};
247259
handle.route().add(msg).replace().execute().await?;
248260
}
249261
}
250262
Ok(())
251263
}
252264

253-
async fn get_route(&self, afi: Afi, ip: IpAddr) -> Result<Vec<RouteAttribute>> {
254-
Self::get_route2(&self.handle, afi, ip).await
265+
async fn get_route(&self, afi: Afi, ip: IpAddr) -> Result<Option<Vec<RouteAttribute>>> {
266+
Self::get_route_from_handle(&self.handle, afi, ip).await
255267
}
256-
async fn get_route2(handle: &Handle, afi: Afi, ip: IpAddr) -> Result<Vec<RouteAttribute>> {
268+
269+
async fn get_route_from_handle(handle: &Handle, prefix_afi: Afi, ip: IpAddr) -> Result<Option<Vec<RouteAttribute>>> {
257270
let msg = RouteMessageBuilder::<IpAddr>::new()
258271
.destination_prefix(ip, if ip.is_ipv4() { 32 } else { 128 })
259272
.expect("destination prefix should be valid")
260273
.build();
261-
let mut msg = handle.route().get(msg).execute();
262-
let Some(rt) = msg.try_next().await? else {
263-
unreachable!();
274+
let rt = match handle.route().get(msg).execute().next().await.unwrap() {
275+
Ok(rt) => rt,
276+
Err(rtnetlink::Error::NetlinkError(error)) if error.raw_code() == ENETUNREACH => return Ok(None),
277+
Err(error) => return Err(error.into()),
264278
};
279+
265280
let mut has_gateway = false;
266-
let attrs = rt.attributes.into_iter().filter(|x| {
267-
if matches!(x, RouteAttribute::Gateway(_) | RouteAttribute::Via(_)) {
268-
has_gateway = true;
269-
true
270-
} else {
271-
matches!(x, RouteAttribute::Oif(_))
272-
}
273-
});
274-
let mut attrs: Vec<_> = attrs.collect();
281+
let mut attrs = rt
282+
.attributes
283+
.into_iter()
284+
.filter(|x| {
285+
if matches!(x, RouteAttribute::Gateway(_) | RouteAttribute::Via(_)) {
286+
has_gateway = true;
287+
true
288+
} else {
289+
matches!(x, RouteAttribute::Oif(_))
290+
}
291+
})
292+
.collect::<Vec<_>>();
275293
if !has_gateway {
276-
if let (Afi::Ipv4, IpAddr::V6(v6)) = (afi, ip) {
294+
if let (Afi::Ipv4, IpAddr::V6(v6)) = (prefix_afi, ip) {
277295
attrs.push(RouteAttribute::Via(RouteVia::Inet6(v6)));
278296
} else {
279297
attrs.push(RouteAttribute::Gateway(ip.into()));
280298
}
281299
}
282-
Ok(attrs)
300+
Ok(Some(attrs))
283301
}
284302

285303
pub async fn terminate(self) {
@@ -292,6 +310,14 @@ impl<K: Kernel> RtNetlink<K> {
292310
}
293311
}
294312

313+
#[derive(Debug, Clone)]
314+
pub struct RouteEntry {
315+
prefix: IpPrefix,
316+
next_hop: IpAddr,
317+
table_id: u32,
318+
attrs: Option<Vec<RouteAttribute>>,
319+
}
320+
295321
#[derive(Debug, Clone, Args, Serialize, Deserialize)]
296322
pub struct RtNetlinkArgs {
297323
/// Time between each routing table scan.

0 commit comments

Comments
 (0)