Drop memory limit. It was useful to debug OOMs but is now unnecessary
[dnsseed-rust] / src / bgp_client.rs
index 5f436b1ca106f12d132952436961cd5df0db07a7..78c2f8829b96181b5f65038d954ff4a549ff420d 100644 (file)
@@ -79,6 +79,8 @@ struct RoutingTable {
        // and Vecs are way more memory-effecient in that case.
        v4_table: HashMap<V4Addr, Vec<(u32, Route)>>,
        v6_table: HashMap<V6Addr, Vec<(u32, Route)>>,
+       max_paths: usize,
+       routes_with_max: usize,
 }
 
 impl RoutingTable {
@@ -86,6 +88,8 @@ impl RoutingTable {
                Self {
                        v4_table: HashMap::with_capacity(900_000),
                        v6_table: HashMap::with_capacity(100_000),
+                       max_paths: 0,
+                       routes_with_max: 0,
                }
        }
 
@@ -117,6 +121,12 @@ impl RoutingTable {
                        ($rt: expr, $v: expr, $id: expr) => { {
                                match $rt.entry($v.into()) {
                                        hash_map::Entry::Occupied(mut entry) => {
+                                               if entry.get().len() == self.max_paths {
+                                                       self.routes_with_max -= 1;
+                                                       if self.routes_with_max == 0 {
+                                                               self.max_paths = 0;
+                                                       }
+                                               }
                                                entry.get_mut().retain(|e| e.0 != $id);
                                                if entry.get_mut().is_empty() {
                                                        entry.remove();
@@ -151,9 +161,22 @@ impl RoutingTable {
        fn announce(&mut self, prefix: NLRIEncoding, route: Route) {
                macro_rules! insert {
                        ($rt: expr, $v: expr, $id: expr) => { {
-                               let entry = $rt.entry($v.into()).or_insert(Vec::new());
+                               let old_max_paths = self.max_paths;
+                               let entry = $rt.entry($v.into()).or_insert_with(|| Vec::with_capacity(old_max_paths));
+                               let entry_had_max = entry.len() == self.max_paths;
                                entry.retain(|e| e.0 != $id);
+                               if entry_had_max {
+                                       entry.reserve_exact(1);
+                               } else {
+                                       entry.reserve_exact(cmp::max(self.max_paths, entry.len() + 1) - entry.len());
+                               }
                                entry.push(($id, route));
+                               if entry.len() > self.max_paths {
+                                       self.max_paths = entry.len();
+                                       self.routes_with_max = 1;
+                               } else if entry.len() == self.max_paths {
+                                       if !entry_had_max { self.routes_with_max += 1; }
+                               }
                        } }
                }
                match prefix {
@@ -259,14 +282,35 @@ impl BGPClient {
                });
 
                let primary_route = path_vecs.pop().unwrap();
-               'asn_candidates: for asn in primary_route.path_suffix.iter().rev() {
-                       if *asn == 0 { continue 'asn_candidates; }
-                       for secondary_route in path_vecs.iter() {
-                               if !secondary_route.path_suffix.contains(asn) {
-                                       continue 'asn_candidates;
+               if path_vecs.len() > 3 {
+                       // If we have at least 3 paths, try to find the last unique ASN which doesn't show up in other paths
+                       // If we hit a T1 that is reasonably assumed to care about net neutrality, return the
+                       // previous ASN.
+                       let mut prev_asn = 0;
+                       'asn_candidates: for asn in primary_route.path_suffix.iter().rev() {
+                               if *asn == 0 { continue 'asn_candidates; }
+                               match *asn {
+                                       // Included: CenturyLink (L3), Cogent, Telia, NTT, GTT, Level3,
+                                       //           GBLX (L3), Zayo, TI Sparkle Seabone, HE, Telefonica
+                                       // Left out from Caida top-20: TATA, PCCW, Vodafone, RETN, Orange, Telstra,
+                                       //                             Singtel, Rostelecom, DTAG
+                                       209|174|1299|2914|3257|3356|3549|6461|6762|6939|12956 if prev_asn != 0 => return prev_asn,
+                                       _ => if path_vecs.iter().any(|route| !route.path_suffix.contains(asn)) {
+                                               if prev_asn != 0 { return prev_asn } else {
+                                                       // Multi-origin prefix, just give up and take the last AS in the
+                                                       // default path
+                                                       break 'asn_candidates;
+                                               }
+                                       } else {
+                                               // We only ever possibly return an ASN if it appears in all paths
+                                               prev_asn = *asn;
+                                       },
                                }
                        }
-                       return *asn;
+                       // All paths were the same, if the first ASN is non-0, return it.
+                       if prev_asn != 0 {
+                               return prev_asn;
+                       }
                }
 
                for asn in primary_route.path_suffix.iter().rev() {
@@ -336,7 +380,7 @@ impl BGPClient {
                } else { None }
        }
 
-       fn connect_given_client(addr: SocketAddr, timeout: Duration, printer: &'static Printer, client: Arc<BGPClient>) {
+       fn connect_given_client(remote_asn: u32, addr: SocketAddr, timeout: Duration, printer: &'static Printer, client: Arc<BGPClient>) {
                tokio::spawn(Delay::new(Instant::now() + timeout / 4).then(move |_| {
                        let connect_timeout = Delay::new(Instant::now() + timeout.clone()).then(|_| {
                                future::err(std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout reached"))
@@ -354,15 +398,16 @@ impl BGPClient {
                                                .then(|_| {
                                                        future::err(())
                                                }));
+                                       let peer_asn = if remote_asn > u16::max_value() as u32 { 23456 } else { remote_asn as u16 };
                                        let _ = sender.try_send(Message::Open(Open {
                                                version: 4,
-                                               peer_asn: 23456,
+                                               peer_asn,
                                                hold_timer: timeout.as_secs() as u16,
-                                               identifier: 0x453b1215, // 69.59.18.21
+                                               identifier: 0x453b1215, // 69.59.18.21. Note that you never actually need to change this.
                                                parameters: vec![OpenParameter::Capabilities(vec![
                                                        OpenCapability::MultiProtocol((AFI::IPV4, SAFI::Unicast)),
                                                        OpenCapability::MultiProtocol((AFI::IPV6, SAFI::Unicast)),
-                                                       OpenCapability::FourByteASN(397444),
+                                                       OpenCapability::FourByteASN(remote_asn),
                                                        OpenCapability::RouteRefresh,
                                                        OpenCapability::AddPath(vec![
                                                                (AFI::IPV4, SAFI::Unicast, AddPathDirection::ReceivePaths),
@@ -395,6 +440,7 @@ impl BGPClient {
                                                                }
                                                                printer.set_stat(Stat::V4RoutingTableSize(route_table.v4_table.len()));
                                                                printer.set_stat(Stat::V6RoutingTableSize(route_table.v6_table.len()));
+                                                               printer.set_stat(Stat::RoutingTablePaths(route_table.max_paths));
                                                        },
                                                        _ => {}
                                                }
@@ -405,7 +451,7 @@ impl BGPClient {
                                        })
                                }).then(move |_| {
                                        if !client_reconn.shutdown.load(Ordering::Relaxed) {
-                                               BGPClient::connect_given_client(addr, timeout, printer, client_reconn);
+                                               BGPClient::connect_given_client(remote_asn, addr, timeout, printer, client_reconn);
                                        }
                                        future::ok(())
                                })
@@ -413,12 +459,12 @@ impl BGPClient {
                );
        }
 
-       pub fn new(addr: SocketAddr, timeout: Duration, printer: &'static Printer) -> Arc<BGPClient> {
+       pub fn new(remote_asn: u32, addr: SocketAddr, timeout: Duration, printer: &'static Printer) -> Arc<BGPClient> {
                let client = Arc::new(BGPClient {
                        routes: Mutex::new(RoutingTable::new()),
                        shutdown: AtomicBool::new(false),
                });
-               BGPClient::connect_given_client(addr, timeout, printer, Arc::clone(&client));
+               BGPClient::connect_given_client(remote_asn, addr, timeout, printer, Arc::clone(&client));
                client
        }
 }