use crate::error::ParserError;
use crate::models::*;
use crate::parser::ReadUtils;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::net::IpAddr;
pub fn parse_geo_peer_table(data: &mut Bytes) -> Result<GeoPeerTable, ParserError> {
let collector_bgp_id = data.read_ipv4_address()?;
let view_name_len = data.read_u16()? as usize;
let view_name = data.read_n_bytes_to_string(view_name_len)?;
data.has_n_remaining(4)?;
let collector_latitude = data.get_f32();
data.has_n_remaining(4)?;
let collector_longitude = data.get_f32();
let mut geo_table = GeoPeerTable::new(
collector_bgp_id,
view_name,
collector_latitude,
collector_longitude,
);
let peer_count = data.read_u16()?;
for _ in 0..peer_count {
let peer_type_raw = data.read_u8()?;
let peer_type = PeerType::from_bits_retain(peer_type_raw);
let peer_bgp_id = data.read_ipv4_address()?;
let peer_ip: IpAddr = if peer_type.contains(PeerType::ADDRESS_FAMILY_IPV6) {
data.read_ipv6_address()?.into()
} else {
data.read_ipv4_address()?.into()
};
let peer_asn = if peer_type.contains(PeerType::AS_SIZE_32BIT) {
Asn::new_32bit(data.read_u32()?)
} else {
Asn::new_16bit(data.read_u16()?)
};
let peer = Peer {
peer_type,
peer_bgp_id,
peer_ip,
peer_asn,
};
data.has_n_remaining(4)?;
let peer_latitude = data.get_f32();
data.has_n_remaining(4)?;
let peer_longitude = data.get_f32();
let geo_peer = GeoPeer::new(peer, peer_latitude, peer_longitude);
geo_table.add_geo_peer(geo_peer);
}
Ok(geo_table)
}
impl GeoPeerTable {
pub fn encode(&self) -> Bytes {
let mut buf = BytesMut::new();
buf.put_u32(self.collector_bgp_id.into());
let view_name_bytes = self.view_name.as_bytes();
buf.put_u16(view_name_bytes.len() as u16);
buf.extend(view_name_bytes);
buf.put_f32(self.collector_latitude);
buf.put_f32(self.collector_longitude);
buf.put_u16(self.geo_peers.len() as u16);
for geo_peer in &self.geo_peers {
buf.put_u8(geo_peer.peer.peer_type.bits());
buf.put_u32(geo_peer.peer.peer_bgp_id.into());
match geo_peer.peer.peer_ip {
std::net::IpAddr::V4(ipv4) => {
buf.put_u32(ipv4.into());
}
std::net::IpAddr::V6(ipv6) => {
buf.extend_from_slice(&ipv6.octets());
}
}
if geo_peer
.peer
.peer_type
.contains(crate::models::PeerType::AS_SIZE_32BIT)
{
buf.put_u32(u32::from(geo_peer.peer.peer_asn));
} else {
buf.put_u16(u16::from(geo_peer.peer.peer_asn));
}
buf.put_f32(geo_peer.peer_latitude);
buf.put_f32(geo_peer.peer_longitude);
}
buf.freeze()
}
}
#[cfg(test)]
mod tests {
use super::*;
use bytes::BufMut;
use bytes::BytesMut;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
#[test]
fn test_parse_geo_peer_table() {
let mut data = BytesMut::new();
data.put_u32(0x0A000001);
let view_name = "test-view";
data.put_u16(view_name.len() as u16);
data.extend_from_slice(view_name.as_bytes());
data.put_f32(51.5074);
data.put_f32(-0.1278);
data.put_u16(2);
data.put_u8(0x00); data.put_u32(0x01010101); data.put_u32(0x02020202); data.put_u16(65001); data.put_f32(40.7128); data.put_f32(-74.0060);
data.put_u8(0x03); data.put_u32(0x03030303); data.extend_from_slice(&[
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
]);
data.put_u32(65002); data.put_f32(f32::NAN); data.put_f32(f32::NAN);
let mut bytes = data.freeze();
let result = parse_geo_peer_table(&mut bytes).unwrap();
assert_eq!(
result.collector_bgp_id,
Ipv4Addr::from_str("10.0.0.1").unwrap()
);
assert_eq!(result.view_name, "test-view");
assert_eq!(result.collector_latitude, 51.5074);
assert_eq!(result.collector_longitude, -0.1278);
assert_eq!(result.geo_peers.len(), 2);
let peer1 = &result.geo_peers[0];
assert_eq!(
peer1.peer.peer_bgp_id,
Ipv4Addr::from_str("1.1.1.1").unwrap()
);
assert_eq!(
peer1.peer.peer_ip,
IpAddr::V4(Ipv4Addr::from_str("2.2.2.2").unwrap())
);
assert_eq!(peer1.peer.peer_asn, Asn::new_16bit(65001));
assert!(!peer1.peer.peer_type.contains(PeerType::ADDRESS_FAMILY_IPV6));
assert!(!peer1.peer.peer_type.contains(PeerType::AS_SIZE_32BIT));
assert_eq!(peer1.peer_latitude, 40.7128);
assert_eq!(peer1.peer_longitude, -74.0060);
let peer2 = &result.geo_peers[1];
assert_eq!(
peer2.peer.peer_bgp_id,
Ipv4Addr::from_str("3.3.3.3").unwrap()
);
assert_eq!(
peer2.peer.peer_ip,
IpAddr::V6(Ipv6Addr::from_str("2001:db8::1").unwrap())
);
assert_eq!(peer2.peer.peer_asn, Asn::new_32bit(65002));
assert!(peer2.peer.peer_type.contains(PeerType::ADDRESS_FAMILY_IPV6));
assert!(peer2.peer.peer_type.contains(PeerType::AS_SIZE_32BIT));
assert!(peer2.peer_latitude.is_nan());
assert!(peer2.peer_longitude.is_nan());
}
#[test]
fn test_parse_geo_peer_table_private_collector() {
let mut data = BytesMut::new();
data.put_u32(0x0A000001);
let view_name = "private-view";
data.put_u16(view_name.len() as u16);
data.extend_from_slice(view_name.as_bytes());
data.put_f32(f32::NAN);
data.put_f32(f32::NAN);
data.put_u16(0);
let mut bytes = data.freeze();
let result = parse_geo_peer_table(&mut bytes).unwrap();
assert!(result.collector_latitude.is_nan());
assert!(result.collector_longitude.is_nan());
assert_eq!(result.geo_peers.len(), 0);
}
#[test]
fn test_geo_peer_table_round_trip() {
let collector_bgp_id = Ipv4Addr::from_str("192.168.1.1").unwrap();
let mut original_table = GeoPeerTable::new(
collector_bgp_id,
"round-trip-test".to_string(),
37.7749, -122.4194, );
let peer1 = Peer::new(
Ipv4Addr::from_str("203.0.113.1").unwrap(),
Ipv4Addr::from_str("203.0.113.2").unwrap().into(),
Asn::new_16bit(64512),
);
let geo_peer1 = GeoPeer::new(peer1, 35.6762, 139.6503); original_table.add_geo_peer(geo_peer1);
let peer2 = Peer::new(
Ipv4Addr::from_str("198.51.100.1").unwrap(),
std::net::Ipv6Addr::from_str("2001:db8:85a3::8a2e:370:7334")
.unwrap()
.into(),
Asn::new_32bit(4200000000),
);
let geo_peer2 = GeoPeer::new(peer2, -33.8688, 151.2093); original_table.add_geo_peer(geo_peer2);
let encoded = original_table.encode();
let mut encoded_bytes = encoded;
let parsed_table = parse_geo_peer_table(&mut encoded_bytes).unwrap();
assert_eq!(
parsed_table.collector_bgp_id,
original_table.collector_bgp_id
);
assert_eq!(parsed_table.view_name, original_table.view_name);
assert_eq!(
parsed_table.collector_latitude,
original_table.collector_latitude
);
assert_eq!(
parsed_table.collector_longitude,
original_table.collector_longitude
);
assert_eq!(parsed_table.geo_peers.len(), original_table.geo_peers.len());
let parsed_peer1 = &parsed_table.geo_peers[0];
let original_peer1 = &original_table.geo_peers[0];
assert_eq!(
parsed_peer1.peer.peer_bgp_id,
original_peer1.peer.peer_bgp_id
);
assert_eq!(parsed_peer1.peer.peer_ip, original_peer1.peer.peer_ip);
assert_eq!(parsed_peer1.peer.peer_asn, original_peer1.peer.peer_asn);
assert_eq!(parsed_peer1.peer.peer_type, original_peer1.peer.peer_type);
assert_eq!(parsed_peer1.peer_latitude, original_peer1.peer_latitude);
assert_eq!(parsed_peer1.peer_longitude, original_peer1.peer_longitude);
let parsed_peer2 = &parsed_table.geo_peers[1];
let original_peer2 = &original_table.geo_peers[1];
assert_eq!(
parsed_peer2.peer.peer_bgp_id,
original_peer2.peer.peer_bgp_id
);
assert_eq!(parsed_peer2.peer.peer_ip, original_peer2.peer.peer_ip);
assert_eq!(parsed_peer2.peer.peer_asn, original_peer2.peer.peer_asn);
assert_eq!(parsed_peer2.peer.peer_type, original_peer2.peer.peer_type);
assert_eq!(parsed_peer2.peer_latitude, original_peer2.peer_latitude);
assert_eq!(parsed_peer2.peer_longitude, original_peer2.peer_longitude);
assert_eq!(parsed_table, original_table);
}
#[test]
fn test_geo_peer_table_encoding() {
let collector_bgp_id = Ipv4Addr::from_str("10.0.0.1").unwrap();
let mut geo_table = GeoPeerTable::new(
collector_bgp_id,
"test-view".to_string(),
51.5074, -0.1278, );
let peer1 = Peer::new(
Ipv4Addr::from_str("1.1.1.1").unwrap(),
Ipv4Addr::from_str("2.2.2.2").unwrap().into(),
Asn::new_16bit(65001),
);
let geo_peer1 = GeoPeer::new(peer1, 40.7128, -74.0060); geo_table.add_geo_peer(geo_peer1);
let peer2 = Peer::new(
Ipv4Addr::from_str("3.3.3.3").unwrap(),
std::net::Ipv6Addr::from_str("2001:db8::1").unwrap().into(),
Asn::new_32bit(65002),
);
let geo_peer2 = GeoPeer::new(peer2, f32::NAN, f32::NAN); geo_table.add_geo_peer(geo_peer2);
let encoded = geo_table.encode();
let mut expected = BytesMut::new();
expected.put_u32(0x0A000001);
let view_name = "test-view";
expected.put_u16(view_name.len() as u16);
expected.extend_from_slice(view_name.as_bytes());
expected.put_f32(51.5074);
expected.put_f32(-0.1278);
expected.put_u16(2);
expected.put_u8(0x00); expected.put_u32(0x01010101); expected.put_u32(0x02020202); expected.put_u16(65001); expected.put_f32(40.7128); expected.put_f32(-74.0060);
expected.put_u8(0x03); expected.put_u32(0x03030303); expected.extend_from_slice(&[
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
]); expected.put_u32(65002); expected.put_f32(f32::NAN); expected.put_f32(f32::NAN);
let expected_bytes = expected.freeze();
assert_eq!(encoded.len(), expected_bytes.len());
assert_eq!(encoded, expected_bytes);
}
}