use crate::encoder::sink::put_u16_len_slice;
use crate::error::{check_max, EncodingError};
use crate::models::{Afi, AsnLength, Peer, PeerIndexTable, PeerType};
use crate::parser::ReadUtils;
use crate::ParserError;
use bytes::{BufMut, Bytes, BytesMut};
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr};
pub fn parse_peer_index_table(data: &mut Bytes) -> Result<PeerIndexTable, ParserError> {
let collector_bgp_id = Ipv4Addr::from(data.read_u32()?);
let view_name_length = data.read_u16()?;
let view_name =
String::from_utf8(data.read_n_bytes(view_name_length as usize)?).unwrap_or("".to_string());
let peer_count = data.read_u16()?;
let mut peers = vec![];
for _index in 0..peer_count {
let peer_type = PeerType::from_bits_retain(data.read_u8()?);
let afi = match peer_type.contains(PeerType::ADDRESS_FAMILY_IPV6) {
true => Afi::Ipv6,
false => Afi::Ipv4,
};
let asn_len = match peer_type.contains(PeerType::AS_SIZE_32BIT) {
true => AsnLength::Bits32,
false => AsnLength::Bits16,
};
let peer_bgp_id = Ipv4Addr::from(data.read_u32()?);
let peer_ip: IpAddr = data.read_address(&afi)?;
let peer_asn = data.read_asn(asn_len)?;
peers.push(Peer {
peer_type,
peer_bgp_id,
peer_ip,
peer_asn,
})
}
let mut id_peer_map = HashMap::new();
let mut peer_ip_id_map = HashMap::new();
for (id, p) in peers.into_iter().enumerate() {
id_peer_map.insert(id as u16, p);
peer_ip_id_map.insert(p.peer_ip, id as u16);
}
Ok(PeerIndexTable {
collector_bgp_id,
view_name,
id_peer_map,
peer_ip_id_map,
})
}
impl PeerIndexTable {
pub fn add_peer(&mut self, peer: Peer) -> Result<u16, EncodingError> {
match self.peer_ip_id_map.get(&peer.peer_ip) {
Some(id) => Ok(*id),
None => {
let next_id = self.peer_ip_id_map.len();
check_max("PeerIndexTable peer count", next_id + 1, u16::MAX as usize)?;
let peer_id = next_id as u16;
self.peer_ip_id_map.insert(peer.peer_ip, peer_id);
self.id_peer_map.insert(peer_id, peer);
Ok(peer_id)
}
}
}
pub fn get_peer_by_id(&self, peer_id: &u16) -> Option<&Peer> {
self.id_peer_map.get(peer_id)
}
pub fn get_peer_id_by_addr(&self, peer_ip: &IpAddr) -> Option<u16> {
self.peer_ip_id_map.get(peer_ip).copied()
}
pub fn encode(&self) -> Result<Bytes, EncodingError> {
let mut buf = BytesMut::new();
buf.put_u32(self.collector_bgp_id.into());
put_u16_len_slice(
&mut buf,
"PeerIndexTable view name length",
self.view_name.as_bytes(),
)?;
let peer_count = self.id_peer_map.len();
check_max("PeerIndexTable peer count", peer_count, u16::MAX as usize)?;
buf.put_u16(peer_count as u16);
let mut peer_ids: Vec<_> = self.id_peer_map.keys().collect();
peer_ids.sort();
for id in peer_ids {
let peer = self.id_peer_map.get(id).unwrap();
buf.put_u8(peer.peer_type.bits());
buf.put_u32(peer.peer_bgp_id.into());
match peer.peer_ip {
IpAddr::V4(ipv4) => {
buf.put_slice(&ipv4.octets());
}
IpAddr::V6(ipv6) => {
buf.put_slice(&ipv6.octets());
}
};
match peer.peer_type.contains(PeerType::AS_SIZE_32BIT) {
true => buf.put_u32(peer.peer_asn.to_u32()),
false => buf.put_u16(peer.peer_asn.to_u32() as u16),
};
}
Ok(buf.freeze())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Asn;
use std::str::FromStr;
#[test]
fn test_peer_index_table_encode() {
let mut index_table = PeerIndexTable {
collector_bgp_id: Ipv4Addr::from(1234),
view_name: String::from("example"),
id_peer_map: HashMap::new(),
peer_ip_id_map: Default::default(),
};
index_table
.add_peer(Peer::new(
Ipv4Addr::from(1234),
IpAddr::from_str("192.168.1.1").unwrap(),
Asn::new_32bit(1234),
))
.unwrap();
index_table
.add_peer(Peer::new(
Ipv4Addr::from(12345),
IpAddr::from_str("192.168.1.2").unwrap(),
Asn::new_32bit(12345),
))
.unwrap();
let encoded = index_table.encode().unwrap();
let parsed_index_table = parse_peer_index_table(&mut encoded.clone()).unwrap();
assert_eq!(index_table, parsed_index_table);
}
#[test]
fn test_get_peer_by_id() {
let mut index_table = PeerIndexTable {
collector_bgp_id: Ipv4Addr::from(1234),
view_name: String::from("example"),
id_peer_map: HashMap::new(),
peer_ip_id_map: Default::default(),
};
let peer1 = Peer::new(
Ipv4Addr::from(1234),
IpAddr::from_str("10.0.0.1").unwrap(),
Asn::new_32bit(1234),
);
let peer2 = Peer::new(
Ipv4Addr::from(12345),
IpAddr::from_str("10.0.0.2").unwrap(),
Asn::new_32bit(12345),
);
let peer1_id = index_table.add_peer(peer1).unwrap();
let peer2_id = index_table.add_peer(peer2).unwrap();
assert_eq!(
index_table.get_peer_by_id(&peer1_id),
Some(&Peer::new(
Ipv4Addr::from(1234),
IpAddr::from_str("10.0.0.1").unwrap(),
Asn::new_32bit(1234),
))
);
assert_eq!(
index_table.get_peer_by_id(&peer2_id),
Some(&Peer::new(
Ipv4Addr::from(12345),
IpAddr::from_str("10.0.0.2").unwrap(),
Asn::new_32bit(12345),
))
);
}
#[test]
fn test_add_peer_rejects_overflow_without_corruption() {
let mut index_table = PeerIndexTable::default();
for i in 0..(u16::MAX as u32) {
let ip = IpAddr::from(Ipv4Addr::from(i + 1));
index_table
.add_peer(Peer::new(Ipv4Addr::from(1), ip, Asn::new_32bit(i)))
.unwrap();
}
assert_eq!(index_table.id_peer_map.len(), u16::MAX as usize);
let overflow_ip = IpAddr::from(Ipv4Addr::from(u16::MAX as u32 + 1));
let overflow_peer = Peer::new(Ipv4Addr::from(1), overflow_ip, Asn::new_32bit(65536));
let err = index_table.add_peer(overflow_peer).unwrap_err();
assert_eq!(
err,
EncodingError::ValueTooLarge {
field: "PeerIndexTable peer count",
actual: u16::MAX as usize + 1,
max: u16::MAX as usize
}
);
assert_eq!(index_table.id_peer_map.len(), u16::MAX as usize);
assert_eq!(index_table.get_peer_id_by_addr(&overflow_ip), None);
let existing_ip = IpAddr::from(Ipv4Addr::from(1u32));
let existing = Peer::new(Ipv4Addr::from(1), existing_ip, Asn::new_32bit(0));
assert_eq!(index_table.add_peer(existing).unwrap(), 0);
index_table.encode().unwrap();
}
}