diff --git a/archiver-lib/src/archiver.rs b/archiver-lib/src/archiver.rs index f133c5b3a263d9..4e6ac846cb8898 100644 --- a/archiver-lib/src/archiver.rs +++ b/archiver-lib/src/archiver.rs @@ -47,7 +47,7 @@ use solana_storage_program::{ }; use std::{ io::{self, ErrorKind}, - net::{SocketAddr, UdpSocket}, + net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}, path::{Path, PathBuf}, result, sync::atomic::{AtomicBool, Ordering}, @@ -804,14 +804,15 @@ impl Archiver { blockstore: &Arc, slots_per_segment: u64, ) -> Result { + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); // Create a client which downloads from the archiver and see that it // can respond with shreds. - let start_slot = Self::get_archiver_segment_slot(archiver_info.storage_addr); + let start_slot = Self::get_archiver_segment_slot(ip_addr, archiver_info.storage_addr); info!("Archiver download: start at {}", start_slot); let exit = Arc::new(AtomicBool::new(false)); let (s_reader, r_reader) = channel(); - let repair_socket = Arc::new(bind_in_range(VALIDATOR_PORT_RANGE).unwrap().1); + let repair_socket = Arc::new(bind_in_range(ip_addr, VALIDATOR_PORT_RANGE).unwrap().1); let t_receiver = receiver( repair_socket.clone(), &exit, @@ -908,8 +909,8 @@ impl Archiver { true } - fn get_archiver_segment_slot(to: SocketAddr) -> u64 { - let (_port, socket) = bind_in_range(VALIDATOR_PORT_RANGE).unwrap(); + fn get_archiver_segment_slot(bind_ip_addr: IpAddr, to: SocketAddr) -> u64 { + let (_port, socket) = bind_in_range(bind_ip_addr, VALIDATOR_PORT_RANGE).unwrap(); socket .set_read_timeout(Some(Duration::from_secs(5))) .unwrap(); diff --git a/archiver/src/main.rs b/archiver/src/main.rs index 2b8178aad79f34..df7c610600e310 100644 --- a/archiver/src/main.rs +++ b/archiver/src/main.rs @@ -13,7 +13,12 @@ use solana_core::{ contact_info::ContactInfo, }; use solana_sdk::{commitment_config::CommitmentConfig, signature::Signer}; -use std::{net::SocketAddr, path::PathBuf, process::exit, sync::Arc}; +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + path::PathBuf, + process::exit, + sync::Arc, +}; fn main() { solana_logger::setup(); @@ -116,6 +121,7 @@ fn main() { &identity_keypair.pubkey(), &gossip_addr, VALIDATOR_PORT_RANGE, + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), ); println!( diff --git a/bench-streamer/src/main.rs b/bench-streamer/src/main.rs index 7b0a80cc7b902c..60eca3242580eb 100644 --- a/bench-streamer/src/main.rs +++ b/bench-streamer/src/main.rs @@ -67,7 +67,8 @@ fn main() -> Result<()> { } let mut port = 0; - let mut addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + let mut addr = SocketAddr::new(ip_addr, 0); let exit = Arc::new(AtomicBool::new(false)); @@ -75,7 +76,7 @@ fn main() -> Result<()> { let mut read_threads = Vec::new(); let recycler = PacketsRecycler::default(); for _ in 0..num_sockets { - let read = solana_net_utils::bind_to(port, false).unwrap(); + let read = solana_net_utils::bind_to(ip_addr, port, false).unwrap(); read.set_read_timeout(Some(Duration::new(1, 0))).unwrap(); addr = read.local_addr().unwrap(); diff --git a/client/src/thin_client.rs b/client/src/thin_client.rs index 94195505f3e17e..ce2e52424d4294 100644 --- a/client/src/thin_client.rs +++ b/client/src/thin_client.rs @@ -26,7 +26,7 @@ use solana_sdk::{ }; use std::{ io, - net::{SocketAddr, UdpSocket}, + net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, RwLock, @@ -603,7 +603,8 @@ impl AsyncClient for ThinClient { } pub fn create_client((rpc, tpu): (SocketAddr, SocketAddr), range: (u16, u16)) -> ThinClient { - let (_, transactions_socket) = solana_net_utils::bind_in_range(range).unwrap(); + let (_, transactions_socket) = + solana_net_utils::bind_in_range(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), range).unwrap(); ThinClient::new(rpc, tpu, transactions_socket) } @@ -612,7 +613,8 @@ pub fn create_client_with_timeout( range: (u16, u16), timeout: Duration, ) -> ThinClient { - let (_, transactions_socket) = solana_net_utils::bind_in_range(range).unwrap(); + let (_, transactions_socket) = + solana_net_utils::bind_in_range(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), range).unwrap(); ThinClient::new_socket_with_timeout(rpc, tpu, transactions_socket, timeout) } diff --git a/core/src/cluster_info.rs b/core/src/cluster_info.rs index ba4a598ecad039..79fe76e21a62a5 100644 --- a/core/src/cluster_info.rs +++ b/core/src/cluster_info.rs @@ -1635,8 +1635,9 @@ impl ClusterInfo { id: &Pubkey, gossip_addr: &SocketAddr, ) -> (ContactInfo, UdpSocket, Option) { + let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); let (port, (gossip_socket, ip_echo)) = - Node::get_gossip_port(gossip_addr, VALIDATOR_PORT_RANGE); + Node::get_gossip_port(gossip_addr, VALIDATOR_PORT_RANGE, bind_ip_addr); let contact_info = Self::gossip_contact_info(id, SocketAddr::new(gossip_addr.ip(), port)); (contact_info, gossip_socket, Some(ip_echo)) @@ -1644,7 +1645,8 @@ impl ClusterInfo { /// A Node with dummy ports to spy on gossip via pull requests pub fn spy_node(id: &Pubkey) -> (ContactInfo, UdpSocket, Option) { - let (_, gossip_socket) = bind_in_range(VALIDATOR_PORT_RANGE).unwrap(); + let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + let (_, gossip_socket) = bind_in_range(bind_ip_addr, VALIDATOR_PORT_RANGE).unwrap(); let contact_info = Self::spy_contact_info(id); (contact_info, gossip_socket, None) @@ -1759,16 +1761,18 @@ impl Node { } } pub fn new_localhost_with_pubkey(pubkey: &Pubkey) -> Self { + let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); let tpu = UdpSocket::bind("127.0.0.1:0").unwrap(); - let (gossip_port, (gossip, ip_echo)) = bind_common_in_range((1024, 65535)).unwrap(); + let (gossip_port, (gossip, ip_echo)) = + bind_common_in_range(bind_ip_addr, (1024, 65535)).unwrap(); let gossip_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), gossip_port); let tvu = UdpSocket::bind("127.0.0.1:0").unwrap(); let tvu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap(); let tpu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap(); let repair = UdpSocket::bind("127.0.0.1:0").unwrap(); - let rpc_port = find_available_port_in_range((1024, 65535)).unwrap(); + let rpc_port = find_available_port_in_range(bind_ip_addr, (1024, 65535)).unwrap(); let rpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_port); - let rpc_pubsub_port = find_available_port_in_range((1024, 65535)).unwrap(); + let rpc_pubsub_port = find_available_port_in_range(bind_ip_addr, (1024, 65535)).unwrap(); let rpc_pubsub_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_pubsub_port); @@ -1811,45 +1815,52 @@ impl Node { fn get_gossip_port( gossip_addr: &SocketAddr, port_range: PortRange, + bind_ip_addr: IpAddr, ) -> (u16, (UdpSocket, TcpListener)) { if gossip_addr.port() != 0 { ( gossip_addr.port(), - bind_common(gossip_addr.port(), false).unwrap_or_else(|e| { + bind_common(bind_ip_addr, gossip_addr.port(), false).unwrap_or_else(|e| { panic!("gossip_addr bind_to port {}: {}", gossip_addr.port(), e) }), ) } else { - bind_common_in_range(port_range).expect("Failed to bind") + bind_common_in_range(bind_ip_addr, port_range).expect("Failed to bind") } } - fn bind(port_range: PortRange) -> (u16, UdpSocket) { - bind_in_range(port_range).expect("Failed to bind") + fn bind(bind_ip_addr: IpAddr, port_range: PortRange) -> (u16, UdpSocket) { + bind_in_range(bind_ip_addr, port_range).expect("Failed to bind") } + pub fn new_with_external_ip( pubkey: &Pubkey, gossip_addr: &SocketAddr, port_range: PortRange, + bind_ip_addr: IpAddr, ) -> Node { - let (gossip_port, (gossip, ip_echo)) = Self::get_gossip_port(gossip_addr, port_range); + let (gossip_port, (gossip, ip_echo)) = + Self::get_gossip_port(gossip_addr, port_range, bind_ip_addr); - let (tvu_port, tvu_sockets) = multi_bind_in_range(port_range, 8).expect("tvu multi_bind"); + let (tvu_port, tvu_sockets) = + multi_bind_in_range(bind_ip_addr, port_range, 8).expect("tvu multi_bind"); let (tvu_forwards_port, tvu_forwards_sockets) = - multi_bind_in_range(port_range, 8).expect("tvu_forwards multi_bind"); + multi_bind_in_range(bind_ip_addr, port_range, 8).expect("tvu_forwards multi_bind"); - let (tpu_port, tpu_sockets) = multi_bind_in_range(port_range, 32).expect("tpu multi_bind"); + let (tpu_port, tpu_sockets) = + multi_bind_in_range(bind_ip_addr, port_range, 32).expect("tpu multi_bind"); let (tpu_forwards_port, tpu_forwards_sockets) = - multi_bind_in_range(port_range, 8).expect("tpu_forwards multi_bind"); + multi_bind_in_range(bind_ip_addr, port_range, 8).expect("tpu_forwards multi_bind"); let (_, retransmit_sockets) = - multi_bind_in_range(port_range, 8).expect("retransmit multi_bind"); + multi_bind_in_range(bind_ip_addr, port_range, 8).expect("retransmit multi_bind"); - let (repair_port, repair) = Self::bind(port_range); - let (serve_repair_port, serve_repair) = Self::bind(port_range); + let (repair_port, repair) = Self::bind(bind_ip_addr, port_range); + let (serve_repair_port, serve_repair) = Self::bind(bind_ip_addr, port_range); - let (_, broadcast) = multi_bind_in_range(port_range, 4).expect("broadcast multi_bind"); + let (_, broadcast) = + multi_bind_in_range(bind_ip_addr, port_range, 4).expect("broadcast multi_bind"); let info = ContactInfo { id: *pubkey, @@ -1889,9 +1900,10 @@ impl Node { pubkey: &Pubkey, gossip_addr: &SocketAddr, port_range: PortRange, + bind_ip_addr: IpAddr, ) -> Node { - let mut new = Self::new_with_external_ip(pubkey, gossip_addr, port_range); - let (storage_port, storage_socket) = Self::bind(port_range); + let mut new = Self::new_with_external_ip(pubkey, gossip_addr, port_range, bind_ip_addr); + let (storage_port, storage_socket) = Self::bind(bind_ip_addr, port_range); new.info.storage_addr = SocketAddr::new(gossip_addr.ip(), storage_port); new.sockets.storage = Some(storage_socket); @@ -2025,6 +2037,7 @@ mod tests { &Pubkey::new_rand(), &socketaddr!(ip, 0), VALIDATOR_PORT_RANGE, + IpAddr::V4(ip), ); check_node_sockets(&node, IpAddr::V4(ip), VALIDATOR_PORT_RANGE); @@ -2034,7 +2047,7 @@ mod tests { fn new_with_external_ip_test_gossip() { let ip = IpAddr::V4(Ipv4Addr::from(0)); let port = { - bind_in_range(VALIDATOR_PORT_RANGE) + bind_in_range(ip, VALIDATOR_PORT_RANGE) .expect("Failed to bind") .0 }; @@ -2042,6 +2055,7 @@ mod tests { &Pubkey::new_rand(), &socketaddr!(0, port), VALIDATOR_PORT_RANGE, + ip, ); check_node_sockets(&node, ip, VALIDATOR_PORT_RANGE); @@ -2056,6 +2070,7 @@ mod tests { &Pubkey::new_rand(), &socketaddr!(ip, 0), VALIDATOR_PORT_RANGE, + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), ); let ip = IpAddr::V4(ip); diff --git a/core/src/gossip_service.rs b/core/src/gossip_service.rs index 5470dd742e9fdb..d6a087b2933a77 100644 --- a/core/src/gossip_service.rs +++ b/core/src/gossip_service.rs @@ -9,7 +9,7 @@ use solana_ledger::bank_forks::BankForks; use solana_perf::recycler::Recycler; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, Signer}; -use std::net::{SocketAddr, TcpListener, UdpSocket}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, UdpSocket}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::channel; use std::sync::{Arc, RwLock}; @@ -163,7 +163,11 @@ pub fn get_multi_client(nodes: &[ContactInfo]) -> (ThinClient, usize) { .collect(); let rpc_addrs: Vec<_> = addrs.iter().map(|addr| addr.0).collect(); let tpu_addrs: Vec<_> = addrs.iter().map(|addr| addr.1).collect(); - let (_, transactions_socket) = solana_net_utils::bind_in_range(VALIDATOR_PORT_RANGE).unwrap(); + let (_, transactions_socket) = solana_net_utils::bind_in_range( + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + VALIDATOR_PORT_RANGE, + ) + .unwrap(); let num_nodes = tpu_addrs.len(); ( ThinClient::new_from_addrs(rpc_addrs, tpu_addrs, transactions_socket), diff --git a/core/src/local_vote_signer_service.rs b/core/src/local_vote_signer_service.rs index b6276017527ae8..4a6dcfeaefb3db 100644 --- a/core/src/local_vote_signer_service.rs +++ b/core/src/local_vote_signer_service.rs @@ -15,9 +15,12 @@ pub struct LocalVoteSignerService { impl LocalVoteSignerService { #[allow(clippy::new_ret_no_self)] pub fn new(port_range: PortRange) -> (Self, SocketAddr) { - let addr = solana_net_utils::find_available_port_in_range(port_range) - .map(|port| SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port)) - .expect("Failed to find an available port for local vote signer service"); + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + let addr = SocketAddr::new( + ip_addr, + solana_net_utils::find_available_port_in_range(ip_addr, port_range) + .expect("Failed to find an available port for local vote signer service"), + ); let exit = Arc::new(AtomicBool::new(false)); let thread_exit = exit.clone(); let thread = Builder::new() diff --git a/core/src/retransmit_stage.rs b/core/src/retransmit_stage.rs index 1b66b48cb4f59e..5c79f889fbcbb5 100644 --- a/core/src/retransmit_stage.rs +++ b/core/src/retransmit_stage.rs @@ -284,6 +284,7 @@ mod tests { use solana_ledger::create_new_tmp_ledger; use solana_net_utils::find_available_port_in_range; use solana_sdk::pubkey::Pubkey; + use std::net::{IpAddr, Ipv4Addr}; #[test] fn test_skip_repair() { @@ -300,11 +301,12 @@ mod tests { let bank_forks = Arc::new(RwLock::new(bank_forks)); let mut me = ContactInfo::new_localhost(&Pubkey::new_rand(), 0); - let port = find_available_port_in_range((8000, 10000)).unwrap(); + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + let port = find_available_port_in_range(ip_addr, (8000, 10000)).unwrap(); let me_retransmit = UdpSocket::bind(format!("127.0.0.1:{}", port)).unwrap(); // need to make sure tvu and tpu are valid addresses me.tvu_forwards = me_retransmit.local_addr().unwrap(); - let port = find_available_port_in_range((8000, 10000)).unwrap(); + let port = find_available_port_in_range(ip_addr, (8000, 10000)).unwrap(); me.tvu = UdpSocket::bind(format!("127.0.0.1:{}", port)) .unwrap() .local_addr() diff --git a/core/src/rpc_service.rs b/core/src/rpc_service.rs index d728b6a4bc4d2e..9252bac6829c40 100644 --- a/core/src/rpc_service.rs +++ b/core/src/rpc_service.rs @@ -262,9 +262,10 @@ mod tests { let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair( ContactInfo::default(), ))); + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); let rpc_addr = SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), - solana_net_utils::find_available_port_in_range((10000, 65535)).unwrap(), + ip_addr, + solana_net_utils::find_available_port_in_range(ip_addr, (10000, 65535)).unwrap(), ); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank.slot(), bank))); let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default())); diff --git a/gossip/src/main.rs b/gossip/src/main.rs index 49bcc081ff87aa..e390eb7b04f3e0 100644 --- a/gossip/src/main.rs +++ b/gossip/src/main.rs @@ -197,8 +197,11 @@ fn main() -> Result<(), Box> { let gossip_addr = SocketAddr::new( gossip_host, value_t!(matches, "gossip_port", u16).unwrap_or_else(|_| { - solana_net_utils::find_available_port_in_range((0, 1)) - .expect("unable to find an available gossip port") + solana_net_utils::find_available_port_in_range( + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + (0, 1), + ) + .expect("unable to find an available gossip port") }), ); diff --git a/net-utils/src/lib.rs b/net-utils/src/lib.rs index 8ffccff0a91ebb..faeaa251bce54b 100644 --- a/net-utils/src/lib.rs +++ b/net-utils/src/lib.rs @@ -3,7 +3,7 @@ use log::*; use rand::{thread_rng, Rng}; use socket2::{Domain, SockAddr, Socket, Type}; use std::io::{self, Read, Write}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket}; +use std::net::{IpAddr, SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket}; use std::sync::mpsc::channel; use std::time::Duration; @@ -257,12 +257,15 @@ fn udp_socket(reuseaddr: bool) -> io::Result { } // 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))> { +pub fn bind_common_in_range( + ip_addr: IpAddr, + 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) { + match bind_common(ip_addr, rand_port, false) { Ok((sock, listener)) => { break Result::Ok((sock.local_addr().unwrap().port(), (sock, listener))); } @@ -280,14 +283,14 @@ pub fn bind_common_in_range(range: PortRange) -> io::Result<(u16, (UdpSocket, Tc } } -pub fn bind_in_range(range: PortRange) -> io::Result<(u16, UdpSocket)> { +pub fn bind_in_range(ip_addr: IpAddr, range: PortRange) -> io::Result<(u16, UdpSocket)> { let sock = udp_socket(false)?; let (start, end) = range; let mut tries_left = end - start; let mut rand_port = thread_rng().gen_range(start, end); loop { - let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), rand_port); + let addr = SocketAddr::new(ip_addr, rand_port); match sock.bind(&SockAddr::from(addr)) { Ok(_) => { @@ -309,7 +312,11 @@ pub fn bind_in_range(range: PortRange) -> io::Result<(u16, UdpSocket)> { } // binds many sockets to the same port in a range -pub fn multi_bind_in_range(range: PortRange, mut num: usize) -> io::Result<(u16, Vec)> { +pub fn multi_bind_in_range( + ip_addr: IpAddr, + range: PortRange, + mut num: usize, +) -> io::Result<(u16, Vec)> { if cfg!(windows) && num != 1 { // See https://github.com/solana-labs/solana/issues/4607 warn!( @@ -321,42 +328,46 @@ pub fn multi_bind_in_range(range: PortRange, mut num: usize) -> io::Result<(u16, let mut sockets = Vec::with_capacity(num); let port = { - let (port, _) = bind_in_range(range)?; + let (port, _) = bind_in_range(ip_addr, range)?; port }; // drop the probe, port should be available... briefly. for _ in 0..num { - sockets.push(bind_to(port, true)?); + sockets.push(bind_to(ip_addr, port, true)?); } Ok((port, sockets)) } -pub fn bind_to(port: u16, reuseaddr: bool) -> io::Result { +pub fn bind_to(ip_addr: IpAddr, port: u16, reuseaddr: bool) -> io::Result { let sock = udp_socket(reuseaddr)?; - let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port); + let addr = SocketAddr::new(ip_addr, port); sock.bind(&SockAddr::from(addr)) .and_then(|_| Result::Ok(sock.into_udp_socket())) } // binds both a UdpSocket and a TcpListener -pub fn bind_common(port: u16, reuseaddr: bool) -> io::Result<(UdpSocket, TcpListener)> { +pub fn bind_common( + ip_addr: IpAddr, + 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 addr = SocketAddr::new(ip_addr, port); let sock_addr = SockAddr::from(addr); sock.bind(&sock_addr).and_then(|_| { TcpListener::bind(&addr).and_then(|listener| Result::Ok((sock.into_udp_socket(), listener))) }) } -pub fn find_available_port_in_range(range: PortRange) -> io::Result { +pub fn find_available_port_in_range(ip_addr: IpAddr, range: PortRange) -> io::Result { 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) { + match bind_common(ip_addr, rand_port, false) { Ok(_) => { break Ok(rand_port); } @@ -377,6 +388,7 @@ pub fn find_available_port_in_range(range: PortRange) -> io::Result { #[cfg(test)] mod tests { use super::*; + use std::net::Ipv4Addr; #[test] fn test_parse_port_or_addr() { @@ -423,17 +435,19 @@ mod tests { #[test] fn test_bind() { - assert_eq!(bind_in_range((2000, 2001)).unwrap().0, 2000); - let x = bind_to(2002, true).unwrap(); - let y = bind_to(2002, true).unwrap(); + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + assert_eq!(bind_in_range(ip_addr, (2000, 2001)).unwrap().0, 2000); + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + let x = bind_to(ip_addr, 2002, true).unwrap(); + let y = bind_to(ip_addr, 2002, true).unwrap(); assert_eq!( x.local_addr().unwrap().port(), y.local_addr().unwrap().port() ); - bind_to(2002, false).unwrap_err(); - bind_in_range((2002, 2003)).unwrap_err(); + bind_to(ip_addr, 2002, false).unwrap_err(); + bind_in_range(ip_addr, (2002, 2003)).unwrap_err(); - let (port, v) = multi_bind_in_range((2010, 2110), 10).unwrap(); + let (port, v) = multi_bind_in_range(ip_addr, (2010, 2110), 10).unwrap(); for sock in &v { assert_eq!(port, sock.local_addr().unwrap().port()); } @@ -442,34 +456,41 @@ mod tests { #[test] #[should_panic] fn test_bind_in_range_nil() { - let _ = bind_in_range((2000, 2000)); + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + let _ = bind_in_range(ip_addr, (2000, 2000)); } #[test] fn test_find_available_port_in_range() { - assert_eq!(find_available_port_in_range((3000, 3001)).unwrap(), 3000); - let port = find_available_port_in_range((3000, 3050)).unwrap(); + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + assert_eq!( + find_available_port_in_range(ip_addr, (3000, 3001)).unwrap(), + 3000 + ); + let port = find_available_port_in_range(ip_addr, (3000, 3050)).unwrap(); assert!(3000 <= port && port < 3050); - let _socket = bind_to(port, false).unwrap(); - find_available_port_in_range((port, port + 1)).unwrap_err(); + let _socket = bind_to(ip_addr, port, false).unwrap(); + find_available_port_in_range(ip_addr, (port, port + 1)).unwrap_err(); } #[test] fn test_bind_common_in_range() { - let (port, _sockets) = bind_common_in_range((3100, 3150)).unwrap(); + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + let (port, _sockets) = bind_common_in_range(ip_addr, (3100, 3150)).unwrap(); assert!(3100 <= port && port < 3150); - bind_common_in_range((port, port + 1)).unwrap_err(); + bind_common_in_range(ip_addr, (port, port + 1)).unwrap_err(); } #[test] fn test_get_public_ip_addr() { solana_logger::setup(); + let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); let (_server_port, (server_udp_socket, server_tcp_listener)) = - bind_common_in_range((3200, 3250)).unwrap(); + bind_common_in_range(ip_addr, (3200, 3250)).unwrap(); let (client_port, (client_udp_socket, client_tcp_listener)) = - bind_common_in_range((3200, 3250)).unwrap(); + bind_common_in_range(ip_addr, (3200, 3250)).unwrap(); let _runtime = ip_echo_server(server_tcp_listener); diff --git a/validator/src/main.rs b/validator/src/main.rs index 428f31923779d0..92bd4bbef4df96 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -732,7 +732,7 @@ pub fn main() { .takes_value(true) .conflicts_with("entrypoint") .validator(solana_net_utils::is_host) - .help("Gossip DNS name or IP address for the node when --entrypoint is not provided [default: 127.0.0.1]"), + .help("IP address for the node to advertise in gossip when --entrypoint is not provided [default: 127.0.0.1]"), ) .arg( clap::Arg::with_name("dynamic_port_range") @@ -829,6 +829,23 @@ pub fn main() { .requires("trusted_validators") .help("Use the RPC service of trusted validators only") ) + .arg( + clap::Arg::with_name("bind_address") + .long("bind-address") + .value_name("HOST") + .takes_value(true) + .validator(solana_net_utils::is_host) + .default_value("0.0.0.0") + .help("IP address to bind the validator ports"), + ) + .arg( + clap::Arg::with_name("rpc_bind_address") + .long("rpc-bind-address") + .value_name("HOST") + .takes_value(true) + .validator(solana_net_utils::is_host) + .help("IP address to bind the RPC port [default: use --bind-address]"), + ) .get_matches(); let identity_keypair = Arc::new( @@ -903,6 +920,7 @@ pub fn main() { rpc_config: JsonRpcConfig { enable_validator_exit: matches.is_present("enable_rpc_exit"), enable_get_confirmed_block: matches.is_present("enable_rpc_get_confirmed_block"), + identity_pubkey: identity_keypair.pubkey(), faucet_addr: matches.value_of("rpc_faucet_addr").map(|address| { solana_net_utils::parse_host_port(address).expect("failed to parse faucet address") }), @@ -920,6 +938,15 @@ pub fn main() { solana_net_utils::parse_port_range(matches.value_of("dynamic_port_range").unwrap()) .expect("invalid dynamic_port_range"); + let bind_address = solana_net_utils::parse_host(matches.value_of("bind_address").unwrap()) + .expect("invalid bind_address"); + let rpc_bind_address = if matches.is_present("rpc_bind_address") { + solana_net_utils::parse_host(matches.value_of("rpc_bind_address").unwrap()) + .expect("invalid rpc_bind_address") + } else { + bind_address + }; + let account_paths = if let Some(account_paths) = matches.value_of("account_paths") { account_paths.split(',').map(PathBuf::from).collect() } else { @@ -1063,8 +1090,12 @@ pub fn main() { let gossip_addr = SocketAddr::new( gossip_host, value_t!(matches, "gossip_port", u16).unwrap_or_else(|_| { - solana_net_utils::find_available_port_in_range((0, 1)) - .expect("unable to find an available gossip port") + solana_net_utils::find_available_port_in_range(bind_address, (0, 1)).unwrap_or_else( + |err| { + eprintln!("Unable to find an available gossip port: {}", err); + exit(1); + }, + ) }), ); @@ -1072,35 +1103,39 @@ pub fn main() { .as_ref() .map(ContactInfo::new_gossip_entry_point); - let mut tcp_ports = vec![]; - let mut node = - Node::new_with_external_ip(&identity_keypair.pubkey(), &gossip_addr, dynamic_port_range); + let mut node = Node::new_with_external_ip( + &identity_keypair.pubkey(), + &gossip_addr, + dynamic_port_range, + bind_address, + ); if !private_rpc { if let Some((rpc_port, rpc_pubsub_port)) = validator_config.rpc_ports { node.info.rpc = SocketAddr::new(node.info.gossip.ip(), rpc_port); node.info.rpc_pubsub = SocketAddr::new(node.info.gossip.ip(), rpc_pubsub_port); - tcp_ports = vec![rpc_port, rpc_pubsub_port]; } } if let Some(ref cluster_entrypoint) = cluster_entrypoint { let udp_sockets = vec![&node.sockets.gossip, &node.sockets.repair]; - let mut tcp_listeners: 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 mut tcp_listeners = vec![]; + if !private_rpc { + if let Some((rpc_port, rpc_pubsub_port)) = validator_config.rpc_ports { + for port in &[rpc_port, rpc_pubsub_port] { + tcp_listeners.push(( + *port, + TcpListener::bind(&SocketAddr::from((rpc_bind_address, *port))) + .unwrap_or_else(|err| { + error!("Unable to bind to tcp/{}: {}", port, err); + std::process::exit(1); + }), + )); + } + } + } + if let Some(ip_echo) = &node.sockets.ip_echo { let ip_echo = ip_echo.try_clone().expect("unable to clone tcp_listener"); tcp_listeners.push((node.info.gossip.port(), ip_echo));