Add a node-specific ip echo service to remove dependency on ifconfig.co (#4137)

This commit is contained in:
Michael Vines 2019-05-03 11:01:35 -07:00 committed by GitHub
parent c8ed41167a
commit 7fe3c75c6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 239 additions and 325 deletions

42
Cargo.lock generated
View File

@ -1056,11 +1056,6 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "ipnetwork"
version = "0.12.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.7.11" version = "0.7.11"
@ -1633,33 +1628,6 @@ name = "pkg-config"
version = "0.3.14" version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pnet_base"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pnet_datalink"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"pnet_base 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pnet_sys 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pnet_sys"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "predicates" name = "predicates"
version = "1.0.0" version = "1.0.0"
@ -2587,14 +2555,14 @@ dependencies = [
name = "solana-netutil" name = "solana-netutil"
version = "0.15.0" version = "0.15.0"
dependencies = [ dependencies = [
"ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pnet_datalink 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"reqwest 0.9.16 (registry+https://github.com/rust-lang/crates.io-index)",
"socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-logger 0.15.0", "solana-logger 0.15.0",
"tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -3532,7 +3500,6 @@ dependencies = [
"checksum indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c60da1c9abea75996b70a931bba6c750730399005b61ccd853cee50ef3d0d0c" "checksum indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c60da1c9abea75996b70a931bba6c750730399005b61ccd853cee50ef3d0d0c"
"checksum influx_db_client 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1af8df5705f0b30bcb504bafc9396d995a555c4d6bd6f9097729ad47b8a49a38" "checksum influx_db_client 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1af8df5705f0b30bcb504bafc9396d995a555c4d6bd6f9097729ad47b8a49a38"
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)" = "70783119ac90828aaba91eae39db32c6c1b8838deea3637e5238efa0130801ab"
"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" "checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d"
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
@ -3596,9 +3563,6 @@ dependencies = [
"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" "checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662"
"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
"checksum pnet_base 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "948dbdd36f46888ada1d497703e6cae53d227ab0e8871638aba492ad1e4a76dc"
"checksum pnet_datalink 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d3b3dd76a11ad99d92fef54b2489f76f4045ebd5251bd1af485d55a7e13db6"
"checksum pnet_sys 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "963b9109a05c3ac370abc3fda61bff20d03743c2947942173871b9cac2b9acb0"
"checksum predicates 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa984b7cd021a0bf5315bcce4c4ae61d2a535db2a8d288fc7578638690a7b7c3" "checksum predicates 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa984b7cd021a0bf5315bcce4c4ae61d2a535db2a8d288fc7578638690a7b7c3"
"checksum predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" "checksum predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178"
"checksum predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" "checksum predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124"

View File

@ -119,20 +119,20 @@ Then the following command will start a new validator node.
If this is a `solana-install`-installation: If this is a `solana-install`-installation:
```bash ```bash
$ clear-fullnode-config.sh $ clear-fullnode-config.sh
$ fullnode.sh --public-address --poll-for-new-genesis-block testnet.solana.com $ fullnode.sh --poll-for-new-genesis-block testnet.solana.com
``` ```
Alternatively, the `solana-install run` command can be used to run the validator Alternatively, the `solana-install run` command can be used to run the validator
node while periodically checking for and applying software updates: node while periodically checking for and applying software updates:
```bash ```bash
$ clear-fullnode-config.sh $ clear-fullnode-config.sh
$ solana-install run fullnode.sh -- --public-address --poll-for-new-genesis-block testnet.solana.com $ solana-install run fullnode.sh -- --poll-for-new-genesis-block testnet.solana.com
``` ```
If you built from source: If you built from source:
```bash ```bash
$ USE_INSTALL=1 ./multinode-demo/clear-fullnode-config.sh $ USE_INSTALL=1 ./multinode-demo/clear-fullnode-config.sh
$ USE_INSTALL=1 ./multinode-demo/fullnode.sh --public-address --poll-for-new-genesis-block testnet.solana.com $ USE_INSTALL=1 ./multinode-demo/fullnode.sh --poll-for-new-genesis-block testnet.solana.com
``` ```
#### Controlling local network port allocation #### Controlling local network port allocation

View File

@ -18,14 +18,14 @@ declare prints=(
# Parts of the tree that are expected to be print free # Parts of the tree that are expected to be print free
declare print_free_tree=( declare print_free_tree=(
'core/src' 'core/src'
'drone' 'drone/src'
'metrics' 'metrics/src'
'netutil' 'netutil/src'
'runtime' 'runtime/src'
'sdk' 'sdk/src'
) )
if _ git grep "${prints[@]/#/-e }" -- "${print_free_tree[@]}"; then if _ git grep --max-depth=0 "${prints[@]/#/-e }" -- "${print_free_tree[@]}"; then
exit 1 exit 1
fi fi

View File

@ -7,7 +7,7 @@ use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Signable, Signature}; use solana_sdk::signature::{Signable, Signature};
use solana_sdk::timing::timestamp; use solana_sdk::timing::timestamp;
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd}; use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, SocketAddr};
/// Structure representing a node on the network /// Structure representing a node on the network
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
@ -56,7 +56,7 @@ impl Eq for ContactInfo {}
#[macro_export] #[macro_export]
macro_rules! socketaddr { macro_rules! socketaddr {
($ip:expr, $port:expr) => { ($ip:expr, $port:expr) => {
std::net::SocketAddr::from((Ipv4Addr::from($ip), $port)) std::net::SocketAddr::from((std::net::Ipv4Addr::from($ip), $port))
}; };
($str:expr) => {{ ($str:expr) => {{
let a: std::net::SocketAddr = $str.parse().unwrap(); let a: std::net::SocketAddr = $str.parse().unwrap();

View File

@ -74,6 +74,7 @@ pub struct Fullnode {
poh_service: PohService, poh_service: PohService,
tpu: Tpu, tpu: Tpu,
tvu: Tvu, tvu: Tvu,
ip_echo_server: solana_netutil::IpEchoServer,
} }
impl Fullnode { impl Fullnode {
@ -159,6 +160,9 @@ impl Fullnode {
)) ))
}; };
let ip_echo_server =
solana_netutil::ip_echo_server(node.sockets.gossip.local_addr().unwrap().port());
let subscriptions = Arc::new(RpcSubscriptions::default()); let subscriptions = Arc::new(RpcSubscriptions::default());
let rpc_pubsub_service = if node.info.rpc_pubsub.port() == 0 { let rpc_pubsub_service = if node.info.rpc_pubsub.port() == 0 {
None None
@ -270,6 +274,7 @@ impl Fullnode {
exit, exit,
poh_service, poh_service,
poh_recorder, poh_recorder,
ip_echo_server,
} }
} }
@ -329,6 +334,7 @@ impl Service for Fullnode {
self.gossip_service.join()?; self.gossip_service.join()?;
self.tpu.join()?; self.tpu.join()?;
self.tvu.join()?; self.tvu.join()?;
self.ip_echo_server.shutdown_now();
Ok(()) Ok(())
} }

View File

@ -618,7 +618,7 @@ mod tests {
use solana_sdk::system_transaction; use solana_sdk::system_transaction;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
use std::net::{Ipv4Addr, SocketAddr, UdpSocket}; use std::net::{SocketAddr, UdpSocket};
#[test] #[test]
fn test_packets_set_addr() { fn test_packets_set_addr() {

View File

@ -5,6 +5,7 @@ use solana::contact_info::ContactInfo;
use solana::fullnode::{Fullnode, FullnodeConfig}; use solana::fullnode::{Fullnode, FullnodeConfig};
use solana::local_vote_signer_service::LocalVoteSignerService; use solana::local_vote_signer_service::LocalVoteSignerService;
use solana::service::Service; use solana::service::Service;
use solana::socketaddr;
use solana_netutil::parse_port_range; use solana_netutil::parse_port_range;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil}; use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use std::fs::File; use std::fs::File;
@ -131,16 +132,10 @@ fn main() {
.takes_value(true) .takes_value(true)
.help("Comma separated persistent accounts location"), .help("Comma separated persistent accounts location"),
) )
.arg(
clap::Arg::with_name("public_address")
.long("public-address")
.takes_value(false)
.help("Advertise public machine address in gossip. By default the local machine address is advertised"),
)
.arg( .arg(
clap::Arg::with_name("gossip_port") clap::Arg::with_name("gossip_port")
.long("gossip-port") .long("gossip-port")
.value_name("PORT") .value_name("HOST:PORT")
.takes_value(true) .takes_value(true)
.help("Gossip port number for the node"), .help("Gossip port number for the node"),
) )
@ -195,19 +190,14 @@ fn main() {
let dynamic_port_range = parse_port_range(matches.value_of("dynamic_port_range").unwrap()) let dynamic_port_range = parse_port_range(matches.value_of("dynamic_port_range").unwrap())
.expect("invalid dynamic_port_range"); .expect("invalid dynamic_port_range");
let gossip_addr = { let mut gossip_addr = solana_netutil::parse_port_or_addr(
let mut addr = solana_netutil::parse_port_or_addr( matches.value_of("gossip_port"),
matches.value_of("gossip_port"), socketaddr!(
[127, 0, 0, 1],
solana_netutil::find_available_port_in_range(dynamic_port_range) solana_netutil::find_available_port_in_range(dynamic_port_range)
.expect("unable to allocate gossip_port"), .expect("unable to find an available gossip port")
); ),
if matches.is_present("public_address") { );
addr.set_ip(solana_netutil::get_public_ip_addr().unwrap());
} else {
addr.set_ip(solana_netutil::get_ip_addr(false).unwrap());
}
addr
};
if let Some(paths) = matches.value_of("accounts") { if let Some(paths) = matches.value_of("accounts") {
fullnode_config.account_paths = Some(paths.to_string()); fullnode_config.account_paths = Some(paths.to_string());
@ -215,9 +205,11 @@ fn main() {
fullnode_config.account_paths = None; fullnode_config.account_paths = None;
} }
let cluster_entrypoint = matches.value_of("network").map(|network| { let cluster_entrypoint = matches.value_of("network").map(|network| {
let gossip_addr = let entrypoint_addr =
solana_netutil::parse_host_port(network).expect("failed to parse network address"); solana_netutil::parse_host_port(network).expect("failed to parse network address");
ContactInfo::new_gossip_entry_point(&gossip_addr) gossip_addr.set_ip(solana_netutil::get_public_ip_addr(&entrypoint_addr).unwrap());
ContactInfo::new_gossip_entry_point(&entrypoint_addr)
}); });
let (_signer_service, _signer_addr) = if let Some(signer_addr) = matches.value_of("signer") { let (_signer_service, _signer_addr) = if let Some(signer_addr) = matches.value_of("signer") {
( (

View File

@ -9,7 +9,6 @@ use solana::gossip_service::discover;
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use std::error; use std::error;
use std::net::Ipv4Addr;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::process::exit; use std::process::exit;
@ -42,12 +41,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
SubCommand::with_name("spy") SubCommand::with_name("spy")
.about("Monitor the gossip network") .about("Monitor the gossip network")
.setting(AppSettings::DisableVersion) .setting(AppSettings::DisableVersion)
.arg(
clap::Arg::with_name("public_address")
.long("public-address")
.takes_value(false)
.help("Advertise public machine address in gossip. By default the local machine address is advertised"),
)
.arg( .arg(
clap::Arg::with_name("pull_only") clap::Arg::with_name("pull_only")
.long("pull-only") .long("pull-only")
@ -131,11 +124,12 @@ fn main() -> Result<(), Box<dyn error::Error>> {
None None
} else { } else {
let mut addr = socketaddr_any!(); let mut addr = socketaddr_any!();
if matches.is_present("public_address") { addr.set_ip(
addr.set_ip(solana_netutil::get_public_ip_addr().unwrap()); solana_netutil::get_public_ip_addr(&network_addr).unwrap_or_else(|err| {
} else { eprintln!("failed to contact {}: {}", network_addr, err);
addr.set_ip(solana_netutil::get_ip_addr(false).unwrap()); exit(1)
} }),
);
Some(addr) Some(addr)
}; };

View File

@ -44,17 +44,18 @@ bootstrap_leader_vote_id_path="$SOLANA_CONFIG_DIR"/bootstrap-leader-vote-id.json
bootstrap_leader_vote_id=$($solana_keygen pubkey "$bootstrap_leader_vote_id_path") bootstrap_leader_vote_id=$($solana_keygen pubkey "$bootstrap_leader_vote_id_path")
trap 'kill "$pid" && wait "$pid"' INT TERM ERR trap 'kill "$pid" && wait "$pid"' INT TERM ERR
$program \
--identity "$bootstrap_leader_id_path" \ default_fullnode_arg --identity "$bootstrap_leader_id_path"
--voting-keypair "$bootstrap_leader_vote_id_path" \ default_fullnode_arg --voting-keypair "$bootstrap_leader_vote_id_path"
--vote-account "$bootstrap_leader_vote_id" \ default_fullnode_arg --vote-account "$bootstrap_leader_vote_id"
--ledger "$SOLANA_CONFIG_DIR"/bootstrap-leader-ledger \ default_fullnode_arg --ledger "$SOLANA_CONFIG_DIR"/bootstrap-leader-ledger
--accounts "$SOLANA_CONFIG_DIR"/bootstrap-leader-accounts \ default_fullnode_arg --accounts "$SOLANA_CONFIG_DIR"/bootstrap-leader-accounts
--gossip-port 8001 \ default_fullnode_arg --rpc-port 8899
--rpc-port 8899 \ default_fullnode_arg --rpc-drone-address 127.0.0.1:9900
--rpc-drone-address 127.0.0.1:9900 \ default_fullnode_arg --gossip-port 8001
"${extra_fullnode_args[@]}" \
> >($bootstrap_leader_logger) 2>&1 & echo "$PS4 $program ${extra_fullnode_args[*]}"
$program "${extra_fullnode_args[@]}" > >($bootstrap_leader_logger) 2>&1 &
pid=$! pid=$!
oom_score_adj "$pid" 1000 oom_score_adj "$pid" 1000

View File

@ -82,7 +82,6 @@ Start a full node
--label LABEL - Append the given label to the fullnode configuration files, useful when running --label LABEL - Append the given label to the fullnode configuration files, useful when running
multiple fullnodes from the same filesystem location multiple fullnodes from the same filesystem location
--stake LAMPORTS - Number of lamports to stake --stake LAMPORTS - Number of lamports to stake
--public-address - advertise public machine address in gossip. By default the local machine address is advertised
--no-voting - start node without vote signer --no-voting - start node without vote signer
--rpc-port port - custom RPC port for this node --rpc-port port - custom RPC port for this node

View File

@ -34,9 +34,6 @@ while [[ ${1:0:1} = - ]]; do
elif [[ $1 = --init-complete-file ]]; then elif [[ $1 = --init-complete-file ]]; then
extra_fullnode_args+=("$1" "$2") extra_fullnode_args+=("$1" "$2")
shift 2 shift 2
elif [[ $1 = --public-address ]]; then
extra_fullnode_args+=("$1")
shift
elif [[ $1 = --stake ]]; then elif [[ $1 = --stake ]]; then
stake="$2" stake="$2"
shift 2 shift 2
@ -64,3 +61,20 @@ done
if [[ -n $3 ]]; then if [[ -n $3 ]]; then
fullnode_usage "$@" fullnode_usage "$@"
fi fi
default_fullnode_arg() {
declare name=$1
declare value=$2
for arg in "${extra_fullnode_args[@]}"; do
if [[ $arg = "$name" ]]; then
return
fi
done
if [[ -n $value ]]; then
extra_fullnode_args+=("$name" "$value")
else
extra_fullnode_args+=("$name")
fi
}

View File

@ -164,23 +164,21 @@ while true; do
if ((stake)); then if ((stake)); then
setup_vote_account "${leader_address%:*}" "$fullnode_id_path" "$fullnode_vote_id_path" "$stake" setup_vote_account "${leader_address%:*}" "$fullnode_id_path" "$fullnode_vote_id_path" "$stake"
fi fi
set +x
$program \ default_fullnode_arg --identity "$fullnode_id_path"
--identity "$fullnode_id_path" \ default_fullnode_arg --voting-keypair "$fullnode_vote_id_path"
--voting-keypair "$fullnode_vote_id_path" \ default_fullnode_arg --vote-account "$fullnode_vote_id"
--vote-account "$fullnode_vote_id" \ default_fullnode_arg --network "$leader_address"
--network "$leader_address" \ default_fullnode_arg --ledger "$ledger_config_dir"
--ledger "$ledger_config_dir" \ default_fullnode_arg --accounts "$accounts_config_dir"
--accounts "$accounts_config_dir" \ default_fullnode_arg --rpc-drone-address "${leader_address%:*}:9900"
--rpc-drone-address "${leader_address%:*}:9900" \ echo "$PS4 $program ${extra_fullnode_args[*]}"
"${extra_fullnode_args[@]}" \ $program "${extra_fullnode_args[@]}" > >($fullnode_logger) 2>&1 &
> >($fullnode_logger) 2>&1 &
pid=$! pid=$!
oom_score_adj "$pid" 1000 oom_score_adj "$pid" 1000
set +x
while true; do while true; do
if ! kill -0 "$pid"; then if ! kill -0 "$pid"; then
wait "$pid" wait "$pid"
exit 0 exit 0

View File

@ -350,7 +350,7 @@ EOF
echo "Waiting for $name to finish booting..." echo "Waiting for $name to finish booting..."
( (
set -x +e set -x +e
for i in $(seq 1 30); do for i in $(seq 1 60); do
timeout --preserve-status --foreground 20s ssh "${sshOptions[@]}" "$publicIp" "ls -l /.instance-startup-complete" timeout --preserve-status --foreground 20s ssh "${sshOptions[@]}" "$publicIp" "ls -l /.instance-startup-complete"
ret=$? ret=$?
if [[ $ret -eq 0 ]]; then if [[ $ret -eq 0 ]]; then

View File

@ -290,7 +290,6 @@ startBootstrapLeader() {
"./solana/net/remote/remote-node.sh \ "./solana/net/remote/remote-node.sh \
$deployMethod \ $deployMethod \
bootstrap-leader \ bootstrap-leader \
$publicNetwork \
$entrypointIp \ $entrypointIp \
$((${#fullnodeIpList[@]} + ${#blockstreamerIpList[@]})) \ $((${#fullnodeIpList[@]} + ${#blockstreamerIpList[@]})) \
\"$RUST_LOG\" \ \"$RUST_LOG\" \
@ -319,7 +318,6 @@ startNode() {
"./solana/net/remote/remote-node.sh \ "./solana/net/remote/remote-node.sh \
$deployMethod \ $deployMethod \
$nodeType \ $nodeType \
$publicNetwork \
$entrypointIp \ $entrypointIp \
$((${#fullnodeIpList[@]} + ${#blockstreamerIpList[@]})) \ $((${#fullnodeIpList[@]} + ${#blockstreamerIpList[@]})) \
\"$RUST_LOG\" \ \"$RUST_LOG\" \

View File

@ -6,13 +6,12 @@ cd "$(dirname "$0")"/../..
set -x set -x
deployMethod="$1" deployMethod="$1"
nodeType="$2" nodeType="$2"
publicNetwork="$3" entrypointIp="$3"
entrypointIp="$4" numNodes="$4"
numNodes="$5" RUST_LOG="$5"
RUST_LOG="$6" skipSetup="$6"
skipSetup="$7" leaderRotation="$7"
leaderRotation="$8" failOnValidatorBootupFailure="$8"
failOnValidatorBootupFailure="$9"
set +x set +x
export RUST_LOG export RUST_LOG
@ -31,7 +30,6 @@ missing() {
[[ -n $deployMethod ]] || missing deployMethod [[ -n $deployMethod ]] || missing deployMethod
[[ -n $nodeType ]] || missing nodeType [[ -n $nodeType ]] || missing nodeType
[[ -n $publicNetwork ]] || missing publicNetwork
[[ -n $entrypointIp ]] || missing entrypointIp [[ -n $entrypointIp ]] || missing entrypointIp
[[ -n $numNodes ]] || missing numNodes [[ -n $numNodes ]] || missing numNodes
[[ -n $skipSetup ]] || missing skipSetup [[ -n $skipSetup ]] || missing skipSetup
@ -84,11 +82,10 @@ local|tar)
fi fi
./multinode-demo/drone.sh > drone.log 2>&1 & ./multinode-demo/drone.sh > drone.log 2>&1 &
args=() args=(
if $publicNetwork; then --enable-rpc-exit
args+=(--public-address) --gossip-port "$entrypointIp":8001
fi )
args+=(--enable-rpc-exit)
./multinode-demo/bootstrap-leader.sh "${args[@]}" > bootstrap-leader.log 2>&1 & ./multinode-demo/bootstrap-leader.sh "${args[@]}" > bootstrap-leader.log 2>&1 &
ln -sTf bootstrap-leader.log fullnode.log ln -sTf bootstrap-leader.log fullnode.log
@ -102,9 +99,6 @@ local|tar)
fi fi
args=() args=()
if $publicNetwork; then
args+=("--public-address")
fi
if [[ $nodeType = blockstreamer ]]; then if [[ $nodeType = blockstreamer ]]; then
args+=( args+=(
--blockstream /tmp/solana-blockstream.sock --blockstream /tmp/solana-blockstream.sock

View File

@ -9,16 +9,22 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
bincode = "1.1.3"
clap = "2.33.0"
log = "0.4.2" log = "0.4.2"
ipnetwork = "0.12.7"
nix = "0.12.0" nix = "0.12.0"
pnet_datalink = "0.21.0"
rand = "0.6.1" rand = "0.6.1"
reqwest = "0.9.0"
socket2 = "0.3.8" socket2 = "0.3.8"
[dev-dependencies]
solana-logger = { path = "../logger", version = "0.15.0" } solana-logger = { path = "../logger", version = "0.15.0" }
tokio = "0.1"
[lib] [lib]
name = "solana_netutil" name = "solana_netutil"
[[bin]]
name = "solana-ip-address"
path = "src/bin/ip_address.rs"
[[bin]]
name = "solana-ip-address-server"
path = "src/bin/ip_address_server.rs"

View File

@ -0,0 +1,26 @@
use clap::{crate_version, App, Arg};
fn main() {
solana_logger::setup();
let matches = App::new("solana-ip-address")
.version(crate_version!())
.arg(
Arg::with_name("host_port")
.index(1)
.required(true)
.help("Host:port to connect to"),
)
.get_matches();
let host_port = matches.value_of("host_port").unwrap();
let addr = solana_netutil::parse_host_port(host_port)
.unwrap_or_else(|_| panic!("failed to parse {}", host_port));
match solana_netutil::get_public_ip_addr(&addr) {
Ok(ip) => println!("{}", ip),
Err(err) => {
eprintln!("{}: {}", addr, err);
std::process::exit(1)
}
}
}

View File

@ -0,0 +1,23 @@
use clap::{crate_version, App, Arg};
fn main() {
solana_logger::setup();
let matches = App::new("solana-ip-address-server")
.version(crate_version!())
.arg(
Arg::with_name("port")
.index(1)
.required(true)
.help("TCP port to bind to"),
)
.get_matches();
let port = matches.value_of("port").unwrap();
let port = port
.parse()
.unwrap_or_else(|_| panic!("Unable to parse {}", port));
let _runtime = solana_netutil::ip_echo_server(port);
loop {
std::thread::park();
}
}

View File

@ -0,0 +1,44 @@
use log::*;
use std::net::SocketAddr;
use tokio;
use tokio::net::TcpListener;
use tokio::prelude::{Future, Stream};
use tokio::runtime::Runtime;
pub type IpEchoServer = Runtime;
/// 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(|_| panic!("Unable to bind to {}", bind_addr));
info!("bound to {:?}", bind_addr);
let server = tcp
.incoming()
.map_err(|err| warn!("accept failed: {:?}", err))
.for_each(move |socket| {
let ip = socket
.peer_addr()
.and_then(|peer_addr| {
bincode::serialize(&peer_addr.ip()).map_err(|err| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("Failed to serialize: {:?}", err),
)
})
})
.unwrap_or_else(|_| vec![]);
let write_future = tokio::io::write_all(socket, ip)
.map_err(|err| warn!("write error: {:?}", err))
.map(|_| ());
tokio::spawn(write_future)
});
let mut rt = Runtime::new().expect("Failed to create Runtime");
rt.spawn(server);
rt
}

View File

@ -1,14 +1,16 @@
//! The `netutil` module assists with networking //! The `netutil` module assists with networking
use log::*;
use nix::sys::socket::setsockopt; use nix::sys::socket::setsockopt;
use nix::sys::socket::sockopt::{ReuseAddr, ReusePort}; use nix::sys::socket::sockopt::{ReuseAddr, ReusePort};
use pnet_datalink as datalink;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use reqwest;
use socket2::{Domain, SockAddr, Socket, Type}; use socket2::{Domain, SockAddr, Socket, Type};
use std::io; use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, ToSocketAddrs, UdpSocket}; use std::io::Read;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket};
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use std::time::Duration;
mod ip_echo_server;
pub use ip_echo_server::*;
/// A data type representing a public Udp socket /// A data type representing a public Udp socket
pub struct UdpSocketPair { pub struct UdpSocketPair {
@ -19,34 +21,43 @@ pub struct UdpSocketPair {
pub type PortRange = (u16, u16); pub type PortRange = (u16, u16);
/// Tries to determine the public IP address of this machine /// Determine the public IP address of this machine by asking an ip_echo_server at the given
pub fn get_public_ip_addr() -> Result<IpAddr, String> { /// address
let body = reqwest::get("http://ifconfig.co/ip") pub fn get_public_ip_addr(ip_echo_server_addr: &SocketAddr) -> Result<IpAddr, String> {
.map_err(|err| err.to_string())? let mut data = Vec::new();
.text()
.map_err(|err| err.to_string())?;
match body.lines().next() { let timeout = Duration::new(5, 0);
Some(ip) => Result::Ok(ip.parse().unwrap()), TcpStream::connect_timeout(ip_echo_server_addr, timeout)
None => Result::Err("Empty response body".to_string()), .and_then(|mut stream| {
} stream
.set_read_timeout(Some(Duration::new(10, 0)))
.expect("set_read_timeout");
stream.read_to_end(&mut data)
})
.and_then(|_| {
bincode::deserialize(&data).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("Failed to deserialize: {:?}", err),
)
})
})
.map_err(|err| err.to_string())
} }
pub fn parse_port_or_addr(optstr: Option<&str>, default_port: u16) -> SocketAddr { pub fn parse_port_or_addr(optstr: Option<&str>, default_addr: SocketAddr) -> SocketAddr {
let daddr = SocketAddr::from(([0, 0, 0, 0], default_port));
if let Some(addrstr) = optstr { if let Some(addrstr) = optstr {
if let Ok(port) = addrstr.parse() { if let Ok(port) = addrstr.parse() {
let mut addr = daddr; let mut addr = default_addr;
addr.set_port(port); addr.set_port(port);
addr addr
} else if let Ok(addr) = addrstr.parse() { } else if let Ok(addr) = addrstr.parse() {
addr addr
} else { } else {
daddr default_addr
} }
} else { } else {
daddr default_addr
} }
} }
@ -95,67 +106,6 @@ pub fn parse_host_port(host_port: &str) -> Result<SocketAddr, String> {
} }
} }
fn find_eth0ish_ip_addr(
ifaces: &mut Vec<datalink::NetworkInterface>,
enable_ipv6: bool,
) -> Option<IpAddr> {
// put eth0 and wifi0, etc. up front of our list of candidates
ifaces.sort_by(|a, b| {
a.name
.chars()
.last()
.unwrap()
.cmp(&b.name.chars().last().unwrap())
});
let mut lo = None;
for iface in ifaces.clone() {
trace!("get_ip_addr considering iface {}", iface.name);
for p in iface.ips {
trace!(" ip {}", p);
if p.ip().is_multicast() {
trace!(" multicast");
continue;
}
match p.ip() {
IpAddr::V4(addr) => {
if addr.is_link_local() {
trace!(" link local");
continue;
}
if p.ip().is_loopback() {
// Fall back to loopback if no better option exists
// (local development and test)
trace!(" loopback");
lo = Some(p.ip());
continue;
}
trace!(" picked {}", p.ip());
return Some(p.ip());
}
IpAddr::V6(_addr) => {
// Select an ipv6 address if requested
if enable_ipv6 {
if p.ip().is_loopback() {
trace!(" loopback");
lo = Some(p.ip());
continue;
}
return Some(p.ip());
}
}
}
}
}
trace!(" picked {:?}", lo);
lo
}
pub fn get_ip_addr(enable_ipv6: bool) -> Option<IpAddr> {
let mut ifaces = datalink::interfaces();
find_eth0ish_ip_addr(&mut ifaces, enable_ipv6)
}
fn udp_socket(reuseaddr: bool) -> io::Result<Socket> { fn udp_socket(reuseaddr: bool) -> io::Result<Socket> {
let sock = Socket::new(Domain::ipv4(), Type::dgram(), None)?; let sock = Socket::new(Domain::ipv4(), Type::dgram(), None)?;
let sock_fd = sock.as_raw_fd(); let sock_fd = sock.as_raw_fd();
@ -252,101 +202,16 @@ 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 ipnetwork::IpNetwork;
#[test]
fn test_find_eth0ish_ip_addr() {
solana_logger::setup();
macro_rules! mock_interface {
($name:ident, $ip_mask:expr) => {
datalink::NetworkInterface {
name: stringify!($name).to_string(),
index: 0,
mac: None,
ips: vec![IpNetwork::V4($ip_mask.parse().unwrap())],
flags: 0,
}
};
}
// loopback bad when alternatives exist
assert_eq!(
find_eth0ish_ip_addr(
&mut vec![
mock_interface!(eth0, "192.168.137.1/8"),
mock_interface!(lo, "127.0.0.1/24")
],
false
),
Some(mock_interface!(eth0, "192.168.137.1/8").ips[0].ip())
);
// find loopback as a last resort
assert_eq!(
find_eth0ish_ip_addr(&mut vec![mock_interface!(lo, "127.0.0.1/24")], false),
Some(mock_interface!(lo, "127.0.0.1/24").ips[0].ip()),
);
// multicast bad
assert_eq!(
find_eth0ish_ip_addr(&mut vec![mock_interface!(eth0, "224.0.1.5/24")], false),
None
);
// finds "wifi0"
assert_eq!(
find_eth0ish_ip_addr(
&mut vec![
mock_interface!(eth0, "224.0.1.5/24"),
mock_interface!(eth2, "192.168.137.1/8"),
mock_interface!(eth3, "10.0.75.1/8"),
mock_interface!(eth4, "172.22.140.113/4"),
mock_interface!(lo, "127.0.0.1/24"),
mock_interface!(wifi0, "192.168.1.184/8"),
],
false
),
Some(mock_interface!(wifi0, "192.168.1.184/8").ips[0].ip())
);
// finds "wifi0" in the middle
assert_eq!(
find_eth0ish_ip_addr(
&mut vec![
mock_interface!(eth0, "224.0.1.5/24"),
mock_interface!(eth2, "192.168.137.1/8"),
mock_interface!(eth3, "10.0.75.1/8"),
mock_interface!(wifi0, "192.168.1.184/8"),
mock_interface!(eth4, "172.22.140.113/4"),
mock_interface!(lo, "127.0.0.1/24"),
],
false
),
Some(mock_interface!(wifi0, "192.168.1.184/8").ips[0].ip())
);
// picks "eth2", is lowest valid "eth"
assert_eq!(
find_eth0ish_ip_addr(
&mut vec![
mock_interface!(eth0, "224.0.1.5/24"),
mock_interface!(eth2, "192.168.137.1/8"),
mock_interface!(eth3, "10.0.75.1/8"),
mock_interface!(eth4, "172.22.140.113/4"),
mock_interface!(lo, "127.0.0.1/24"),
],
false
),
Some(mock_interface!(eth2, "192.168.137.1/8").ips[0].ip())
);
}
#[test] #[test]
fn test_parse_port_or_addr() { fn test_parse_port_or_addr() {
let p1 = parse_port_or_addr(Some("9000"), 1); let p1 = parse_port_or_addr(Some("9000"), SocketAddr::from(([1, 2, 3, 4], 1)));
assert_eq!(p1.port(), 9000); assert_eq!(p1.port(), 9000);
let p2 = parse_port_or_addr(Some("127.0.0.1:7000"), 1); let p2 = parse_port_or_addr(Some("127.0.0.1:7000"), SocketAddr::from(([1, 2, 3, 4], 1)));
assert_eq!(p2.port(), 7000); assert_eq!(p2.port(), 7000);
let p2 = parse_port_or_addr(Some("hi there"), 1); let p2 = parse_port_or_addr(Some("hi there"), SocketAddr::from(([1, 2, 3, 4], 1)));
assert_eq!(p2.port(), 1); assert_eq!(p2.port(), 1);
let p3 = parse_port_or_addr(None, 1); let p3 = parse_port_or_addr(None, SocketAddr::from(([1, 2, 3, 4], 1)));
assert_eq!(p3.port(), 1); assert_eq!(p3.port(), 1);
} }

View File

@ -4,14 +4,14 @@ use solana::contact_info::ContactInfo;
use solana::replicator::Replicator; use solana::replicator::Replicator;
use solana::socketaddr; use solana::socketaddr;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil}; use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use std::net::Ipv4Addr;
use std::process::exit; use std::process::exit;
use std::sync::Arc; use std::sync::Arc;
fn main() { fn main() {
solana_logger::setup(); solana_logger::setup();
let matches = App::new(crate_name!()).about(crate_description!()) let matches = App::new(crate_name!())
.about(crate_description!())
.version(crate_version!()) .version(crate_version!())
.arg( .arg(
Arg::with_name("identity") Arg::with_name("identity")
@ -39,12 +39,6 @@ fn main() {
.required(true) .required(true)
.help("use DIR as persistent ledger location"), .help("use DIR as persistent ledger location"),
) )
.arg(
clap::Arg::with_name("public_address")
.long("public-address")
.takes_value(false)
.help("Advertise public machine address in gossip. By default the local machine address is advertised"),
)
.get_matches(); .get_matches();
let ledger_path = matches.value_of("ledger").unwrap(); let ledger_path = matches.value_of("ledger").unwrap();
@ -58,13 +52,16 @@ fn main() {
Keypair::new() Keypair::new()
}; };
let network_addr = matches
.value_of("network")
.map(|network| {
solana_netutil::parse_host_port(network).expect("failed to parse network address")
})
.unwrap();
let gossip_addr = { let gossip_addr = {
let mut addr = socketaddr!([127, 0, 0, 1], 8700); let mut addr = socketaddr!([127, 0, 0, 1], 8700);
if matches.is_present("public_address") { addr.set_ip(solana_netutil::get_public_ip_addr(&network_addr).unwrap());
addr.set_ip(solana_netutil::get_public_ip_addr().unwrap());
} else {
addr.set_ip(solana_netutil::get_ip_addr(false).unwrap());
}
addr addr
}; };
let node = let node =
@ -76,13 +73,6 @@ fn main() {
gossip_addr gossip_addr
); );
let network_addr = matches
.value_of("network")
.map(|network| {
solana_netutil::parse_host_port(network).expect("failed to parse network address")
})
.unwrap();
let leader_info = ContactInfo::new_gossip_entry_point(&network_addr); let leader_info = ContactInfo::new_gossip_entry_point(&network_addr);
let storage_keypair = Arc::new(Keypair::new()); let storage_keypair = Arc::new(Keypair::new());
let mut replicator = Replicator::new( let mut replicator = Replicator::new(