diff --git a/iroh/Cargo.toml b/iroh/Cargo.toml index 89554fe55a..e8902e1761 100644 --- a/iroh/Cargo.toml +++ b/iroh/Cargo.toml @@ -95,7 +95,7 @@ webpki = { package = "rustls-webpki", version = "0.102" } webpki-roots = "0.26" x509-parser = "0.16" z32 = "1.0.3" -net-report = { package = "iroh-net-report", path = "../iroh-net-report", version = "0.29", default-features = false } +net-report = { package = "iroh-net-report", path = "../iroh-net-report", version = "0.30", default-features = false } # metrics iroh-metrics = { version = "0.30", default-features = false } diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 30b58d745c..197861cae9 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -34,7 +34,7 @@ use crate::{ dns::DnsDiscovery, pkarr::PkarrPublisher, ConcurrentDiscovery, Discovery, DiscoveryTask, }, dns::{default_resolver, DnsResolver}, - magicsock::{self, Handle, QuicMappedAddr}, + magicsock::{self, Handle, NodeIdMappedAddr}, tls, watchable::Watcher, }; @@ -533,7 +533,6 @@ impl Endpoint { async fn bind(static_config: StaticConfig, msock_opts: magicsock::Options) -> Result { let msock = magicsock::MagicSock::spawn(msock_opts).await?; trace!("created magicsock"); - trace!("created quinn endpoint"); debug!(version = env!("CARGO_PKG_VERSION"), "iroh Endpoint created"); let ep = Self { msock: msock.clone(), @@ -627,7 +626,7 @@ impl Endpoint { &self, node_id: NodeId, alpn: &[u8], - addr: QuicMappedAddr, + addr: NodeIdMappedAddr, ) -> Result { debug!("Attempting connection..."); let client_config = { @@ -994,7 +993,7 @@ impl Endpoint { async fn get_mapping_addr_and_maybe_start_discovery( &self, node_addr: NodeAddr, - ) -> Result<(QuicMappedAddr, Option)> { + ) -> Result<(NodeIdMappedAddr, Option)> { let node_id = node_addr.node_id; // Only return a mapped addr if we have some way of dialing this node, in other diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index a470a53365..9b6a45ad20 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -142,12 +142,12 @@ pub(crate) struct Options { impl Default for Options { fn default() -> Self { - let secret_key = SecretKey::generate(); + let secret_key = SecretKey::generate(rand::rngs::OsRng); let server_config = make_default_server_config(&secret_key); Options { addr_v4: None, addr_v6: None, - secret_key: SecretKey::generate(rand::rngs::OsRng), + secret_key, relay_map: RelayMap::empty(), node_map: None, discovery: None, @@ -243,6 +243,8 @@ pub(crate) struct MagicSock { my_relay: Watchable>, /// Tracks the networkmap node entity for each node discovery key. node_map: NodeMap, + /// Tracks the mapped IP addresses + ip_mapped_addrs: IpMappedAddrs, /// UDP IPv4 socket pconn4: UdpConn, /// UDP IPv6 socket @@ -373,7 +375,7 @@ impl MagicSock { } /// Returns the socket address which can be used by the QUIC layer to dial this node. - pub(crate) fn get_mapping_addr(&self, node_id: NodeId) -> Option { + pub(crate) fn get_mapping_addr(&self, node_id: NodeId) -> Option { self.node_map.get_quic_mapped_addr_for_node_key(node_id) } @@ -461,151 +463,180 @@ impl MagicSock { "connection closed", )); } - - let dest = QuicMappedAddr(transmit.destination); trace!( - dst = %dest, + dst = %transmit.destination, src = ?transmit.src_ip, len = %transmit.contents.len(), "sending", ); - let mut transmit = transmit.clone(); - match self - .node_map - .get_send_addrs(dest, self.ipv6_reported.load(Ordering::Relaxed)) - { - Some((node_id, udp_addr, relay_url, msgs)) => { - let mut pings_sent = false; - // If we have pings to send, we *have* to send them out first. - if !msgs.is_empty() { - if let Err(err) = self.try_send_ping_actions(msgs) { - warn!( - node = %node_id.fmt_short(), - "failed to handle ping actions: {err:#}", - ); + + // Check if the destination address is a NodeIdMappedAddr. If so, + // get the node's relay address and best direct address, as well + // as any pings that need to be sent for hole-punching purposes. + if let Ok(dest) = NodeIdMappedAddr::try_from(transmit.destination) { + let mut transmit = transmit.clone(); + match self + .node_map + .get_send_addrs(dest, self.ipv6_reported.load(Ordering::Relaxed)) + { + Some((node_id, udp_addr, relay_url, msgs)) => { + let mut pings_sent = false; + // If we have pings to send, we *have* to send them out first. + if !msgs.is_empty() { + if let Err(err) = self.try_send_ping_actions(msgs) { + warn!( + node = %node_id.fmt_short(), + "failed to handle ping actions: {err:#}", + ); + } + pings_sent = true; } - pings_sent = true; - } - let mut udp_sent = false; - let mut udp_error = None; - let mut relay_sent = false; - let mut relay_error = None; + let mut udp_sent = false; + let mut udp_error = None; + let mut relay_sent = false; + let mut relay_error = None; - // send udp - if let Some(addr) = udp_addr { - // rewrite target address - transmit.destination = addr; - match self.try_send_udp(addr, &transmit) { - Ok(()) => { - trace!(node = %node_id.fmt_short(), dst = %addr, + // send udp + if let Some(addr) = udp_addr { + // rewrite target address + transmit.destination = addr; + match self.try_send_udp(addr, &transmit) { + Ok(()) => { + trace!(node = %node_id.fmt_short(), dst = %addr, "sent transmit over UDP"); - udp_sent = true; - } - Err(err) => { - // No need to print "WouldBlock" errors to the console - if err.kind() != io::ErrorKind::WouldBlock { - warn!( - node = %node_id.fmt_short(), - dst = %addr, - "failed to send udp: {err:#}" - ); + udp_sent = true; + } + Err(err) => { + // No need to print "WouldBlock" errors to the console + if err.kind() != io::ErrorKind::WouldBlock { + warn!( + node = %node_id.fmt_short(), + dst = %addr, + "failed to send udp: {err:#}" + ); + } + udp_error = Some(err); } - udp_error = Some(err); } } - } - // send relay - if let Some(ref relay_url) = relay_url { - match self.try_send_relay(relay_url, node_id, split_packets(&transmit)) { - Ok(()) => { - relay_sent = true; - } - Err(err) => { - relay_error = Some(err); + // send relay + if let Some(ref relay_url) = relay_url { + match self.try_send_relay(relay_url, node_id, split_packets(&transmit)) { + Ok(()) => { + relay_sent = true; + } + Err(err) => { + relay_error = Some(err); + } } } - } - let udp_pending = udp_error - .as_ref() - .map(|err| err.kind() == io::ErrorKind::WouldBlock) - .unwrap_or_default(); - let relay_pending = relay_error - .as_ref() - .map(|err| err.kind() == io::ErrorKind::WouldBlock) - .unwrap_or_default(); - if udp_pending && relay_pending { - // Handle backpressure. - Err(io::Error::new(io::ErrorKind::WouldBlock, "pending")) - } else { - if relay_sent || udp_sent { - trace!( - node = %node_id.fmt_short(), - send_udp = ?udp_addr, - send_relay = ?relay_url, - "sent transmit", - ); - } else if !pings_sent { - // Returning Ok here means we let QUIC handle a timeout for a lost - // packet, same would happen if we returned any errors. The - // philosophy of quinn-udp is that a UDP connection could come back - // at any time so these errors should be treated as transient and - // are just timeouts. Hence we opt for returning Ok. See - // test_try_send_no_udp_addr_or_relay_url to explore this further. - debug!( - node = %node_id.fmt_short(), - "no UDP or relay paths available for node, voiding transmit", - ); - // We log this as debug instead of error, because this is a - // situation that comes up under normal operation. If this were an - // error log, it would unnecessarily pollute logs. - // This situation happens essentially when `pings_sent` is false, - // `relay_url` is `None`, so `relay_sent` is false, and the UDP - // path is blocking, so `udp_sent` is false and `udp_pending` is - // true. - // Alternatively returning a WouldBlock error here would - // potentially needlessly block sending on the relay path for the - // next datagram. + let udp_pending = udp_error + .as_ref() + .map(|err| err.kind() == io::ErrorKind::WouldBlock) + .unwrap_or_default(); + let relay_pending = relay_error + .as_ref() + .map(|err| err.kind() == io::ErrorKind::WouldBlock) + .unwrap_or_default(); + if udp_pending && relay_pending { + // Handle backpressure. + return Err(io::Error::new(io::ErrorKind::WouldBlock, "pending")); + } else { + if relay_sent || udp_sent { + trace!( + node = %node_id.fmt_short(), + send_udp = ?udp_addr, + send_relay = ?relay_url, + "sent transmit", + ); + } else if !pings_sent { + // Returning Ok here means we let QUIC handle a timeout for a lost + // packet, same would happen if we returned any errors. The + // philosophy of quinn-udp is that a UDP connection could come back + // at any time so these errors should be treated as transient and + // are just timeouts. Hence we opt for returning Ok. See + // test_try_send_no_udp_addr_or_relay_url to explore this further. + debug!( + node = %node_id.fmt_short(), + "no UDP or relay paths available for node, voiding transmit", + ); + // We log this as debug instead of error, because this is a + // situation that comes up under normal operation. If this were an + // error log, it would unnecessarily pollute logs. + // This situation happens essentially when `pings_sent` is false, + // `relay_url` is `None`, so `relay_sent` is false, and the UDP + // path is blocking, so `udp_sent` is false and `udp_pending` is + // true. + // Alternatively returning a WouldBlock error here would + // potentially needlessly block sending on the relay path for the + // next datagram. + } + return Ok(()); } - Ok(()) + } + None => { + error!(%dest, "no NodeState for mapped address, voiding transmit"); + // Returning Ok here means we let QUIC timeout. Returning WouldBlock + // triggers a hot loop. Returning an error would immediately fail a + // connection. The philosophy of quinn-udp is that a UDP connection could + // come back at any time or missing should be transient so chooses to let + // these kind of errors time out. See test_try_send_no_send_addr to try + // this out. + return Ok(()); } } - None => { - // Check if this is addr is used to perform QUIC Address Discovery - if let Some(addr) = self.node_map.qad_addr_for_send(&dest.0) { - // rewrite target address - transmit.destination = addr; - // send udp - match self.try_send_udp(addr, &transmit) { - Ok(()) => { - trace!(dst = %addr, - "sent QAD transmit over UDP"); - } - Err(err) => { - // No need to print "WouldBlock" errors to the console - if err.kind() == io::ErrorKind::WouldBlock { - return Err(io::Error::new(io::ErrorKind::WouldBlock, "pending")); - } else { - warn!( - dst = %addr, - "failed to send QAD message over udp: {err:#}" - ); - } + } + // Check if the destination address is an IpMappedAddr, which we use for + // quic address discovery, whose endpoints don't have `NodeId`s. + // If so, send the packet over udp. + else if let Ok(dest) = IpMappedAddr::try_from(transmit.destination) { + let mut transmit = transmit.clone(); + + // Get the socket addr + if let Some(addr) = self.ip_mapped_addrs.get_ip_addr(&dest) { + // rewrite target address + transmit.destination = addr; + // send udp + match self.try_send_udp(addr, &transmit) { + Ok(()) => { + trace!(dst = %addr, + "sent IpMapped transmit over UDP"); + } + Err(err) => { + // No need to print "WouldBlock" errors to the console + if err.kind() == io::ErrorKind::WouldBlock { + return Err(io::Error::new(io::ErrorKind::WouldBlock, "pending")); + } else { + warn!( + dst = %addr, + "failed to send IpMapped message over udp: {err:#}" + ); } } - } else { - error!(%dest, "no NodeState for mapped address, voiding transmit"); } - // Returning Ok here means we let QUIC timeout. Returning WouldBlock - // triggers a hot loop. Returning an error would immediately fail a - // connection. The philosophy of quinn-udp is that a UDP connection could + return Ok(()); + } else { + error!(%dest, "no socketaddr for mapped address, voiding transmit"); + // Returning Ok here means we let QUIC timeout. + // Returning an error would immediately fail a connection. + // The philosophy of quinn-udp is that a UDP connection could // come back at any time or missing should be transient so chooses to let // these kind of errors time out. See test_try_send_no_send_addr to try // this out. - Ok(()) + return Ok(()); } + } else { + error!(%transmit.destination, "unrecognized address, voiding transmit"); + // Returning Ok here means we let QUIC timeout. + // Returning an error would immediately fail a connection. + // The philosophy of quinn-udp is that a UDP connection could + // come back at any time or missing should be transient so chooses to let + // these kind of errors time out. See test_try_send_no_send_addr to try + // this out. + return Ok(()); } } @@ -754,7 +785,7 @@ impl MagicSock { /// /// All the `bufs` and `metas` should have initialized packets in them. /// - /// This fixes up the datagrams to use the correct [`QuicMappedAddr`] and extracts DISCO + /// This fixes up the datagrams to use the correct [`NodeIdMappedAddr`] and extracts DISCO /// packets, processing them inside the magic socket. fn process_udp_datagrams( &self, @@ -766,7 +797,7 @@ impl MagicSock { // Adding the IP address we received something on results in Quinn using this // address on the send path to send from. However we let Quinn use a - // QuicMappedAddress, not a real address. So we used to substitute our bind address + // NodeIdMappedAddress, not a real address. So we used to substitute our bind address // here so that Quinn would send on the right address. But that would sometimes // result in the wrong address family and Windows trips up on that. // @@ -833,11 +864,13 @@ impl MagicSock { } if buf_contains_quic_datagrams { - // Update the NodeMap and remap RecvMeta to the QuicMappedAddr. + // Update the NodeMap and remap RecvMeta to the NodeIdMappedAddr. match self.node_map.receive_udp(meta.addr) { None => { - // Check if this address is used for QUIC address discovery - if let Some(addr) = self.node_map.qad_addr_for_recv(&meta.addr) { + // Check if this address is mapped to an IpMappedAddr + if let Some(ip_mapped_addr) = + self.ip_mapped_addrs.get_mapped_addr(&meta.addr) + { trace!( src = ?meta.addr, count = %quic_datagram_count, @@ -845,7 +878,7 @@ impl MagicSock { "UDP recv QUIC address discovery packets", ); quic_packets_total += quic_datagram_count; - meta.addr = addr; + meta.addr = ip_mapped_addr.0; } else { warn!( src = ?meta.addr, @@ -1611,6 +1644,7 @@ impl Handle { pconn6, disco_secrets: DiscoSecrets::default(), node_map, + ip_mapped_addrs: IpMappedAddrs::new(), udp_disco_sender, discovery, direct_addrs: Default::default(), @@ -1750,14 +1784,24 @@ impl DiscoSecrets { where F: FnOnce(&mut SharedSecret) -> T, { + let mut inner = self.0.lock().expect("poisoned"); let x = inner.entry(node_id).or_insert_with(|| { let public_key = public_ed_box(&node_id.public()); + SharedSecret::new(secret, &public_key) }); - {}} + cb(x) + } + fn encode_and_seal( &self, + this_secret_key: &crypto_box::SecretKey, + this_node_id: NodeId, + other_node_id: NodeId, + msg: &disco::Message, ) -> Bytes { let mut seal = msg.as_bytes(); + self.get(this_secret_key, other_node_id, |secret| { + secret.seal(&mut seal) }); disco::encode_message(&this_node_id, seal).into() } @@ -1946,12 +1990,11 @@ impl AsyncUdpSocket for MagicSock { // ready. let ipv4_poller = self.pconn4.create_io_poller(); let ipv6_poller = self.pconn6.as_ref().map(|sock| sock.create_io_poller()); - let relay_sender = self.relay_actor_sender.clone(); + let relay_sender = self.relay_datagram_send_channel.clone(); Box::pin(IoPoller { ipv4_poller, ipv6_poller, relay_sender, - relay_send_waker: self.relay_send_waker.clone(), }) } @@ -2675,7 +2718,7 @@ impl Actor { let res = set.join_all().await; for addrs in res { addrs.iter().for_each(|addr| { - self.msock.node_map.add_qad_addr(*addr); + self.msock.ip_mapped_addrs.add(*addr); }); } } @@ -2916,12 +2959,14 @@ fn split_packets(transmit: &quinn_udp::Transmit) -> RelayContents { /// comes in as the inner [`SocketAddr`], in those interfaces we have to be careful to do /// the conversion to this type. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) struct QuicMappedAddr(pub(crate) SocketAddr); +pub(crate) struct NodeIdMappedAddr(pub(crate) SocketAddr); -/// Counter to always generate unique addresses for [`QuicMappedAddr`]. -static ADDR_COUNTER: AtomicU64 = AtomicU64::new(1); +/// Counter to always generate unique addresses for [`NodeIdMappedAddr`]. +static NODE_ID_ADDR_COUNTER: AtomicU64 = AtomicU64::new(1); -impl QuicMappedAddr { +const MAPPED_ADDR_PORT: u16 = 12345; + +impl NodeIdMappedAddr { /// The Prefix/L of our Unique Local Addresses. const ADDR_PREFIXL: u8 = 0xfd; /// The Global ID used in our Unique Local Addresses. @@ -2938,18 +2983,150 @@ impl QuicMappedAddr { addr[1..6].copy_from_slice(&Self::ADDR_GLOBAL_ID); addr[6..8].copy_from_slice(&Self::ADDR_SUBNET); - let counter = ADDR_COUNTER.fetch_add(1, Ordering::Relaxed); + let counter = NODE_ID_ADDR_COUNTER.fetch_add(1, Ordering::Relaxed); + addr[8..16].copy_from_slice(&counter.to_be_bytes()); + + Self(SocketAddr::new( + IpAddr::V6(Ipv6Addr::from(addr)), + MAPPED_ADDR_PORT, + )) + } +} + +impl TryFrom for NodeIdMappedAddr { + type Error = anyhow::Error; + + fn try_from(value: SocketAddr) -> std::result::Result { + match value { + SocketAddr::V4(_) => anyhow::bail!("NodeIdMappedAddrs are all Ipv6, addr {value:?}"), + SocketAddr::V6(addr) => { + if addr.port() != MAPPED_ADDR_PORT { + anyhow::bail!("not a mapped addr"); + } + let octets = addr.ip().octets(); + if octets[6..8] != NodeIdMappedAddr::ADDR_SUBNET { + anyhow::bail!("not a NodeIdMappedAddr"); + } + Ok(NodeIdMappedAddr(value)) + } + } + } +} +impl std::fmt::Display for NodeIdMappedAddr { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "NodeIdMappedAddr({})", self.0) + } +} + +/// A mirror for the `NodeIdMappedAddr`, mapping a fake Ipv6 address with an actual IP address. +/// +/// You can consider this as nothing more than a lookup key for an IP the [`MagicSock`] knows +/// about. +/// +/// And in our QUIC-facing socket APIs like [`AsyncUdpSocket`] it +/// comes in as the inner [`SocketAddr`], in those interfaces we have to be careful to do +/// the conversion to this type. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub(crate) struct IpMappedAddr(pub(crate) SocketAddr); + +/// Counter to always generate unique addresses for [`NodeIdMappedAddr`]. +static IP_ADDR_COUNTER: AtomicU64 = AtomicU64::new(1); + +impl IpMappedAddr { + /// The Prefix/L of our Unique Local Addresses. + const ADDR_PREFIXL: u8 = 0xfd; + /// The Global ID used in our Unique Local Addresses. + const ADDR_GLOBAL_ID: [u8; 5] = [21, 7, 10, 81, 11]; + /// The Subnet ID used in our Unique Local Addresses. + const ADDR_SUBNET: [u8; 2] = [0, 1]; + + /// Generates a globally unique fake UDP address. + /// + /// This generates and IPv6 Unique Local Address according to RFC 4193. + pub(crate) fn generate() -> Self { + let mut addr = [0u8; 16]; + addr[0] = Self::ADDR_PREFIXL; + addr[1..6].copy_from_slice(&Self::ADDR_GLOBAL_ID); + addr[6..8].copy_from_slice(&Self::ADDR_SUBNET); + + let counter = IP_ADDR_COUNTER.fetch_add(1, Ordering::Relaxed); addr[8..16].copy_from_slice(&counter.to_be_bytes()); - Self(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr)), 12345)) + Self(SocketAddr::new( + IpAddr::V6(Ipv6Addr::from(addr)), + MAPPED_ADDR_PORT, + )) } } -impl std::fmt::Display for QuicMappedAddr { +impl TryFrom for IpMappedAddr { + type Error = anyhow::Error; + + fn try_from(value: SocketAddr) -> std::result::Result { + match value { + SocketAddr::V4(_) => anyhow::bail!("IpMappedAddrs are all Ipv6, addr {value:?}"), + SocketAddr::V6(addr) => { + if addr.port() != MAPPED_ADDR_PORT { + anyhow::bail!("not a mapped addr"); + } + let octets = addr.ip().octets(); + if octets[6..8] != IpMappedAddr::ADDR_SUBNET { + anyhow::bail!("not an IpMappedAddr"); + } + Ok(IpMappedAddr(value)) + } + } + } +} + +impl std::fmt::Display for IpMappedAddr { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "QuicMappedAddr({})", self.0) + write!(f, "IpMappedAddr({})", self.0) + } +} + +#[derive(Debug, Clone)] +/// A Map of [`IpMappedAddrs`] to [`SocketAddrs`] +pub(crate) struct IpMappedAddrs(Arc>>); + +impl IpMappedAddrs { + pub fn new() -> Self { + Self(Arc::new(std::sync::Mutex::new(BTreeMap::new()))) + } + + /// Add a [`SocketAddr`] to the map and the generated [`IpMappedAddr`] it is now associated with back. + /// + /// If this [`SocketAddr`] already exists in the map, it returns its associated [`IpMappedAddr`]. + pub fn add(&self, ip_addr: SocketAddr) -> IpMappedAddr { + let mut map = self.0.lock().expect("poisoned"); + for (mapped_addr, ip) in map.iter() { + if ip == &ip_addr { + return *mapped_addr; + } + } + let ip_mapped_addr = IpMappedAddr::generate(); + map.insert(ip_mapped_addr, ip_addr); + ip_mapped_addr + } + + /// Get the [`IpMappedAddr`] for the given [`SocketAddr`]. + pub fn get_mapped_addr(&self, ip_addr: &SocketAddr) -> Option { + let map = self.0.lock().expect("poisoned"); + for (mapped_addr, ip) in map.iter() { + if ip == ip_addr { + return Some(*mapped_addr); + } + } + None + } + + /// Get the [`SocketAddr`] for the given [`IpMappedAddr`]. + pub fn get_ip_addr(&self, mapped_addr: &IpMappedAddr) -> Option { + let map = self.0.lock().expect("poisoned"); + map.get(mapped_addr).copied() } } + fn disco_message_sent(msg: &disco::Message) { match msg { disco::Message::Ping(_) => { @@ -4018,7 +4195,7 @@ mod tests { async fn magicsock_connect( ep: &quinn::Endpoint, ep_secret_key: SecretKey, - addr: QuicMappedAddr, + addr: NodeIdMappedAddr, node_id: NodeId, ) -> Result { // Endpoint::connect sets this, do the same to have similar behaviour. @@ -4044,7 +4221,7 @@ mod tests { async fn magicsock_connect_with_transport_config( ep: &quinn::Endpoint, ep_secret_key: SecretKey, - addr: QuicMappedAddr, + addr: NodeIdMappedAddr, node_id: NodeId, transport_config: Arc, ) -> Result { @@ -4073,7 +4250,7 @@ mod tests { let msock_1 = magicsock_ep(secret_key_1.clone()).await.unwrap(); // Generate an address not present in the NodeMap. - let bad_addr = QuicMappedAddr::generate(); + let bad_addr = NodeIdMappedAddr::generate(); // 500ms is rather fast here. Running this locally it should always be the correct // timeout. If this is too slow however the test will not become flaky as we are diff --git a/iroh/src/magicsock/node_map.rs b/iroh/src/magicsock/node_map.rs index f45e2026db..edabf0826f 100644 --- a/iroh/src/magicsock/node_map.rs +++ b/iroh/src/magicsock/node_map.rs @@ -17,7 +17,7 @@ use self::{ node_state::{NodeState, Options, PingHandled}, }; use super::{ - metrics::Metrics as MagicsockMetrics, ActorMessage, DiscoMessageSource, QuicMappedAddr, + metrics::Metrics as MagicsockMetrics, ActorMessage, DiscoMessageSource, NodeIdMappedAddr, }; #[cfg(any(test, feature = "test-utils"))] use crate::endpoint::PathSelection; @@ -45,7 +45,7 @@ const MAX_INACTIVE_NODES: usize = 30; /// - The node's ID in this map, only useful if you know the ID from an insert or lookup. /// This is static and never changes. /// -/// - The [`QuicMappedAddr`] which internally identifies the node to the QUIC stack. This +/// - The [`NodeIdMappedAddr`] which internally identifies the node to the QUIC stack. This /// is static and never changes. /// /// - The nodes's public key, aka `PublicKey` or "node_key". This is static and never changes, @@ -54,7 +54,7 @@ const MAX_INACTIVE_NODES: usize = 30; /// - A public socket address on which they are reachable on the internet, known as ip-port. /// These come and go as the node moves around on the internet /// -/// An index of nodeInfos by node key, QuicMappedAddr, and discovered ip:port endpoints. +/// An index of nodeInfos by node key, NodeIdMappedAddr, and discovered ip:port endpoints. #[derive(Default, Debug)] pub(super) struct NodeMap { inner: Mutex, @@ -64,13 +64,11 @@ pub(super) struct NodeMap { pub(super) struct NodeMapInner { by_node_key: HashMap, by_ip_port: HashMap, - by_quic_mapped_addr: HashMap, + by_quic_mapped_addr: HashMap, by_id: HashMap, next_id: usize, #[cfg(any(test, feature = "test-utils"))] path_selection: PathSelection, - // special set of relay socket addresses that we use to do quic address discovery - qad_addrs: BTreeSet, } /// Identifier to look up a [`NodeState`] in the [`NodeMap`]. @@ -81,7 +79,7 @@ pub(super) struct NodeMapInner { enum NodeStateKey { Idx(usize), NodeId(NodeId), - QuicMappedAddr(QuicMappedAddr), + NodeIdMappedAddr(NodeIdMappedAddr), IpPort(IpPort), } @@ -155,71 +153,19 @@ impl NodeMap { .add_node_addr(node_addr, source) } - /// Add a the SocketAddr used to perform QUIC Address Discovery to the nodemap - pub(super) fn add_qad_addr(&self, udp_addr: SocketAddr) { - self.inner - .lock() - .expect("poisoned") - .qad_addrs - .insert(udp_addr); - } - - /// Return a correctly canonicalized SocketAddr if this address is one - /// used to perform QUIC Address Discovery - pub(super) fn qad_addr_for_send(&self, addr: &SocketAddr) -> Option { - // all addresses given to the endpoint are Ipv6 addresses, so we need to - // canonicalize before we check for the actual addr we are trying to send to - let canonicalized_addr = SocketAddr::new(addr.ip().to_canonical(), addr.port()); - if self - .inner - .lock() - .expect("poisoned") - .qad_addrs - .contains(&canonicalized_addr) - { - Some(canonicalized_addr) - } else { - None - } - } - - /// Return a correctly formed SocketAddr if this address is one used to - /// perform QUIC Address Discovery - pub(super) fn qad_addr_for_recv(&self, addr: &SocketAddr) -> Option { - if self - .inner - .lock() - .expect("poisoned") - .qad_addrs - .contains(addr) - { - match addr.ip() { - IpAddr::V4(ipv4_addr) => { - // if this is an ipv4 addr, we need to map it back to - // an ipv6 addr, since all addresses we use to dial on - // the underlying quinn endpoint are mapped ipv6 addrs - Some(SocketAddr::new( - ipv4_addr.to_ipv6_mapped().into(), - addr.port(), - )) - } - IpAddr::V6(_) => Some(*addr), - } - } else { - None - } - } - /// Number of nodes currently listed. pub(super) fn node_count(&self) -> usize { self.inner.lock().expect("poisoned").node_count() } - pub(super) fn receive_udp(&self, udp_addr: SocketAddr) -> Option<(PublicKey, QuicMappedAddr)> { + pub(super) fn receive_udp( + &self, + udp_addr: SocketAddr, + ) -> Option<(PublicKey, NodeIdMappedAddr)> { self.inner.lock().expect("poisoned").receive_udp(udp_addr) } - pub(super) fn receive_relay(&self, relay_url: &RelayUrl, src: NodeId) -> QuicMappedAddr { + pub(super) fn receive_relay(&self, relay_url: &RelayUrl, src: NodeId) -> NodeIdMappedAddr { self.inner .lock() .expect("poisoned") @@ -258,7 +204,7 @@ impl NodeMap { pub(super) fn get_quic_mapped_addr_for_node_key( &self, node_key: NodeId, - ) -> Option { + ) -> Option { self.inner .lock() .expect("poisoned") @@ -302,7 +248,7 @@ impl NodeMap { #[allow(clippy::type_complexity)] pub(super) fn get_send_addrs( &self, - addr: QuicMappedAddr, + addr: NodeIdMappedAddr, have_ipv6: bool, ) -> Option<( PublicKey, @@ -311,7 +257,7 @@ impl NodeMap { Vec, )> { let mut inner = self.inner.lock().expect("poisoned"); - let ep = inner.get_mut(NodeStateKey::QuicMappedAddr(addr))?; + let ep = inner.get_mut(NodeStateKey::NodeIdMappedAddr(addr))?; let public_key = *ep.public_key(); trace!(dest = %addr, node_id = %public_key.fmt_short(), "dst mapped to NodeId"); let (udp_addr, relay_url, msgs) = ep.get_send_addrs(have_ipv6); @@ -461,7 +407,7 @@ impl NodeMapInner { match id { NodeStateKey::Idx(id) => Some(id), NodeStateKey::NodeId(node_key) => self.by_node_key.get(&node_key).copied(), - NodeStateKey::QuicMappedAddr(addr) => self.by_quic_mapped_addr.get(&addr).copied(), + NodeStateKey::NodeIdMappedAddr(addr) => self.by_quic_mapped_addr.get(&addr).copied(), NodeStateKey::IpPort(ipp) => self.by_ip_port.get(&ipp).copied(), } } @@ -492,7 +438,7 @@ impl NodeMapInner { } /// Marks the node we believe to be at `ipp` as recently used. - fn receive_udp(&mut self, udp_addr: SocketAddr) -> Option<(NodeId, QuicMappedAddr)> { + fn receive_udp(&mut self, udp_addr: SocketAddr) -> Option<(NodeId, NodeIdMappedAddr)> { let ip_port: IpPort = udp_addr.into(); let Some(node_state) = self.get_mut(NodeStateKey::IpPort(ip_port)) else { info!(src=%udp_addr, "receive_udp: no node_state found for addr, ignore"); @@ -503,7 +449,7 @@ impl NodeMapInner { } #[instrument(skip_all, fields(src = %src.fmt_short()))] - fn receive_relay(&mut self, relay_url: &RelayUrl, src: NodeId) -> QuicMappedAddr { + fn receive_relay(&mut self, relay_url: &RelayUrl, src: NodeId) -> NodeIdMappedAddr { #[cfg(any(test, feature = "test-utils"))] let path_selection = self.path_selection; let node_state = self.get_or_insert_with(NodeStateKey::NodeId(src), || { diff --git a/iroh/src/magicsock/node_map/node_state.rs b/iroh/src/magicsock/node_map/node_state.rs index a2f97b46b3..cf5ba41259 100644 --- a/iroh/src/magicsock/node_map/node_state.rs +++ b/iroh/src/magicsock/node_map/node_state.rs @@ -24,7 +24,7 @@ use super::{ use crate::endpoint::PathSelection; use crate::{ disco::{self, SendAddr}, - magicsock::{ActorMessage, MagicsockMetrics, QuicMappedAddr, Timer, HEARTBEAT_INTERVAL}, + magicsock::{ActorMessage, MagicsockMetrics, NodeIdMappedAddr, Timer, HEARTBEAT_INTERVAL}, watchable::{Watchable, Watcher}, }; @@ -103,7 +103,7 @@ pub(super) struct NodeState { /// [`NodeMap`]: super::NodeMap id: usize, /// The UDP address used on the QUIC-layer to address this node. - quic_mapped_addr: QuicMappedAddr, + quic_mapped_addr: NodeIdMappedAddr, /// The global identifier for this endpoint. node_id: NodeId, /// The last time we pinged all endpoints. @@ -156,7 +156,7 @@ pub(super) struct Options { impl NodeState { pub(super) fn new(id: usize, options: Options) -> Self { - let quic_mapped_addr = QuicMappedAddr::generate(); + let quic_mapped_addr = NodeIdMappedAddr::generate(); if options.relay_url.is_some() { // we potentially have a relay connection to the node @@ -191,7 +191,7 @@ impl NodeState { &self.node_id } - pub(super) fn quic_mapped_addr(&self) -> &QuicMappedAddr { + pub(super) fn quic_mapped_addr(&self) -> &NodeIdMappedAddr { &self.quic_mapped_addr } @@ -1487,7 +1487,7 @@ mod tests { ( NodeState { id: 0, - quic_mapped_addr: QuicMappedAddr::generate(), + quic_mapped_addr: NodeIdMappedAddr::generate(), node_id: key.public(), last_full_ping: None, relay_url: None, @@ -1517,7 +1517,7 @@ mod tests { let key = SecretKey::generate(rand::thread_rng()); NodeState { id: 1, - quic_mapped_addr: QuicMappedAddr::generate(), + quic_mapped_addr: NodeIdMappedAddr::generate(), node_id: key.public(), last_full_ping: None, relay_url: relay_and_state(key.public(), send_addr.clone()), @@ -1538,7 +1538,7 @@ mod tests { let key = SecretKey::generate(rand::thread_rng()); NodeState { id: 2, - quic_mapped_addr: QuicMappedAddr::generate(), + quic_mapped_addr: NodeIdMappedAddr::generate(), node_id: key.public(), last_full_ping: None, relay_url: Some(( @@ -1582,7 +1582,7 @@ mod tests { ( NodeState { id: 3, - quic_mapped_addr: QuicMappedAddr::generate(), + quic_mapped_addr: NodeIdMappedAddr::generate(), node_id: key.public(), last_full_ping: None, relay_url: relay_and_state(key.public(), send_addr.clone()), @@ -1691,7 +1691,6 @@ mod tests { ]), next_id: 5, path_selection: PathSelection::default(), - qad_addrs: BTreeSet::new(), }); let mut got = node_map.list_remote_infos(later); got.sort_by_key(|p| p.node_id);