use crate::printer::{Printer, Stat};
use crate::timeout_stream::TimeoutStream;
-struct Route {
- path: Vec<u32>,
+const PATH_SUFFIX_LEN: usize = 3;
+#[derive(Clone)]
+struct Route { // 32 bytes
+ path_suffix: [u32; PATH_SUFFIX_LEN],
+ path_len: u32,
pref: u32,
med: u32,
}
struct RoutingTable {
- v4_table: HashMap<(Ipv4Addr, u8), HashMap<u32, Arc<Route>>>,
- v6_table: HashMap<(Ipv6Addr, u8), HashMap<u32, Arc<Route>>>,
+ v4_table: HashMap<(Ipv4Addr, u8), HashMap<u32, Route>>,
+ v6_table: HashMap<(Ipv6Addr, u8), HashMap<u32, Route>>,
}
impl RoutingTable {
}
}
- fn get_route_attrs(&self, ip: IpAddr) -> Vec<Arc<Route>> {
+ fn get_route_attrs(&self, ip: IpAddr) -> (u8, Vec<&Route>) {
macro_rules! lookup_res {
($addrty: ty, $addr: expr, $table: expr, $addr_bits: expr) => { {
//TODO: Optimize this (probably means making the tables btrees)!
- for i in (0..$addr_bits).rev() {
- let mut lookup = $addr.octets();
- for b in 0..(i / 8) {
- lookup[lookup.len() - b - 1] = 0;
- }
- lookup[lookup.len() - (i/8) - 1] &= !(((1u16 << (i % 8)) - 1) as u8);
+ let mut lookup = $addr.octets();
+ for i in 0..$addr_bits {
let lookup_addr = <$addrty>::from(lookup);
if let Some(routes) = $table.get(&(lookup_addr, $addr_bits - i as u8)).map(|hm| hm.values()) {
if routes.len() > 0 {
- return routes.map(|x| Arc::clone(&x)).collect();
+ return ($addr_bits - i as u8, routes.collect());
}
}
+ lookup[lookup.len() - (i/8) - 1] &= !(1u8 << (i % 8));
}
- vec![]
+ (0, vec![])
} }
}
match ip {
};
}
- fn announce(&mut self, prefix: NLRIEncoding, route: Arc<Route>) {
+ fn announce(&mut self, prefix: NLRIEncoding, route: Route) {
match prefix {
NLRIEncoding::IP(p) => {
let (ip, len) = <(IpAddr, u8)>::from(&p);
}
impl BGPClient {
pub fn get_asn(&self, addr: IpAddr) -> u32 {
- let mut path_vecs = self.routes.lock().unwrap().get_route_attrs(addr).clone();
+ let lock = self.routes.lock().unwrap();
+ let mut path_vecs = lock.get_route_attrs(addr).1;
if path_vecs.is_empty() { return 0; }
path_vecs.sort_unstable_by(|path_a, path_b| {
path_a.pref.cmp(&path_b.pref)
- .then(path_b.path.len().cmp(&path_a.path.len()))
+ .then(path_b.path_len.cmp(&path_a.path_len))
.then(path_b.med.cmp(&path_a.med))
});
let primary_route = path_vecs.pop().unwrap();
- 'asn_candidates: for asn in primary_route.path.iter().rev() {
+ '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.contains(asn) {
+ if !secondary_route.path_suffix.contains(asn) {
continue 'asn_candidates;
}
}
return *asn;
}
- *primary_route.path.last().unwrap_or(&0)
+
+ for asn in primary_route.path_suffix.iter().rev() {
+ if *asn != 0 {
+ return *asn;
+ }
+ }
+ 0
+ }
+
+ pub fn get_path(&self, addr: IpAddr) -> (u8, [u32; PATH_SUFFIX_LEN]) {
+ let lock = self.routes.lock().unwrap();
+ let (prefixlen, mut path_vecs) = lock.get_route_attrs(addr);
+ if path_vecs.is_empty() { return (0, [0; PATH_SUFFIX_LEN]); }
+
+ path_vecs.sort_unstable_by(|path_a, path_b| {
+ path_a.pref.cmp(&path_b.pref)
+ .then(path_b.path_len.cmp(&path_a.path_len))
+ .then(path_b.med.cmp(&path_a.med))
+ });
+
+ let primary_route = path_vecs.pop().unwrap();
+ (prefixlen, primary_route.path_suffix)
}
pub fn disconnect(&self) {
}
}
if let Some(mut aspath) = as4_path.or(as_path) {
- let mut path = Vec::new();
+ let mut pathvec = Vec::new();
for seg in aspath.segments.drain(..) {
match seg {
- Segment::AS_SEQUENCE(mut asn) => path.append(&mut asn),
+ Segment::AS_SEQUENCE(mut asn) => pathvec.append(&mut asn),
Segment::AS_SET(_) => {}, // Ignore sets for now, they're not that common anyway
}
}
+ let path_len = pathvec.len() as u32;
+ pathvec.dedup_by(|a, b| (*a).eq(b)); // Drop prepends, cause we don't care in this case
+
+ let mut path_suffix = [0; PATH_SUFFIX_LEN];
+ for (idx, asn) in pathvec.iter().rev().enumerate() {
+ path_suffix[PATH_SUFFIX_LEN - idx - 1] = *asn;
+ if idx == PATH_SUFFIX_LEN - 1 { break; }
+ }
+
return Some(Route {
- path: path.clone(),
+ path_suffix,
+ path_len,
pref,
med,
})
route_table.withdraw(r);
}
if let Some(path) = Self::map_attrs(upd.attributes) {
- let path_arc = Arc::new(path);
for r in upd.announced_routes {
- route_table.announce(r, Arc::clone(&path_arc));
+ route_table.announce(r, path.clone());
}
}
printer.set_stat(Stat::V4RoutingTableSize(route_table.v4_table.len()));