Add --bind-address and --rpc-bind-address validator arguments (#8628)

This commit is contained in:
Michael Vines
2020-03-04 22:46:43 -07:00
committed by GitHub
parent 01607b9860
commit 448b957a13
12 changed files with 186 additions and 93 deletions

View File

@ -47,7 +47,7 @@ use solana_storage_program::{
}; };
use std::{ use std::{
io::{self, ErrorKind}, io::{self, ErrorKind},
net::{SocketAddr, UdpSocket}, net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
path::{Path, PathBuf}, path::{Path, PathBuf},
result, result,
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
@ -804,14 +804,15 @@ impl Archiver {
blockstore: &Arc<Blockstore>, blockstore: &Arc<Blockstore>,
slots_per_segment: u64, slots_per_segment: u64,
) -> Result<u64> { ) -> Result<u64> {
let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
// Create a client which downloads from the archiver and see that it // Create a client which downloads from the archiver and see that it
// can respond with shreds. // 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); info!("Archiver download: start at {}", start_slot);
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let (s_reader, r_reader) = channel(); 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( let t_receiver = receiver(
repair_socket.clone(), repair_socket.clone(),
&exit, &exit,
@ -908,8 +909,8 @@ impl Archiver {
true true
} }
fn get_archiver_segment_slot(to: SocketAddr) -> u64 { fn get_archiver_segment_slot(bind_ip_addr: IpAddr, to: SocketAddr) -> u64 {
let (_port, socket) = bind_in_range(VALIDATOR_PORT_RANGE).unwrap(); let (_port, socket) = bind_in_range(bind_ip_addr, VALIDATOR_PORT_RANGE).unwrap();
socket socket
.set_read_timeout(Some(Duration::from_secs(5))) .set_read_timeout(Some(Duration::from_secs(5)))
.unwrap(); .unwrap();

View File

@ -13,7 +13,12 @@ use solana_core::{
contact_info::ContactInfo, contact_info::ContactInfo,
}; };
use solana_sdk::{commitment_config::CommitmentConfig, signature::Signer}; 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() { fn main() {
solana_logger::setup(); solana_logger::setup();
@ -116,6 +121,7 @@ fn main() {
&identity_keypair.pubkey(), &identity_keypair.pubkey(),
&gossip_addr, &gossip_addr,
VALIDATOR_PORT_RANGE, VALIDATOR_PORT_RANGE,
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
); );
println!( println!(

View File

@ -67,7 +67,8 @@ fn main() -> Result<()> {
} }
let mut port = 0; 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)); let exit = Arc::new(AtomicBool::new(false));
@ -75,7 +76,7 @@ fn main() -> Result<()> {
let mut read_threads = Vec::new(); let mut read_threads = Vec::new();
let recycler = PacketsRecycler::default(); let recycler = PacketsRecycler::default();
for _ in 0..num_sockets { 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(); read.set_read_timeout(Some(Duration::new(1, 0))).unwrap();
addr = read.local_addr().unwrap(); addr = read.local_addr().unwrap();

View File

@ -26,7 +26,7 @@ use solana_sdk::{
}; };
use std::{ use std::{
io, io,
net::{SocketAddr, UdpSocket}, net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
sync::{ sync::{
atomic::{AtomicBool, AtomicUsize, Ordering}, atomic::{AtomicBool, AtomicUsize, Ordering},
RwLock, RwLock,
@ -603,7 +603,8 @@ impl AsyncClient for ThinClient {
} }
pub fn create_client((rpc, tpu): (SocketAddr, SocketAddr), range: (u16, u16)) -> 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) ThinClient::new(rpc, tpu, transactions_socket)
} }
@ -612,7 +613,8 @@ pub fn create_client_with_timeout(
range: (u16, u16), range: (u16, u16),
timeout: Duration, timeout: Duration,
) -> ThinClient { ) -> 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) ThinClient::new_socket_with_timeout(rpc, tpu, transactions_socket, timeout)
} }

View File

@ -1635,8 +1635,9 @@ impl ClusterInfo {
id: &Pubkey, id: &Pubkey,
gossip_addr: &SocketAddr, gossip_addr: &SocketAddr,
) -> (ContactInfo, UdpSocket, Option<TcpListener>) { ) -> (ContactInfo, UdpSocket, Option<TcpListener>) {
let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
let (port, (gossip_socket, ip_echo)) = 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)); let contact_info = Self::gossip_contact_info(id, SocketAddr::new(gossip_addr.ip(), port));
(contact_info, gossip_socket, Some(ip_echo)) (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 /// A Node with dummy ports to spy on gossip via pull requests
pub fn spy_node(id: &Pubkey) -> (ContactInfo, UdpSocket, Option<TcpListener>) { pub fn spy_node(id: &Pubkey) -> (ContactInfo, UdpSocket, Option<TcpListener>) {
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); let contact_info = Self::spy_contact_info(id);
(contact_info, gossip_socket, None) (contact_info, gossip_socket, None)
@ -1759,16 +1761,18 @@ impl Node {
} }
} }
pub fn new_localhost_with_pubkey(pubkey: &Pubkey) -> Self { 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 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 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 = UdpSocket::bind("127.0.0.1:0").unwrap();
let tvu_forwards = 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 tpu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap();
let repair = 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_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 = let rpc_pubsub_addr =
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_pubsub_port); SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_pubsub_port);
@ -1811,45 +1815,52 @@ impl Node {
fn get_gossip_port( fn get_gossip_port(
gossip_addr: &SocketAddr, gossip_addr: &SocketAddr,
port_range: PortRange, port_range: PortRange,
bind_ip_addr: IpAddr,
) -> (u16, (UdpSocket, TcpListener)) { ) -> (u16, (UdpSocket, TcpListener)) {
if gossip_addr.port() != 0 { if gossip_addr.port() != 0 {
( (
gossip_addr.port(), 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) panic!("gossip_addr bind_to port {}: {}", gossip_addr.port(), e)
}), }),
) )
} else { } 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) { fn bind(bind_ip_addr: IpAddr, port_range: PortRange) -> (u16, UdpSocket) {
bind_in_range(port_range).expect("Failed to bind") bind_in_range(bind_ip_addr, port_range).expect("Failed to bind")
} }
pub fn new_with_external_ip( pub fn new_with_external_ip(
pubkey: &Pubkey, pubkey: &Pubkey,
gossip_addr: &SocketAddr, gossip_addr: &SocketAddr,
port_range: PortRange, port_range: PortRange,
bind_ip_addr: IpAddr,
) -> Node { ) -> 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) = 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) = 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) = 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 (repair_port, repair) = Self::bind(bind_ip_addr, port_range);
let (serve_repair_port, serve_repair) = Self::bind(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 { let info = ContactInfo {
id: *pubkey, id: *pubkey,
@ -1889,9 +1900,10 @@ impl Node {
pubkey: &Pubkey, pubkey: &Pubkey,
gossip_addr: &SocketAddr, gossip_addr: &SocketAddr,
port_range: PortRange, port_range: PortRange,
bind_ip_addr: IpAddr,
) -> Node { ) -> Node {
let mut new = Self::new_with_external_ip(pubkey, gossip_addr, 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(port_range); let (storage_port, storage_socket) = Self::bind(bind_ip_addr, port_range);
new.info.storage_addr = SocketAddr::new(gossip_addr.ip(), storage_port); new.info.storage_addr = SocketAddr::new(gossip_addr.ip(), storage_port);
new.sockets.storage = Some(storage_socket); new.sockets.storage = Some(storage_socket);
@ -2025,6 +2037,7 @@ mod tests {
&Pubkey::new_rand(), &Pubkey::new_rand(),
&socketaddr!(ip, 0), &socketaddr!(ip, 0),
VALIDATOR_PORT_RANGE, VALIDATOR_PORT_RANGE,
IpAddr::V4(ip),
); );
check_node_sockets(&node, IpAddr::V4(ip), VALIDATOR_PORT_RANGE); check_node_sockets(&node, IpAddr::V4(ip), VALIDATOR_PORT_RANGE);
@ -2034,7 +2047,7 @@ mod tests {
fn new_with_external_ip_test_gossip() { fn new_with_external_ip_test_gossip() {
let ip = IpAddr::V4(Ipv4Addr::from(0)); let ip = IpAddr::V4(Ipv4Addr::from(0));
let port = { let port = {
bind_in_range(VALIDATOR_PORT_RANGE) bind_in_range(ip, VALIDATOR_PORT_RANGE)
.expect("Failed to bind") .expect("Failed to bind")
.0 .0
}; };
@ -2042,6 +2055,7 @@ mod tests {
&Pubkey::new_rand(), &Pubkey::new_rand(),
&socketaddr!(0, port), &socketaddr!(0, port),
VALIDATOR_PORT_RANGE, VALIDATOR_PORT_RANGE,
ip,
); );
check_node_sockets(&node, ip, VALIDATOR_PORT_RANGE); check_node_sockets(&node, ip, VALIDATOR_PORT_RANGE);
@ -2056,6 +2070,7 @@ mod tests {
&Pubkey::new_rand(), &Pubkey::new_rand(),
&socketaddr!(ip, 0), &socketaddr!(ip, 0),
VALIDATOR_PORT_RANGE, VALIDATOR_PORT_RANGE,
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
); );
let ip = IpAddr::V4(ip); let ip = IpAddr::V4(ip);

View File

@ -9,7 +9,7 @@ use solana_ledger::bank_forks::BankForks;
use solana_perf::recycler::Recycler; use solana_perf::recycler::Recycler;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, Signer}; 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::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
@ -163,7 +163,11 @@ pub fn get_multi_client(nodes: &[ContactInfo]) -> (ThinClient, usize) {
.collect(); .collect();
let rpc_addrs: Vec<_> = addrs.iter().map(|addr| addr.0).collect(); let rpc_addrs: Vec<_> = addrs.iter().map(|addr| addr.0).collect();
let tpu_addrs: Vec<_> = addrs.iter().map(|addr| addr.1).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(); let num_nodes = tpu_addrs.len();
( (
ThinClient::new_from_addrs(rpc_addrs, tpu_addrs, transactions_socket), ThinClient::new_from_addrs(rpc_addrs, tpu_addrs, transactions_socket),

View File

@ -15,9 +15,12 @@ pub struct LocalVoteSignerService {
impl LocalVoteSignerService { impl LocalVoteSignerService {
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
pub fn new(port_range: PortRange) -> (Self, SocketAddr) { pub fn new(port_range: PortRange) -> (Self, SocketAddr) {
let addr = solana_net_utils::find_available_port_in_range(port_range) let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
.map(|port| SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port)) let addr = SocketAddr::new(
.expect("Failed to find an available port for local vote signer service"); 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 exit = Arc::new(AtomicBool::new(false));
let thread_exit = exit.clone(); let thread_exit = exit.clone();
let thread = Builder::new() let thread = Builder::new()

View File

@ -284,6 +284,7 @@ mod tests {
use solana_ledger::create_new_tmp_ledger; use solana_ledger::create_new_tmp_ledger;
use solana_net_utils::find_available_port_in_range; use solana_net_utils::find_available_port_in_range;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use std::net::{IpAddr, Ipv4Addr};
#[test] #[test]
fn test_skip_repair() { fn test_skip_repair() {
@ -300,11 +301,12 @@ mod tests {
let bank_forks = Arc::new(RwLock::new(bank_forks)); let bank_forks = Arc::new(RwLock::new(bank_forks));
let mut me = ContactInfo::new_localhost(&Pubkey::new_rand(), 0); 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(); let me_retransmit = UdpSocket::bind(format!("127.0.0.1:{}", port)).unwrap();
// need to make sure tvu and tpu are valid addresses // need to make sure tvu and tpu are valid addresses
me.tvu_forwards = me_retransmit.local_addr().unwrap(); 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)) me.tvu = UdpSocket::bind(format!("127.0.0.1:{}", port))
.unwrap() .unwrap()
.local_addr() .local_addr()

View File

@ -262,9 +262,10 @@ mod tests {
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair( let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair(
ContactInfo::default(), ContactInfo::default(),
))); )));
let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
let rpc_addr = SocketAddr::new( let rpc_addr = SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), ip_addr,
solana_net_utils::find_available_port_in_range((10000, 65535)).unwrap(), 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 bank_forks = Arc::new(RwLock::new(BankForks::new(bank.slot(), bank)));
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default())); let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));

View File

@ -197,7 +197,10 @@ fn main() -> Result<(), Box<dyn error::Error>> {
let gossip_addr = SocketAddr::new( let gossip_addr = SocketAddr::new(
gossip_host, gossip_host,
value_t!(matches, "gossip_port", u16).unwrap_or_else(|_| { value_t!(matches, "gossip_port", u16).unwrap_or_else(|_| {
solana_net_utils::find_available_port_in_range((0, 1)) 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") .expect("unable to find an available gossip port")
}), }),
); );

View File

@ -3,7 +3,7 @@ use log::*;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use socket2::{Domain, SockAddr, Socket, Type}; use socket2::{Domain, SockAddr, Socket, Type};
use std::io::{self, Read, Write}; 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::sync::mpsc::channel;
use std::time::Duration; use std::time::Duration;
@ -257,12 +257,15 @@ fn udp_socket(reuseaddr: bool) -> io::Result<Socket> {
} }
// Find a port in the given range that is available for both TCP and UDP // 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 (start, end) = range;
let mut tries_left = end - start; let mut tries_left = end - start;
let mut rand_port = thread_rng().gen_range(start, end); let mut rand_port = thread_rng().gen_range(start, end);
loop { loop {
match bind_common(rand_port, false) { match bind_common(ip_addr, rand_port, false) {
Ok((sock, listener)) => { Ok((sock, listener)) => {
break Result::Ok((sock.local_addr().unwrap().port(), (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 sock = udp_socket(false)?;
let (start, end) = range; let (start, end) = range;
let mut tries_left = end - start; let mut tries_left = end - start;
let mut rand_port = thread_rng().gen_range(start, end); let mut rand_port = thread_rng().gen_range(start, end);
loop { 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)) { match sock.bind(&SockAddr::from(addr)) {
Ok(_) => { 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 // 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<UdpSocket>)> { pub fn multi_bind_in_range(
ip_addr: IpAddr,
range: PortRange,
mut num: usize,
) -> io::Result<(u16, Vec<UdpSocket>)> {
if cfg!(windows) && num != 1 { if cfg!(windows) && num != 1 {
// See https://github.com/solana-labs/solana/issues/4607 // See https://github.com/solana-labs/solana/issues/4607
warn!( 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 mut sockets = Vec::with_capacity(num);
let port = { let port = {
let (port, _) = bind_in_range(range)?; let (port, _) = bind_in_range(ip_addr, range)?;
port port
}; // drop the probe, port should be available... briefly. }; // drop the probe, port should be available... briefly.
for _ in 0..num { for _ in 0..num {
sockets.push(bind_to(port, true)?); sockets.push(bind_to(ip_addr, port, true)?);
} }
Ok((port, sockets)) Ok((port, sockets))
} }
pub fn bind_to(port: u16, reuseaddr: bool) -> io::Result<UdpSocket> { pub fn bind_to(ip_addr: IpAddr, port: u16, reuseaddr: bool) -> io::Result<UdpSocket> {
let sock = udp_socket(reuseaddr)?; 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)) sock.bind(&SockAddr::from(addr))
.and_then(|_| Result::Ok(sock.into_udp_socket())) .and_then(|_| Result::Ok(sock.into_udp_socket()))
} }
// binds both a UdpSocket and a TcpListener // 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 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); let sock_addr = SockAddr::from(addr);
sock.bind(&sock_addr).and_then(|_| { sock.bind(&sock_addr).and_then(|_| {
TcpListener::bind(&addr).and_then(|listener| Result::Ok((sock.into_udp_socket(), listener))) TcpListener::bind(&addr).and_then(|listener| Result::Ok((sock.into_udp_socket(), listener)))
}) })
} }
pub fn find_available_port_in_range(range: PortRange) -> io::Result<u16> { pub fn find_available_port_in_range(ip_addr: IpAddr, range: PortRange) -> io::Result<u16> {
let (start, end) = range; let (start, end) = range;
let mut tries_left = end - start; let mut tries_left = end - start;
let mut rand_port = thread_rng().gen_range(start, end); let mut rand_port = thread_rng().gen_range(start, end);
loop { loop {
match bind_common(rand_port, false) { match bind_common(ip_addr, rand_port, false) {
Ok(_) => { Ok(_) => {
break Ok(rand_port); break Ok(rand_port);
} }
@ -377,6 +388,7 @@ pub fn find_available_port_in_range(range: PortRange) -> io::Result<u16> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::net::Ipv4Addr;
#[test] #[test]
fn test_parse_port_or_addr() { fn test_parse_port_or_addr() {
@ -423,17 +435,19 @@ mod tests {
#[test] #[test]
fn test_bind() { fn test_bind() {
assert_eq!(bind_in_range((2000, 2001)).unwrap().0, 2000); let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
let x = bind_to(2002, true).unwrap(); assert_eq!(bind_in_range(ip_addr, (2000, 2001)).unwrap().0, 2000);
let y = bind_to(2002, true).unwrap(); 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!( assert_eq!(
x.local_addr().unwrap().port(), x.local_addr().unwrap().port(),
y.local_addr().unwrap().port() y.local_addr().unwrap().port()
); );
bind_to(2002, false).unwrap_err(); bind_to(ip_addr, 2002, false).unwrap_err();
bind_in_range((2002, 2003)).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 { for sock in &v {
assert_eq!(port, sock.local_addr().unwrap().port()); assert_eq!(port, sock.local_addr().unwrap().port());
} }
@ -442,34 +456,41 @@ mod tests {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_bind_in_range_nil() { 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] #[test]
fn test_find_available_port_in_range() { fn test_find_available_port_in_range() {
assert_eq!(find_available_port_in_range((3000, 3001)).unwrap(), 3000); let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
let port = find_available_port_in_range((3000, 3050)).unwrap(); 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); assert!(3000 <= port && port < 3050);
let _socket = bind_to(port, false).unwrap(); let _socket = bind_to(ip_addr, port, false).unwrap();
find_available_port_in_range((port, port + 1)).unwrap_err(); find_available_port_in_range(ip_addr, (port, port + 1)).unwrap_err();
} }
#[test] #[test]
fn test_bind_common_in_range() { 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); 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] #[test]
fn test_get_public_ip_addr() { fn test_get_public_ip_addr() {
solana_logger::setup(); solana_logger::setup();
let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
let (_server_port, (server_udp_socket, server_tcp_listener)) = 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)) = 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); let _runtime = ip_echo_server(server_tcp_listener);

View File

@ -732,7 +732,7 @@ pub fn main() {
.takes_value(true) .takes_value(true)
.conflicts_with("entrypoint") .conflicts_with("entrypoint")
.validator(solana_net_utils::is_host) .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( .arg(
clap::Arg::with_name("dynamic_port_range") clap::Arg::with_name("dynamic_port_range")
@ -829,6 +829,23 @@ pub fn main() {
.requires("trusted_validators") .requires("trusted_validators")
.help("Use the RPC service of trusted validators only") .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(); .get_matches();
let identity_keypair = Arc::new( let identity_keypair = Arc::new(
@ -921,6 +938,15 @@ pub fn main() {
solana_net_utils::parse_port_range(matches.value_of("dynamic_port_range").unwrap()) solana_net_utils::parse_port_range(matches.value_of("dynamic_port_range").unwrap())
.expect("invalid dynamic_port_range"); .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") { let account_paths = if let Some(account_paths) = matches.value_of("account_paths") {
account_paths.split(',').map(PathBuf::from).collect() account_paths.split(',').map(PathBuf::from).collect()
} else { } else {
@ -1064,8 +1090,12 @@ pub fn main() {
let gossip_addr = SocketAddr::new( let gossip_addr = SocketAddr::new(
gossip_host, gossip_host,
value_t!(matches, "gossip_port", u16).unwrap_or_else(|_| { value_t!(matches, "gossip_port", u16).unwrap_or_else(|_| {
solana_net_utils::find_available_port_in_range((0, 1)) solana_net_utils::find_available_port_in_range(bind_address, (0, 1)).unwrap_or_else(
.expect("unable to find an available gossip port") |err| {
eprintln!("Unable to find an available gossip port: {}", err);
exit(1);
},
)
}), }),
); );
@ -1073,35 +1103,39 @@ pub fn main() {
.as_ref() .as_ref()
.map(ContactInfo::new_gossip_entry_point); .map(ContactInfo::new_gossip_entry_point);
let mut tcp_ports = vec![]; let mut node = Node::new_with_external_ip(
let mut node = &identity_keypair.pubkey(),
Node::new_with_external_ip(&identity_keypair.pubkey(), &gossip_addr, dynamic_port_range); &gossip_addr,
dynamic_port_range,
bind_address,
);
if !private_rpc { if !private_rpc {
if let Some((rpc_port, rpc_pubsub_port)) = validator_config.rpc_ports { 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 = SocketAddr::new(node.info.gossip.ip(), rpc_port);
node.info.rpc_pubsub = SocketAddr::new(node.info.gossip.ip(), rpc_pubsub_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 { if let Some(ref cluster_entrypoint) = cluster_entrypoint {
let udp_sockets = vec![&node.sockets.gossip, &node.sockets.repair]; let udp_sockets = vec![&node.sockets.gossip, &node.sockets.repair];
let mut tcp_listeners: Vec<(_, _)> = tcp_ports let mut tcp_listeners = vec![];
.iter() if !private_rpc {
.map(|port| { if let Some((rpc_port, rpc_pubsub_port)) = validator_config.rpc_ports {
( for port in &[rpc_port, rpc_pubsub_port] {
tcp_listeners.push((
*port, *port,
TcpListener::bind(&SocketAddr::from(([0, 0, 0, 0], *port))).unwrap_or_else( TcpListener::bind(&SocketAddr::from((rpc_bind_address, *port)))
|err| { .unwrap_or_else(|err| {
error!("Unable to bind to tcp/{}: {}", port, err); error!("Unable to bind to tcp/{}: {}", port, err);
std::process::exit(1); std::process::exit(1);
}, }),
), ));
) }
}) }
.collect(); }
if let Some(ip_echo) = &node.sockets.ip_echo { if let Some(ip_echo) = &node.sockets.ip_echo {
let ip_echo = ip_echo.try_clone().expect("unable to clone tcp_listener"); let ip_echo = ip_echo.try_clone().expect("unable to clone tcp_listener");
tcp_listeners.push((node.info.gossip.port(), ip_echo)); tcp_listeners.push((node.info.gossip.port(), ip_echo));