Skip to content

Commit 5aba17e

Browse files
authored
feat(relay)!: relay only mode now configurable (#3056)
## Description This will allow us to configure relay only mode at runtime. ~~Useful for easy testing without having to rebuild with the `DEV_RELAY_ONLY` environment variable set.~~ The `DEV_RELAY_ONLY` compile time env var has been completely dropped and a `relay_only` option has been threaded through the entire stack when `test-utils` is enabled. An example of it can be followed with the `iroh/examples/transfer.rs` where you can set up a provide and fetch node with `--relay-only`. ## Breaking Changes <!-- Optional, if there are any breaking changes document them, including how to migrate older code. --> ## Notes & open questions <!-- Any notes, remarks or open questions you have to make about the PR. --> ## Change checklist - [ ] Self-review. - [ ] Documentation updates following the [style guide](https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html#appendix-a-full-conventions-text), if relevant. - [ ] Tests if relevant. - [ ] All breaking changes documented.
1 parent f50db17 commit 5aba17e

File tree

9 files changed

+158
-49
lines changed

9 files changed

+158
-49
lines changed

iroh/bench/src/bin/bulk.rs

+3-10
Original file line numberDiff line numberDiff line change
@@ -39,24 +39,17 @@ pub fn run_iroh(opt: Opt) -> Result<()> {
3939
metrics.insert(::iroh::metrics::NetReportMetrics::new(reg));
4040
metrics.insert(::iroh::metrics::PortmapMetrics::new(reg));
4141
#[cfg(feature = "local-relay")]
42-
if opt.with_relay {
42+
if opt.only_relay {
4343
metrics.insert(::iroh::metrics::RelayMetrics::new(reg));
4444
}
4545
})?;
4646
}
4747

48-
#[cfg(not(feature = "local-relay"))]
49-
if opt.with_relay {
50-
anyhow::bail!(
51-
"Must compile the benchmark with the `local-relay` feature flag to use this option"
52-
);
53-
}
54-
5548
let server_span = tracing::error_span!("server");
5649
let runtime = rt();
5750

5851
#[cfg(feature = "local-relay")]
59-
let (relay_url, _guard) = if opt.with_relay {
52+
let (relay_url, _guard) = if opt.only_relay {
6053
let (_, relay_url, _guard) = runtime.block_on(::iroh::test_utils::run_relay_server())?;
6154

6255
(Some(relay_url), Some(_guard))
@@ -120,7 +113,7 @@ pub fn run_iroh(opt: Opt) -> Result<()> {
120113
"PortmapMetrics",
121114
core.get_collector::<::iroh::metrics::PortmapMetrics>(),
122115
);
123-
// if None, (this is the case if opt.with_relay is false), then this is skipped internally:
116+
// if None, (this is the case if opt.only_relay is false), then this is skipped internally:
124117
#[cfg(feature = "local-relay")]
125118
collect_and_print(
126119
"RelayMetrics",

iroh/bench/src/iroh.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ pub fn server_endpoint(
3333
let mut builder = Endpoint::builder();
3434
#[cfg(feature = "local-relay")]
3535
{
36-
builder = builder.insecure_skip_relay_cert_verify(relay_url.is_some())
36+
builder = builder.insecure_skip_relay_cert_verify(relay_url.is_some());
37+
let path_selection = match opt.only_relay {
38+
true => iroh::endpoint::PathSelection::RelayOnly,
39+
false => iroh::endpoint::PathSelection::default(),
40+
};
41+
builder = builder.path_selection(path_selection);
3742
}
3843
let ep = builder
3944
.alpns(vec![ALPN.to_vec()])
@@ -89,7 +94,12 @@ pub async fn connect_client(
8994
let mut builder = Endpoint::builder();
9095
#[cfg(feature = "local-relay")]
9196
{
92-
builder = builder.insecure_skip_relay_cert_verify(relay_url.is_some())
97+
builder = builder.insecure_skip_relay_cert_verify(relay_url.is_some());
98+
let path_selection = match opt.only_relay {
99+
true => iroh::endpoint::PathSelection::RelayOnly,
100+
false => iroh::endpoint::PathSelection::default(),
101+
};
102+
builder = builder.path_selection(path_selection);
93103
}
94104
let endpoint = builder
95105
.alpns(vec![ALPN.to_vec()])

iroh/bench/src/lib.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,11 @@ pub struct Opt {
6969
#[clap(long, default_value = "1200")]
7070
pub initial_mtu: u16,
7171
/// Whether to run a local relay and have the server and clients connect to that.
72-
///
73-
/// Can be combined with the `DEV_RELAY_ONLY` environment variable (at compile time)
74-
/// to test throughput for relay-only traffic locally.
75-
/// (e.g. `DEV_RELAY_ONLY=true cargo run --release -- iroh --with-relay`)
72+
/// This will force all traffic over the relay and can be used to test
73+
/// throughput for relay-only traffic.
74+
#[cfg(feature = "local-relay")]
7675
#[clap(long, default_value_t = false)]
77-
pub with_relay: bool,
76+
pub only_relay: bool,
7877
}
7978

8079
pub enum EndpointSelector {

iroh/examples/transfer.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use bytes::Bytes;
88
use clap::{Parser, Subcommand};
99
use indicatif::HumanBytes;
1010
use iroh::{
11-
endpoint::ConnectionError, Endpoint, NodeAddr, RelayMap, RelayMode, RelayUrl, SecretKey,
11+
endpoint::{ConnectionError, PathSelection},
12+
Endpoint, NodeAddr, RelayMap, RelayMode, RelayUrl, SecretKey,
1213
};
1314
use iroh_base::ticket::NodeTicket;
1415
use tracing::info;
@@ -29,12 +30,16 @@ enum Commands {
2930
size: u64,
3031
#[clap(long)]
3132
relay_url: Option<String>,
33+
#[clap(long, default_value = "false")]
34+
relay_only: bool,
3235
},
3336
Fetch {
3437
#[arg(index = 1)]
3538
ticket: String,
3639
#[clap(long)]
3740
relay_url: Option<String>,
41+
#[clap(long, default_value = "false")]
42+
relay_only: bool,
3843
},
3944
}
4045

@@ -44,14 +49,22 @@ async fn main() -> anyhow::Result<()> {
4449
let cli = Cli::parse();
4550

4651
match &cli.command {
47-
Commands::Provide { size, relay_url } => provide(*size, relay_url.clone()).await?,
48-
Commands::Fetch { ticket, relay_url } => fetch(ticket, relay_url.clone()).await?,
52+
Commands::Provide {
53+
size,
54+
relay_url,
55+
relay_only,
56+
} => provide(*size, relay_url.clone(), *relay_only).await?,
57+
Commands::Fetch {
58+
ticket,
59+
relay_url,
60+
relay_only,
61+
} => fetch(ticket, relay_url.clone(), *relay_only).await?,
4962
}
5063

5164
Ok(())
5265
}
5366

54-
async fn provide(size: u64, relay_url: Option<String>) -> anyhow::Result<()> {
67+
async fn provide(size: u64, relay_url: Option<String>, relay_only: bool) -> anyhow::Result<()> {
5568
let secret_key = SecretKey::generate(rand::rngs::OsRng);
5669
let relay_mode = match relay_url {
5770
Some(relay_url) => {
@@ -61,10 +74,15 @@ async fn provide(size: u64, relay_url: Option<String>) -> anyhow::Result<()> {
6174
}
6275
None => RelayMode::Default,
6376
};
77+
let path_selection = match relay_only {
78+
true => PathSelection::RelayOnly,
79+
false => PathSelection::default(),
80+
};
6481
let endpoint = Endpoint::builder()
6582
.secret_key(secret_key)
6683
.alpns(vec![TRANSFER_ALPN.to_vec()])
6784
.relay_mode(relay_mode)
85+
.path_selection(path_selection)
6886
.bind()
6987
.await?;
7088

@@ -142,7 +160,7 @@ async fn provide(size: u64, relay_url: Option<String>) -> anyhow::Result<()> {
142160
Ok(())
143161
}
144162

145-
async fn fetch(ticket: &str, relay_url: Option<String>) -> anyhow::Result<()> {
163+
async fn fetch(ticket: &str, relay_url: Option<String>, relay_only: bool) -> anyhow::Result<()> {
146164
let ticket: NodeTicket = ticket.parse()?;
147165
let secret_key = SecretKey::generate(rand::rngs::OsRng);
148166
let relay_mode = match relay_url {
@@ -153,10 +171,15 @@ async fn fetch(ticket: &str, relay_url: Option<String>) -> anyhow::Result<()> {
153171
}
154172
None => RelayMode::Default,
155173
};
174+
let path_selection = match relay_only {
175+
true => PathSelection::RelayOnly,
176+
false => PathSelection::default(),
177+
};
156178
let endpoint = Endpoint::builder()
157179
.secret_key(secret_key)
158180
.alpns(vec![TRANSFER_ALPN.to_vec()])
159181
.relay_mode(relay_mode)
182+
.path_selection(path_selection)
160183
.bind()
161184
.await?;
162185

iroh/src/endpoint.rs

+26
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ const DISCOVERY_WAIT_PERIOD: Duration = Duration::from_millis(500);
7474

7575
type DiscoveryBuilder = Box<dyn FnOnce(&SecretKey) -> Option<Box<dyn Discovery>> + Send + Sync>;
7676

77+
/// Defines the mode of path selection for all traffic flowing through
78+
/// the endpoint.
79+
#[cfg(any(test, feature = "test-utils"))]
80+
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
81+
pub enum PathSelection {
82+
/// Uses all available paths
83+
#[default]
84+
All,
85+
/// Forces all traffic to go exclusively through relays
86+
RelayOnly,
87+
}
88+
7789
/// Builder for [`Endpoint`].
7890
///
7991
/// By default the endpoint will generate a new random [`SecretKey`], which will result in a
@@ -97,6 +109,8 @@ pub struct Builder {
97109
insecure_skip_relay_cert_verify: bool,
98110
addr_v4: Option<SocketAddrV4>,
99111
addr_v6: Option<SocketAddrV6>,
112+
#[cfg(any(test, feature = "test-utils"))]
113+
path_selection: PathSelection,
100114
}
101115

102116
impl Default for Builder {
@@ -115,6 +129,8 @@ impl Default for Builder {
115129
insecure_skip_relay_cert_verify: false,
116130
addr_v4: None,
117131
addr_v6: None,
132+
#[cfg(any(test, feature = "test-utils"))]
133+
path_selection: PathSelection::default(),
118134
}
119135
}
120136
}
@@ -160,6 +176,8 @@ impl Builder {
160176
dns_resolver,
161177
#[cfg(any(test, feature = "test-utils"))]
162178
insecure_skip_relay_cert_verify: self.insecure_skip_relay_cert_verify,
179+
#[cfg(any(test, feature = "test-utils"))]
180+
path_selection: self.path_selection,
163181
};
164182
Endpoint::bind(static_config, msock_opts, self.alpn_protocols).await
165183
}
@@ -417,6 +435,14 @@ impl Builder {
417435
self.insecure_skip_relay_cert_verify = skip_verify;
418436
self
419437
}
438+
439+
/// This implies we only use the relay to communicate
440+
/// and do not attempt to do any hole punching.
441+
#[cfg(any(test, feature = "test-utils"))]
442+
pub fn path_selection(mut self, path_selection: PathSelection) -> Self {
443+
self.path_selection = path_selection;
444+
self
445+
}
420446
}
421447

422448
/// Configuration for a [`quinn::Endpoint`] that cannot be changed at runtime.

iroh/src/magicsock.rs

+17-8
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
//!
33
//! Based on tailscale/wgengine/magicsock
44
//!
5-
//! ### `DEV_RELAY_ONLY` env var:
6-
//! When present at *compile time*, this env var will force all packets
7-
//! to be sent over the relay connection, regardless of whether or
5+
//! ### `RelayOnly` path selection:
6+
//! When set this will force all packets to be sent over
7+
//! the relay connection, regardless of whether or
88
//! not we have a direct UDP address for the given node.
99
//!
1010
//! The intended use is for testing the relay protocol inside the MagicSock
@@ -61,6 +61,8 @@ use self::{
6161
relay_actor::{RelayActor, RelayActorMessage, RelayRecvDatagram},
6262
udp_conn::UdpConn,
6363
};
64+
#[cfg(any(test, feature = "test-utils"))]
65+
use crate::endpoint::PathSelection;
6466
use crate::{
6567
defaults::timeouts::NET_REPORT_TIMEOUT,
6668
disco::{self, CallMeMaybe, SendAddr},
@@ -128,6 +130,10 @@ pub(crate) struct Options {
128130
/// May only be used in tests.
129131
#[cfg(any(test, feature = "test-utils"))]
130132
pub(crate) insecure_skip_relay_cert_verify: bool,
133+
134+
/// Configuration for what path selection to use
135+
#[cfg(any(test, feature = "test-utils"))]
136+
pub(crate) path_selection: PathSelection,
131137
}
132138

133139
impl Default for Options {
@@ -143,6 +149,8 @@ impl Default for Options {
143149
dns_resolver: crate::dns::default_resolver().clone(),
144150
#[cfg(any(test, feature = "test-utils"))]
145151
insecure_skip_relay_cert_verify: false,
152+
#[cfg(any(test, feature = "test-utils"))]
153+
path_selection: PathSelection::default(),
146154
}
147155
}
148156
}
@@ -1493,11 +1501,6 @@ impl Handle {
14931501
/// Creates a magic [`MagicSock`] listening on [`Options::addr_v4`] and [`Options::addr_v6`].
14941502
async fn new(opts: Options) -> Result<Self> {
14951503
let me = opts.secret_key.public().fmt_short();
1496-
if crate::util::relay_only_mode() {
1497-
warn!(
1498-
"creating a MagicSock that will only send packets over a relay relay connection."
1499-
);
1500-
}
15011504

15021505
Self::with_name(me, opts)
15031506
.instrument(error_span!("magicsock"))
@@ -1518,6 +1521,8 @@ impl Handle {
15181521
proxy_url,
15191522
#[cfg(any(test, feature = "test-utils"))]
15201523
insecure_skip_relay_cert_verify,
1524+
#[cfg(any(test, feature = "test-utils"))]
1525+
path_selection,
15211526
} = opts;
15221527

15231528
let relay_datagram_recv_queue = Arc::new(RelayDatagramRecvQueue::new());
@@ -1548,6 +1553,9 @@ impl Handle {
15481553

15491554
// load the node data
15501555
let node_map = node_map.unwrap_or_default();
1556+
#[cfg(any(test, feature = "test-utils"))]
1557+
let node_map = NodeMap::load_from_vec(node_map, path_selection);
1558+
#[cfg(not(any(test, feature = "test-utils")))]
15511559
let node_map = NodeMap::load_from_vec(node_map);
15521560

15531561
let secret_encryption_key = secret_ed_box(secret_key.secret());
@@ -3815,6 +3823,7 @@ mod tests {
38153823
dns_resolver: crate::dns::default_resolver().clone(),
38163824
proxy_url: None,
38173825
insecure_skip_relay_cert_verify: true,
3826+
path_selection: PathSelection::default(),
38183827
};
38193828
let msock = MagicSock::spawn(opts).await?;
38203829
let server_config = crate::endpoint::make_server_config(

0 commit comments

Comments
 (0)