Peer at link-local addresses: fe80::1%eth0

A peer's endpoint may be a link-local IPv6 address with the interface it
is on. Such a peer is the address and the interface together, wherever
peers are told apart: admission, collisions, hold-downs, the refusal log,
the status page. The interface is looked up by name each time it is
needed, so one that appears later or is made anew is found.

Our routes go out with our link-local address in both places of
MP_REACH_NLRI's next hop, as FRR sends its own. A peer's link-local next
hop is taken alone, twice, or after "::" (BIRD), and resolved in the
kernel on the interface of the session rather than by looking fe80::/64
up, which every interface has.

The lab has a new phase, linklocal: sessions with FRR (by interface, with
radvd beside the daemon) and BIRD over a LAN with link-local addresses
only, both families, pings across, the interface down and up.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Rayhaan JaufeerallyandClaude Fable 5.1 committed 2026-10-02 18:24:13 +02:00
1 parent 92e013ca01
commit fd2248feb5
38 files changed
+975 -151

No files matched your search

+1 -1
View File
@@ -41,7 +41,7 @@ async fn server_offering(
TestServer::start_with(LOCAL_ASN, |c| {
c.peers = vec![PeerConfig {
name: "route-server-client".into(),
endpoint: SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 1),
endpoint: SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 1).into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: None,
+1 -1
View File
@@ -19,7 +19,7 @@ const TIMEOUT: Duration = Duration::from_secs(5);
fn peer_at(endpoint: SocketAddr) -> PeerConfig {
PeerConfig {
name: "r1".into(),
endpoint,
endpoint: endpoint.into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: None,
+1 -1
View File
@@ -57,7 +57,7 @@ async fn server_with(
});
c.peers = vec![PeerConfig {
name: "r1".into(),
endpoint,
endpoint: endpoint.into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: None,
+1 -1
View File
@@ -37,7 +37,7 @@ async fn server_dialing(listener: &TcpListener) -> Result<TestServer> {
c.connect_retry = Duration::from_millis(200);
c.peers = vec![PeerConfig {
name: "r1".into(),
endpoint,
endpoint: endpoint.into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: None,
+1 -1
View File
@@ -167,7 +167,7 @@ async fn test_refused_routes_can_be_seen_and_counted() -> Result<()> {
c.accept_unconfigured_peers = false;
c.peers = vec![PeerConfig {
name: "refused-demo".into(),
endpoint,
endpoint: endpoint.into(),
remote_asn: 3333,
hold_time_secs: 90,
local_address: None,
+4 -4
View File
@@ -28,7 +28,7 @@ async fn server_dialing(listener: &TcpListener, remote_asn: u32) -> Result<TestS
c.connect_retry = Duration::from_millis(200);
c.peers = vec![PeerConfig {
name: "r1".into(),
endpoint,
endpoint: endpoint.into(),
remote_asn,
hold_time_secs: 90,
local_address: None,
@@ -185,7 +185,7 @@ async fn test_inbound_from_configured_address_uses_config() -> Result<()> {
c.connect_retry = Duration::from_secs(60);
c.peers = vec![PeerConfig {
name: "r1".into(),
endpoint: SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 1),
endpoint: SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 1).into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: None,
@@ -236,7 +236,7 @@ async fn test_local_address_is_used_as_the_source() -> Result<()> {
c.connect_retry = Duration::from_millis(200);
c.peers = vec![PeerConfig {
name: "r1".into(),
endpoint,
endpoint: endpoint.into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: Some(Ipv4Addr::LOCALHOST.into()),
@@ -256,7 +256,7 @@ async fn test_local_address_is_used_as_the_source() -> Result<()> {
c.connect_retry = Duration::from_millis(200);
c.peers = vec![PeerConfig {
name: "r1".into(),
endpoint,
endpoint: endpoint.into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: Some(Ipv4Addr::new(192, 0, 2, 1).into()),
@@ -41,7 +41,7 @@ async fn server_with(import: ImportPolicy) -> Result<(TestServer, TcpListener)>
c.connect_retry = Duration::from_millis(50);
c.peers = vec![PeerConfig {
name: "r1".into(),
endpoint,
endpoint: endpoint.into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: None,
@@ -184,7 +184,7 @@ async fn test_no_family_in_common_refuses_the_session() -> Result<()> {
// Inbound connections from this address inherit the peer's settings.
c.peers = vec![PeerConfig {
name: "v6-only".into(),
endpoint: SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 1),
endpoint: SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 1).into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: None,
+1 -1
View File
@@ -48,7 +48,7 @@ async fn server_with(role: Option<PeerRole>, require_role: bool) -> Result<Setup
c.originate = vec![LocalRoute::new(prefix("193.0.0.0/21"), Default::default())];
c.peers = vec![PeerConfig {
name: "r1".into(),
endpoint,
endpoint: endpoint.into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: None,
+1 -1
View File
@@ -66,7 +66,7 @@ async fn server_with(
});
c.peers = vec![PeerConfig {
name: "r1".into(),
endpoint,
endpoint: endpoint.into(),
remote_asn: PEER_ASN,
hold_time_secs: 90,
local_address: None,