Skip to content

Commit 3462197

Browse files
committed
fix: align redirect-to-ip behaviour with RFC draft
Actually the previous implementation is almost what the RFC means; adding a "device" protocol is not needed. "If the 'target address' is invalid or unreachable then the extended community SHOULD be ignored" -> `unreachable` should be replaced by `throw` Also fix rtnetlink multicast groups.
1 parent e12a2fb commit 3462197

2 files changed

Lines changed: 29 additions & 37 deletions

File tree

src/integration_tests/kernel_linux.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ async fn test_ipv4_redirect_to_ipv6() -> anyhow::Result<()> {
254254
}
255255

256256
#[apply(test_local!)]
257-
async fn test_unreachable_routes() -> anyhow::Result<()> {
257+
async fn test_ignored_routes() -> anyhow::Result<()> {
258258
let (conn, handle, _) = rtnetlink::new_connection()?;
259259
tokio::spawn(conn);
260260

@@ -267,7 +267,7 @@ async fn test_unreachable_routes() -> anyhow::Result<()> {
267267
RouteMessageBuilder::<IpAddr>::new()
268268
.destination_prefix(p.prefix(), p.len())
269269
.unwrap()
270-
.kind(RouteType::Unreachable)
270+
.kind(RouteType::Throw)
271271
.build()
272272
})
273273
.collect();
@@ -339,7 +339,7 @@ async fn test_unreachable_routes() -> anyhow::Result<()> {
339339
.map(|prefix| {
340340
let prefix = prefix.parse::<IpPrefix>().unwrap();
341341
let mut msg = RouteMessageBuilder::<IpAddr>::new()
342-
.kind(RouteType::Unreachable)
342+
.kind(RouteType::Throw)
343343
.table_id(table_id)
344344
.destination_prefix(prefix.prefix(), prefix.len())
345345
.unwrap()

src/kernel/rtnl.rs

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ use clap::Args;
66
use futures::channel::mpsc::UnboundedReceiver;
77
use futures::{StreamExt, try_join};
88
use libc::{EHOSTUNREACH, ENETUNREACH};
9+
use log::{debug, trace, warn};
910
use rtnetlink::Error::NetlinkError;
1011
use rtnetlink::packet_core::{NetlinkMessage, NetlinkPayload};
11-
use rtnetlink::packet_route::address::{AddressAttribute, AddressMessage};
1212
use rtnetlink::packet_route::route::{RouteAddress, RouteAttribute, RouteMessage, RouteType, RouteVia};
1313
use rtnetlink::packet_route::rule::{RuleAction, RuleAttribute, RuleMessage};
1414
use rtnetlink::packet_route::{AddressFamily, RouteNetlinkMessage};
15-
use rtnetlink::{Handle, RouteMessageBuilder};
15+
use rtnetlink::{Handle, MulticastGroup, RouteMessageBuilder};
1616
use serde::{Deserialize, Serialize};
1717
use std::collections::{BTreeMap, BTreeSet};
1818
use std::io;
@@ -21,9 +21,10 @@ use std::time::Duration;
2121
use tokio::select;
2222
use tokio::time::{Interval, interval};
2323

24-
// TODO: maintain device info similar to BIRD's "device" protocol, and use it to
25-
// ensure only direct traffic to neighbours
26-
// For now, we allow any address by `ip route get`.
24+
// We allow redirecting to any address by resolving its next hop using `ip route
25+
// get`. This aligns with the RFC draft v4.
26+
//
27+
// Not handling with ECMP for now.
2728

2829
#[derive(Debug)]
2930
pub struct RtNetlink<K: Kernel> {
@@ -37,9 +38,11 @@ pub struct RtNetlink<K: Kernel> {
3738

3839
impl<K: Kernel> RtNetlink<K> {
3940
pub fn new(args: RtNetlinkArgs) -> io::Result<Self> {
40-
let (conn, handle, msgs) = rtnetlink::new_connection()?;
41+
use MulticastGroup::*;
42+
let (conn, handle, msgs) = rtnetlink::new_multicast_connection(&[Ipv4Route, Ipv6Route, Ipv4Rule, Ipv6Rule])?;
4143
let scan_time = args.route_scan_time;
4244
tokio::spawn(conn);
45+
warn!("rtnetlink spawned");
4346
Ok(Self {
4447
args,
4548
handle,
@@ -85,9 +88,9 @@ impl<K: Kernel> RtNetlink<K> {
8588
if let Some(attrs) = &attrs {
8689
msg.attributes.extend(attrs.iter().cloned());
8790
} else {
88-
msg.header.kind = RouteType::Unreachable;
91+
msg.header.kind = RouteType::Throw;
8992
};
90-
if let Err(error) = self.handle.route().add(msg).execute().await {
93+
if let Err(error) = self.handle.route().add(msg).replace().execute().await {
9194
if table_created {
9295
self.rules.remove(&table_id);
9396
self.del_rule(table_id).await;
@@ -137,7 +140,7 @@ impl<K: Kernel> RtNetlink<K> {
137140
.table_id(table_id)
138141
.build();
139142
if self.handle.route().del(msg.clone()).execute().await.is_err() {
140-
msg.header.kind = RouteType::Unreachable;
143+
msg.header.kind = RouteType::Throw;
141144
grace(self.handle.route().del(msg).execute().await, "failed to delete route");
142145
}
143146
}
@@ -203,32 +206,20 @@ impl<K: Kernel> RtNetlink<K> {
203206
.unwrap_or_else(|| af_to_wildcard(msg.header.address_family))
204207
}
205208

206-
fn addr_msg_dst_prefix(msg: AddressMessage) -> IpPrefix {
207-
use AddressAttribute::Address;
208-
let dst_len = msg.header.prefix_len;
209-
(dst_len != 0)
210-
.then(|| {
211-
(msg.attributes.into_iter())
212-
.filter_map(|x| if let Address(ip) = x { Some(ip) } else { None })
213-
.map(|x| IpPrefix::new(x, dst_len))
214-
.next()
215-
})
216-
.flatten()
217-
.unwrap_or_else(|| af_to_wildcard(msg.header.family))
218-
}
219-
220209
select! {
221-
_ = self.timer.tick() => self.process_all().await,
222-
Some((msg, _)) = self.msgs.next() => match msg.payload {
223-
InnerMessage(msg) => match msg {
224-
NewRoute(msg) | DelRoute(msg) => self.process_prefix(route_msg_dst_prefix(msg)).await,
225-
NewAddress(msg) | DelAddress(msg) => self.process_prefix(addr_msg_dst_prefix(msg)).await,
226-
NewRule(msg) | DelRule(msg) => self.process_prefix(af_to_wildcard(msg.header.family)).await,
227-
NewLink(_) | DelLink(_) => self.process_all().await,
210+
_ = self.timer.tick() => { warn!("timer tick"); self.process_all().await }
211+
Some((msg, _)) = self.msgs.next() => {
212+
trace!("new rtnetlink msg: {msg:?}");
213+
match msg.payload {
214+
InnerMessage(msg) => match msg {
215+
// TODO: filter out multicast routes
216+
NewRoute(msg) | DelRoute(msg) => self.process_prefix(route_msg_dst_prefix(msg)).await,
217+
NewRule(msg) | DelRule(msg) => self.process_prefix(af_to_wildcard(msg.header.family)).await,
218+
_ => Ok(()),
219+
},
228220
_ => Ok(()),
229-
},
230-
_ => Ok(()),
231-
},
221+
}
222+
}
232223
}
233224
}
234225

@@ -249,6 +240,7 @@ impl<K: Kernel> RtNetlink<K> {
249240
for RouteEntry { prefix, next_hop, table_id, attrs } in iter {
250241
let new_attrs = Self::get_route_from_handle(handle, prefix.afi(), *next_hop).await?;
251242
if *attrs != new_attrs {
243+
debug!("route attrs: {attrs:?} -> {new_attrs:?}");
252244
*attrs = new_attrs.clone();
253245
let mut msg = RouteMessageBuilder::<IpAddr>::new()
254246
.destination_prefix(prefix.prefix(), prefix.len())
@@ -258,7 +250,7 @@ impl<K: Kernel> RtNetlink<K> {
258250
if let Some(attrs) = &attrs {
259251
msg.attributes.extend(attrs.iter().cloned());
260252
} else {
261-
msg.header.kind = RouteType::Unreachable;
253+
msg.header.kind = RouteType::Throw;
262254
};
263255
handle.route().add(msg).replace().execute().await?;
264256
}

0 commit comments

Comments
 (0)