Skip to content

Commit c61ea6a

Browse files
elenaf9dvc94chmxinden
authored
protocols/: Add basic AutoNAT implementation (#2262)
This commit adds a behaviour protocol that implements the AutoNAT specification. It enables users to detect whether they are behind a NAT. The Autonat Protocol implements a Codec for the Request-Response protocol, and wraps it in a new Network Behaviour with some additional functionality. Co-authored-by: David Craven <david@craven.ch> Co-authored-by: Max Inden <mail@max-inden.de>
1 parent ddc0351 commit c61ea6a

File tree

14 files changed

+2970
-0
lines changed

14 files changed

+2970
-0
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ categories = ["network-programming", "asynchronous"]
1212

1313
[features]
1414
default = [
15+
"autonat",
1516
"deflate",
1617
"dns-async-std",
1718
"floodsub",
@@ -34,6 +35,7 @@ default = [
3435
"websocket",
3536
"yamux",
3637
]
38+
autonat = ["libp2p-autonat"]
3739
deflate = ["libp2p-deflate"]
3840
dns-async-std = ["libp2p-dns", "libp2p-dns/async-std"]
3941
dns-tokio = ["libp2p-dns", "libp2p-dns/tokio"]
@@ -73,6 +75,8 @@ futures-timer = "3.0.2" # Explicit dependency to be used in `wasm-bindgen` featu
7375
getrandom = "0.2.3" # Explicit dependency to be used in `wasm-bindgen` feature
7476
instant = "0.1.11" # Explicit dependency to be used in `wasm-bindgen` feature
7577
lazy_static = "1.2"
78+
79+
libp2p-autonat = { version = "0.20.0", path = "protocols/autonat", optional = true }
7680
libp2p-core = { version = "0.31.0", path = "core", default-features = false }
7781
libp2p-floodsub = { version = "0.33.0", path = "protocols/floodsub", optional = true }
7882
libp2p-gossipsub = { version = "0.35.0", path = "./protocols/gossipsub", optional = true }
@@ -120,6 +124,7 @@ members = [
120124
"misc/peer-id-generator",
121125
"muxers/mplex",
122126
"muxers/yamux",
127+
"protocols/autonat",
123128
"protocols/floodsub",
124129
"protocols/gossipsub",
125130
"protocols/rendezvous",

protocols/autonat/Cargo.toml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[package]
2+
name = "libp2p-autonat"
3+
edition = "2021"
4+
rust-version = "1.56.1"
5+
version = "0.20.0"
6+
authors = ["David Craven <david@craven.ch>", "Elena Frank <elena.frank@protonmail.com>"]
7+
license = "MIT"
8+
repository = "https://github.com/libp2p/rust-libp2p"
9+
keywords = ["peer-to-peer", "libp2p", "networking"]
10+
categories = ["network-programming", "asynchronous"]
11+
12+
[build-dependencies]
13+
prost-build = "0.6"
14+
15+
[dependencies]
16+
async-trait = "0.1"
17+
futures = "0.3"
18+
futures-timer = "3.0"
19+
instant = "0.1"
20+
libp2p-core = { version = "0.31.0", path = "../../core", default-features = false }
21+
libp2p-swarm = { version = "0.33.0", path = "../../swarm" }
22+
libp2p-request-response = { version = "0.15.0", path = "../request-response" }
23+
log = "0.4"
24+
rand = "0.8"
25+
prost = "0.8"
26+
27+
[dev-dependencies]
28+
async-std = { version = "1.10", features = ["attributes"] }
29+
env_logger = "0.9"
30+
structopt = "0.3"
31+
32+
33+
[dev-dependencies.libp2p]
34+
path = "../../"
35+
default-features = false
36+
features = ["autonat", "dns-async-std", "identify", "mplex", "noise", "tcp-async-io", "websocket", "yamux"]

protocols/autonat/build.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2021 Protocol Labs.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the "Software"),
5+
// to deal in the Software without restriction, including without limitation
6+
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7+
// and/or sell copies of the Software, and to permit persons to whom the
8+
// Software is furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19+
// DEALINGS IN THE SOFTWARE.
20+
21+
fn main() {
22+
prost_build::compile_protos(&["src/structs.proto"], &["src"]).unwrap();
23+
}

protocols/autonat/examples/client.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright 2021 Protocol Labs.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the "Software"),
5+
// to deal in the Software without restriction, including without limitation
6+
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7+
// and/or sell copies of the Software, and to permit persons to whom the
8+
// Software is furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19+
// DEALINGS IN THE SOFTWARE.
20+
21+
//! Basic example that combines the AutoNAT and identify protocols.
22+
//!
23+
//! The identify protocol informs the local peer of its external addresses, that are then send in AutoNAT dial-back
24+
//! requests to the server.
25+
//!
26+
//! To run this example, follow the instructions in `examples/server` to start a server, then run in a new terminal:
27+
//! ```sh
28+
//! cargo run --example client -- --server-address <server-addr> --server-peer-id <server-peer-id> --listen_port <port>
29+
//! ```
30+
//! The `listen_port` parameter is optional and allows to set a fixed port at which the local client should listen.
31+
32+
use futures::prelude::*;
33+
use libp2p::autonat;
34+
use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent};
35+
use libp2p::multiaddr::Protocol;
36+
use libp2p::swarm::{Swarm, SwarmEvent};
37+
use libp2p::{identity, Multiaddr, NetworkBehaviour, PeerId};
38+
use std::error::Error;
39+
use std::net::Ipv4Addr;
40+
use std::time::Duration;
41+
use structopt::StructOpt;
42+
43+
#[derive(Debug, StructOpt)]
44+
#[structopt(name = "libp2p autonat")]
45+
struct Opt {
46+
#[structopt(long)]
47+
listen_port: Option<u16>,
48+
49+
#[structopt(long)]
50+
server_address: Multiaddr,
51+
52+
#[structopt(long)]
53+
server_peer_id: PeerId,
54+
}
55+
56+
#[async_std::main]
57+
async fn main() -> Result<(), Box<dyn Error>> {
58+
env_logger::init();
59+
60+
let opt = Opt::from_args();
61+
62+
let local_key = identity::Keypair::generate_ed25519();
63+
let local_peer_id = PeerId::from(local_key.public());
64+
println!("Local peer id: {:?}", local_peer_id);
65+
66+
let transport = libp2p::development_transport(local_key.clone()).await?;
67+
68+
let behaviour = Behaviour::new(local_key.public());
69+
70+
let mut swarm = Swarm::new(transport, behaviour, local_peer_id);
71+
swarm.listen_on(
72+
Multiaddr::empty()
73+
.with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED))
74+
.with(Protocol::Tcp(opt.listen_port.unwrap_or(0))),
75+
)?;
76+
77+
swarm
78+
.behaviour_mut()
79+
.auto_nat
80+
.add_server(opt.server_peer_id, Some(opt.server_address));
81+
82+
loop {
83+
match swarm.select_next_some().await {
84+
SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {:?}", address),
85+
SwarmEvent::Behaviour(event) => println!("{:?}", event),
86+
e => println!("{:?}", e),
87+
}
88+
}
89+
}
90+
91+
#[derive(NetworkBehaviour)]
92+
#[behaviour(out_event = "Event")]
93+
struct Behaviour {
94+
identify: Identify,
95+
auto_nat: autonat::Behaviour,
96+
}
97+
98+
impl Behaviour {
99+
fn new(local_public_key: identity::PublicKey) -> Self {
100+
Self {
101+
identify: Identify::new(IdentifyConfig::new(
102+
"/ipfs/0.1.0".into(),
103+
local_public_key.clone(),
104+
)),
105+
auto_nat: autonat::Behaviour::new(
106+
local_public_key.to_peer_id(),
107+
autonat::Config {
108+
retry_interval: Duration::from_secs(10),
109+
refresh_interval: Duration::from_secs(30),
110+
boot_delay: Duration::from_secs(5),
111+
throttle_server_period: Duration::ZERO,
112+
..Default::default()
113+
},
114+
),
115+
}
116+
}
117+
}
118+
119+
#[derive(Debug)]
120+
enum Event {
121+
AutoNat(autonat::Event),
122+
Identify(IdentifyEvent),
123+
}
124+
125+
impl From<IdentifyEvent> for Event {
126+
fn from(v: IdentifyEvent) -> Self {
127+
Self::Identify(v)
128+
}
129+
}
130+
131+
impl From<autonat::Event> for Event {
132+
fn from(v: autonat::Event) -> Self {
133+
Self::AutoNat(v)
134+
}
135+
}

protocols/autonat/examples/server.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2021 Protocol Labs.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the "Software"),
5+
// to deal in the Software without restriction, including without limitation
6+
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7+
// and/or sell copies of the Software, and to permit persons to whom the
8+
// Software is furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19+
// DEALINGS IN THE SOFTWARE.
20+
21+
//! Basic example for a AutoNAT server that supports the /libp2p/autonat/1.0.0 and "/ipfs/0.1.0" protocols.
22+
//!
23+
//! To start the server run:
24+
//! ```sh
25+
//! cargo run --example server -- --listen_port <port>
26+
//! ```
27+
//! The `listen_port` parameter is optional and allows to set a fixed port at which the local peer should listen.
28+
29+
use futures::prelude::*;
30+
use libp2p::autonat;
31+
use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent};
32+
use libp2p::multiaddr::Protocol;
33+
use libp2p::swarm::{Swarm, SwarmEvent};
34+
use libp2p::{identity, Multiaddr, NetworkBehaviour, PeerId};
35+
use std::error::Error;
36+
use std::net::Ipv4Addr;
37+
use structopt::StructOpt;
38+
39+
#[derive(Debug, StructOpt)]
40+
#[structopt(name = "libp2p autonat")]
41+
struct Opt {
42+
#[structopt(long)]
43+
listen_port: Option<u16>,
44+
}
45+
46+
#[async_std::main]
47+
async fn main() -> Result<(), Box<dyn Error>> {
48+
env_logger::init();
49+
50+
let opt = Opt::from_args();
51+
52+
let local_key = identity::Keypair::generate_ed25519();
53+
let local_peer_id = PeerId::from(local_key.public());
54+
println!("Local peer id: {:?}", local_peer_id);
55+
56+
let transport = libp2p::development_transport(local_key.clone()).await?;
57+
58+
let behaviour = Behaviour::new(local_key.public());
59+
60+
let mut swarm = Swarm::new(transport, behaviour, local_peer_id);
61+
swarm.listen_on(
62+
Multiaddr::empty()
63+
.with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED))
64+
.with(Protocol::Tcp(opt.listen_port.unwrap_or(0))),
65+
)?;
66+
67+
loop {
68+
match swarm.select_next_some().await {
69+
SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {:?}", address),
70+
SwarmEvent::Behaviour(event) => println!("{:?}", event),
71+
e => println!("{:?}", e),
72+
}
73+
}
74+
}
75+
76+
#[derive(NetworkBehaviour)]
77+
#[behaviour(out_event = "Event")]
78+
struct Behaviour {
79+
identify: Identify,
80+
auto_nat: autonat::Behaviour,
81+
}
82+
83+
impl Behaviour {
84+
fn new(local_public_key: identity::PublicKey) -> Self {
85+
Self {
86+
identify: Identify::new(IdentifyConfig::new(
87+
"/ipfs/0.1.0".into(),
88+
local_public_key.clone(),
89+
)),
90+
auto_nat: autonat::Behaviour::new(
91+
local_public_key.to_peer_id(),
92+
autonat::Config::default(),
93+
),
94+
}
95+
}
96+
}
97+
98+
#[derive(Debug)]
99+
enum Event {
100+
AutoNat(autonat::Event),
101+
Identify(IdentifyEvent),
102+
}
103+
104+
impl From<IdentifyEvent> for Event {
105+
fn from(v: IdentifyEvent) -> Self {
106+
Self::Identify(v)
107+
}
108+
}
109+
110+
impl From<autonat::Event> for Event {
111+
fn from(v: autonat::Event) -> Self {
112+
Self::AutoNat(v)
113+
}
114+
}

0 commit comments

Comments
 (0)