Skip to content

Commit c0bf0a3

Browse files
authored
squelch router advertisements on uplink ports (#236)
1 parent 78fa4e7 commit c0bf0a3

2 files changed

Lines changed: 79 additions & 1 deletion

File tree

common/src/illumos.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,54 @@ pub async fn vlan_create(over: &str, vlan_id: u16, vlan: &str) -> Result<()> {
177177
dladm_quiet(&["create-vlan", "-t", "-v", &vlan_id, "-l", over, vlan]).await
178178
}
179179

180+
/// What address family to use when enabling/disabling route exchange for an
181+
/// interface.
182+
#[derive(Debug, Clone, Copy)]
183+
pub enum AddressFamily {
184+
Ipv4,
185+
Ipv6,
186+
}
187+
188+
impl AddressFamily {
189+
fn as_str(&self) -> &'static str {
190+
match self {
191+
Self::Ipv4 => "ipv4",
192+
Self::Ipv6 => "ipv6",
193+
}
194+
}
195+
}
196+
197+
/// Enable or disable route exchange for a particular interface. Disabling
198+
/// has the following implications.
199+
///
200+
/// IPv6:
201+
/// - The host OS will ignore all NDP router solicitation (RS) and router
202+
/// advertisement (RA) messages.
203+
/// - The host OS will suppress RIPng send/receive
204+
///
205+
/// IPv4:
206+
/// - The host OS discards inbound RIP packets
207+
/// - The host OS suppresses ICMP router discovery
208+
pub async fn set_interface_exchange_routes(
209+
iface: &str,
210+
enabled: bool,
211+
address_family: AddressFamily,
212+
) -> Result<()> {
213+
let value =
214+
if enabled { "exchange_routes=on" } else { "exchange_routes=off" };
215+
216+
ipadm_quiet(&[
217+
"set-ifprop",
218+
"-t",
219+
"-p",
220+
value,
221+
"-m",
222+
address_family.as_str(),
223+
iface,
224+
])
225+
.await
226+
}
227+
180228
/// Remove a vlan link
181229
pub async fn vlan_delete(vlan: &str) -> Result<()> {
182230
dladm_quiet(&["delete-vlan", vlan]).await

uplinkd/src/main.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use anyhow::Context;
3939
use anyhow::Result;
4040
use anyhow::anyhow;
4141
use clap::Parser;
42+
use common::illumos::AddressFamily;
4243
use libc::c_int;
4344
use oxnet::IpNet;
4445
use oxnet::Ipv4Net;
@@ -452,7 +453,36 @@ async fn create_addrobj(
452453
// some point.
453454
error!(log, "failed to create {addr}: {e:?}");
454455
e
455-
})
456+
})?;
457+
458+
// The uplink ports on the switch are router ports. This means that we
459+
// should not be modifying the switch zone OS routing tables in response to
460+
// router advertisements we receive. Router advertisements are for hosts,
461+
// routers are not supposed to respond to them on router ports. By disabling
462+
// route exchange in the illumos host, we are asking the OS not to respond
463+
// to router advertisements as a host. Maghemite takes responsibility
464+
// for generating and handling router advertisements and solicitations as
465+
// a router.
466+
illumos::set_interface_exchange_routes(iface, false, AddressFamily::Ipv6)
467+
.await
468+
.map_err(|e| {
469+
error!(
470+
log,
471+
"failed to disable ipv6 route exchange on interface {iface}: {e:?}"
472+
);
473+
e
474+
})?;
475+
illumos::set_interface_exchange_routes(iface, false, AddressFamily::Ipv4)
476+
.await
477+
.map_err(|e| {
478+
error!(
479+
log,
480+
"failed to disable ipv4 route exchange on interface {iface}: {e:?}"
481+
);
482+
e
483+
})?;
484+
485+
Ok(())
456486
}
457487

458488
// Query illumos for all of the addresses on the interfaces we've been asked to

0 commit comments

Comments
 (0)