Fix bind errors (#5986)
* Add ability to bind to a common tcp/udp port * Extend port range for local-net sanity and fix validator executable
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use clap::{crate_version, App, Arg};
|
||||
use std::net::{SocketAddr, TcpListener};
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup();
|
||||
@@ -16,7 +17,9 @@ fn main() {
|
||||
let port = port
|
||||
.parse()
|
||||
.unwrap_or_else(|_| panic!("Unable to parse {}", port));
|
||||
let _runtime = solana_netutil::ip_echo_server(port);
|
||||
let bind_addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||||
let tcp_listener = TcpListener::bind(bind_addr).expect("unable to start tcp listener");
|
||||
let _runtime = solana_netutil::ip_echo_server(tcp_listener);
|
||||
loop {
|
||||
std::thread::park();
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ use std::time::Duration;
|
||||
use tokio;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
use tokio::reactor::Handle;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio_codec::{BytesCodec, Decoder};
|
||||
|
||||
@@ -32,11 +33,10 @@ impl IpEchoServerMessage {
|
||||
|
||||
/// Starts a simple TCP server on the given port that echos the IP address of any peer that
|
||||
/// connects. Used by |get_public_ip_addr|
|
||||
pub fn ip_echo_server(port: u16) -> IpEchoServer {
|
||||
let bind_addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||||
let tcp = TcpListener::bind(&bind_addr)
|
||||
.unwrap_or_else(|err| panic!("Unable to bind to {}: {}", bind_addr, err));
|
||||
info!("bound to {:?}", bind_addr);
|
||||
pub fn ip_echo_server(tcp: std::net::TcpListener) -> IpEchoServer {
|
||||
info!("bound to {:?}", tcp.local_addr());
|
||||
let tcp =
|
||||
TcpListener::from_std(tcp, &Handle::default()).expect("Failed to convert std::TcpListener");
|
||||
|
||||
let server = tcp
|
||||
.incoming()
|
||||
|
@@ -58,22 +58,9 @@ pub fn get_public_ip_addr(ip_echo_server_addr: &SocketAddr) -> Result<IpAddr, St
|
||||
// `ip_echo_server_addr`
|
||||
pub fn verify_reachable_ports(
|
||||
ip_echo_server_addr: &SocketAddr,
|
||||
tcp_ports: &[u16],
|
||||
tcp_listeners: Vec<(u16, TcpListener)>,
|
||||
udp_sockets: &[&UdpSocket],
|
||||
) {
|
||||
let tcp: Vec<(_, _)> = tcp_ports
|
||||
.iter()
|
||||
.map(|port| {
|
||||
(
|
||||
port,
|
||||
TcpListener::bind(&SocketAddr::from(([0, 0, 0, 0], *port))).unwrap_or_else(|err| {
|
||||
error!("Unable to bind to tcp/{}: {}", port, err);
|
||||
std::process::exit(1);
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let udp: Vec<(_, _)> = udp_sockets
|
||||
.iter()
|
||||
.map(|udp_socket| {
|
||||
@@ -88,9 +75,10 @@ pub fn verify_reachable_ports(
|
||||
|
||||
info!(
|
||||
"Checking that tcp ports {:?} and udp ports {:?} are reachable from {:?}",
|
||||
tcp_ports, udp_ports, ip_echo_server_addr
|
||||
tcp_listeners, udp_ports, ip_echo_server_addr
|
||||
);
|
||||
|
||||
let tcp_ports: Vec<_> = tcp_listeners.iter().map(|(port, _)| *port).collect();
|
||||
let _ = ip_echo_server_request(
|
||||
ip_echo_server_addr,
|
||||
IpEchoServerMessage::new(&tcp_ports, &udp_ports),
|
||||
@@ -98,9 +86,8 @@ pub fn verify_reachable_ports(
|
||||
.map_err(|err| warn!("ip_echo_server request failed: {}", err));
|
||||
|
||||
// Wait for a connection to open on each TCP port
|
||||
for (port, tcp_listener) in tcp {
|
||||
for (port, tcp_listener) in tcp_listeners {
|
||||
let (sender, receiver) = channel();
|
||||
let port = *port;
|
||||
std::thread::spawn(move || {
|
||||
debug!("Waiting for incoming connection on tcp/{}", port);
|
||||
let _ = tcp_listener.incoming().next().expect("tcp incoming failed");
|
||||
@@ -230,6 +217,30 @@ fn udp_socket(reuseaddr: bool) -> io::Result<Socket> {
|
||||
Ok(sock)
|
||||
}
|
||||
|
||||
// Find a port in the given range that is available for both TCP and UDP
|
||||
pub fn bind_common_in_range(range: PortRange) -> io::Result<(u16, (UdpSocket, TcpListener))> {
|
||||
let (start, end) = range;
|
||||
let mut tries_left = end - start;
|
||||
let mut rand_port = thread_rng().gen_range(start, end);
|
||||
loop {
|
||||
match bind_common(rand_port, false) {
|
||||
Ok((sock, listener)) => {
|
||||
break Result::Ok((sock.local_addr().unwrap().port(), (sock, listener)));
|
||||
}
|
||||
Err(err) => {
|
||||
if tries_left == 0 {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
rand_port += 1;
|
||||
if rand_port == end {
|
||||
rand_port = start;
|
||||
}
|
||||
tries_left -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind_in_range(range: PortRange) -> io::Result<(u16, UdpSocket)> {
|
||||
let sock = udp_socket(false)?;
|
||||
|
||||
@@ -292,6 +303,21 @@ pub fn bind_to(port: u16, reuseaddr: bool) -> io::Result<UdpSocket> {
|
||||
}
|
||||
}
|
||||
|
||||
// binds both a UdpSocket and a TcpListener
|
||||
pub fn bind_common(port: u16, reuseaddr: bool) -> io::Result<(UdpSocket, TcpListener)> {
|
||||
let sock = udp_socket(reuseaddr)?;
|
||||
|
||||
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port);
|
||||
let sock_addr = SockAddr::from(addr);
|
||||
match sock.bind(&sock_addr) {
|
||||
Ok(_) => match TcpListener::bind(&addr) {
|
||||
Ok(listener) => Result::Ok((sock.into_udp_socket(), listener)),
|
||||
Err(err) => Err(err),
|
||||
},
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_available_port_in_range(range: PortRange) -> io::Result<u16> {
|
||||
let (start, end) = range;
|
||||
let mut tries_left = end - start;
|
||||
@@ -386,4 +412,10 @@ mod tests {
|
||||
let port = find_available_port_in_range((3000, 3050)).unwrap();
|
||||
assert!(3000 <= port && port < 3050);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bind_common_in_range() {
|
||||
let (port, _) = bind_common_in_range((3000, 3050)).unwrap();
|
||||
assert!(3000 <= port && port < 3050);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user