e2ae5010a6
Updates libp2p to the latest version. This adds tokio 0.3 support and brings back yamux support. This also updates some discv5 configuration parameters for leaner discovery queries
162 lines
6.7 KiB
Rust
162 lines
6.7 KiB
Rust
//! This houses various NAT hole punching strategies.
|
|
//!
|
|
//! Currently supported strategies:
|
|
//! - UPnP
|
|
|
|
use crate::{NetworkConfig, NetworkMessage};
|
|
use if_addrs::get_if_addrs;
|
|
use slog::{debug, info, warn};
|
|
use std::net::{IpAddr, SocketAddr, SocketAddrV4};
|
|
use tokio::sync::mpsc;
|
|
use types::EthSpec;
|
|
|
|
/// Configuration required to construct the UPnP port mappings.
|
|
pub struct UPnPConfig {
|
|
/// The local tcp port.
|
|
tcp_port: u16,
|
|
/// The local udp port.
|
|
udp_port: u16,
|
|
/// Whether discovery is enabled or not.
|
|
disable_discovery: bool,
|
|
}
|
|
|
|
impl From<&NetworkConfig> for UPnPConfig {
|
|
fn from(config: &NetworkConfig) -> Self {
|
|
UPnPConfig {
|
|
tcp_port: config.libp2p_port,
|
|
udp_port: config.discovery_port,
|
|
disable_discovery: config.disable_discovery,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Attempts to construct external port mappings with UPnP.
|
|
pub fn construct_upnp_mappings<T: EthSpec>(
|
|
config: UPnPConfig,
|
|
network_send: mpsc::UnboundedSender<NetworkMessage<T>>,
|
|
log: slog::Logger,
|
|
) {
|
|
info!(log, "UPnP Attempting to initialise routes");
|
|
match igd::search_gateway(Default::default()) {
|
|
Err(e) => info!(log, "UPnP not available"; "error" => %e),
|
|
Ok(gateway) => {
|
|
// Need to find the local listening address matched with the router subnet
|
|
let interfaces = match get_if_addrs() {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
info!(log, "UPnP failed to get local interfaces"; "error" => %e);
|
|
return;
|
|
}
|
|
};
|
|
let local_ip = interfaces.iter().find_map(|interface| {
|
|
// Just use the first IP of the first interface that is not a loopback and not an
|
|
// ipv6 address.
|
|
if !interface.is_loopback() {
|
|
if let IpAddr::V4(_) = interface.ip() {
|
|
Some(interface.ip())
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
});
|
|
|
|
let local_ip = match local_ip {
|
|
None => {
|
|
info!(log, "UPnP failed to find local IP address");
|
|
return;
|
|
}
|
|
Some(v) => v,
|
|
};
|
|
|
|
match local_ip {
|
|
IpAddr::V4(address) => {
|
|
let libp2p_socket = SocketAddrV4::new(address, config.tcp_port);
|
|
let external_ip = gateway.get_external_ip();
|
|
// We add specific port mappings rather than getting the router to arbitrary assign
|
|
// one.
|
|
// I've found this to be more reliable. If multiple users are behind a single
|
|
// router, they should ideally try to set different port numbers.
|
|
let tcp_socket = match gateway.add_port(
|
|
igd::PortMappingProtocol::TCP,
|
|
libp2p_socket.port(),
|
|
libp2p_socket,
|
|
0,
|
|
"lighthouse-tcp",
|
|
) {
|
|
Err(e) => {
|
|
info!(log, "UPnP TCP route not set"; "error" => %e);
|
|
None
|
|
}
|
|
Ok(_) => {
|
|
info!(log, "UPnP TCP route established"; "external_socket" => format!("{}:{}", external_ip.as_ref().map(|ip| ip.to_string()).unwrap_or_else(|_| "".into()), config.tcp_port));
|
|
external_ip
|
|
.as_ref()
|
|
.map(|ip| SocketAddr::new(ip.clone().into(), config.tcp_port))
|
|
.ok()
|
|
}
|
|
};
|
|
|
|
let udp_socket = if !config.disable_discovery {
|
|
let discovery_socket = SocketAddrV4::new(address, config.udp_port);
|
|
match gateway.add_port(
|
|
igd::PortMappingProtocol::UDP,
|
|
discovery_socket.port(),
|
|
discovery_socket,
|
|
0,
|
|
"lighthouse-udp",
|
|
) {
|
|
Err(e) => {
|
|
info!(log, "UPnP UDP route not set"; "error" => %e);
|
|
None
|
|
}
|
|
Ok(_) => {
|
|
info!(log, "UPnP UDP route established"; "external_socket" => format!("{}:{}", external_ip.as_ref().map(|ip| ip.to_string()).unwrap_or_else(|_| "".into()), config.tcp_port));
|
|
external_ip
|
|
.map(|ip| SocketAddr::new(ip.into(), config.tcp_port))
|
|
.ok()
|
|
}
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// report any updates to the network service.
|
|
network_send.send(NetworkMessage::UPnPMappingEstablished{ tcp_socket, udp_socket })
|
|
.unwrap_or_else(|e| warn!(log, "Could not send message to the network service"; "error" => %e));
|
|
}
|
|
_ => debug!(log, "UPnP no routes constructed. IPv6 not supported"),
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Removes the specified TCP and UDP port mappings.
|
|
pub fn remove_mappings(tcp_port: Option<u16>, udp_port: Option<u16>, log: &slog::Logger) {
|
|
if tcp_port.is_some() || udp_port.is_some() {
|
|
debug!(log, "Removing UPnP port mappings");
|
|
match igd::search_gateway(Default::default()) {
|
|
Ok(gateway) => {
|
|
if let Some(tcp_port) = tcp_port {
|
|
match gateway.remove_port(igd::PortMappingProtocol::TCP, tcp_port) {
|
|
Ok(()) => debug!(log, "UPnP Removed TCP port mapping"; "port" => tcp_port),
|
|
Err(e) => {
|
|
debug!(log, "UPnP Failed to remove TCP port mapping"; "port" => tcp_port, "error" => %e)
|
|
}
|
|
}
|
|
}
|
|
if let Some(udp_port) = udp_port {
|
|
match gateway.remove_port(igd::PortMappingProtocol::UDP, udp_port) {
|
|
Ok(()) => debug!(log, "UPnP Removed UDP port mapping"; "port" => udp_port),
|
|
Err(e) => {
|
|
debug!(log, "UPnP Failed to remove UDP port mapping"; "port" => udp_port, "error" => %e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Err(e) => debug!(log, "UPnP failed to remove mappings"; "error" => %e),
|
|
}
|
|
}
|
|
}
|