Skip to content

Commit b2e8557

Browse files
fix(iroh-net): prevent adding addressing info that points back to us (#2333)
## Description Failed disco boxes for which the sender is our own node is are because we tried to contact another node for which the addressing information we have points back to us. We are the sender (this explains the source of the disco box being ourselves) but we are not the meant recipient (thus the failed decryption - we should always be able to decrypt a message meant to ourselves - ). This is of course undesirable. This Pr should mostly mitigate this issue, but a failed disco box to ourselves can happen in other less likely ways. ## Breaking Changes n/a ## Notes & open questions n/a ## Change checklist - [x] Self-review. - [x] Documentation updates if relevant. - [ ] ~Tests if relevant.~ - [ ] ~All breaking changes documented.~
1 parent d37a4a4 commit b2e8557

File tree

9 files changed

+245
-102
lines changed

9 files changed

+245
-102
lines changed

iroh-docs/src/engine/live.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ use crate::{
3131
use super::gossip::{GossipActor, ToGossipActor};
3232
use super::state::{NamespaceStates, Origin, SyncReason};
3333

34+
/// Name used for logging when new node addresses are added from the docs engine.
35+
const SOURCE_NAME: &str = "docs_engine";
36+
3437
/// An iroh-docs operation
3538
///
3639
/// This is the message that is broadcast over iroh-gossip.
@@ -437,7 +440,7 @@ impl<B: iroh_blobs::store::Store> LiveActor<B> {
437440
// add addresses of peers to our endpoint address book
438441
for peer in peers.into_iter() {
439442
let peer_id = peer.node_id;
440-
if let Err(err) = self.endpoint.add_node_addr(peer) {
443+
if let Err(err) = self.endpoint.add_node_addr_with_source(peer, SOURCE_NAME) {
441444
warn!(peer = %peer_id.fmt_short(), "failed to add known addrs: {err:?}");
442445
}
443446
}

iroh-gossip/src/net.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const TO_ACTOR_CAP: usize = 64;
3838
const IN_EVENT_CAP: usize = 1024;
3939
/// Channel capacity for endpoint change message queue (single)
4040
const ON_ENDPOINTS_CAP: usize = 64;
41+
/// Name used for logging when new node addresses are added from gossip.
42+
const SOURCE_NAME: &str = "gossip";
4143

4244
/// Events emitted from the gossip protocol
4345
pub type Event = proto::Event<PublicKey>;
@@ -578,7 +580,10 @@ impl Actor {
578580
Ok(info) => {
579581
debug!(peer = ?node_id, "add known addrs: {info:?}");
580582
let node_addr = NodeAddr { node_id, info };
581-
if let Err(err) = self.endpoint.add_node_addr(node_addr) {
583+
if let Err(err) = self
584+
.endpoint
585+
.add_node_addr_with_source(node_addr, SOURCE_NAME)
586+
{
582587
debug!(peer = ?node_id, "add known failed: {err:?}");
583588
}
584589
}

iroh-net/src/discovery.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ use crate::{AddrInfo, Endpoint, NodeId};
1313
pub mod dns;
1414
pub mod pkarr_publish;
1515

16+
/// Name used for logging when new node addresses are added from discovery.
17+
const SOURCE_NAME: &str = "discovery";
18+
1619
/// Node discovery for [`super::Endpoint`].
1720
///
1821
/// The purpose of this trait is to hook up a node discovery mechanism that
@@ -252,7 +255,7 @@ impl DiscoveryTask {
252255
info: r.addr_info,
253256
node_id,
254257
};
255-
ep.add_node_addr(addr).ok();
258+
ep.add_node_addr_with_source(addr, SOURCE_NAME).ok();
256259
if let Some(tx) = on_first_tx.take() {
257260
tx.send(Ok(())).ok();
258261
}
@@ -669,11 +672,6 @@ mod test_dns_pkarr {
669672
// wait until our shared state received the update from pkarr publishing
670673
dns_pkarr_server.on_node(&ep1.node_id(), timeout).await?;
671674

672-
let node_addr = NodeAddr::new(ep1.node_id());
673-
674-
// add empty node address. We *should* launch discovery before attempting to dial.
675-
ep2.add_node_addr(node_addr)?;
676-
677675
// we connect only by node id!
678676
let res = ep2.connect(ep1.node_id().into(), TEST_ALPN).await;
679677
assert!(res.is_ok(), "connection established");

iroh-net/src/endpoint.rs

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -531,21 +531,48 @@ impl Endpoint {
531531
/// This updates the local state for the remote node. If the provided [`NodeAddr`]
532532
/// contains a [`RelayUrl`] this will be used as the new relay server for this node. If
533533
/// it contains any new IP endpoints they will also be stored and tried when next
534-
/// connecting to this node.
534+
/// connecting to this node. Any address that matches this node's direct addresses will be
535+
/// silently ignored.
536+
///
537+
/// See also [`Endpoint::add_node_addr_with_source`].
535538
///
536539
/// # Errors
537540
///
538-
/// Will return an error if we attempt to add our own [`PublicKey`] to the node map.
541+
/// Will return an error if we attempt to add our own [`PublicKey`] to the node map or if the
542+
/// direct addresses are a subset of ours.
539543
pub fn add_node_addr(&self, node_addr: NodeAddr) -> Result<()> {
544+
self.add_node_addr_inner(node_addr, magicsock::Source::App)
545+
}
546+
547+
/// Informs this [`Endpoint`] about addresses of the iroh-net node, noting the source.
548+
///
549+
/// This updates the local state for the remote node. If the provided [`NodeAddr`] contains a
550+
/// [`RelayUrl`] this will be used as the new relay server for this node. If it contains any
551+
/// new IP endpoints they will also be stored and tried when next connecting to this node. Any
552+
/// address that matches this node's direct addresses will be silently ignored. The *source* is
553+
/// used for logging exclusively and will not be stored.
554+
///
555+
/// # Errors
556+
///
557+
/// Will return an error if we attempt to add our own [`PublicKey`] to the node map or if the
558+
/// direct addresses are a subset of ours.
559+
pub fn add_node_addr_with_source(
560+
&self,
561+
node_addr: NodeAddr,
562+
source: &'static str,
563+
) -> Result<()> {
564+
self.add_node_addr_inner(node_addr, magicsock::Source::NamedApp { name: source })
565+
}
566+
567+
fn add_node_addr_inner(&self, node_addr: NodeAddr, source: magicsock::Source) -> Result<()> {
540568
// Connecting to ourselves is not supported.
541569
if node_addr.node_id == self.node_id() {
542570
bail!(
543571
"Adding our own address is not supported ({} is the node id of this node)",
544572
node_addr.node_id.fmt_short()
545573
);
546574
}
547-
self.msock.add_node_addr(node_addr);
548-
Ok(())
575+
self.msock.add_node_addr(node_addr, source)
549576
}
550577

551578
// # Getter methods for properties of this Endpoint itself.
@@ -1386,8 +1413,9 @@ mod tests {
13861413

13871414
#[tokio::test]
13881415
async fn endpoint_conn_type_stream() {
1416+
const TIMEOUT: Duration = std::time::Duration::from_secs(15);
13891417
let _logging_guard = iroh_test::logging::setup();
1390-
let (relay_map, relay_url, _relay_guard) = run_relay_server().await.unwrap();
1418+
let (relay_map, _relay_url, _relay_guard) = run_relay_server().await.unwrap();
13911419
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(42);
13921420
let ep1_secret_key = SecretKey::generate_with_rng(&mut rng);
13931421
let ep2_secret_key = SecretKey::generate_with_rng(&mut rng);
@@ -1408,31 +1436,25 @@ mod tests {
14081436
.await
14091437
.unwrap();
14101438

1411-
async fn handle_direct_conn(ep: Endpoint, node_id: PublicKey) -> Result<()> {
1412-
let node_addr = NodeAddr::new(node_id);
1413-
ep.add_node_addr(node_addr)?;
1414-
let stream = ep.conn_type_stream(node_id)?;
1415-
async fn get_direct_event(
1416-
src: &PublicKey,
1417-
dst: &PublicKey,
1418-
mut stream: ConnectionTypeStream,
1419-
) -> Result<()> {
1420-
let src = src.fmt_short();
1421-
let dst = dst.fmt_short();
1422-
while let Some(conn_type) = stream.next().await {
1423-
tracing::info!(me = %src, dst = %dst, conn_type = ?conn_type);
1424-
if matches!(conn_type, ConnectionType::Direct(_)) {
1425-
return Ok(());
1426-
}
1439+
async fn handle_direct_conn(ep: &Endpoint, node_id: PublicKey) -> Result<()> {
1440+
let mut stream = ep.conn_type_stream(node_id)?;
1441+
let src = ep.node_id().fmt_short();
1442+
let dst = node_id.fmt_short();
1443+
while let Some(conn_type) = stream.next().await {
1444+
tracing::info!(me = %src, dst = %dst, conn_type = ?conn_type);
1445+
if matches!(conn_type, ConnectionType::Direct(_)) {
1446+
return Ok(());
14271447
}
1428-
anyhow::bail!("conn_type stream ended before `ConnectionType::Direct`");
14291448
}
1430-
tokio::time::timeout(
1431-
Duration::from_secs(15),
1432-
get_direct_event(&ep.node_id(), &node_id, stream),
1433-
)
1434-
.await??;
1435-
Ok(())
1449+
anyhow::bail!("conn_type stream ended before `ConnectionType::Direct`");
1450+
}
1451+
1452+
async fn accept(ep: &Endpoint) -> NodeId {
1453+
let incoming = ep.accept().await.unwrap();
1454+
let conn = incoming.await.unwrap();
1455+
let node_id = get_remote_node_id(&conn).unwrap();
1456+
tracing::info!(node_id=%node_id.fmt_short(), "accepted connection");
1457+
node_id
14361458
}
14371459

14381460
let ep1_nodeid = ep1.node_id();
@@ -1445,39 +1467,31 @@ mod tests {
14451467
);
14461468
tracing::info!("node id 2 {ep2_nodeid}");
14471469

1448-
let res_ep1 = tokio::spawn(handle_direct_conn(ep1.clone(), ep2_nodeid));
1470+
let ep1_side = async move {
1471+
accept(&ep1).await;
1472+
handle_direct_conn(&ep1, ep2_nodeid).await
1473+
};
1474+
1475+
let ep2_side = async move {
1476+
ep2.connect(ep1_nodeaddr, TEST_ALPN).await.unwrap();
1477+
handle_direct_conn(&ep2, ep1_nodeid).await
1478+
};
1479+
1480+
let res_ep1 = tokio::spawn(tokio::time::timeout(TIMEOUT, ep1_side));
14491481

14501482
let ep1_abort_handle = res_ep1.abort_handle();
14511483
let _ep1_guard = CallOnDrop::new(move || {
14521484
ep1_abort_handle.abort();
14531485
});
14541486

1455-
let res_ep2 = tokio::spawn(handle_direct_conn(ep2.clone(), ep1_nodeid));
1487+
let res_ep2 = tokio::spawn(tokio::time::timeout(TIMEOUT, ep2_side));
14561488
let ep2_abort_handle = res_ep2.abort_handle();
14571489
let _ep2_guard = CallOnDrop::new(move || {
14581490
ep2_abort_handle.abort();
14591491
});
1460-
async fn accept(ep: Endpoint) -> NodeId {
1461-
let incoming = ep.accept().await.unwrap();
1462-
let conn = incoming.await.unwrap();
1463-
get_remote_node_id(&conn).unwrap()
1464-
}
1465-
1466-
// create a node addr with no direct connections
1467-
let ep1_nodeaddr = NodeAddr::from_parts(ep1_nodeid, Some(relay_url), vec![]);
1468-
1469-
let accept_res = tokio::spawn(accept(ep1.clone()));
1470-
let accept_abort_handle = accept_res.abort_handle();
1471-
let _accept_guard = CallOnDrop::new(move || {
1472-
accept_abort_handle.abort();
1473-
});
1474-
1475-
let _conn_2 = ep2.connect(ep1_nodeaddr, TEST_ALPN).await.unwrap();
1476-
1477-
let got_id = accept_res.await.unwrap();
1478-
assert_eq!(ep2_nodeid, got_id);
14791492

1480-
res_ep1.await.unwrap().unwrap();
1481-
res_ep2.await.unwrap().unwrap();
1493+
let (r1, r2) = tokio::try_join!(res_ep1, res_ep2).unwrap();
1494+
r1.expect("ep1 timeout").unwrap();
1495+
r2.expect("ep2 timeout").unwrap();
14821496
}
14831497
}

iroh-net/src/magicsock.rs

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pub use self::node_map::{
8080
ConnectionType, ConnectionTypeStream, ControlMsg, DirectAddrInfo, NodeInfo as ConnectionInfo,
8181
};
8282
pub(super) use self::timer::Timer;
83+
pub(crate) use node_map::Source;
8384

8485
/// How long we consider a STUN-derived endpoint valid for. UDP NAT mappings typically
8586
/// expire at 30 seconds, so this is a few seconds shy of that.
@@ -367,8 +368,39 @@ impl MagicSock {
367368

368369
/// Add addresses for a node to the magic socket's addresbook.
369370
#[instrument(skip_all, fields(me = %self.me))]
370-
pub(crate) fn add_node_addr(&self, addr: NodeAddr) {
371-
self.node_map.add_node_addr(addr);
371+
pub fn add_node_addr(&self, mut addr: NodeAddr, source: node_map::Source) -> Result<()> {
372+
let my_addresses = self.endpoints.get().last_endpoints;
373+
let mut pruned = 0;
374+
for my_addr in my_addresses.into_iter().map(|ep| ep.addr) {
375+
if addr.info.direct_addresses.remove(&my_addr) {
376+
warn!(node_id=addr.node_id.fmt_short(), %my_addr, %source, "not adding our addr for node");
377+
pruned += 1;
378+
}
379+
}
380+
if !addr.info.is_empty() {
381+
self.node_map.add_node_addr(addr, source);
382+
Ok(())
383+
} else if pruned != 0 {
384+
Err(anyhow::anyhow!(
385+
"empty addressing info, {pruned} direct addresses have been pruned"
386+
))
387+
} else {
388+
Err(anyhow::anyhow!("empty addressing info"))
389+
}
390+
}
391+
392+
/// Updates our direct addresses.
393+
///
394+
/// On a successful update, our address is published to discovery.
395+
pub(super) fn update_direct_addresses(&self, eps: Vec<DirectAddr>) {
396+
let updated = self.endpoints.update(DiscoveredEndpoints::new(eps)).is_ok();
397+
if updated {
398+
let eps = self.endpoints.read();
399+
eps.log_endpoint_change();
400+
self.node_map
401+
.on_direct_addr_discovered(eps.iter().map(|ep| ep.addr));
402+
self.publish_my_addr();
403+
}
372404
}
373405

374406
/// Get a reference to the DNS resolver used in this [`MagicSock`].
@@ -2095,15 +2127,7 @@ impl Actor {
20952127
// The STUN address(es) are always first.
20962128
// Despite this sorting, clients are not relying on this sorting for decisions;
20972129

2098-
let updated = msock
2099-
.endpoints
2100-
.update(DiscoveredEndpoints::new(eps))
2101-
.is_ok();
2102-
if updated {
2103-
let eps = msock.endpoints.read();
2104-
eps.log_endpoint_change();
2105-
msock.publish_my_addr();
2106-
}
2130+
msock.update_direct_addresses(eps);
21072131

21082132
// Regardless of whether our local endpoints changed, we now want to send any queued
21092133
// call-me-maybe messages.
@@ -2722,6 +2746,14 @@ mod tests {
27222746

27232747
use super::*;
27242748

2749+
impl MagicSock {
2750+
#[track_caller]
2751+
pub fn add_test_addr(&self, node_addr: NodeAddr) {
2752+
self.add_node_addr(node_addr, Source::NamedApp { name: "test" })
2753+
.unwrap()
2754+
}
2755+
}
2756+
27252757
/// Magicsock plus wrappers for sending packets
27262758
#[derive(Clone)]
27272759
struct MagicStack {
@@ -2791,7 +2823,7 @@ mod tests {
27912823
direct_addresses: new_addrs.iter().map(|ep| ep.addr).collect(),
27922824
},
27932825
};
2794-
m.endpoint.magic_sock().add_node_addr(addr);
2826+
m.endpoint.magic_sock().add_test_addr(addr);
27952827
}
27962828
}
27972829

0 commit comments

Comments
 (0)