use bytes::{Buf, BufMut};
use crate::capability::{Afi, Safi};
use crate::constants::{HEADER_LEN, MARKER, message_type};
use crate::error::{DecodeError, EncodeError};
use crate::orf::{
OrfPayload, decode_route_refresh_orf, encode_route_refresh_orf, route_refresh_orf_len,
};
const BODY_LEN: usize = 4;
const TOTAL_LEN: usize = HEADER_LEN + BODY_LEN;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RouteRefreshSubtype {
Normal,
BoRR,
EoRR,
Unknown(
u8,
),
}
impl RouteRefreshSubtype {
#[must_use]
pub fn from_u8(value: u8) -> Self {
match value {
0 => Self::Normal,
1 => Self::BoRR,
2 => Self::EoRR,
other => Self::Unknown(other),
}
}
#[must_use]
pub fn as_u8(self) -> u8 {
match self {
Self::Normal => 0,
Self::BoRR => 1,
Self::EoRR => 2,
Self::Unknown(value) => value,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RouteRefreshMessage {
pub afi_raw: u16,
pub subtype_raw: u8,
pub safi_raw: u8,
pub orf: Option<OrfPayload>,
}
impl RouteRefreshMessage {
#[must_use]
pub fn new(afi: Afi, safi: Safi) -> Self {
Self::new_with_subtype(afi, safi, RouteRefreshSubtype::Normal)
}
#[must_use]
pub fn new_with_subtype(afi: Afi, safi: Safi, subtype: RouteRefreshSubtype) -> Self {
Self {
afi_raw: afi as u16,
subtype_raw: subtype.as_u8(),
safi_raw: safi as u8,
orf: None,
}
}
#[must_use]
pub fn new_with_orf(afi: Afi, safi: Safi, orf: OrfPayload) -> Self {
Self {
afi_raw: afi as u16,
subtype_raw: 0,
safi_raw: safi as u8,
orf: Some(orf),
}
}
#[must_use]
pub fn afi(&self) -> Option<Afi> {
Afi::from_u16(self.afi_raw)
}
#[must_use]
pub fn safi(&self) -> Option<Safi> {
Safi::from_u8(self.safi_raw)
}
#[must_use]
pub fn subtype(&self) -> RouteRefreshSubtype {
RouteRefreshSubtype::from_u8(self.subtype_raw)
}
pub fn decode(buf: &mut impl Buf, body_len: usize) -> Result<Self, DecodeError> {
if body_len < BODY_LEN {
return Err(DecodeError::InvalidLength {
length: u16::try_from(HEADER_LEN + body_len).unwrap_or(u16::MAX),
});
}
if buf.remaining() < body_len {
return Err(DecodeError::Incomplete {
needed: body_len,
available: buf.remaining(),
});
}
let afi_raw = buf.get_u16();
let subtype_raw = buf.get_u8();
let safi_raw = buf.get_u8();
let orf = if body_len > BODY_LEN {
let mut orf_buf = buf.copy_to_bytes(body_len - BODY_LEN);
let family = Afi::from_u16(afi_raw).zip(Safi::from_u8(safi_raw));
Some(decode_route_refresh_orf(&mut orf_buf, family)?)
} else {
None
};
Ok(Self {
afi_raw,
subtype_raw,
safi_raw,
orf,
})
}
pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> {
let total = self.encoded_len();
let total_u16 = u16::try_from(total).map_err(|_| EncodeError::ValueOutOfRange {
field: "route_refresh_message_length",
value: total.to_string(),
})?;
buf.put_slice(&MARKER);
buf.put_u16(total_u16);
buf.put_u8(message_type::ROUTE_REFRESH);
buf.put_u16(self.afi_raw);
buf.put_u8(self.subtype_raw);
buf.put_u8(self.safi_raw);
if let Some(orf) = &self.orf {
encode_route_refresh_orf(orf, buf)?;
}
Ok(())
}
#[must_use]
pub fn encoded_len(&self) -> usize {
TOTAL_LEN + self.orf.as_ref().map_or(0, route_refresh_orf_len)
}
}
impl std::fmt::Display for RouteRefreshMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let subtype = match self.subtype() {
RouteRefreshSubtype::Normal => "Normal".to_string(),
RouteRefreshSubtype::BoRR => "BoRR".to_string(),
RouteRefreshSubtype::EoRR => "EoRR".to_string(),
RouteRefreshSubtype::Unknown(value) => format!("Unknown({value})"),
};
let orf = if let Some(payload) = &self.orf {
format!(
" ORF(when={:?}, groups={})",
payload.when_to_refresh,
payload.groups.len()
)
} else {
String::new()
};
match (self.afi(), self.safi()) {
(Some(afi), Some(safi)) => {
write!(
f,
"ROUTE-REFRESH subtype={subtype} AFI={afi:?} SAFI={safi:?}{orf}"
)
}
_ => write!(
f,
"ROUTE-REFRESH subtype={subtype} AFI={} SAFI={}{orf}",
self.afi_raw, self.safi_raw
),
}
}
}
#[cfg(test)]
mod tests {
use bytes::{Bytes, BytesMut};
use super::*;
#[test]
fn roundtrip_ipv4_unicast() {
let msg = RouteRefreshMessage::new(Afi::Ipv4, Safi::Unicast);
let mut buf = BytesMut::with_capacity(TOTAL_LEN);
msg.encode(&mut buf).unwrap();
assert_eq!(buf.len(), TOTAL_LEN);
let mut bytes = buf.freeze();
bytes.advance(HEADER_LEN);
let decoded = RouteRefreshMessage::decode(&mut bytes, BODY_LEN).unwrap();
assert_eq!(decoded, msg);
assert_eq!(decoded.afi(), Some(Afi::Ipv4));
assert_eq!(decoded.safi(), Some(Safi::Unicast));
assert_eq!(decoded.subtype(), RouteRefreshSubtype::Normal);
}
#[test]
fn roundtrip_ipv6_unicast() {
let msg = RouteRefreshMessage::new(Afi::Ipv6, Safi::Unicast);
let mut buf = BytesMut::with_capacity(TOTAL_LEN);
msg.encode(&mut buf).unwrap();
let mut bytes = buf.freeze();
bytes.advance(HEADER_LEN);
let decoded = RouteRefreshMessage::decode(&mut bytes, BODY_LEN).unwrap();
assert_eq!(decoded, msg);
assert_eq!(decoded.afi(), Some(Afi::Ipv6));
assert_eq!(decoded.safi(), Some(Safi::Unicast));
assert_eq!(decoded.subtype(), RouteRefreshSubtype::Normal);
}
#[test]
fn roundtrip_borr() {
let msg = RouteRefreshMessage::new_with_subtype(
Afi::Ipv4,
Safi::Unicast,
RouteRefreshSubtype::BoRR,
);
let mut buf = BytesMut::with_capacity(TOTAL_LEN);
msg.encode(&mut buf).unwrap();
let mut bytes = buf.freeze();
bytes.advance(HEADER_LEN);
let decoded = RouteRefreshMessage::decode(&mut bytes, BODY_LEN).unwrap();
assert_eq!(decoded, msg);
assert_eq!(decoded.subtype(), RouteRefreshSubtype::BoRR);
}
#[test]
fn roundtrip_eorr() {
let msg = RouteRefreshMessage::new_with_subtype(
Afi::Ipv6,
Safi::Unicast,
RouteRefreshSubtype::EoRR,
);
let mut buf = BytesMut::with_capacity(TOTAL_LEN);
msg.encode(&mut buf).unwrap();
let mut bytes = buf.freeze();
bytes.advance(HEADER_LEN);
let decoded = RouteRefreshMessage::decode(&mut bytes, BODY_LEN).unwrap();
assert_eq!(decoded, msg);
assert_eq!(decoded.subtype(), RouteRefreshSubtype::EoRR);
}
#[test]
fn plain_four_byte_body_has_no_orf() {
let data: &[u8] = &[0, 1, 0, 1];
let mut buf = Bytes::copy_from_slice(data);
let msg = RouteRefreshMessage::decode(&mut buf, 4).unwrap();
assert_eq!(msg.orf, None);
}
#[test]
fn reject_body_length_three() {
let data: &[u8] = &[0, 1, 0];
let mut buf = Bytes::copy_from_slice(data);
assert!(RouteRefreshMessage::decode(&mut buf, 3).is_err());
}
#[test]
fn reject_truncated_orf_body() {
let data: &[u8] = &[0, 1, 0, 1];
let mut buf = Bytes::copy_from_slice(data);
assert!(RouteRefreshMessage::decode(&mut buf, 10).is_err());
}
#[test]
fn roundtrip_orf_route_refresh() {
use crate::nlri::{Ipv4Prefix, Prefix};
use crate::orf::{
AddressPrefixOrf, OrfAction, OrfEntries, OrfEntryGroup, OrfMatch, OrfPayload, OrfType,
WhenToRefresh,
};
use std::net::Ipv4Addr;
let payload = OrfPayload {
when_to_refresh: WhenToRefresh::Immediate,
groups: vec![OrfEntryGroup {
orf_type: OrfType::AddressPrefix,
entries: OrfEntries::AddressPrefix(vec![AddressPrefixOrf {
action: OrfAction::Add,
match_: OrfMatch::Permit,
sequence: 10,
min_len: 24,
max_len: 32,
prefix: Some(Prefix::V4(Ipv4Prefix::new(Ipv4Addr::new(10, 0, 0, 0), 8))),
}]),
}],
};
let msg = RouteRefreshMessage::new_with_orf(Afi::Ipv4, Safi::Unicast, payload);
let mut buf = BytesMut::with_capacity(msg.encoded_len());
msg.encode(&mut buf).unwrap();
assert_eq!(buf.len(), msg.encoded_len());
let mut bytes = buf.freeze();
bytes.advance(HEADER_LEN);
let body_len = msg.encoded_len() - HEADER_LEN;
let decoded = RouteRefreshMessage::decode(&mut bytes, body_len).unwrap();
assert_eq!(decoded, msg);
assert!(decoded.orf.is_some());
}
#[test]
fn decode_unknown_afi_succeeds() {
let data: &[u8] = &[0, 99, 0, 1];
let mut buf = Bytes::copy_from_slice(data);
let msg = RouteRefreshMessage::decode(&mut buf, 4).unwrap();
assert_eq!(msg.afi_raw, 99);
assert_eq!(msg.afi(), None);
assert_eq!(msg.safi(), Some(Safi::Unicast));
assert_eq!(msg.subtype(), RouteRefreshSubtype::Normal);
}
#[test]
fn decode_unknown_safi_succeeds() {
let data: &[u8] = &[0, 1, 0, 99];
let mut buf = Bytes::copy_from_slice(data);
let msg = RouteRefreshMessage::decode(&mut buf, 4).unwrap();
assert_eq!(msg.safi_raw, 99);
assert_eq!(msg.safi(), None);
assert_eq!(msg.afi(), Some(Afi::Ipv4));
assert_eq!(msg.subtype(), RouteRefreshSubtype::Normal);
}
#[test]
fn decode_orf_unknown_safi_preserves_address_prefix_group_as_raw() {
use crate::constants::orf;
use crate::orf::{OrfEntries, OrfType, WhenToRefresh};
let data: &[u8] = &[
0,
1,
0,
128, orf::WHEN_IMMEDIATE,
orf::TYPE_ADDRESS_PREFIX,
0,
8,
orf::ACTION_ADD,
0,
0,
0,
1,
0,
0,
40,
];
let mut buf = Bytes::copy_from_slice(data);
let msg = RouteRefreshMessage::decode(&mut buf, data.len()).unwrap();
let payload = msg.orf.unwrap();
assert_eq!(payload.when_to_refresh, WhenToRefresh::Immediate);
assert_eq!(payload.groups[0].orf_type, OrfType::AddressPrefix);
assert_eq!(
payload.groups[0].entries,
OrfEntries::Raw(Bytes::from_static(&[orf::ACTION_ADD, 0, 0, 0, 1, 0, 0, 40]))
);
}
#[test]
fn decode_unknown_subtype_succeeds() {
let data: &[u8] = &[0, 1, 9, 1];
let mut buf = Bytes::copy_from_slice(data);
let msg = RouteRefreshMessage::decode(&mut buf, 4).unwrap();
assert_eq!(msg.subtype(), RouteRefreshSubtype::Unknown(9));
}
#[test]
fn encoded_len_is_23() {
let msg = RouteRefreshMessage::new(Afi::Ipv4, Safi::Unicast);
assert_eq!(msg.encoded_len(), 23);
}
#[test]
fn display_known_family() {
let msg = RouteRefreshMessage::new(Afi::Ipv6, Safi::Unicast);
let s = format!("{msg}");
assert!(s.contains("Ipv6"));
assert!(s.contains("Unicast"));
assert!(s.contains("Normal"));
}
#[test]
fn display_unknown_family() {
let msg = RouteRefreshMessage {
afi_raw: 99,
subtype_raw: 7,
safi_raw: 42,
orf: None,
};
let s = format!("{msg}");
assert!(s.contains("99"));
assert!(s.contains("42"));
assert!(s.contains("Unknown(7)"));
}
}