From 1a5bae8ef10deee053fd5681147c2d45d37cc1bb Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Mon, 24 Oct 2016 18:25:27 +0200 Subject: [PATCH] Extended network options (#2845) * More network configuration options * Filter UDP requests * Fixed tests * Fixed test warning --- parity/cli/config.full.toml | 3 ++ parity/cli/config.toml | 3 ++ parity/cli/mod.rs | 15 ++++++++++ parity/cli/usage.txt | 10 ++++++- parity/configuration.rs | 22 ++++++++++++++- parity/helpers.rs | 5 +++- sync/src/api.rs | 51 ++++++++++++++++++++++++++++++++-- sync/src/lib.rs | 2 +- util/network/src/discovery.rs | 37 ++++++++++++++++-------- util/network/src/host.rs | 43 +++++++++++++++++++--------- util/network/src/ip_utils.rs | 16 +++++++++++ util/network/src/lib.rs | 12 ++++++++ util/network/src/node_table.rs | 29 +++++++++---------- util/src/kvdb.rs | 2 +- 14 files changed, 204 insertions(+), 46 deletions(-) diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index b82762684..fd2e11f98 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -29,6 +29,9 @@ id = "0x1" bootnodes = [] discovery = true warp = true +allow_ips = "all" +snapshot_peers = 0 +max_pending_peers = 64 reserved_only = false reserved_peers = "./path_to_file" diff --git a/parity/cli/config.toml b/parity/cli/config.toml index 5fcd4ce73..e6f01e1ae 100644 --- a/parity/cli/config.toml +++ b/parity/cli/config.toml @@ -18,6 +18,9 @@ discovery = true nat = "any" min_peers = 10 max_peers = 20 +max_pending_peers = 30 +snapshot_peers = 40 +allow_ips = "public" reserved_only = true reserved_peers = "./path/to/reserved_peers" diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index d4558a50d..27e0cb4dc 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -114,8 +114,14 @@ usage! { or |c: &Config| otry!(c.network).min_peers.clone(), flag_max_peers: u16 = 50u16, or |c: &Config| otry!(c.network).max_peers.clone(), + flag_max_pending_peers: u16 = 64u16, + or |c: &Config| otry!(c.network).max_pending_peers.clone(), + flag_snapshot_peers: u16 = 0u16, + or |c: &Config| otry!(c.network).snapshot_peers.clone(), flag_nat: String = "any", or |c: &Config| otry!(c.network).nat.clone(), + flag_allow_ips: String = "all", + or |c: &Config| otry!(c.network).allow_ips.clone(), flag_network_id: Option = None, or |c: &Config| otry!(c.network).id.clone().map(Some), flag_bootnodes: Option = None, @@ -307,7 +313,10 @@ struct Network { port: Option, min_peers: Option, max_peers: Option, + snapshot_peers: Option, + max_pending_peers: Option, nat: Option, + allow_ips: Option, id: Option, bootnodes: Option>, discovery: Option, @@ -494,6 +503,9 @@ mod tests { flag_port: 30303u16, flag_min_peers: 25u16, flag_max_peers: 50u16, + flag_max_pending_peers: 64u16, + flag_snapshot_peers: 0u16, + flag_allow_ips: "all".into(), flag_nat: "any".into(), flag_network_id: Some("0x1".into()), flag_bootnodes: Some("".into()), @@ -653,6 +665,9 @@ mod tests { port: None, min_peers: Some(10), max_peers: Some(20), + max_pending_peers: Some(30), + snapshot_peers: Some(40), + allow_ips: Some("public".into()), nat: Some("any".into()), id: None, bootnodes: None, diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index fe6203842..af8e83f0e 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -71,7 +71,9 @@ Networking Options: --port PORT Override the port on which the node should listen (default: {flag_port}). --min-peers NUM Try to maintain at least NUM peers (default: {flag_min_peers}). - --max-peers NUM Allow up to that many peers (default: {flag_max_peers}). + --max-peers NUM Allow up to NUM peers (default: {flag_max_peers}). + --snapshot-peers NUM Allow additional NUM peers for a snapshot sync + (default: {flag_snapshot_peers}). --nat METHOD Specify method to use for determining public address. Must be one of: any, none, upnp, extip: (default: {flag_nat}). @@ -86,6 +88,12 @@ Networking Options: These nodes will always have a reserved slot on top of the normal maximum peers. (default: {flag_reserved_peers:?}) --reserved-only Connect only to reserved nodes. (default: {flag_reserved_only}) + --allow-ips FILTER Filter outbound connections. Must be one of: + private - connect to private network IP addresses only; + public - connect to public network IP addresses only; + all - connect to any IP address. + (default: {flag_allow_ips}) + --max-pending-peers NUM Allow up to NUM pending connections. (default: {flag_max_pending_peers}) API and Console Options: --no-jsonrpc Disable the JSON-RPC API server. (default: {flag_no_jsonrpc}) diff --git a/parity/configuration.rs b/parity/configuration.rs index 060679b0e..5680e6110 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -22,7 +22,7 @@ use std::cmp::max; use cli::{Args, ArgsError}; use util::{Hashable, U256, Uint, Bytes, version_data, Secret, Address}; use util::log::Colour; -use ethsync::{NetworkConfiguration, is_valid_node_url}; +use ethsync::{NetworkConfiguration, is_valid_node_url, AllowIP}; use ethcore::client::{VMType, Mode}; use ethcore::miner::MinerOptions; @@ -332,10 +332,27 @@ impl Configuration { max(self.min_peers(), peers) } + fn allow_ips(&self) -> Result { + match self.args.flag_allow_ips.as_str() { + "all" => Ok(AllowIP::All), + "public" => Ok(AllowIP::Public), + "private" => Ok(AllowIP::Private), + _ => Err("Invalid IP filter value".to_owned()), + } + } + fn min_peers(&self) -> u32 { self.args.flag_peers.unwrap_or(self.args.flag_min_peers) as u32 } + fn max_pending_peers(&self) -> u32 { + self.args.flag_max_pending_peers as u32 + } + + fn snapshot_peers(&self) -> u32 { + self.args.flag_snapshot_peers as u32 + } + fn work_notify(&self) -> Vec { self.args.flag_notify_work.as_ref().map_or_else(Vec::new, |s| s.split(',').map(|s| s.to_owned()).collect()) } @@ -474,6 +491,9 @@ impl Configuration { ret.discovery_enabled = !self.args.flag_no_discovery && !self.args.flag_nodiscover; ret.max_peers = self.max_peers(); ret.min_peers = self.min_peers(); + ret.snapshot_peers = self.snapshot_peers(); + ret.allow_ips = try!(self.allow_ips()); + ret.max_pending_peers = self.max_pending_peers(); let mut net_path = PathBuf::from(self.directories().db); net_path.push("network"); ret.config_path = Some(net_path.to_str().unwrap().to_owned()); diff --git a/parity/helpers.rs b/parity/helpers.rs index a965314f5..5d6859f5b 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -185,7 +185,7 @@ pub fn to_bootnodes(bootnodes: &Option) -> Result, String> { #[cfg(test)] pub fn default_network_config() -> ::ethsync::NetworkConfiguration { - use ethsync::NetworkConfiguration; + use ethsync::{NetworkConfiguration, AllowIP}; NetworkConfiguration { config_path: Some(replace_home("$HOME/.parity/network")), net_config_path: None, @@ -198,6 +198,9 @@ pub fn default_network_config() -> ::ethsync::NetworkConfiguration { use_secret: None, max_peers: 50, min_peers: 25, + snapshot_peers: 0, + max_pending_peers: 64, + allow_ips: AllowIP::All, reserved_nodes: Vec::new(), allow_non_reserved: true, } diff --git a/sync/src/api.rs b/sync/src/api.rs index d0d734024..fb8fdc691 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -18,7 +18,8 @@ use std::sync::Arc; use std::collections::HashMap; use util::Bytes; use network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId, ProtocolId, - NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, NetworkError}; + NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, NetworkError, + AllowIP as NetworkAllowIP}; use util::{U256, H256}; use io::{TimerToken}; use ethcore::client::{BlockChainClient, ChainNotify}; @@ -102,13 +103,15 @@ pub struct EthSync { handler: Arc, /// The main subprotocol name subprotocol_name: [u8; 3], + /// Configuration + config: NetworkConfiguration, } impl EthSync { /// Creates and register protocol with the network service pub fn new(config: SyncConfig, chain: Arc, snapshot_service: Arc, network_config: NetworkConfiguration) -> Result, NetworkError> { let chain_sync = ChainSync::new(config, &*chain); - let service = try!(NetworkService::new(try!(network_config.into_basic()))); + let service = try!(NetworkService::new(try!(network_config.clone().into_basic()))); let sync = Arc::new(EthSync{ network: service, handler: Arc::new(SyncProtocolHandler { @@ -118,6 +121,7 @@ impl EthSync { overlay: RwLock::new(HashMap::new()), }), subprotocol_name: config.subprotocol_name, + config: network_config, }); Ok(sync) @@ -276,6 +280,29 @@ impl ManageNetwork for EthSync { } } +/// IP fiter +#[derive(Binary, Clone, Debug, PartialEq, Eq)] +pub enum AllowIP { + /// Connect to any address + All, + /// Connect to private network only + Private, + /// Connect to public network only + Public, +} + +impl AllowIP { + /// Attempt to parse the peer mode from a string. + pub fn parse(s: &str) -> Option { + match s { + "all" => Some(AllowIP::All), + "private" => Some(AllowIP::Private), + "public" => Some(AllowIP::Public), + _ => None, + } + } +} + #[derive(Binary, Debug, Clone, PartialEq, Eq)] /// Network service configuration pub struct NetworkConfiguration { @@ -301,10 +328,16 @@ pub struct NetworkConfiguration { pub max_peers: u32, /// Min number of connected peers to maintain pub min_peers: u32, + /// Max pending peers. + pub max_pending_peers: u32, + /// Reserved snapshot sync peers. + pub snapshot_peers: u32, /// List of reserved node addresses. pub reserved_nodes: Vec, /// The non-reserved peer mode. pub allow_non_reserved: bool, + /// IP Filtering + pub allow_ips: AllowIP, } impl NetworkConfiguration { @@ -340,7 +373,14 @@ impl NetworkConfiguration { use_secret: self.use_secret, max_peers: self.max_peers, min_peers: self.min_peers, + max_handshakes: self.max_pending_peers, + reserved_protocols: hash_map![WARP_SYNC_PROTOCOL_ID => self.snapshot_peers], reserved_nodes: self.reserved_nodes, + allow_ips: match self.allow_ips { + AllowIP::All => NetworkAllowIP::All, + AllowIP::Private => NetworkAllowIP::Private, + AllowIP::Public => NetworkAllowIP::Public, + }, non_reserved_mode: if self.allow_non_reserved { NonReservedPeerMode::Accept } else { NonReservedPeerMode::Deny }, }) } @@ -360,7 +400,14 @@ impl From for NetworkConfiguration { use_secret: other.use_secret, max_peers: other.max_peers, min_peers: other.min_peers, + max_pending_peers: other.max_handshakes, + snapshot_peers: *other.reserved_protocols.get(&WARP_SYNC_PROTOCOL_ID).unwrap_or(&0), reserved_nodes: other.reserved_nodes, + allow_ips: match other.allow_ips { + NetworkAllowIP::All => AllowIP::All, + NetworkAllowIP::Private => AllowIP::Private, + NetworkAllowIP::Public => AllowIP::Public, + }, allow_non_reserved: match other.non_reserved_mode { NonReservedPeerMode::Accept => true, _ => false } , } } diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 6cfe2a26c..532c05711 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -61,7 +61,7 @@ mod api { } pub use api::{EthSync, SyncProvider, SyncClient, NetworkManagerClient, ManageNetwork, SyncConfig, - ServiceConfiguration, NetworkConfiguration, PeerInfo}; + ServiceConfiguration, NetworkConfiguration, PeerInfo, AllowIP}; pub use chain::{SyncStatus, SyncState}; pub use network::{is_valid_node_url, NonReservedPeerMode, NetworkError}; diff --git a/util/network/src/discovery.rs b/util/network/src/discovery.rs index 61eaf4094..595ac7605 100644 --- a/util/network/src/discovery.rs +++ b/util/network/src/discovery.rs @@ -29,6 +29,7 @@ use node_table::*; use error::NetworkError; use io::{StreamToken, IoContext}; use ethkey::{Secret, KeyPair, sign, recover}; +use AllowIP; use PROTOCOL_VERSION; @@ -95,6 +96,7 @@ pub struct Discovery { send_queue: VecDeque, check_timestamps: bool, adding_nodes: Vec, + allow_ips: AllowIP, } pub struct TableUpdates { @@ -103,7 +105,7 @@ pub struct TableUpdates { } impl Discovery { - pub fn new(key: &KeyPair, listen: SocketAddr, public: NodeEndpoint, token: StreamToken) -> Discovery { + pub fn new(key: &KeyPair, listen: SocketAddr, public: NodeEndpoint, token: StreamToken, allow_ips: AllowIP) -> Discovery { let socket = UdpSocket::bound(&listen).expect("Error binding UDP socket"); Discovery { id: key.public().clone(), @@ -118,14 +120,17 @@ impl Discovery { send_queue: VecDeque::new(), check_timestamps: true, adding_nodes: Vec::new(), + allow_ips: allow_ips, } } /// Add a new node to discovery table. Pings the node. pub fn add_node(&mut self, e: NodeEntry) { - let endpoint = e.endpoint.clone(); - self.update_node(e); - self.ping(&endpoint); + if e.endpoint.is_allowed(self.allow_ips) { + let endpoint = e.endpoint.clone(); + self.update_node(e); + self.ping(&endpoint); + } } /// Add a list of nodes. Pings a few nodes each round @@ -137,7 +142,9 @@ impl Discovery { /// Add a list of known nodes to the table. pub fn init_node_list(&mut self, mut nodes: Vec) { for n in nodes.drain(..) { - self.update_node(n); + if n.endpoint.is_allowed(self.allow_ips) { + self.update_node(n); + } } } @@ -394,10 +401,11 @@ impl Discovery { try!(self.check_timestamp(timestamp)); let mut added_map = HashMap::new(); let entry = NodeEntry { id: node.clone(), endpoint: source.clone() }; - if !entry.endpoint.is_valid() || !entry.endpoint.is_global() { + if !entry.endpoint.is_valid() { debug!(target: "discovery", "Got bad address: {:?}", entry); - } - else { + } else if !entry.endpoint.is_allowed(self.allow_ips) { + debug!(target: "discovery", "Address not allowed: {:?}", entry); + } else { self.update_node(entry.clone()); added_map.insert(node.clone(), entry); } @@ -470,6 +478,10 @@ impl Discovery { debug!(target: "discovery", "Bad address: {:?}", endpoint); continue; } + if !endpoint.is_allowed(self.allow_ips) { + debug!(target: "discovery", "Address not allowed: {:?}", endpoint); + continue; + } let node_id: NodeId = try!(r.val_at(3)); if node_id == self.id { continue; @@ -539,6 +551,7 @@ mod tests { use std::str::FromStr; use rustc_serialize::hex::FromHex; use ethkey::{Random, Generator}; + use AllowIP; #[test] fn find_node() { @@ -563,8 +576,8 @@ mod tests { let key2 = Random.generate().unwrap(); let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40444").unwrap(), udp_port: 40444 }; let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40445").unwrap(), udp_port: 40445 }; - let mut discovery1 = Discovery::new(&key1, ep1.address.clone(), ep1.clone(), 0); - let mut discovery2 = Discovery::new(&key2, ep2.address.clone(), ep2.clone(), 0); + let mut discovery1 = Discovery::new(&key1, ep1.address.clone(), ep1.clone(), 0, AllowIP::All); + let mut discovery2 = Discovery::new(&key2, ep2.address.clone(), ep2.clone(), 0, AllowIP::All); let node1 = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@127.0.0.1:7770").unwrap(); let node2 = Node::from_str("enode://b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@127.0.0.1:7771").unwrap(); @@ -596,7 +609,7 @@ mod tests { fn removes_expired() { let key = Random.generate().unwrap(); let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40446").unwrap(), udp_port: 40447 }; - let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0); + let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0, AllowIP::All); for _ in 0..1200 { discovery.add_node(NodeEntry { id: NodeId::random(), endpoint: ep.clone() }); } @@ -624,7 +637,7 @@ mod tests { fn packets() { let key = Random.generate().unwrap(); let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40447").unwrap(), udp_port: 40447 }; - let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0); + let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0, AllowIP::All); discovery.check_timestamps = false; let from = SocketAddr::from_str("99.99.99.99:40445").unwrap(); diff --git a/util/network/src/host.rs b/util/network/src/host.rs index a6d61d26f..866534397 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -34,7 +34,7 @@ use rlp::*; use session::{Session, SessionInfo, SessionData}; use error::*; use io::*; -use {NetworkProtocolHandler, NonReservedPeerMode, PROTOCOL_VERSION}; +use {NetworkProtocolHandler, NonReservedPeerMode, AllowIP, PROTOCOL_VERSION}; use node_table::*; use stats::NetworkStats; use discovery::{Discovery, TableUpdates, NodeEntry}; @@ -45,8 +45,7 @@ use parking_lot::{Mutex, RwLock}; type Slab = ::slab::Slab; const MAX_SESSIONS: usize = 1024 + MAX_HANDSHAKES; -const MAX_HANDSHAKES: usize = 80; -const MAX_HANDSHAKES_PER_ROUND: usize = 32; +const MAX_HANDSHAKES: usize = 1024; // Tokens const TCP_ACCEPT: usize = SYS_TIMER + 1; @@ -89,12 +88,18 @@ pub struct NetworkConfiguration { pub use_secret: Option, /// Minimum number of connected peers to maintain pub min_peers: u32, - /// Maximum allowd number of peers + /// Maximum allowed number of peers pub max_peers: u32, + /// Maximum handshakes + pub max_handshakes: u32, + /// Reserved protocols. Peers with protocol get additional connection slots. + pub reserved_protocols: HashMap, /// List of reserved node addresses. pub reserved_nodes: Vec, /// The non-reserved peer mode. pub non_reserved_mode: NonReservedPeerMode, + /// IP filter + pub allow_ips: AllowIP, } impl Default for NetworkConfiguration { @@ -118,6 +123,9 @@ impl NetworkConfiguration { use_secret: None, min_peers: 25, max_peers: 50, + max_handshakes: 64, + reserved_protocols: HashMap::new(), + allow_ips: AllowIP::All, reserved_nodes: Vec::new(), non_reserved_mode: NonReservedPeerMode::Accept, } @@ -364,7 +372,7 @@ pub struct Host { impl Host { /// Create a new instance - pub fn new(config: NetworkConfiguration, stats: Arc) -> Result { + pub fn new(mut config: NetworkConfiguration, stats: Arc) -> Result { trace!(target: "host", "Creating new Host object"); let mut listen_address = match config.listen_address { @@ -394,6 +402,7 @@ impl Host { let boot_nodes = config.boot_nodes.clone(); let reserved_nodes = config.reserved_nodes.clone(); + config.max_handshakes = min(config.max_handshakes, MAX_HANDSHAKES as u32); let mut host = Host { info: RwLock::new(HostInfo { @@ -532,6 +541,7 @@ impl Host { } let local_endpoint = self.info.read().local_endpoint.clone(); let public_address = self.info.read().config.public_address.clone(); + let allow_ips = self.info.read().config.allow_ips; let public_endpoint = match public_address { None => { let public_address = select_public_address(local_endpoint.address.port()); @@ -563,7 +573,7 @@ impl Host { if info.config.discovery_enabled && info.config.non_reserved_mode == NonReservedPeerMode::Accept { let mut udp_addr = local_endpoint.address.clone(); udp_addr.set_port(local_endpoint.udp_port); - Some(Discovery::new(&info.keys, udp_addr, public_endpoint, DISCOVERY)) + Some(Discovery::new(&info.keys, udp_addr, public_endpoint, DISCOVERY, allow_ips)) } else { None } }; @@ -618,14 +628,14 @@ impl Host { } fn connect_peers(&self, io: &IoContext) { - let (min_peers, mut pin) = { + let (min_peers, mut pin, max_handshakes, allow_ips) = { let info = self.info.read(); if info.capabilities.is_empty() { return; } let config = &info.config; - (config.min_peers, config.non_reserved_mode == NonReservedPeerMode::Deny) + (config.min_peers, config.non_reserved_mode == NonReservedPeerMode::Deny, config.max_handshakes as usize, config.allow_ips) }; let session_count = self.session_count(); @@ -642,22 +652,22 @@ impl Host { let handshake_count = self.handshake_count(); // allow 16 slots for incoming connections - let handshake_limit = MAX_HANDSHAKES - 16; - if handshake_count >= handshake_limit { + if handshake_count >= max_handshakes { return; } // iterate over all nodes, reserved ones coming first. // if we are pinned to only reserved nodes, ignore all others. let nodes = reserved_nodes.iter().cloned().chain(if !pin { - self.nodes.read().nodes() + self.nodes.read().nodes(allow_ips) } else { Vec::new() }); + let max_handshakes_per_round = max_handshakes / 2; let mut started: usize = 0; for id in nodes.filter(|ref id| !self.have_session(id) && !self.connecting_to(id)) - .take(min(MAX_HANDSHAKES_PER_ROUND, handshake_limit - handshake_count)) { + .take(min(max_handshakes_per_round, max_handshakes - handshake_count)) { self.connect_peer(&id, io); started += 1; } @@ -790,7 +800,14 @@ impl Host { let session_count = self.session_count(); let (max_peers, reserved_only) = { let info = self.info.read(); - (info.config.max_peers, info.config.non_reserved_mode == NonReservedPeerMode::Deny) + let mut max_peers = info.config.max_peers; + for cap in s.info.capabilities.iter() { + if let Some(num) = info.config.reserved_protocols.get(&cap.protocol) { + max_peers += *num; + break; + } + } + (max_peers, info.config.non_reserved_mode == NonReservedPeerMode::Deny) }; if session_count >= max_peers as usize || reserved_only { diff --git a/util/network/src/ip_utils.rs b/util/network/src/ip_utils.rs index 9bdafb7c7..7ccf75200 100644 --- a/util/network/src/ip_utils.rs +++ b/util/network/src/ip_utils.rs @@ -56,6 +56,22 @@ impl SocketAddrExt for Ipv6Addr { } } +impl SocketAddrExt for IpAddr { + fn is_unspecified_s(&self) -> bool { + match *self { + IpAddr::V4(ref ip) => ip.is_unspecified_s(), + IpAddr::V6(ref ip) => ip.is_unspecified_s(), + } + } + + fn is_global_s(&self) -> bool { + match *self { + IpAddr::V4(ref ip) => ip.is_global_s(), + IpAddr::V6(ref ip) => ip.is_global_s(), + } + } +} + #[cfg(not(windows))] mod getinterfaces { use std::{mem, io, ptr}; diff --git a/util/network/src/lib.rs b/util/network/src/lib.rs index 627458c1c..fcd36235a 100644 --- a/util/network/src/lib.rs +++ b/util/network/src/lib.rs @@ -137,3 +137,15 @@ impl NonReservedPeerMode { } } } + +/// IP fiter +#[derive(Clone, Debug, PartialEq, Eq, Copy)] +pub enum AllowIP { + /// Connect to any address + All, + /// Connect to private network only + Private, + /// Connect to public network only + Public, +} + diff --git a/util/network/src/node_table.rs b/util/network/src/node_table.rs index c90e35a27..97fb29607 100644 --- a/util/network/src/node_table.rs +++ b/util/network/src/node_table.rs @@ -30,6 +30,7 @@ use util::UtilError; use rlp::*; use time::Tm; use error::NetworkError; +use AllowIP; use discovery::{TableUpdates, NodeEntry}; use ip_utils::*; pub use rustc_serialize::json::Json; @@ -53,9 +54,15 @@ impl NodeEndpoint { SocketAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a.ip().clone(), self.udp_port, a.flowinfo(), a.scope_id())), } } -} -impl NodeEndpoint { + pub fn is_allowed(&self, filter: AllowIP) -> bool { + match filter { + AllowIP::All => true, + AllowIP::Private => !self.address.ip().is_global_s(), + AllowIP::Public => self.address.ip().is_global_s(), + } + } + pub fn from_rlp(rlp: &UntrustedRlp) -> Result { let tcp_port = try!(rlp.val_at::(2)); let udp_port = try!(rlp.val_at::(1)); @@ -98,13 +105,6 @@ impl NodeEndpoint { SocketAddr::V6(a) => !a.ip().is_unspecified_s() } } - - pub fn is_global(&self) -> bool { - match self.address { - SocketAddr::V4(a) => a.ip().is_global_s(), - SocketAddr::V6(a) => a.ip().is_global_s() - } - } } impl FromStr for NodeEndpoint { @@ -219,8 +219,8 @@ impl NodeTable { } /// Returns node ids sorted by number of failures - pub fn nodes(&self) -> Vec { - let mut refs: Vec<&Node> = self.nodes.values().filter(|n| !self.useless_nodes.contains(&n.id)).collect(); + pub fn nodes(&self, filter: AllowIP) -> Vec { + let mut refs: Vec<&Node> = self.nodes.values().filter(|n| !self.useless_nodes.contains(&n.id) && n.endpoint.is_allowed(filter)).collect(); refs.sort_by(|a, b| a.failures.cmp(&b.failures)); refs.iter().map(|n| n.id.clone()).collect() } @@ -278,7 +278,7 @@ impl NodeTable { let mut json = String::new(); json.push_str("{\n"); json.push_str("\"nodes\": [\n"); - let node_ids = self.nodes(); + let node_ids = self.nodes(AllowIP::All); for i in 0 .. node_ids.len() { let node = self.nodes.get(&node_ids[i]).unwrap(); json.push_str(&format!("\t{{ \"url\": \"{}\", \"failures\": {} }}{}\n", node, node.failures, if i == node_ids.len() - 1 {""} else {","})) @@ -361,6 +361,7 @@ mod tests { use std::net::*; use util::hash::*; use devtools::*; + use AllowIP; #[test] fn endpoint_parse() { @@ -406,7 +407,7 @@ mod tests { table.note_failure(&id1); table.note_failure(&id2); - let r = table.nodes(); + let r = table.nodes(AllowIP::All); assert_eq!(r[0][..], id3[..]); assert_eq!(r[1][..], id2[..]); assert_eq!(r[2][..], id1[..]); @@ -428,7 +429,7 @@ mod tests { { let table = NodeTable::new(Some(temp_path.as_path().to_str().unwrap().to_owned())); - let r = table.nodes(); + let r = table.nodes(AllowIP::All); assert_eq!(r[0][..], id1[..]); assert_eq!(r[1][..], id2[..]); } diff --git a/util/src/kvdb.rs b/util/src/kvdb.rs index 4c5b7a3d0..3a2652c3d 100644 --- a/util/src/kvdb.rs +++ b/util/src/kvdb.rs @@ -608,7 +608,6 @@ mod tests { use super::*; use devtools::*; use std::str::FromStr; - use std::path::PathBuf; fn test_db(config: &DatabaseConfig) { let path = RandomTempPath::create_dir(); @@ -673,6 +672,7 @@ mod tests { #[test] #[cfg(target_os = "linux")] fn df_to_rotational() { + use std::path::PathBuf; // Example df output. let example_df = vec![70, 105, 108, 101, 115, 121, 115, 116, 101, 109, 32, 32, 32, 32, 32, 49, 75, 45, 98, 108, 111, 99, 107, 115, 32, 32, 32, 32, 32, 85, 115, 101, 100, 32, 65, 118, 97, 105, 108, 97, 98, 108, 101, 32, 85, 115, 101, 37, 32, 77, 111, 117, 110, 116, 101, 100, 32, 111, 110, 10, 47, 100, 101, 118, 47, 115, 100, 97, 49, 32, 32, 32, 32, 32, 32, 32, 54, 49, 52, 48, 57, 51, 48, 48, 32, 51, 56, 56, 50, 50, 50, 51, 54, 32, 32, 49, 57, 52, 52, 52, 54, 49, 54, 32, 32, 54, 55, 37, 32, 47, 10]; let expected_output = Some(PathBuf::from("/sys/block/sda/queue/rotational"));