Skip to content

Commit

Permalink
reat: emove derp regions
Browse files Browse the repository at this point in the history
derp servers are only addressed by url now
  • Loading branch information
dignifiedquire committed Nov 23, 2023
1 parent 0773e30 commit 5d5fda7
Show file tree
Hide file tree
Showing 19 changed files with 770 additions and 879 deletions.
11 changes: 4 additions & 7 deletions iroh-net/examples/magic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::net::SocketAddr;
use clap::Parser;
use iroh_base::base32;
use iroh_net::{
defaults::TEST_REGION_ID,
derp::{DerpMap, DerpMode},
key::SecretKey,
magic_endpoint::accept_conn,
Expand Down Expand Up @@ -36,7 +35,7 @@ enum Command {
#[clap(long)]
addrs: Option<Vec<SocketAddr>>,
#[clap(long)]
derp_region: Option<u16>,
derp_url: Option<Url>,
},
}

Expand All @@ -55,8 +54,7 @@ async fn main() -> anyhow::Result<()> {

let derp_mode = match args.derp_url {
None => DerpMode::Default,
// use `region_id` 65535, which is reserved for testing and experiments
Some(url) => DerpMode::Custom(DerpMap::from_url(url, TEST_REGION_ID)),
Some(url) => DerpMode::Custom(DerpMap::from_url(url)),
};

let endpoint = MagicEndpoint::builder()
Expand Down Expand Up @@ -97,10 +95,9 @@ async fn main() -> anyhow::Result<()> {
Command::Connect {
peer_id,
addrs,
derp_region,
derp_url,
} => {
let addr =
NodeAddr::from_parts(peer_id.parse()?, derp_region, addrs.unwrap_or_default());
let addr = NodeAddr::from_parts(peer_id.parse()?, derp_url, addrs.unwrap_or_default());
let conn = endpoint.connect(addr, EXAMPLE_ALPN).await?;
info!("connected");

Expand Down
4 changes: 3 additions & 1 deletion iroh-net/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
};

use url::Url;

use super::portmapper;

/// Fake WireGuard endpoint IP address that means to
Expand Down Expand Up @@ -82,7 +84,7 @@ pub struct NetInfo {
/// connected to multiple DERP servers (to send to other nodes)
/// but PreferredDERP is the instance number that the node
/// subscribes to traffic at. Zero means disconnected or unknown.
pub preferred_derp: u16,
pub preferred_derp: Option<Url>,

/// LinkType is the current link type, if known.
pub link_type: Option<LinkType>,
Expand Down
50 changes: 25 additions & 25 deletions iroh-net/src/defaults.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Default values used in [`iroh-net`][`crate`]
use crate::derp::{DerpMap, DerpNode, DerpRegion, UseIpv4, UseIpv6};
use url::Url;

use crate::derp::{DerpMap, DerpNode, DerpRegion};

/// Hostname of the default NA Derp.
pub const NA_DERP_HOSTNAME: &str = "use1-1.derp.iroh.network.";
Expand Down Expand Up @@ -29,41 +31,39 @@ pub fn default_derp_map() -> DerpMap {
}

/// Get the default [`DerpRegion`] for NA.
pub fn default_na_derp_region() -> DerpRegion {
pub fn default_na_derp_region() -> (Url, DerpRegion) {
// The default NA derper run by number0.
let url: Url = format!("https://{NA_DERP_HOSTNAME}").parse().unwrap();
let default_n0_derp = DerpNode {
name: "na-default-1".into(),
region_id: NA_REGION_ID,
url: format!("https://{NA_DERP_HOSTNAME}").parse().unwrap(),
url: url.clone(),
stun_only: false,
stun_port: DEFAULT_DERP_STUN_PORT,
ipv4: UseIpv4::Some(NA_DERP_IPV4),
ipv6: UseIpv6::TryDns,
};
DerpRegion {
region_id: NA_REGION_ID,
nodes: vec![default_n0_derp.into()],
avoid: false,
region_code: "default-1".into(),
}
(
url,
DerpRegion {
nodes: vec![default_n0_derp.into()],
avoid: false,
region_code: "default-1".into(),
},
)
}

/// Get the default [`DerpRegion`] for EU.
pub fn default_eu_derp_region() -> DerpRegion {
pub fn default_eu_derp_region() -> (Url, DerpRegion) {
// The default EU derper run by number0.
let url: Url = format!("https://{EU_DERP_HOSTNAME}").parse().unwrap();
let default_n0_derp = DerpNode {
name: "eu-default-1".into(),
region_id: EU_REGION_ID,
url: format!("https://{EU_DERP_HOSTNAME}").parse().unwrap(),
url: url.clone(),
stun_only: false,
stun_port: DEFAULT_DERP_STUN_PORT,
ipv4: UseIpv4::Some(EU_DERP_IPV4),
ipv6: UseIpv6::TryDns,
};
DerpRegion {
region_id: EU_REGION_ID,
nodes: vec![default_n0_derp.into()],
avoid: false,
region_code: "default-2".into(),
}
(
url,
DerpRegion {
nodes: vec![default_n0_derp.into()],
avoid: false,
region_code: "default-2".into(),
},
)
}
2 changes: 1 addition & 1 deletion iroh-net/src/derp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub(crate) mod types;
pub use self::client::{Client as DerpClient, ReceivedMessage};
pub use self::codec::MAX_PACKET_SIZE;
pub use self::http::Client as HttpClient;
pub use self::map::{DerpMap, DerpMode, DerpNode, DerpRegion, UseIpv4, UseIpv6};
pub use self::map::{DerpMap, DerpMode, DerpNode, DerpRegion};
pub use self::metrics::Metrics;
pub use self::server::{
ClientConnHandler, MaybeTlsStream as MaybeTlsStreamServer, PacketForwarderHandler, Server,
Expand Down
12 changes: 1 addition & 11 deletions iroh-net/src/derp/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ mod tests {
use tracing::{info, info_span, Instrument};
use tracing_subscriber::{prelude::*, EnvFilter};

use crate::derp::{DerpNode, DerpRegion, ReceivedMessage, UseIpv4, UseIpv6};
use crate::derp::{DerpNode, DerpRegion, ReceivedMessage};
use crate::key::{PublicKey, SecretKey};

#[tokio::test]
Expand Down Expand Up @@ -83,16 +83,11 @@ mod tests {
};
info!("addr: {addr}:{port}");
let region = DerpRegion {
region_id: 1,
avoid: false,
nodes: vec![DerpNode {
name: "test_node".to_string(),
region_id: 1,
url: format!("http://localhost:{port}").parse().unwrap(),
stun_only: false,
stun_port: 0,
ipv4: UseIpv4::Some(addr),
ipv6: UseIpv6::Disabled,
}
.into()],
region_code: "test_region".to_string(),
Expand Down Expand Up @@ -232,16 +227,11 @@ mod tests {
info!("DERP listening on: {addr}:{port}");

let region = DerpRegion {
region_id: 1,
avoid: false,
nodes: vec![DerpNode {
name: "test_node".to_string(),
region_id: 1,
url: format!("https://localhost:{port}").parse().unwrap(),
stun_only: false,
stun_port: 0,
ipv4: UseIpv4::Some(addr),
ipv6: UseIpv6::Disabled,
}
.into()],
region_code: "test_region".to_string(),
Expand Down
128 changes: 52 additions & 76 deletions iroh-net/src/derp/http/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use url::Url;
use crate::derp::{
client::Client as DerpClient, client::ClientBuilder as DerpClientBuilder,
client::ClientReceiver as DerpClientReceiver, metrics::Metrics, server::PacketForwarderHandler,
DerpNode, DerpRegion, MeshKey, PacketForwarder, ReceivedMessage, UseIpv4, UseIpv6,
DerpNode, DerpRegion, MeshKey, PacketForwarder, ReceivedMessage,
};
use crate::dns::DNS_RESOLVER;
use crate::key::{PublicKey, SecretKey};
Expand Down Expand Up @@ -1013,7 +1013,7 @@ impl Actor {
/// connect to.
fn target_string(&self, reg: &DerpRegion) -> String {
// TODO: if self.Url, return the url string
format!("region {} ({})", reg.region_id, reg.region_code)
format!("region {}", reg.region_code)
}

async fn dial_url(&self) -> Result<TcpStream, ClientError> {
Expand Down Expand Up @@ -1150,74 +1150,62 @@ async fn dial_node(node: &DerpNode, prefer_ipv6: bool) -> Result<TcpStream, Clie
// TODO: Add support for HTTP proxies.
debug!("dial node: {:?}", node);

match (node.ipv4.is_enabled(), node.ipv6.is_enabled()) {
(true, true) => {
let node1 = node.clone();
let fut1 = Box::pin(
async move { start_dial(&node1, UseIp::Ipv4(node.ipv4), prefer_ipv6).await }
.instrument(info_span!("dial", proto = "ipv4")),
);
let node1 = node.clone();
let fut1 = Box::pin(
async move { start_dial(&node1, prefer_ipv6).await }
.instrument(info_span!("dial", proto = "ipv4")),
);

let node2 = node.clone();
let fut2 = Box::pin(
async move { start_dial(&node2, prefer_ipv6).await }
.instrument(info_span!("dial", proto = "ipv6")),
);

match futures::future::select(fut1, fut2).await {
futures::future::Either::Left((ipv4, ipv6)) => match ipv4 {
Ok(conn) => Ok(conn),
Err(_) => ipv6.await,
},
futures::future::Either::Right((ipv6, ipv4)) => match ipv6 {
Ok(conn) => Ok(conn),
Err(_) => ipv4.await,
},
}
}

let node2 = node.clone();
let fut2 = Box::pin(
async move { start_dial(&node2, UseIp::Ipv6(node.ipv6), prefer_ipv6).await }
.instrument(info_span!("dial", proto = "ipv6")),
);
async fn resolve_host(node: &DerpNode, prefer_ipv6: bool) -> Result<IpAddr, ClientError> {
let host = node
.url
.host()
.ok_or_else(|| ClientError::InvalidUrl("missing host".into()))?;
match host {
url::Host::Domain(domain) => {
// Need to do a DNS lookup
let addrs = DNS_RESOLVER
.lookup_ip(domain)
.await
.map_err(|e| ClientError::Dns(Some(e)))?;

match futures::future::select(fut1, fut2).await {
futures::future::Either::Left((ipv4, ipv6)) => match ipv4 {
Ok(conn) => Ok(conn),
Err(_) => ipv6.await,
},
futures::future::Either::Right((ipv6, ipv4)) => match ipv6 {
Ok(conn) => Ok(conn),
Err(_) => ipv4.await,
},
if prefer_ipv6 {
if let Some(addr) = addrs.iter().find(|addr| addr.is_ipv6()) {
return Ok(addr);
}
}
addrs
.into_iter()
.next()
.ok_or_else(|| ClientError::Dns(None))
}
(true, false) => start_dial(node, UseIp::Ipv4(node.ipv4), prefer_ipv6).await,
(false, true) => start_dial(node, UseIp::Ipv6(node.ipv6), prefer_ipv6).await,
(false, false) => Err(ClientError::IPDisabled),
url::Host::Ipv4(ip) => Ok(IpAddr::V4(ip)),
url::Host::Ipv6(ip) => Ok(IpAddr::V6(ip)),
}
}

async fn start_dial(
node: &DerpNode,
dst_primary: UseIp,
prefer_ipv6: bool,
) -> Result<TcpStream, ClientError> {
trace!("dial start: {:?}", dst_primary);
if matches!(dst_primary, UseIp::Ipv4(_)) && prefer_ipv6 {
tokio::time::sleep(Duration::from_millis(200)).await;
// Start v4 dial
}
let host: IpAddr = match dst_primary {
UseIp::Ipv4(UseIpv4::Some(addr)) => addr.into(),
UseIp::Ipv6(UseIpv6::Some(addr)) => addr.into(),
_ => {
let host = node
.url
.host()
.ok_or_else(|| ClientError::InvalidUrl("missing host".into()))?;
match host {
url::Host::Domain(domain) => {
// Need to do a DNS lookup
let addr = DNS_RESOLVER
.lookup_ip(domain)
.await
.map_err(|e| ClientError::Dns(Some(e)))?
.iter()
.find(|addr| match dst_primary {
UseIp::Ipv4(_) => addr.is_ipv4(),
UseIp::Ipv6(_) => addr.is_ipv6(),
});
addr.ok_or_else(|| ClientError::Dns(None))?
}
url::Host::Ipv4(ip) => IpAddr::V4(ip),
url::Host::Ipv6(ip) => IpAddr::V6(ip),
}
}
};
async fn start_dial(node: &DerpNode, prefer_ipv6: bool) -> Result<TcpStream, ClientError> {
trace!("dial start");

let host = resolve_host(node, prefer_ipv6).await?;
let port =
match node.url.port() {
Some(port) => port,
Expand All @@ -1242,7 +1230,7 @@ async fn start_dial(
.map_err(ClientError::DialIO)?;
// TODO: ipv6 vs ipv4 specific connection

trace!("dial done: {:?}", dst_primary);
trace!("dial done");
Ok(tcp_stream)
}

Expand Down Expand Up @@ -1356,13 +1344,6 @@ impl PacketForwarder for Client {
}
}

// TODO: move to net or some reusable place
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum UseIp {
Ipv4(UseIpv4),
Ipv6(UseIpv6),
}

fn downcast_upgrade(
upgraded: Upgraded,
) -> anyhow::Result<(
Expand Down Expand Up @@ -1426,16 +1407,11 @@ mod tests {
async fn test_recv_detail_connect_error() -> Result<()> {
let key = SecretKey::generate();
let bad_region = DerpRegion {
region_id: 1,
avoid: false,
nodes: vec![DerpNode {
name: "test_node".to_string(),
region_id: 1,
url: "https://bad.url".parse().unwrap(),
stun_only: false,
stun_port: 0,
ipv4: UseIpv4::Some("35.175.99.112".parse().unwrap()),
ipv6: UseIpv6::Disabled,
}
.into()],
region_code: "test_region".to_string(),
Expand Down
2 changes: 1 addition & 1 deletion iroh-net/src/derp/http/mesh_clients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl MeshClients {
MeshAddrs::Addrs(urls) => urls.to_owned(),
MeshAddrs::DerpMap(derp_map) => {
let mut urls = Vec::new();
for region in derp_map.regions() {
for (_url, region) in derp_map.regions() {
for node in region.nodes.iter() {
// note: `node.host_name` is expected to include the scheme
let mut url = node.url.clone();
Expand Down
Loading

0 comments on commit 5d5fda7

Please sign in to comment.