Skip to content

Commit 269b7f0

Browse files
divagant-martianackintoshAgeManning
authored
Shift dual stack support to use two sockets instead of mapped addresses (#160)
* progress dealing with a second recv socket * wip * remove unused file * start changes to two send sockets * wip * Make build pass * Make Discv5::new requires ListenConfig and update `find_node` example * Remove useless comments * Remove unnecessary qualification * Make tests pass * Make SendHandler uses two sockets * listen_socket holds two sockets * Add a test for the case of self-request * Add tests for running a simple query test * Add tests for non-contactable nodes * Tweak listen_sockets * Checking if loopback address * No longer need to treat mapped address * cargo fmt * Fix doc * Update README * Tweak ports to avoid `Address already in use` * clippy * fmt * get tests working without ipv6 - kinda * Revert "get tests working without ipv6 - kinda" This reverts commit 31c1b9a. * Skip some tests if IPv6 is not supported * Separate `test_self_request` into ipv4 and ipv6. * Add a comment * Add `listen_config` to Discv5ConfigBuilder * Remove `listen_config` from Discv5 * Fix tests and examples * Fix doc * cargo fmt * Fix example * cargo fmt * fixes * update readme * update readme * revert query interval to match comments * Zero out ipv6 extra fields * fix unused import warns * better error handling in send addressing comments from @AgeManning * improve ergonomics for ListenConfig. Not super sure about this one * give direct access to the ListenConfig * remove duplicated word in omment * Document `ListenConfig` functions Co-authored-by: Age Manning <[email protected]> * Improve docs for `ListenConfig` functions Co-authored-by: Age Manning <[email protected]> * Document `ListenConfig` functions Co-authored-by: Age Manning <[email protected]> * Document `ListenConfig` functions Co-authored-by: Age Manning <[email protected]> --------- Co-authored-by: ackintosh <[email protected]> Co-authored-by: Age Manning <[email protected]>
1 parent fe8f0c7 commit 269b7f0

File tree

19 files changed

+872
-294
lines changed

19 files changed

+872
-294
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ tokio = { version = "1.15.0", features = ["full"] }
4545
rand_xorshift = "0.3.0"
4646
rand_core = "0.6.3"
4747
clap = { version = "3.1", features = ["derive"] }
48+
if-addrs = "0.10.1"
4849

4950
[features]
5051
libp2p = ["libp2p-core"]

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,9 @@ A simple example of creating this service is as follows:
3838

3939
```rust
4040
use discv5::{enr, enr::{CombinedKey, NodeId}, TokioExecutor, Discv5, Discv5ConfigBuilder};
41+
use discv5::socket::ListenConfig;
4142
use std::net::SocketAddr;
4243

43-
// listening address and port
44-
let listen_addr = "0.0.0.0:9000".parse::<SocketAddr>().unwrap();
45-
4644
// construct a local ENR
4745
let enr_key = CombinedKey::generate_secp256k1();
4846
let enr = enr::EnrBuilder::new("v4").build(&enr_key).unwrap();
@@ -54,8 +52,14 @@ A simple example of creating this service is as follows:
5452
.build()
5553
.unwrap();
5654

55+
// configuration for the sockets to listen on
56+
let listen_config = ListenConfig::Ipv4 {
57+
ip: Ipv4Addr::UNSPECIFIED,
58+
port: 9000,
59+
};
60+
5761
// default configuration
58-
let config = Discv5ConfigBuilder::new().build();
62+
let config = Discv5ConfigBuilder::new(listen_config).build();
5963

6064
// construct the discv5 server
6165
let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap();
@@ -65,7 +69,7 @@ A simple example of creating this service is as follows:
6569
// discv5.add_enr(<ENR>)
6670

6771
// start the discv5 server
68-
runtime.block_on(discv5.start(listen_addr));
72+
runtime.block_on(discv5.start());
6973

7074
// run a find_node query
7175
runtime.block_on(async {

examples/custom_executor.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
//! $ cargo run --example custom_executor <BASE64ENR>
1010
//! ```
1111
12-
use discv5::{enr, enr::CombinedKey, Discv5, Discv5ConfigBuilder, Discv5Event};
13-
use std::net::SocketAddr;
12+
use discv5::{enr, enr::CombinedKey, Discv5, Discv5ConfigBuilder, Discv5Event, ListenConfig};
13+
use std::net::Ipv4Addr;
1414

1515
fn main() {
1616
// allows detailed logging with the RUST_LOG env variable
@@ -22,7 +22,10 @@ fn main() {
2222
.try_init();
2323

2424
// listening address and port
25-
let listen_addr = "0.0.0.0:9000".parse::<SocketAddr>().unwrap();
25+
let listen_config = ListenConfig::Ipv4 {
26+
ip: Ipv4Addr::UNSPECIFIED,
27+
port: 9000,
28+
};
2629

2730
let enr_key = CombinedKey::generate_secp256k1();
2831
// construct a local ENR
@@ -36,7 +39,7 @@ fn main() {
3639
.unwrap();
3740

3841
// default configuration - uses the current executor
39-
let config = Discv5ConfigBuilder::new().build();
42+
let config = Discv5ConfigBuilder::new(listen_config).build();
4043

4144
// construct the discv5 server
4245
let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap();
@@ -61,7 +64,7 @@ fn main() {
6164

6265
runtime.block_on(async {
6366
// start the discv5 service
64-
discv5.start(listen_addr).await.unwrap();
67+
discv5.start().await.unwrap();
6568
println!("Server started");
6669

6770
// get an event stream

examples/find_nodes.rs

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,17 @@ use clap::Parser;
1919
use discv5::{
2020
enr,
2121
enr::{k256, CombinedKey},
22-
Discv5, Discv5ConfigBuilder, Discv5Event,
22+
Discv5, Discv5ConfigBuilder, Discv5Event, ListenConfig,
2323
};
2424
use std::{
25-
net::{Ipv4Addr, Ipv6Addr, SocketAddr},
25+
net::{Ipv4Addr, Ipv6Addr},
2626
time::Duration,
2727
};
2828
use tracing::{info, warn};
2929

3030
#[derive(Parser)]
3131
struct FindNodesArgs {
32-
/// Type of socket to bind ['ds', 'ip4', 'ip6']. The dual stack option will enable mapped
33-
/// addresses over an IpV6 socket.
32+
/// Type of socket to bind ['ds', 'ip4', 'ip6'].
3433
#[clap(long, default_value_t = SocketKind::Ds)]
3534
socket_kind: SocketKind,
3635
/// IpV4 to advertise in the ENR. This is needed so that other IpV4 nodes can connect to us.
@@ -43,6 +42,10 @@ struct FindNodesArgs {
4342
/// randomly.
4443
#[clap(long)]
4544
port: Option<u16>,
45+
/// Port to bind for ipv6. If none is provided, a random one in the 9000 - 9999 range will be picked
46+
/// randomly.
47+
#[clap(long)]
48+
port6: Option<u16>,
4649
/// Use a default test key.
4750
#[clap(long)]
4851
use_test_key: bool,
@@ -67,6 +70,12 @@ async fn main() {
6770
let port = args
6871
.port
6972
.unwrap_or_else(|| (rand::random::<u16>() % 1000) + 9000);
73+
let port6 = args.port.unwrap_or_else(|| loop {
74+
let port6 = (rand::random::<u16>() % 1000) + 9000;
75+
if port6 != port {
76+
return port6;
77+
}
78+
});
7079

7180
let enr_key = if args.use_test_key {
7281
// A fixed key for testing
@@ -93,29 +102,28 @@ async fn main() {
93102
if let Some(ip6) = args.enr_ip6 {
94103
// if the given address is the UNSPECIFIED address we want to advertise localhost
95104
if ip6.is_unspecified() {
96-
builder.ip6(Ipv6Addr::LOCALHOST).udp6(port);
105+
builder.ip6(Ipv6Addr::LOCALHOST).udp6(port6);
97106
} else {
98-
builder.ip6(ip6).udp6(port);
107+
builder.ip6(ip6).udp6(port6);
99108
}
100109
}
101110
builder.build(&enr_key).unwrap()
102111
};
103112

113+
// the address to listen on.
114+
let listen_config = match args.socket_kind {
115+
SocketKind::Ip4 => ListenConfig::new_ipv4(Ipv4Addr::UNSPECIFIED, port),
116+
SocketKind::Ip6 => ListenConfig::new_ipv6(Ipv6Addr::UNSPECIFIED, port6),
117+
SocketKind::Ds => ListenConfig::default()
118+
.with_ipv4(Ipv4Addr::UNSPECIFIED, port)
119+
.with_ipv6(Ipv6Addr::UNSPECIFIED, port6),
120+
};
121+
104122
// default configuration with packet filtering
105-
// let config = Discv5ConfigBuilder::new().enable_packet_filter().build();
123+
// let config = Discv5ConfigBuilder::new(listen_config).enable_packet_filter().build();
106124

107125
// default configuration without packet filtering
108-
let config = Discv5ConfigBuilder::new()
109-
.ip_mode(match args.socket_kind {
110-
SocketKind::Ip4 => discv5::IpMode::Ip4,
111-
SocketKind::Ip6 => discv5::IpMode::Ip6 {
112-
enable_mapped_addresses: false,
113-
},
114-
SocketKind::Ds => discv5::IpMode::Ip6 {
115-
enable_mapped_addresses: true,
116-
},
117-
})
118-
.build();
126+
let config = Discv5ConfigBuilder::new(listen_config).build();
119127

120128
info!("Node Id: {}", enr.node_id());
121129
if args.enr_ip6.is_some() || args.enr_ip4.is_some() {
@@ -127,12 +135,6 @@ async fn main() {
127135
enr.udp4_socket()
128136
);
129137
}
130-
// the address to listen on.
131-
let bind_addr = match args.socket_kind {
132-
SocketKind::Ip4 => Ipv4Addr::UNSPECIFIED.into(),
133-
SocketKind::Ip6 | SocketKind::Ds => Ipv6Addr::UNSPECIFIED.into(),
134-
};
135-
let socket_addr = SocketAddr::new(bind_addr, port);
136138

137139
// construct the discv5 server
138140
let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap();
@@ -154,7 +156,7 @@ async fn main() {
154156
}
155157

156158
// start the discv5 service
157-
discv5.start(socket_addr).await.unwrap();
159+
discv5.start().await.unwrap();
158160
let mut event_stream = discv5.event_stream().await.unwrap();
159161
let check_evs = args.events;
160162

examples/request_enr.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
//!
1414
//! This requires the "libp2p" feature.
1515
#[cfg(feature = "libp2p")]
16-
use discv5::{enr, enr::CombinedKey, Discv5, Discv5Config};
16+
use discv5::Discv5ConfigBuilder;
1717
#[cfg(feature = "libp2p")]
18-
use std::net::SocketAddr;
18+
use discv5::ListenConfig;
19+
#[cfg(feature = "libp2p")]
20+
use discv5::{enr, enr::CombinedKey, Discv5};
21+
#[cfg(feature = "libp2p")]
22+
use std::net::Ipv4Addr;
1923

2024
#[cfg(not(feature = "libp2p"))]
2125
fn main() {}
@@ -31,15 +35,18 @@ async fn main() {
3135
.try_init();
3236

3337
// listening address and port
34-
let listen_addr = "0.0.0.0:9000".parse::<SocketAddr>().unwrap();
38+
let listen_config = ListenConfig::Ipv4 {
39+
ip: Ipv4Addr::UNSPECIFIED,
40+
port: 9000,
41+
};
3542

3643
// generate a new enr key
3744
let enr_key = CombinedKey::generate_secp256k1();
3845
// construct a local ENR
3946
let enr = enr::EnrBuilder::new("v4").build(&enr_key).unwrap();
4047

4148
// default discv5 configuration
42-
let config = Discv5Config::default();
49+
let config = Discv5ConfigBuilder::new(listen_config).build();
4350

4451
let multiaddr = std::env::args()
4552
.nth(1)
@@ -49,7 +56,7 @@ async fn main() {
4956
let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap();
5057

5158
// start the discv5 service
52-
discv5.start(listen_addr).await.unwrap();
59+
discv5.start().await.unwrap();
5360

5461
// search for the ENR
5562
match discv5.request_enr(multiaddr).await {

examples/simple_server.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
//! $ cargo run --example simple_server -- <ENR-IP> <ENR-PORT> <BASE64ENR>
1111
//! ```
1212
13-
use discv5::{enr, enr::CombinedKey, Discv5, Discv5Config, Discv5Event};
14-
use std::net::{Ipv4Addr, SocketAddr};
13+
use discv5::{enr, enr::CombinedKey, Discv5, Discv5ConfigBuilder, Discv5Event, ListenConfig};
14+
use std::net::Ipv4Addr;
1515

1616
#[tokio::main]
1717
async fn main() {
@@ -37,7 +37,10 @@ async fn main() {
3737
};
3838

3939
// listening address and port
40-
let listen_addr = "0.0.0.0:9000".parse::<SocketAddr>().unwrap();
40+
let listen_config = ListenConfig::Ipv4 {
41+
ip: Ipv4Addr::UNSPECIFIED,
42+
port: 9000,
43+
};
4144

4245
let enr_key = CombinedKey::generate_secp256k1();
4346

@@ -69,7 +72,7 @@ async fn main() {
6972
}
7073

7174
// default configuration
72-
let config = Discv5Config::default();
75+
let config = Discv5ConfigBuilder::new(listen_config).build();
7376

7477
// construct the discv5 server
7578
let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap();
@@ -93,7 +96,7 @@ async fn main() {
9396
}
9497

9598
// start the discv5 service
96-
discv5.start(listen_addr).await.unwrap();
99+
discv5.start().await.unwrap();
97100
println!("Server started");
98101

99102
// get an event stream

src/config.rs

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
ipmode::IpMode, kbucket::MAX_NODES_PER_BUCKET, Enr, Executor, PermitBanList, RateLimiter,
2+
kbucket::MAX_NODES_PER_BUCKET, socket::ListenConfig, Enr, Executor, PermitBanList, RateLimiter,
33
RateLimiterBuilder,
44
};
55
///! A set of configuration parameters to tune the discovery protocol.
@@ -64,10 +64,6 @@ pub struct Discv5Config {
6464
/// seconds.
6565
pub ping_interval: Duration,
6666

67-
/// Configures the type of socket to bind to. This also affects the selection of address to use
68-
/// to contact an ENR.
69-
pub ip_mode: IpMode,
70-
7167
/// Reports all discovered ENR's when traversing the DHT to the event stream. Default true.
7268
pub report_discovered_peers: bool,
7369

@@ -99,10 +95,18 @@ pub struct Discv5Config {
9995
/// A custom executor which can spawn the discv5 tasks. This must be a tokio runtime, with
10096
/// timing support. By default, the executor that created the discv5 struct will be used.
10197
pub executor: Option<Box<dyn Executor + Send + Sync>>,
98+
99+
/// Configuration for the sockets to listen on.
100+
pub listen_config: ListenConfig,
101+
}
102+
103+
#[derive(Debug)]
104+
pub struct Discv5ConfigBuilder {
105+
config: Discv5Config,
102106
}
103107

104-
impl Default for Discv5Config {
105-
fn default() -> Self {
108+
impl Discv5ConfigBuilder {
109+
pub fn new(listen_config: ListenConfig) -> Self {
106110
// This is only applicable if enable_packet_filter is set.
107111
let filter_rate_limiter = Some(
108112
RateLimiterBuilder::new()
@@ -113,7 +117,8 @@ impl Default for Discv5Config {
113117
.expect("The total rate limit has been specified"),
114118
);
115119

116-
Self {
120+
// set default values
121+
let config = Discv5Config {
117122
enable_packet_filter: false,
118123
request_timeout: Duration::from_secs(1),
119124
vote_duration: Duration::from_secs(30),
@@ -136,21 +141,11 @@ impl Default for Discv5Config {
136141
filter_max_bans_per_ip: Some(5),
137142
permit_ban_list: PermitBanList::default(),
138143
ban_duration: Some(Duration::from_secs(3600)), // 1 hour
139-
ip_mode: IpMode::default(),
140144
executor: None,
141-
}
142-
}
143-
}
144-
145-
#[derive(Debug, Default)]
146-
pub struct Discv5ConfigBuilder {
147-
config: Discv5Config,
148-
}
145+
listen_config,
146+
};
149147

150-
impl Discv5ConfigBuilder {
151-
// set default values
152-
pub fn new() -> Self {
153-
Discv5ConfigBuilder::default()
148+
Discv5ConfigBuilder { config }
154149
}
155150

156151
/// Whether to enable the incoming packet filter.
@@ -307,13 +302,6 @@ impl Discv5ConfigBuilder {
307302
self
308303
}
309304

310-
/// Configures the type of socket to bind to. This also affects the selection of address to use
311-
/// to contact an ENR.
312-
pub fn ip_mode(&mut self, ip_mode: IpMode) -> &mut Self {
313-
self.config.ip_mode = ip_mode;
314-
self
315-
}
316-
317305
pub fn build(&mut self) -> Discv5Config {
318306
// If an executor is not provided, assume a current tokio runtime is running.
319307
if self.config.executor.is_none() {
@@ -347,6 +335,7 @@ impl std::fmt::Debug for Discv5Config {
347335
.field("incoming_bucket_limit", &self.incoming_bucket_limit)
348336
.field("ping_interval", &self.ping_interval)
349337
.field("ban_duration", &self.ban_duration)
338+
.field("listen_config", &self.listen_config)
350339
.finish()
351340
}
352341
}

0 commit comments

Comments
 (0)