Browse Source

solana-dos can now DoS gossip nodes (#9652)

automerge
Michael Vines 5 years ago
parent
commit
d1cbccd9ba

+ 38 - 23
core/src/gossip_service.rs

@@ -63,7 +63,7 @@ impl GossipService {
     }
     }
 }
 }
 
 
-/// Discover Nodes and Archivers in a cluster
+/// Discover Validators and Archivers in a cluster
 pub fn discover_cluster(
 pub fn discover_cluster(
     entrypoint: &SocketAddr,
     entrypoint: &SocketAddr,
     num_nodes: usize,
     num_nodes: usize,
@@ -76,16 +76,17 @@ pub fn discover_cluster(
         None,
         None,
         None,
         None,
     )
     )
+    .map(|(_all_peers, validators, archivers)| (validators, archivers))
 }
 }
 
 
 pub fn discover(
 pub fn discover(
     entrypoint: Option<&SocketAddr>,
     entrypoint: Option<&SocketAddr>,
-    num_nodes: Option<usize>,
+    num_nodes: Option<usize>, // num_nodes only counts validators and archivers, excludes spy nodes
     timeout: Option<u64>,
     timeout: Option<u64>,
     find_node_by_pubkey: Option<Pubkey>,
     find_node_by_pubkey: Option<Pubkey>,
     find_node_by_gossip_addr: Option<&SocketAddr>,
     find_node_by_gossip_addr: Option<&SocketAddr>,
     my_gossip_addr: Option<&SocketAddr>,
     my_gossip_addr: Option<&SocketAddr>,
-) -> std::io::Result<(Vec<ContactInfo>, Vec<ContactInfo>)> {
+) -> std::io::Result<(Vec<ContactInfo>, Vec<ContactInfo>, Vec<ContactInfo>)> {
     let exit = Arc::new(AtomicBool::new(false));
     let exit = Arc::new(AtomicBool::new(false));
     let (gossip_service, ip_echo, spy_ref) = make_gossip_node(entrypoint, &exit, my_gossip_addr);
     let (gossip_service, ip_echo, spy_ref) = make_gossip_node(entrypoint, &exit, my_gossip_addr);
 
 
@@ -98,7 +99,7 @@ pub fn discover(
 
 
     let _ip_echo_server = ip_echo.map(solana_net_utils::ip_echo_server);
     let _ip_echo_server = ip_echo.map(solana_net_utils::ip_echo_server);
 
 
-    let (met_criteria, secs, tvu_peers, storage_peers) = spy(
+    let (met_criteria, secs, all_peers, tvu_peers, storage_peers) = spy(
         spy_ref.clone(),
         spy_ref.clone(),
         num_nodes,
         num_nodes,
         timeout,
         timeout,
@@ -115,7 +116,7 @@ pub fn discover(
             secs,
             secs,
             spy_ref.contact_info_trace()
             spy_ref.contact_info_trace()
         );
         );
-        return Ok((tvu_peers, storage_peers));
+        return Ok((all_peers, tvu_peers, storage_peers));
     }
     }
 
 
     if !tvu_peers.is_empty() {
     if !tvu_peers.is_empty() {
@@ -123,7 +124,7 @@ pub fn discover(
             "discover failed to match criteria by timeout...\n{}",
             "discover failed to match criteria by timeout...\n{}",
             spy_ref.contact_info_trace()
             spy_ref.contact_info_trace()
         );
         );
-        return Ok((tvu_peers, storage_peers));
+        return Ok((all_peers, tvu_peers, storage_peers));
     }
     }
 
 
     info!("discover failed...\n{}", spy_ref.contact_info_trace());
     info!("discover failed...\n{}", spy_ref.contact_info_trace());
@@ -178,9 +179,16 @@ fn spy(
     timeout: Option<u64>,
     timeout: Option<u64>,
     find_node_by_pubkey: Option<Pubkey>,
     find_node_by_pubkey: Option<Pubkey>,
     find_node_by_gossip_addr: Option<&SocketAddr>,
     find_node_by_gossip_addr: Option<&SocketAddr>,
-) -> (bool, u64, Vec<ContactInfo>, Vec<ContactInfo>) {
+) -> (
+    bool,
+    u64,
+    Vec<ContactInfo>,
+    Vec<ContactInfo>,
+    Vec<ContactInfo>,
+) {
     let now = Instant::now();
     let now = Instant::now();
     let mut met_criteria = false;
     let mut met_criteria = false;
+    let mut all_peers: Vec<ContactInfo> = Vec::new();
     let mut tvu_peers: Vec<ContactInfo> = Vec::new();
     let mut tvu_peers: Vec<ContactInfo> = Vec::new();
     let mut storage_peers: Vec<ContactInfo> = Vec::new();
     let mut storage_peers: Vec<ContactInfo> = Vec::new();
     let mut i = 1;
     let mut i = 1;
@@ -191,26 +199,32 @@ fn spy(
             }
             }
         }
         }
 
 
+        all_peers = spy_ref
+            .all_peers()
+            .into_iter()
+            .map(|x| x.0)
+            .collect::<Vec<_>>();
         tvu_peers = spy_ref.all_tvu_peers().into_iter().collect::<Vec<_>>();
         tvu_peers = spy_ref.all_tvu_peers().into_iter().collect::<Vec<_>>();
         storage_peers = spy_ref.all_storage_peers();
         storage_peers = spy_ref.all_storage_peers();
 
 
-        let mut nodes: Vec<_> = tvu_peers.iter().chain(storage_peers.iter()).collect();
-        nodes.sort();
-        nodes.dedup();
-
         let found_node_by_pubkey = if let Some(pubkey) = find_node_by_pubkey {
         let found_node_by_pubkey = if let Some(pubkey) = find_node_by_pubkey {
-            nodes.iter().any(|x| x.id == pubkey)
+            all_peers.iter().any(|x| x.id == pubkey)
         } else {
         } else {
             false
             false
         };
         };
 
 
         let found_node_by_gossip_addr = if let Some(gossip_addr) = find_node_by_gossip_addr {
         let found_node_by_gossip_addr = if let Some(gossip_addr) = find_node_by_gossip_addr {
-            nodes.iter().any(|x| x.gossip == *gossip_addr)
+            all_peers.iter().any(|x| x.gossip == *gossip_addr)
         } else {
         } else {
             false
             false
         };
         };
 
 
         if let Some(num) = num_nodes {
         if let Some(num) = num_nodes {
+            // Only consider validators and archives for `num_nodes`
+            let mut nodes: Vec<_> = tvu_peers.iter().chain(storage_peers.iter()).collect();
+            nodes.sort();
+            nodes.dedup();
+
             if nodes.len() >= num {
             if nodes.len() >= num {
                 if found_node_by_pubkey || found_node_by_gossip_addr {
                 if found_node_by_pubkey || found_node_by_gossip_addr {
                     met_criteria = true;
                     met_criteria = true;
@@ -234,6 +248,7 @@ fn spy(
     (
     (
         met_criteria,
         met_criteria,
         now.elapsed().as_secs(),
         now.elapsed().as_secs(),
+        all_peers,
         tvu_peers,
         tvu_peers,
         storage_peers,
         storage_peers,
     )
     )
@@ -295,21 +310,21 @@ mod tests {
 
 
         let spy_ref = Arc::new(cluster_info);
         let spy_ref = Arc::new(cluster_info);
 
 
-        let (met_criteria, secs, tvu_peers, _) = spy(spy_ref.clone(), None, Some(1), None, None);
+        let (met_criteria, secs, _, tvu_peers, _) = spy(spy_ref.clone(), None, Some(1), None, None);
         assert_eq!(met_criteria, false);
         assert_eq!(met_criteria, false);
         assert_eq!(secs, 1);
         assert_eq!(secs, 1);
         assert_eq!(tvu_peers, spy_ref.tvu_peers());
         assert_eq!(tvu_peers, spy_ref.tvu_peers());
 
 
         // Find num_nodes
         // Find num_nodes
-        let (met_criteria, _, _, _) = spy(spy_ref.clone(), Some(1), None, None, None);
+        let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), Some(1), None, None, None);
         assert_eq!(met_criteria, true);
         assert_eq!(met_criteria, true);
-        let (met_criteria, _, _, _) = spy(spy_ref.clone(), Some(2), None, None, None);
+        let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), Some(2), None, None, None);
         assert_eq!(met_criteria, true);
         assert_eq!(met_criteria, true);
 
 
         // Find specific node by pubkey
         // Find specific node by pubkey
-        let (met_criteria, _, _, _) = spy(spy_ref.clone(), None, None, Some(peer0), None);
+        let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), None, None, Some(peer0), None);
         assert_eq!(met_criteria, true);
         assert_eq!(met_criteria, true);
-        let (met_criteria, _, _, _) = spy(
+        let (met_criteria, _, _, _, _) = spy(
             spy_ref.clone(),
             spy_ref.clone(),
             None,
             None,
             Some(0),
             Some(0),
@@ -319,11 +334,11 @@ mod tests {
         assert_eq!(met_criteria, false);
         assert_eq!(met_criteria, false);
 
 
         // Find num_nodes *and* specific node by pubkey
         // Find num_nodes *and* specific node by pubkey
-        let (met_criteria, _, _, _) = spy(spy_ref.clone(), Some(1), None, Some(peer0), None);
+        let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), Some(1), None, Some(peer0), None);
         assert_eq!(met_criteria, true);
         assert_eq!(met_criteria, true);
-        let (met_criteria, _, _, _) = spy(spy_ref.clone(), Some(3), Some(0), Some(peer0), None);
+        let (met_criteria, _, _, _, _) = spy(spy_ref.clone(), Some(3), Some(0), Some(peer0), None);
         assert_eq!(met_criteria, false);
         assert_eq!(met_criteria, false);
-        let (met_criteria, _, _, _) = spy(
+        let (met_criteria, _, _, _, _) = spy(
             spy_ref.clone(),
             spy_ref.clone(),
             Some(1),
             Some(1),
             Some(0),
             Some(0),
@@ -333,11 +348,11 @@ mod tests {
         assert_eq!(met_criteria, false);
         assert_eq!(met_criteria, false);
 
 
         // Find specific node by gossip address
         // Find specific node by gossip address
-        let (met_criteria, _, _, _) =
+        let (met_criteria, _, _, _, _) =
             spy(spy_ref.clone(), None, None, None, Some(&peer0_info.gossip));
             spy(spy_ref.clone(), None, None, None, Some(&peer0_info.gossip));
         assert_eq!(met_criteria, true);
         assert_eq!(met_criteria, true);
 
 
-        let (met_criteria, _, _, _) = spy(
+        let (met_criteria, _, _, _, _) = spy(
             spy_ref.clone(),
             spy_ref.clone(),
             None,
             None,
             Some(0),
             Some(0),

+ 2 - 2
dos/src/main.rs

@@ -91,7 +91,7 @@ fn run_dos(
 }
 }
 
 
 fn main() {
 fn main() {
-    solana_logger::setup();
+    solana_logger::setup_with_default("solana=info");
     let matches = App::new(crate_name!())
     let matches = App::new(crate_name!())
         .about(crate_description!())
         .about(crate_description!())
         .version(solana_clap_utils::version!())
         .version(solana_clap_utils::version!())
@@ -148,7 +148,7 @@ fn main() {
     let data_type = value_t_or_exit!(matches, "data_type", String);
     let data_type = value_t_or_exit!(matches, "data_type", String);
 
 
     info!("Finding cluster entry: {:?}", entrypoint_addr);
     info!("Finding cluster entry: {:?}", entrypoint_addr);
-    let (nodes, _archivers) = discover(
+    let (nodes, _validators, _archivers) = discover(
         Some(&entrypoint_addr),
         Some(&entrypoint_addr),
         None,
         None,
         Some(60),
         Some(60),

+ 16 - 13
gossip/src/main.rs

@@ -205,7 +205,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
                 }),
                 }),
             );
             );
 
 
-            let (nodes, _archivers) = discover(
+            let (_all_peers, validators, _archivers) = discover(
                 entrypoint_addr.as_ref(),
                 entrypoint_addr.as_ref(),
                 num_nodes,
                 num_nodes,
                 timeout,
                 timeout,
@@ -216,28 +216,28 @@ fn main() -> Result<(), Box<dyn error::Error>> {
 
 
             if timeout.is_some() {
             if timeout.is_some() {
                 if let Some(num) = num_nodes {
                 if let Some(num) = num_nodes {
-                    if nodes.len() < num {
+                    if validators.len() < num {
                         let add = if num_nodes_exactly.is_some() {
                         let add = if num_nodes_exactly.is_some() {
                             ""
                             ""
                         } else {
                         } else {
                             " or more"
                             " or more"
                         };
                         };
                         eprintln!(
                         eprintln!(
-                            "Error: Insufficient nodes discovered.  Expecting {}{}",
+                            "Error: Insufficient validators discovered.  Expecting {}{}",
                             num, add,
                             num, add,
                         );
                         );
                         exit(1);
                         exit(1);
                     }
                     }
                 }
                 }
                 if let Some(node) = pubkey {
                 if let Some(node) = pubkey {
-                    if nodes.iter().find(|x| x.id == node).is_none() {
+                    if validators.iter().find(|x| x.id == node).is_none() {
                         eprintln!("Error: Could not find node {:?}", node);
                         eprintln!("Error: Could not find node {:?}", node);
                         exit(1);
                         exit(1);
                     }
                     }
                 }
                 }
             }
             }
             if let Some(num_nodes_exactly) = num_nodes_exactly {
             if let Some(num_nodes_exactly) = num_nodes_exactly {
-                if nodes.len() > num_nodes_exactly {
+                if validators.len() > num_nodes_exactly {
                     eprintln!(
                     eprintln!(
                         "Error: Extra nodes discovered.  Expecting exactly {}",
                         "Error: Extra nodes discovered.  Expecting exactly {}",
                         num_nodes_exactly
                         num_nodes_exactly
@@ -251,7 +251,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
             let all = matches.is_present("all");
             let all = matches.is_present("all");
             let entrypoint_addr = parse_entrypoint(&matches);
             let entrypoint_addr = parse_entrypoint(&matches);
             let timeout = value_t_or_exit!(matches, "timeout", u64);
             let timeout = value_t_or_exit!(matches, "timeout", u64);
-            let (nodes, _archivers) = discover(
+            let (_all_peers, validators, _archivers) = discover(
                 entrypoint_addr.as_ref(),
                 entrypoint_addr.as_ref(),
                 Some(1),
                 Some(1),
                 Some(timeout),
                 Some(timeout),
@@ -260,7 +260,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
                 None,
                 None,
             )?;
             )?;
 
 
-            let rpc_addrs: Vec<_> = nodes
+            let rpc_addrs: Vec<_> = validators
                 .iter()
                 .iter()
                 .filter_map(|contact_info| {
                 .filter_map(|contact_info| {
                     if (any || all || Some(contact_info.gossip) == entrypoint_addr)
                     if (any || all || Some(contact_info.gossip) == entrypoint_addr)
@@ -291,7 +291,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
                 .unwrap()
                 .unwrap()
                 .parse::<Pubkey>()
                 .parse::<Pubkey>()
                 .unwrap();
                 .unwrap();
-            let (nodes, _archivers) = discover(
+            let (_all_peers, validators, _archivers) = discover(
                 entrypoint_addr.as_ref(),
                 entrypoint_addr.as_ref(),
                 None,
                 None,
                 None,
                 None,
@@ -299,15 +299,18 @@ fn main() -> Result<(), Box<dyn error::Error>> {
                 None,
                 None,
                 None,
                 None,
             )?;
             )?;
-            let node = nodes.iter().find(|x| x.id == pubkey).unwrap();
+            let validator = validators.iter().find(|x| x.id == pubkey).unwrap();
 
 
-            if !ContactInfo::is_valid_address(&node.rpc) {
-                eprintln!("Error: RPC service is not enabled on node {:?}", pubkey);
+            if !ContactInfo::is_valid_address(&validator.rpc) {
+                eprintln!(
+                    "Error: RPC service is not enabled on validator {:?}",
+                    pubkey
+                );
                 exit(1);
                 exit(1);
             }
             }
-            println!("\nSending stop request to node {:?}", pubkey);
+            println!("\nSending stop request to validator {:?}", pubkey);
 
 
-            let result = RpcClient::new_socket(node.rpc).validator_exit()?;
+            let result = RpcClient::new_socket(validator.rpc).validator_exit()?;
             if result {
             if result {
                 println!("Stop signal accepted");
                 println!("Stop signal accepted");
             } else {
             } else {

+ 4 - 0
system-test/stability-testcases/gossip-dos-test.sh

@@ -25,6 +25,10 @@ bootstrapInstall() {
 }
 }
 
 
 bootstrapInstall "edge"
 bootstrapInstall "edge"
+solana-install-init --version
+solana-install-init edge
+solana-gossip --version
+solana-dos --version
 
 
 killall solana-gossip || true
 killall solana-gossip || true
 solana-gossip spy --gossip-port 8001 > "$logDir"/gossip.log 2>&1 &
 solana-gossip spy --gossip-port 8001 > "$logDir"/gossip.log 2>&1 &

+ 2 - 0
system-test/stability-testcases/gossip-dos-test.yml

@@ -1,3 +1,5 @@
 steps:
 steps:
   - command: "system-test/stability-testcases/gossip-dos-test.sh"
   - command: "system-test/stability-testcases/gossip-dos-test.sh"
     label: "Gossip DoS Test"
     label: "Gossip DoS Test"
+    artifact_paths:
+      - "system-test/stability-testcases/logs/*"