use std::fmt;
use std::net::{IpAddr, Ipv6Addr};
use bytes::BytesMut;
use octseq::{EmptyBuilder, FreezeBuilder, Octets, OctetsBuilder, OctetsFrom, ShortBuf};
use log::warn;
use crate::bgp::aspath::HopPath;
use crate::bgp::communities::StandardCommunity;
use crate::bgp::message::{Header, MsgType, UpdateMessage, SessionConfig};
use crate::bgp::nlri::afisafi::{AfiSafiNlri, NlriParse, NlriCompose};
use crate::bgp::path_attributes::{Attribute, AttributeHeader, MpReachNextHop, PaMap, PathAttributeType};
use crate::bgp::types::{AfiSafiType, NextHop};
use crate::util::parser::ParseError;
#[derive(Debug)]
pub struct UpdateBuilder<Target, A> {
target: Target,
announcements: Option<MpReachNlriBuilder<A>>,
withdrawals: Option<MpUnreachNlriBuilder<A>>,
attributes: PaMap,
}
impl<T, A> UpdateBuilder<T, A> {
const MAX_PDU: usize = 4096; }
impl<Target, A> UpdateBuilder<Target, A>
where
A: AfiSafiNlri + NlriCompose,
Target: OctetsBuilder + octseq::Truncate,
{
pub fn from_target(mut target: Target) -> Result<Self, ShortBuf> {
target.truncate(0);
let mut h = Header::<&[u8]>::new();
h.set_length(19 + 2 + 2);
h.set_type(MsgType::Update);
let _ = target.append_slice(h.as_ref());
Ok(UpdateBuilder {
target,
announcements: None,
withdrawals: None,
attributes: PaMap::empty(),
})
}
}
impl<Target, A> UpdateBuilder<Target, A>
where
A: AfiSafiNlri + NlriCompose,
Target: EmptyBuilder + OctetsBuilder + octseq::Truncate,
{
pub fn from_attributes_builder(
attributes: PaMap,
) -> UpdateBuilder<Target, A> {
let mut res = UpdateBuilder::from_target(Target::empty()).unwrap();
res.attributes = attributes;
res
}
pub fn from_workshop(
ws: crate::bgp::workshop::route::RouteWorkshop<A>
) -> UpdateBuilder<Target, A> {
let mut res = Self::from_attributes_builder(ws.attributes().clone());
let _ = res.add_announcement(ws.into_nlri());
res
}
pub fn from_update_message<'a, Octs: 'a + Octets>(
pdu: &'a UpdateMessage<Octs>,
_session_config: &SessionConfig,
target: Target
) -> Result<UpdateBuilder<Target, A>, ComposeError>
where
Vec<u8>: OctetsFrom<Octs::Range<'a>>
{
let mut builder = UpdateBuilder::from_target(target)
.map_err(|_| ComposeError::ShortBuf)?;
let pa_map = PaMap::from_update_pdu(pdu)?;
builder.attributes = pa_map;
Ok(builder)
}
pub fn add_withdrawal(&mut self, withdrawal: A)
-> Result<(), ComposeError>
{
if let Some(ref mut w) = self.withdrawals.as_mut() {
w.add_withdrawal(withdrawal);
} else {
self.withdrawals = Some(MpUnreachNlriBuilder::from(withdrawal));
}
Ok(())
}
pub fn withdrawals_from_iter<I>(&mut self, withdrawals: I)
-> Result<(), ComposeError>
where I: IntoIterator<Item = A>
{
withdrawals.into_iter().try_for_each(|w| self.add_withdrawal(w) )?;
Ok(())
}
pub fn append_withdrawals(&mut self, withdrawals: Vec<A>)
-> Result<(), ComposeError>
{
for w in withdrawals {
self.add_withdrawal(w)?;
}
Ok(())
}
pub fn set_aspath(&mut self , aspath: HopPath)
-> Result<(), ComposeError>
{
let wireformat = octseq::builder::infallible(
aspath.to_as_path::<Vec<u8>>()
);
if wireformat.compose_len() > u16::MAX.into() {
return Err(ComposeError::AttributeTooLarge(
PathAttributeType::AsPath,
wireformat.compose_len()
));
}
self.attributes.set(aspath);
Ok(())
}
pub fn set_nexthop(&mut self, nexthop: NextHop)
-> Result<(), ComposeError>
{
self.set_mp_nexthop(nexthop)
}
pub fn set_mp_nexthop(&mut self, nexthop: NextHop)
-> Result<(), ComposeError>
{
if let Some(ref mut a) = self.announcements.as_mut() {
a.set_nexthop(nexthop)?;
} else {
self.announcements =
Some(MpReachNlriBuilder::for_nexthop(nexthop))
;
}
Ok(())
}
pub fn set_nexthop_ll_addr(&mut self, addr: Ipv6Addr)
-> Result<(), ComposeError>
{
if let Some(ref mut a) = self.announcements.as_mut() {
a.set_nexthop_ll_addr(addr);
} else {
let nexthop = NextHop::Ipv6LL{global: Ipv6Addr::from(0), link_local: addr};
self.announcements =
Some(MpReachNlriBuilder::for_nexthop(nexthop))
;
}
Ok(())
}
pub fn add_announcement(&mut self, announcement: A)
-> Result<(), ComposeError>
{
if let Some(ref mut a) = self.announcements.as_mut() {
a.add_announcement(announcement);
} else {
self.announcements =
Some(MpReachNlriBuilder::for_nlri(announcement))
;
}
Ok(())
}
pub fn announcements_from_iter<I>(&mut self, announcements: I)
-> Result<(), ComposeError>
where I: IntoIterator<Item = A>
{
announcements.into_iter().try_for_each(|w| self.add_announcement(w))?;
Ok(())
}
pub fn add_announcements_from_pdu<'a, Octs, O>(
&mut self,
source: &'a UpdateMessage<Octs>,
_session_config: &SessionConfig
)
where
A: AfiSafiNlri + NlriCompose + NlriParse<'a, O, Octs, Output = A>,
Octs: Octets<Range<'a> = O>,
O: Octets,
{
if source.announcements().is_ok_and(|i| i.count() == 0) {
return;
}
if let Some(ref mut a) = self.announcements.as_mut() {
a.add_announcements_from_pdu::<Octs, O>(source, _session_config);
} else {
let mut a = MpReachNlriBuilder::new();
a.add_announcements_from_pdu::<Octs, O>(source, _session_config);
self.announcements = Some(a);
}
}
pub fn add_withdrawals_from_pdu<'a, Octs, O>(
&mut self,
source: &'a UpdateMessage<Octs>,
_session_config: &SessionConfig
)
where
A: AfiSafiNlri + NlriCompose + NlriParse<'a, O, Octs, Output = A>,
Octs: Octets<Range<'a> = O>,
O: Octets,
{
if source.withdrawals().is_ok_and(|i| i.count() == 0) {
return;
}
if let Some(ref mut w) = self.withdrawals.as_mut() {
w.add_withdrawals_from_pdu::<Octs, O>(source, _session_config);
} else {
let mut w = MpUnreachNlriBuilder::new();
w.add_withdrawals_from_pdu::<Octs, O>(source, _session_config);
self.withdrawals = Some(w);
}
}
pub fn add_community(
&mut self,
community: StandardCommunity,
) -> Result<(), ComposeError> {
if !self.attributes.contains::<StandardCommunitiesList>() {
self.attributes.set(StandardCommunitiesList::new());
}
let mut builder = self
.attributes
.take::<StandardCommunitiesList>()
.unwrap(); builder.add_community(community);
self.attributes.set(builder);
Ok(())
}
}
impl<Target, A> UpdateBuilder<Target, A>
where
A: Clone + AfiSafiNlri + NlriCompose
{
pub fn into_message(self, session_config: &SessionConfig) ->
Result<UpdateMessage<<Target as FreezeBuilder>::Octets>, ComposeError>
where
Target: OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate,
<Target as FreezeBuilder>::Octets: Octets
{
self.is_valid()?;
Ok(UpdateMessage::from_octets(
self.finish(session_config).map_err(|_| ShortBuf)?, session_config,
)?)
}
pub fn attributes(&self) -> &PaMap {
&self.attributes
}
pub fn from_map(&self, map: &mut PaMap) -> PaMap {
let mut pab = PaMap::empty();
pab.merge_upsert(map);
pab
}
fn is_valid(&self) -> Result<(), ComposeError> {
if self.announcements.as_ref()
.is_some_and(|b| b.announcements.is_empty())
{
return Err(ComposeError::EmptyMpReachNlri);
}
if self.withdrawals.as_ref().is_some_and(|b| b.is_empty()) &&
(
self.announcements.as_ref().is_some_and(|b| !b.is_empty())
|| !self.attributes.is_empty()
)
{
return Err(ComposeError::EmptyMpUnreachNlri);
}
Ok(())
}
fn calculate_pdu_length(&self, _session_config: &SessionConfig) -> usize {
let mut res: usize = 16 + 2 + 1;
res += 2;
res += 2 + self.attributes.bytes_len();
if let Some(mp_reach) = &self.announcements {
res += mp_reach.compose_len();
}
if let Some(mp_unreach) = &self.withdrawals {
res += mp_unreach.compose_len();
}
res
}
fn larger_than(&self, max: usize, session_config: &SessionConfig) -> bool {
if let Some(b) = &self.announcements {
if b.announcements.len() * 2 > max {
return true;
}
}
self.calculate_pdu_length(session_config) > max
}
pub fn take_message(mut self, session_config: &SessionConfig) -> (
Result<UpdateMessage<<Target as FreezeBuilder>::Octets>, ComposeError>,
Option<Self>
)
where
Target: Clone + OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate,
<Target as FreezeBuilder>::Octets: Octets
{
if !self.larger_than(Self::MAX_PDU, session_config) {
return (self.into_message(session_config), None)
}
let maybe_pdu = if let Some(ref mut unreach_builder) =
self.withdrawals
{
let mut split_at = 0;
if !unreach_builder.withdrawals.is_empty() {
let mut compose_len = 0;
for (idx, w) in unreach_builder.withdrawals.iter().enumerate()
{
compose_len += w.compose_len();
if compose_len > 4000 {
split_at = idx;
break;
}
}
let this_batch = unreach_builder.split(split_at);
let mut builder =
UpdateBuilder::from_target(self.target.clone()).unwrap();
builder.withdrawals = Some(this_batch);
Some(builder.into_message(session_config))
} else {
None
}
} else {
None
};
if let Some(pdu) = maybe_pdu {
return (pdu, Some(self))
}
let maybe_pdu = if let Some(ref mut reach_builder) =
self.announcements
{
let mut split_at = 0;
let other_attrs_len = self.attributes.bytes_len();
let limit = Self::MAX_PDU
- (16 + 2 + 1 + 2 + 2)
- 8 - reach_builder.get_nexthop().compose_len()
- other_attrs_len;
if !reach_builder.announcements.is_empty() {
let mut compose_len = 0;
for (idx, a) in reach_builder.announcements.iter().enumerate() {
compose_len += a.compose_len();
if compose_len > limit {
split_at = idx;
break;
}
}
let this_batch = reach_builder.split(split_at);
let mut builder = Self::from_target(
self.target.clone()
).unwrap();
builder.attributes = self.attributes.clone();
builder.announcements = Some(this_batch);
Some(builder.into_message(session_config))
} else {
None
}
} else {
None
}
;
if let Some(pdu) = maybe_pdu {
return (pdu, Some(self))
}
let pdu_len = self.calculate_pdu_length(session_config);
(Err(ComposeError::PduTooLarge(pdu_len)), None)
}
pub fn into_messages(self, session_config: &SessionConfig) -> Result<
Vec<UpdateMessage<<Target as FreezeBuilder>::Octets>>,
ComposeError
>
where
Target: Clone + OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate,
<Target as FreezeBuilder>::Octets: Octets
{
let mut res = Vec::new();
let mut remainder = Some(self);
loop {
if remainder.is_none() {
return Ok(res);
}
let (pdu, new_remainder) =
remainder.take().unwrap().take_message(session_config)
;
match pdu {
Ok(pdu) => {
res.push(pdu);
remainder = new_remainder;
}
Err(e) => {
warn!("error in into_messages(): {}", e);
return Err(e)
}
}
}
}
pub fn into_pdu_iter(self, session_config: &SessionConfig)
-> PduIterator<'_, Target, A>
{
PduIterator { builder: Some(self), session_config }
}
fn finish(mut self, session_config: &SessionConfig)
-> Result<<Target as FreezeBuilder>::Octets, Target::AppendError>
where
Target: OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate,
<Target as FreezeBuilder>::Octets: Octets
{
let total_pdu_len = self.calculate_pdu_length(session_config);
let mut header = Header::for_slice_mut(self.target.as_mut());
header.set_length(u16::try_from(total_pdu_len).unwrap());
self.target.append_slice(&0_u16.to_be_bytes())?;
let mut attributes_len = self.attributes.bytes_len();
if let Some(ref mp_reach_builder) = self.announcements {
attributes_len += mp_reach_builder.compose_len();
}
if let Some(ref mp_unreach_builder) = self.withdrawals {
attributes_len += mp_unreach_builder.compose_len();
}
let _ = self.target.append_slice(
&u16::try_from(attributes_len).unwrap().to_be_bytes()
);
if let Some(mp_reach_builder) = self.announcements {
mp_reach_builder.compose(&mut self.target)?;
}
if let Some(mp_unreach_builder) = self.withdrawals {
mp_unreach_builder.compose(&mut self.target)?;
}
self.attributes
.attributes()
.iter()
.filter(|(tc, _pa)| **tc != MpReachNextHop::TYPE_CODE)
.try_for_each(|(_tc, pa)| pa.compose(&mut self.target))?;
Ok(self.target.freeze())
}
}
pub struct PduIterator<'a, Target, A> {
builder: Option<UpdateBuilder<Target, A>>,
session_config: &'a SessionConfig,
}
impl<Target, A> Iterator for PduIterator<'_, Target, A>
where
A: AfiSafiNlri + NlriCompose + Clone,
Target: Clone + OctetsBuilder + FreezeBuilder + AsMut<[u8]> + octseq::Truncate,
<Target as FreezeBuilder>::Octets: Octets
{
type Item = Result<
UpdateMessage<<Target as FreezeBuilder>::Octets>,
ComposeError
>;
fn next(&mut self) -> Option<Self::Item> {
self.builder.as_ref()?;
let (res, remainder) =
self.builder.take().unwrap().take_message(self.session_config)
;
self.builder = remainder;
Some(res)
}
}
impl<A: AfiSafiNlri + NlriCompose> UpdateBuilder<Vec<u8>, A> {
pub fn new_vec() -> Self {
UpdateBuilder::from_target(Vec::with_capacity(23)).unwrap()
}
}
impl<A: AfiSafiNlri + NlriCompose> UpdateBuilder<BytesMut, A> {
pub fn new_bytes() -> Self {
Self::from_target(BytesMut::new()).unwrap()
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct MpReachNlriBuilder<AfiSafi> {
announcements: Vec<AfiSafi>,
nexthop: NextHop,
}
impl<A> MpReachNlriBuilder<A> {
pub fn add_announcements_from_pdu<'a, Octs, O>(
&mut self,
source: &'a UpdateMessage<Octs>,
_session_config: &SessionConfig
)
where
A: AfiSafiNlri + NlriCompose + NlriParse<'a, O, Octs, Output = A>,
Octs: Octets<Range<'a> = O>,
O: Octets,
{
if let Ok(Some(iter)) = source.typed_announcements::<_, A>() {
for a in iter {
self.add_announcement(a.unwrap());
}
}
}
}
impl<A: AfiSafiNlri + NlriCompose> MpReachNlriBuilder<A> {
pub fn new() -> Self {
Self {
announcements: Vec::new(),
nexthop: NextHop::new(A::afi_safi())
}
}
pub(crate) fn is_empty(&self) -> bool {
self.announcements.is_empty()
}
pub fn for_nlri(nlri: A) -> Self {
Self {
announcements: vec![nlri],
nexthop: NextHop::new(A::afi_safi())
}
}
pub fn for_nexthop(nexthop: NextHop) -> Self {
Self {
announcements: Vec::new(),
nexthop,
}
}
pub fn for_nlri_and_nexthop(nlri: A, nexthop: NextHop) -> Self {
Self {
announcements: vec![nlri],
nexthop,
}
}
pub fn afi_safi(&self) -> AfiSafiType {
A::afi_safi()
}
pub fn add_announcement(&mut self, nlri: A) {
self.announcements.push(nlri);
}
pub fn set_nexthop(&mut self, nexthop: NextHop) -> Result<(), ComposeError> {
self.nexthop = nexthop;
Ok(())
}
pub fn set_nexthop_ll_addr(&mut self, addr: Ipv6Addr) {
match self.nexthop {
NextHop::Unicast(IpAddr::V6(a)) => {
self.nexthop = NextHop::Ipv6LL{global: a, link_local: addr};
}
NextHop::Ipv6LL{global, ..} => {
self.nexthop = NextHop::Ipv6LL{global, link_local: addr};
}
_ => unreachable!()
}
}
pub(crate) fn get_nexthop(&self) -> &NextHop {
&self.nexthop
}
pub(crate) fn split(&mut self, n: usize) -> Self {
let this_batch = self.announcements.drain(..n).collect();
MpReachNlriBuilder {
announcements: this_batch,
..*self
}
}
pub fn value_len(&self) -> usize {
2 + 1 + 1 + self.nexthop.compose_len()
+ self.announcements.iter().fold(0, |sum, w| sum + w.compose_len())
}
pub fn compose_value<Target: OctetsBuilder>(
&self,
target: &mut Target
) -> Result<(), Target::AppendError>
{
target.append_slice(&A::afi_safi().as_bytes())?;
self.nexthop.compose(target)?;
target.append_slice(&[0x00])?;
for a in &self.announcements {
a.compose(target)?
}
Ok(())
}
}
impl<A: AfiSafiNlri + NlriCompose> Default for MpReachNlriBuilder<A> {
fn default() -> Self {
Self::new()
}
}
impl NextHop {
fn compose_len(&self) -> usize {
1 + match *self {
NextHop::Unicast(IpAddr::V4(_)) | NextHop::Multicast(IpAddr::V4(_)) => 4,
NextHop::Unicast(IpAddr::V6(_)) | NextHop::Multicast(IpAddr::V6(_)) => 16,
NextHop::Ipv6LL{..} => 32,
NextHop::MplsVpnUnicast(_rd, IpAddr::V4(_)) => 8 + 4,
NextHop::MplsVpnUnicast(_rd, IpAddr::V6(_)) => 8 + 16,
NextHop::Empty => 0, NextHop::Unimplemented(_afisafi) => {
warn!(
"unexpected compose_len called on NextHop::Unimplemented \
returning usize::MAX, this will cause failure."
);
usize::MAX
}
}
}
pub(crate) fn compose<Target: OctetsBuilder>(&self, target: &mut Target)
-> Result<(), Target::AppendError>
{
target.append_slice(&[u8::try_from(self.compose_len()).unwrap() - 1])?;
match *self {
NextHop::Unicast(IpAddr::V4(a)) | NextHop::Multicast(IpAddr::V4(a)) =>
target.append_slice(&a.octets())?,
NextHop::Unicast(IpAddr::V6(a)) | NextHop::Multicast(IpAddr::V6(a)) =>
target.append_slice(&a.octets())?,
NextHop::Ipv6LL{global, link_local} => {
target.append_slice(&global.octets())?;
target.append_slice(&link_local.octets())?;
}
NextHop::MplsVpnUnicast(rd, IpAddr::V4(a)) => {
target.append_slice(rd.as_ref())?;
target.append_slice(&a.octets())?;
}
NextHop::MplsVpnUnicast(rd, IpAddr::V6(a)) => {
target.append_slice(rd.as_ref())?;
target.append_slice(&a.octets())?;
}
NextHop::Empty => { },
NextHop::Unimplemented(_afisafi) => todo!(),
}
Ok(())
}
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct MpUnreachNlriBuilder<AfiSafi> {
withdrawals: Vec<AfiSafi>,
}
impl<A> MpUnreachNlriBuilder<A>
where
{
pub fn add_withdrawals_from_pdu<'a, Octs, O>(
&mut self,
source: &'a UpdateMessage<Octs>,
_session_config: &SessionConfig
)
where
A: AfiSafiNlri + NlriCompose + NlriParse<'a, O, Octs, Output = A>,
Octs: Octets<Range<'a> = O>,
O: Octets,
{
if let Ok(Some(iter)) = source.typed_withdrawals::<_, A>() {
for w in iter {
self.add_withdrawal(w.unwrap());
}
}
}
}
impl<A: NlriCompose> MpUnreachNlriBuilder<A> {
pub fn new() -> Self {
Self {
withdrawals: Vec::new(),
}
}
pub fn from(withdrawal: A) -> Self {
Self {
withdrawals: vec![withdrawal],
}
}
pub(crate) fn split(&mut self, n: usize) -> Self {
let this_batch = self.withdrawals.drain(..n).collect();
MpUnreachNlriBuilder {
withdrawals: this_batch,
}
}
pub(crate) fn value_len(&self) -> usize {
3 + self.withdrawals.iter().fold(0, |sum, w| sum + w.compose_len())
}
pub(crate) fn is_empty(&self) -> bool {
self.withdrawals.is_empty()
}
pub fn add_withdrawal(&mut self, nlri: A) {
self.withdrawals.push(nlri);
}
pub(crate) fn compose_value<Target: OctetsBuilder>(&self, target: &mut Target)
-> Result<(), Target::AppendError>
{
target.append_slice(&A::afi_safi().as_bytes())?;
for w in &self.withdrawals {
w.compose(target)?;
}
Ok(())
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct StandardCommunitiesList {
communities: Vec<StandardCommunity>,
#[cfg_attr(feature = "serde", serde(skip))]
len: usize, #[cfg_attr(feature = "serde", serde(skip))]
extended: bool,
}
impl StandardCommunitiesList {
pub(crate) fn new() -> StandardCommunitiesList {
StandardCommunitiesList {
communities: Vec::new(),
len: 0,
extended: false
}
}
pub(crate) fn with_capacity(c: usize) -> StandardCommunitiesList {
StandardCommunitiesList {
communities: Vec::with_capacity(c),
len: 0,
extended: false
}
}
pub fn communities(&self) -> &Vec<StandardCommunity> {
&self.communities
}
pub fn add_community(&mut self, community: StandardCommunity) {
if !self.extended && self.len + 4 > 255 {
self.extended = true;
}
self.len += 4;
self.communities.push(community);
}
}
#[derive(Debug)]
pub enum ComposeError{
PduTooLarge(usize),
AttributeTooLarge(PathAttributeType, usize),
AttributesTooLarge(usize),
IllegalCombination,
EmptyMpReachNlri,
EmptyMpUnreachNlri,
WrongAddressType,
InvalidAttribute,
ShortBuf,
ParseError(ParseError),
Todo,
}
impl ComposeError {
#[allow(dead_code)]
fn todo() -> Self {
ComposeError::Todo
}
}
impl From<ShortBuf> for ComposeError {
fn from(_: ShortBuf) -> ComposeError {
ComposeError::ShortBuf
}
}
impl From<ParseError> for ComposeError {
fn from(pe: ParseError) -> ComposeError {
ComposeError::ParseError(pe)
}
}
impl std::error::Error for ComposeError { }
impl fmt::Display for ComposeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ComposeError::PduTooLarge(n) => {
write!(f, "oversized PDU: {n} bytes")
}
ComposeError::AttributeTooLarge(attr, n) => {
write!(f, "oversized attribute {attr}: {n} bytes")
}
ComposeError::AttributesTooLarge(n) => {
write!(f, "total path attributes too large: {n} bytes")
}
ComposeError::IllegalCombination => {
write!(f, "illegal combination of prefixes/attributes")
}
ComposeError::EmptyMpReachNlri => {
write!(f, "missing NLRI in MP_REACH_NLRI")
}
ComposeError::EmptyMpUnreachNlri => {
write!(f, "missing NLRI in MP_UNREACH_NLRI")
}
ComposeError::WrongAddressType => {
write!(f, "wrong address type")
}
ComposeError::InvalidAttribute => {
write!(f, "invalid attribute")
}
ComposeError::ShortBuf => {
ShortBuf.fmt(f)
}
ComposeError::ParseError(pe) => {
write!(f, "parse error in builder: {}", pe)
}
ComposeError::Todo => {
write!(f, "not implemented yet")
}
}
}
}
#[cfg(test)]
mod tests {
use std::fs::File;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
use memmap2::Mmap;
use octseq::Parser;
use inetnum::addr::Prefix;
use inetnum::asn::Asn;
use crate::bgp::aspath::HopPath;
use crate::bgp::communities::{StandardCommunity, Tag};
use crate::bgp::nlri::afisafi::{
Ipv4UnicastNlri,
Ipv4MulticastNlri,
Ipv6UnicastNlri,
Ipv4FlowSpecNlri,
NlriType,
};
use crate::bgp::types::{AfiSafiType, OriginType, PathId};
use super::*;
fn print_pcap<T: AsRef<[u8]>>(buf: T) {
print!("000000 ");
for b in buf.as_ref() {
print!("{:02x} ", b);
}
println!();
}
#[test]
fn empty_nlri_iterators() {
let mut builder = UpdateBuilder::new_vec();
builder.add_withdrawal(
Ipv6UnicastNlri::from_str("2001:db8::/32").unwrap()
).unwrap();
let msg = builder.into_message(&SessionConfig::modern()).unwrap();
print_pcap(msg.as_ref());
assert_eq!(msg.withdrawals().unwrap().count(), 1);
let mut builder2 = UpdateBuilder::new_vec();
builder2.add_withdrawal(
Ipv4UnicastNlri::from_str("10.0.0.0/8").unwrap()
).unwrap();
let msg2 = builder2.into_message(&SessionConfig::modern()).unwrap();
print_pcap(msg2.as_ref());
assert_eq!(msg2.withdrawals().unwrap().count(), 1);
}
#[test]
fn build_empty() {
let builder: UpdateBuilder<_, Ipv4UnicastNlri> = UpdateBuilder::new_vec();
let msg = builder.finish(&SessionConfig::modern()).unwrap();
assert!(
UpdateMessage::from_octets(msg, &SessionConfig::modern()).is_ok()
);
}
#[test]
fn build_withdrawals_basic_v4() {
let mut builder = UpdateBuilder::new_vec();
let withdrawals = [
"0.0.0.0/0",
"10.2.1.0/24",
"10.2.2.0/24",
"10.2.0.0/23",
"10.2.4.0/25",
"10.0.0.0/7",
"10.0.0.0/8",
"10.0.0.0/9",
].map(|s| Ipv4UnicastNlri::from_str(s).unwrap())
.into_iter()
.collect::<Vec<_>>();
let _ = builder.append_withdrawals(withdrawals.clone());
let msg = builder.finish(&SessionConfig::modern()).unwrap();
if let Err(e) = UpdateMessage::from_octets(&msg, &SessionConfig::modern()) {
dbg!(e);
}
assert!(
UpdateMessage::from_octets(&msg, &SessionConfig::modern())
.is_ok()
);
print_pcap(&msg);
let mut builder2 = UpdateBuilder::new_vec();
for w in withdrawals {
builder2.add_withdrawal(w).unwrap();
}
let msg2 = builder2.finish(&SessionConfig::modern()).unwrap();
assert!(
UpdateMessage::from_octets(&msg2, &SessionConfig::modern())
.is_ok()
);
print_pcap(&msg2);
assert_eq!(msg, msg2);
}
#[test]
fn take_message_many_withdrawals() {
let mut builder = UpdateBuilder::new_vec();
let mut prefixes: Vec<Ipv4UnicastNlri> = vec![];
for i in 1..3000_u32 {
prefixes.push(
Ipv4UnicastNlri::try_from(
Prefix::new_v4(
Ipv4Addr::from((i << 10).to_be_bytes()),
22
).unwrap()
).unwrap()
);
}
let prefixes_len = prefixes.len();
builder.withdrawals_from_iter(prefixes).unwrap();
let mut w_cnt = 0;
let remainder = if let (pdu1, Some(remainder)) = builder.take_message(&SessionConfig::modern()) {
match pdu1 {
Ok(pdu) => {
w_cnt += pdu.withdrawals().unwrap().count();
remainder
}
Err(e) => panic!("{}", e)
}
} else {
panic!("wrong 1");
};
let remainder2 = if let (pdu2, Some(remainder2)) = remainder.take_message(&SessionConfig::modern()) {
match pdu2 {
Ok(pdu) => {
w_cnt += pdu.withdrawals().unwrap().count();
remainder2
}
Err(e) => panic!("{}", e)
}
} else {
panic!("wrong 2");
};
if let (pdu3, None) = remainder2.take_message(&SessionConfig::modern()) {
match pdu3 {
Ok(pdu) => {
w_cnt += pdu.withdrawals().unwrap().count();
}
Err(e) => panic!("{}", e)
}
} else {
panic!("wrong 3");
};
assert_eq!(w_cnt, prefixes_len);
}
#[test]
fn take_message_many_withdrawals_2() {
let mut builder = UpdateBuilder::new_vec();
let mut prefixes: Vec<Ipv4UnicastNlri> = vec![];
for i in 1..1500_u32 {
prefixes.push(
Ipv4UnicastNlri::try_from(
Prefix::new_v4(
Ipv4Addr::from((i << 10).to_be_bytes()),
22
).unwrap()
).unwrap()
);
}
let prefixes_len = prefixes.len();
builder.append_withdrawals(prefixes).unwrap();
let mut w_cnt = 0;
let mut remainder = Some(builder);
loop {
if remainder.is_none() {
break
}
let (pdu, new_remainder) = remainder.take().unwrap().take_message(&SessionConfig::modern());
match pdu {
Ok(pdu) => {
w_cnt += pdu.withdrawals().unwrap().count();
remainder = new_remainder;
}
Err(e) => panic!("{}", e)
}
}
assert_eq!(w_cnt, prefixes_len);
}
#[test]
fn into_messages_many_withdrawals() {
let mut builder = UpdateBuilder::new_vec();
let mut prefixes: Vec<Ipv4UnicastNlri> = vec![];
for i in 1..1500_u32 {
prefixes.push(
Ipv4UnicastNlri::try_from(
Prefix::new_v4(
Ipv4Addr::from((i << 10).to_be_bytes()),
22
).unwrap()
).unwrap()
);
}
let prefixes_len = prefixes.len();
builder.append_withdrawals(prefixes).unwrap();
let mut w_cnt = 0;
for pdu in builder.into_messages(&SessionConfig::modern()).unwrap() {
w_cnt += pdu.withdrawals().unwrap().count();
}
assert_eq!(w_cnt, prefixes_len);
}
#[test]
fn into_messages_many_announcements() {
let mut builder = UpdateBuilder::new_vec();
let mut prefixes: Vec<Ipv4UnicastNlri> = vec![];
for i in 1..1500_u32 {
prefixes.push(
Ipv4UnicastNlri::try_from(
Prefix::new_v4(
Ipv4Addr::from((i << 10).to_be_bytes()),
22
).unwrap()
).unwrap()
);
}
let prefixes_len = prefixes.len();
for p in prefixes {
builder.add_announcement(p).unwrap();
}
let mut w_cnt = 0;
for pdu in builder.into_messages(&SessionConfig::modern()).unwrap() {
w_cnt += pdu.announcements().unwrap().count();
}
assert_eq!(w_cnt, prefixes_len);
}
#[test]
fn into_messages_many_withdrawals_mp() {
let mut builder = UpdateBuilder::new_vec();
let prefixes_num = 1024;
for i in 0..prefixes_num {
builder.add_withdrawal(
Ipv6UnicastNlri::from_str(&format!("2001:db:{:04}::/48", i))
.unwrap()
).unwrap();
}
let mut w_cnt = 0;
for pdu in builder.into_messages(&SessionConfig::modern()).unwrap() {
w_cnt += pdu.withdrawals().unwrap().count();
}
assert_eq!(w_cnt, prefixes_num);
}
#[test]
fn into_messages_many_announcements_mp() {
let mut builder = UpdateBuilder::new_vec();
let prefixes_num = 1_000_000;
for i in 0..prefixes_num {
builder.add_announcement(
Ipv6UnicastNlri::try_from(Prefix::new_v6((i << 96).into(), 32).unwrap()).unwrap()
).unwrap();
}
builder.attributes.set(crate::bgp::types::LocalPref(123));
builder.attributes.set(crate::bgp::types::MultiExitDisc(123));
(1..=300).for_each(|n| {
builder.add_community(StandardCommunity::new(n.into(), Tag::new(123))).unwrap();
});
let mut a_cnt = 0;
for pdu in builder.into_pdu_iter(&SessionConfig::modern()) {
let pdu = pdu.unwrap();
assert!(pdu.as_ref().len() <= UpdateBuilder::<(), Ipv4UnicastNlri>::MAX_PDU);
a_cnt += pdu.announcements().unwrap().count();
assert!(pdu.local_pref().unwrap().is_some());
assert!(pdu.multi_exit_disc().unwrap().is_some());
assert_eq!(pdu.communities().unwrap().unwrap().count(), 300);
}
assert_eq!(a_cnt, usize::try_from(prefixes_num).unwrap());
}
#[test]
fn build_withdrawals_basic_v4_addpath() {
let mut builder = UpdateBuilder::new_vec();
let withdrawals = [
"0.0.0.0/0",
"10.2.1.0/24",
"10.2.2.0/24",
"10.2.0.0/23",
"10.2.4.0/25",
"10.0.0.0/7",
"10.0.0.0/8",
"10.0.0.0/9",
].iter().enumerate().map(|(idx, s)|
Ipv4UnicastNlri::from_str(s).unwrap()
.into_addpath(PathId(idx.try_into().unwrap()))
).collect::<Vec<_>>();
let _ = builder.append_withdrawals(withdrawals);
let msg = builder.finish(&SessionConfig::modern()).unwrap();
let mut sc = SessionConfig::modern();
sc.add_addpath_rxtx(AfiSafiType::Ipv4Unicast);
assert!(
UpdateMessage::from_octets(&msg, &sc)
.is_ok()
);
print_pcap(&msg);
}
#[test]
fn build_withdrawals_basic_v6_single() {
let mut builder = UpdateBuilder::new_vec();
let withdrawals = vec![
Ipv6UnicastNlri::from_str("2001:db8::/32").unwrap()
];
let _ = builder.append_withdrawals(withdrawals);
let msg = builder.finish(&SessionConfig::modern()).unwrap();
println!("msg raw len: {}", &msg.len());
print_pcap(&msg);
UpdateMessage::from_octets(&msg, &SessionConfig::modern()).unwrap();
}
#[test]
fn build_withdrawals_basic_v6_from_iter() {
let mut builder = UpdateBuilder::new_vec();
let mut withdrawals = vec![];
for i in 1..512_u128 {
withdrawals.push(
Ipv6UnicastNlri::try_from(
Prefix::new_v6(
Ipv6Addr::from((i << 64).to_be_bytes()),
64
).unwrap()
).unwrap()
);
}
let _ = builder.withdrawals_from_iter(withdrawals);
let raw = builder.finish(&SessionConfig::modern()).unwrap();
print_pcap(&raw);
UpdateMessage::from_octets(&raw, &SessionConfig::modern()).unwrap();
}
#[test]
fn build_conv_mp_mix() {
let buf = vec![
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x88 + 8, 0x02, 0x00, 0x00, 0x00, 0x71, 0x80,
0x0e, 0x5a, 0x00, 0x02, 0x01, 0x20, 0xfc, 0x00,
0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80,
0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0x00,
0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff,
0x00, 0x01, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0xff,
0xff, 0x00, 0x02, 0x40, 0x20, 0x01, 0x0d, 0xb8,
0xff, 0xff, 0x00, 0x03, 0x40, 0x01, 0x01, 0x00,
0x40, 0x02, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00,
0xc8, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00,
24, 1, 1, 1,
24, 1, 1, 2
];
let upd = UpdateMessage::from_octets(&buf, &SessionConfig::modern()).unwrap();
print_pcap(upd.as_ref());
assert!(upd.has_conventional_nlri() && upd.has_mp_nlri().unwrap());
assert_eq!(upd.announcements().unwrap().count(), 7);
}
#[test]
fn build_announcements_conventional() {
let mut builder = UpdateBuilder::new_vec();
let prefixes = [
"1.0.1.0/24",
"1.0.2.0/24",
"1.0.3.0/24",
"1.0.4.0/24",
].map(|p| Ipv4UnicastNlri::from_str(p).unwrap());
builder.announcements_from_iter(prefixes).unwrap();
builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp));
builder.set_nexthop(NextHop::Unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into())).unwrap();
let path = HopPath::from([
Asn::from_u32(123); 70
]);
builder.set_aspath(path).unwrap();
let raw = builder.finish(&SessionConfig::modern()).unwrap();
print_pcap(raw);
}
#[test]
fn build_announcements_mp() {
let mut builder = UpdateBuilder::new_vec();
let prefixes = [
"2001:db8:1::/48",
"2001:db8:2::/48",
"2001:db8:3::/48",
].map(|p| Ipv6UnicastNlri::from_str(p).unwrap());
builder.announcements_from_iter(prefixes).unwrap();
builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp));
builder.set_nexthop(NextHop::Unicast(Ipv6Addr::from_str("fe80:1:2:3::").unwrap().into())).unwrap();
let path = HopPath::from([
Asn::from_u32(100),
Asn::from_u32(200),
Asn::from_u32(300),
]);
builder.set_aspath(path).unwrap();
let raw = builder.finish(&SessionConfig::modern()).unwrap();
print_pcap(raw);
}
#[test]
fn build_announcements_mp_missing_nlri() {
let mut builder = UpdateBuilder::<_, Ipv6UnicastNlri>::new_vec();
builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp));
builder.set_nexthop(NextHop::Unicast(Ipv6Addr::from_str("fe80:1:2:3::").unwrap().into())).unwrap();
let path = HopPath::from([
Asn::from_u32(100),
Asn::from_u32(200),
Asn::from_u32(300),
]);
builder.set_aspath(path).unwrap();
assert!(matches!(
builder.into_message(&SessionConfig::modern()),
Err(ComposeError::EmptyMpReachNlri)
));
}
#[test]
fn build_announcements_mp_link_local() {
let mut builder = UpdateBuilder::new_vec();
let prefixes = [
"2001:db8:1::/48",
"2001:db8:2::/48",
"2001:db8:3::/48",
].map(|p| Ipv6UnicastNlri::from_str(p).unwrap());
builder.announcements_from_iter(prefixes).unwrap();
builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp));
builder.set_nexthop_ll_addr("fe80:1:2:3::".parse().unwrap()).unwrap();
let path = HopPath::from([
Asn::from_u32(100),
Asn::from_u32(200),
Asn::from_u32(300),
]);
builder.set_aspath(path).unwrap();
let msg = builder.into_message(&SessionConfig::modern()).unwrap();
msg.print_pcap();
}
#[test]
fn build_announcements_mp_ll_no_nlri() {
let mut builder = UpdateBuilder::<_, Ipv6UnicastNlri>::new_vec();
builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp));
builder.set_nexthop_ll_addr("fe80:1:2:3::".parse().unwrap()).unwrap();
let path = HopPath::from([
Asn::from_u32(100),
Asn::from_u32(200),
Asn::from_u32(300),
]);
builder.set_aspath(path).unwrap();
assert!(matches!(
builder.into_message(&SessionConfig::modern()),
Err(ComposeError::EmptyMpReachNlri)
));
}
#[test]
fn build_standard_communities() {
let mut builder = UpdateBuilder::new_vec();
let prefixes = [
"1.0.1.0/24",
"1.0.2.0/24",
"1.0.3.0/24",
"1.0.4.0/24",
].map(|p| Ipv4UnicastNlri::from_str(p).unwrap());
builder.announcements_from_iter(prefixes).unwrap();
builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp));
builder.set_nexthop(NextHop::Unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into())).unwrap();
let path = HopPath::from([
Asn::from_u32(100),
Asn::from_u32(200),
Asn::from_u32(300),
]);
builder.set_aspath(path).unwrap();
builder.add_community("AS1234:666".parse().unwrap()).unwrap();
builder.add_community("NO_EXPORT".parse().unwrap()).unwrap();
for n in 1..100 {
builder.add_community(format!("AS999:{n}").parse().unwrap()).unwrap();
}
builder.into_message(&SessionConfig::modern()).unwrap();
}
#[test]
fn build_other_attributes() {
let mut builder = UpdateBuilder::new_vec();
let prefixes = [
"1.0.1.0/24",
"1.0.2.0/24",
"1.0.3.0/24",
"1.0.4.0/24",
].map(|p| Ipv4UnicastNlri::from_str(p).unwrap());
builder.announcements_from_iter(prefixes).unwrap();
builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp));
builder.set_nexthop(NextHop::Unicast(Ipv4Addr::from_str("1.2.3.4").unwrap().into())).unwrap();
let path = HopPath::from([
Asn::from_u32(100),
Asn::from_u32(200),
Asn::from_u32(300),
]);
builder.set_aspath(path).unwrap();
builder.attributes.set(crate::bgp::types::MultiExitDisc(1234));
builder.attributes.set(crate::bgp::types::LocalPref(9876));
let msg = builder.into_message(&SessionConfig::modern()).unwrap();
msg.print_pcap();
}
#[test]
fn from_update_message() {
let raw = vec![
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x37, 0x02,
0x00, 0x00, 0x00, 0x1b, 0x40, 0x01, 0x01, 0x00, 0x40, 0x02,
0x06, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x40, 0x03, 0x04,
0x0a, 0xff, 0x00, 0x65, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00,
0x01, 0x20, 0x0a, 0x0a, 0x0a, 0x02
];
let sc = SessionConfig::modern();
let upd = UpdateMessage::from_octets(&raw, &sc).unwrap();
let target = BytesMut::new();
let mut builder = UpdateBuilder::from_update_message(&upd, &sc, target).unwrap();
assert_eq!(builder.attributes.len(), 4);
builder.add_announcement(
Ipv4UnicastNlri::from_str("10.10.10.2/32").unwrap()
).unwrap();
let upd2 = builder.into_message(&SessionConfig::modern()).unwrap();
assert!(
upd.typed_announcements::<_, Ipv4UnicastNlri>().unwrap().unwrap().eq(
upd2.typed_announcements::<_, Ipv4UnicastNlri>().unwrap().unwrap()
)
);
}
#[test]
fn build_overwriting_attributes() {
let raw = vec![
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x37, 0x02,
0x00, 0x00, 0x00, 0x1b, 0x40, 0x01, 0x01, 0x00, 0x40, 0x02,
0x06, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x40, 0x03, 0x04,
0x0a, 0xff, 0x00, 0x65, 0x80, 0x04, 0x04, 0x00, 0x00, 0x00,
0x01, 0x20, 0x0a, 0x0a, 0x0a, 0x02
];
let sc = SessionConfig::modern();
let upd = UpdateMessage::from_octets(&raw, &sc).unwrap();
for pa in upd.clone().path_attributes().unwrap() {
eprintln!("{:?}", pa.unwrap().to_owned().unwrap());
}
let target = BytesMut::new();
let mut builder = UpdateBuilder::from_update_message(&upd, &sc, target).unwrap();
assert_eq!(builder.attributes.len(), 4);
builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp));
builder.attributes.set(crate::bgp::types::Origin(OriginType::Egp));
builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp));
assert_eq!(builder.attributes.len(), 4);
builder.add_announcement(
Ipv4UnicastNlri::from_str("10.10.10.2/32").unwrap()
).unwrap();
let upd2 = builder.into_message(&SessionConfig::modern()).unwrap();
assert_eq!(upd2.origin(), Ok(Some(OriginType::Igp)));
}
#[test]
fn build_ordered_attributes() {
let mut builder = UpdateBuilder::<_, Ipv4UnicastNlri>::new_vec();
builder.add_community(
StandardCommunity::from_str("AS1234:999").unwrap()
).unwrap();
builder.attributes.set(crate::bgp::types::Origin(OriginType::Igp));
builder.add_community(
StandardCommunity::from_str("AS1234:1000").unwrap()
).unwrap();
assert_eq!(builder.attributes.len(), 2);
let pdu = builder.into_message(&SessionConfig::modern()).unwrap();
let mut prev_type_code = 0_u8;
for pa in pdu.path_attributes().unwrap() {
let type_code = pa.unwrap().type_code();
assert!(prev_type_code < type_code);
prev_type_code = type_code;
}
assert_eq!(pdu.communities().unwrap().unwrap().count(), 2);
}
fn parse_build_compare(raw: &[u8]) {
let sc = SessionConfig::modern();
let original =
match UpdateMessage::from_octets(raw, &sc) {
Ok(msg) => msg,
Err(_e) => {
return;
}
};
let target = BytesMut::new();
let reach_afisafi = original.announcement_fams().last()
.or(
original.withdrawal_fams().last()
)
.unwrap_or(NlriType::Ipv4Unicast);
let composed = match reach_afisafi {
NlriType::Ipv4Unicast => {
let mut builder = UpdateBuilder::<_, Ipv4UnicastNlri>::from_update_message(&original, &sc, target).unwrap();
builder.add_announcements_from_pdu(&original, &sc);
builder.add_withdrawals_from_pdu(&original, &sc);
builder.into_message(&sc)
}
NlriType::Ipv4Multicast => {
let mut builder = UpdateBuilder::<_, Ipv4MulticastNlri>::from_update_message(&original, &sc, target).unwrap();
builder.add_announcements_from_pdu(&original, &sc);
builder.add_withdrawals_from_pdu(&original, &sc);
builder.into_message(&sc)
}
NlriType::Ipv4MplsUnicast => todo!(),
NlriType::Ipv4MplsVpnUnicast => todo!(),
NlriType::Ipv4RouteTarget => todo!(),
NlriType::Ipv4FlowSpec => {
let mut builder = UpdateBuilder::<_, Ipv4FlowSpecNlri<_>>::from_update_message(&original, &sc, target).unwrap();
builder.add_announcements_from_pdu(&original, &sc);
builder.add_withdrawals_from_pdu(&original, &sc);
builder.into_message(&sc)
}
NlriType::Ipv6Unicast => {
let mut builder = UpdateBuilder::<_, Ipv6UnicastNlri>::from_update_message(&original, &sc, target).unwrap();
builder.add_announcements_from_pdu(&original, &sc);
builder.add_withdrawals_from_pdu(&original, &sc);
builder.into_message(&sc)
}
NlriType::Ipv6Multicast => todo!(),
NlriType::Ipv6MplsUnicast => todo!(),
NlriType::Ipv6MplsVpnUnicast => todo!(),
NlriType::Ipv6FlowSpec => todo!(),
NlriType::L2VpnVpls => todo!(),
NlriType::L2VpnEvpn => todo!(),
NlriType::Unsupported(_, _) => todo!(),
_ => todo!(), };
let composed = match composed {
Ok(msg) => msg,
Err(e) => {
print_pcap(raw);
panic!("error: {e}");
}
};
if std::panic::catch_unwind(|| {
assert_eq!(
original.announcements().unwrap().count(),
composed.announcements().unwrap().count(),
);
assert_eq!(
original.withdrawals().unwrap().count(),
composed.withdrawals().unwrap().count(),
);
assert!(
composed.path_attributes().unwrap().count().abs_diff(
original.path_attributes().unwrap().count()
) <= 2
);
assert_eq!(original.origin(), composed.origin());
assert_eq!(original.multi_exit_disc(), composed.multi_exit_disc());
assert_eq!(original.local_pref(), composed.local_pref());
}).is_err() {
eprintln!("--");
print_pcap(raw);
print_pcap(composed.as_ref());
eprintln!("--");
panic!("tmp");
}
}
#[test]
fn parse_build_compare_1() {
eprintln!();
parse_build_compare(&[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x37, 0x02, 0x00, 0x00, 0x00, 0x1b, 0x40,
0x01, 0x01, 0x00, 0x40, 0x02, 0x06, 0x02, 0x01,
0x00, 0x01, 0x00, 0x00, 0x40, 0x03, 0x04, 0x0a,
0xff, 0x00, 0x65, 0x80, 0x04, 0x04, 0x00, 0x00,
0x00, 0x01, 0x20, 0x0a, 0x0a, 0x0a, 0x02
]
);
parse_build_compare(&[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x46, 0x02, 0x00, 0x00, 0x00, 0x2f, 0x90,
0x0e, 0x00, 0x12, 0x00, 0x01, 0x85, 0x00, 0x00,
0x0c, 0x07, 0x81, 0x08, 0x08, 0x81, 0x00, 0x0b,
0x81, 0x08, 0x0c, 0x81, 0x01, 0x40, 0x01, 0x01,
0x00, 0x40, 0x02, 0x00, 0x40, 0x05, 0x04, 0x00,
0x00, 0x00, 0x64, 0xc0, 0x10, 0x08, 0x80, 0x09,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10
]
);
parse_build_compare(&[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x52, 0x02, 0x00, 0x00, 0x00, 0x3b, 0x80,
0x0e, 0x1d, 0x00, 0x01, 0x02, 0x04, 0x0a, 0x09,
0x0a, 0x09, 0x00, 0x1a, 0xc6, 0x33, 0x64, 0x00,
0x1a, 0xc6, 0x33, 0x64, 0x40, 0x1a, 0xc6, 0x33,
0x64, 0x80, 0x1a, 0xc6, 0x33, 0x64, 0xc0, 0x40,
0x01, 0x01, 0x00, 0x40, 0x02, 0x06, 0x02, 0x01,
0x00, 0x00, 0x01, 0xf4, 0x40, 0x03, 0x04, 0x0a,
0x09, 0x0a, 0x09, 0x80, 0x04, 0x04, 0x00, 0x00,
0x00, 0x00
]
);
eprintln!();
}
#[ignore]
#[test]
fn parse_build_compare_bulk() {
let filename = "examples/raw_bgp_updates";
let file = File::open(filename).unwrap();
let mmap = unsafe { Mmap::map(&file).unwrap() };
let fh = &mmap[..];
let mut parser = Parser::from_ref(&fh);
let mut n = 0;
const MAX: usize = usize::MAX;
while parser.remaining() > 0 && n < MAX {
let pos = parser.pos();
parser.advance(16).unwrap();
let len = parser.parse_u16_be().unwrap();
parser.seek(pos).unwrap();
parse_build_compare(
parser.parse_octets(len.into()).unwrap()
);
n += 1;
eprint!("\r{n} ");
}
eprintln!("parse_build_compare'd {n}");
}
#[test]
fn build_mp_unreach_extended_length() {
let raw = vec![
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x02, 0xd1, 0x02, 0x00, 0x00, 0x02, 0xba, 0x40,
0x01, 0x01, 0x00, 0x40, 0x02, 0x1a, 0x02, 0x06,
0x00, 0x00, 0x1a, 0x0b, 0x00, 0x00, 0x51, 0x1c,
0x00, 0x00, 0xbc, 0xa5, 0x00, 0x00, 0x1b, 0x1b,
0x00, 0x00, 0xf3, 0x20, 0x00, 0x03, 0x2e, 0xa9,
0x80, 0x04, 0x04, 0x00, 0x00, 0x00, 0x08, 0xd0,
0x08, 0x02, 0x60, 0x00, 0x00, 0x0b, 0x26, 0x00,
0x00, 0x0c, 0xa3, 0x00, 0x00, 0x1a, 0x10, 0x00,
0x00, 0x1a, 0x29, 0x00, 0x00, 0x20, 0x01, 0x00,
0x00, 0x21, 0x16, 0x00, 0x00, 0x21, 0x2c, 0x00,
0x00, 0x21, 0x93, 0x00, 0x00, 0x21, 0xbc, 0x00,
0x00, 0x21, 0xc1, 0x00, 0x00, 0x23, 0xa3, 0x00,
0x00, 0x31, 0x0b, 0x00, 0x00, 0x31, 0x6e, 0x00,
0x00, 0x32, 0xca, 0x00, 0x00, 0x33, 0xf6, 0x00,
0x00, 0x3c, 0x12, 0x00, 0x00, 0x3c, 0x59, 0x00,
0x00, 0x3d, 0x38, 0x00, 0x00, 0x3d, 0xe8, 0x00,
0x00, 0x52, 0x26, 0x00, 0x00, 0x53, 0x12, 0x00,
0x00, 0x53, 0x33, 0x00, 0x00, 0x53, 0xcd, 0x00,
0x00, 0x60, 0x3e, 0x00, 0x00, 0x60, 0xa3, 0x00,
0x00, 0x61, 0x05, 0x00, 0x00, 0x67, 0x2f, 0x00,
0x00, 0x71, 0x79, 0x00, 0x00, 0x71, 0xc4, 0x00,
0x00, 0x71, 0xfe, 0x00, 0x00, 0x73, 0x94, 0x00,
0x00, 0x78, 0x87, 0x00, 0x00, 0x78, 0x9d, 0x00,
0x00, 0x78, 0xd8, 0x00, 0x00, 0x78, 0xf8, 0x00,
0x00, 0x7a, 0x3e, 0x00, 0x00, 0x7a, 0x8a, 0x00,
0x00, 0x7a, 0x90, 0x00, 0x00, 0x7a, 0xc6, 0x00,
0x00, 0x7b, 0xd7, 0x00, 0x00, 0x84, 0x70, 0x00,
0x00, 0x84, 0xb4, 0x00, 0x00, 0x86, 0x98, 0x00,
0x00, 0x87, 0x2a, 0x00, 0x00, 0x88, 0x8f, 0x00,
0x00, 0x88, 0xb8, 0x00, 0x00, 0x88, 0xd8, 0x00,
0x00, 0x89, 0x44, 0x00, 0x00, 0x8a, 0x7f, 0x00,
0x00, 0x8a, 0xb4, 0x00, 0x00, 0x8b, 0x0e, 0x00,
0x00, 0x8b, 0xdf, 0x00, 0x00, 0x8b, 0xe2, 0x00,
0x00, 0x98, 0xaf, 0x00, 0x00, 0x9b, 0x37, 0x00,
0x00, 0xa0, 0xa2, 0x00, 0x00, 0xa2, 0x81, 0x00,
0x00, 0xa2, 0xfa, 0x00, 0x00, 0xa5, 0x0d, 0x00,
0x00, 0xa5, 0x33, 0x00, 0x00, 0xa6, 0x16, 0x00,
0x00, 0xa6, 0xac, 0x00, 0x00, 0xa8, 0xae, 0x00,
0x00, 0xa9, 0x35, 0x00, 0x00, 0xaa, 0x0a, 0x00,
0x00, 0xaa, 0xcf, 0x00, 0x00, 0xab, 0xfe, 0x00,
0x00, 0xac, 0xee, 0x00, 0x00, 0xad, 0x62, 0x00,
0x00, 0xaf, 0x2b, 0x00, 0x00, 0xaf, 0x71, 0x00,
0x00, 0xaf, 0xe5, 0x00, 0x00, 0xb8, 0x9a, 0x00,
0x00, 0xb9, 0xca, 0x00, 0x00, 0xb9, 0xe2, 0x00,
0x00, 0xba, 0x1d, 0x00, 0x00, 0xba, 0x6b, 0x00,
0x00, 0xba, 0x83, 0x00, 0x00, 0xba, 0x9f, 0x00,
0x00, 0xbb, 0xe0, 0x00, 0x00, 0xbc, 0x26, 0x00,
0x00, 0xbc, 0x81, 0x00, 0x00, 0xbc, 0xa5, 0x00,
0x00, 0xbc, 0xdb, 0x00, 0x00, 0xbd, 0x0f, 0x00,
0x00, 0xbd, 0x25, 0x00, 0x00, 0xbd, 0x61, 0x00,
0x00, 0xbd, 0x83, 0x00, 0x00, 0xbe, 0x82, 0x00,
0x00, 0xbe, 0xca, 0x00, 0x00, 0xbf, 0x5d, 0x00,
0x00, 0xbf, 0xa7, 0x00, 0x00, 0xc1, 0x61, 0x00,
0x00, 0xc1, 0x86, 0x00, 0x00, 0xc1, 0xa1, 0x00,
0x00, 0xc1, 0xe9, 0x00, 0x00, 0xc2, 0x73, 0x00,
0x00, 0xc2, 0x95, 0x00, 0x00, 0xc4, 0x52, 0x00,
0x00, 0xc4, 0xa4, 0x00, 0x00, 0xc5, 0x29, 0x00,
0x00, 0xc6, 0x53, 0x00, 0x00, 0xc6, 0xc9, 0x00,
0x00, 0xc7, 0x03, 0x00, 0x00, 0xc7, 0x0e, 0x00,
0x00, 0xc7, 0x47, 0x00, 0x00, 0xc7, 0x94, 0x00,
0x00, 0xc7, 0x95, 0x00, 0x00, 0xc8, 0x61, 0x00,
0x00, 0xc8, 0x8d, 0x00, 0x00, 0xc9, 0xcb, 0x00,
0x00, 0xc9, 0xd5, 0x00, 0x00, 0xcb, 0xbc, 0x00,
0x00, 0xcb, 0xe1, 0x00, 0x00, 0xdc, 0xd6, 0x00,
0x00, 0xdd, 0x10, 0x00, 0x00, 0xdd, 0x67, 0x00,
0x00, 0xdd, 0x71, 0x00, 0x00, 0xdd, 0x7d, 0x00,
0x00, 0xdd, 0xee, 0x00, 0x00, 0xde, 0x8b, 0x00,
0x00, 0xde, 0xb2, 0x00, 0x00, 0xdf, 0x5e, 0x00,
0x00, 0xdf, 0xc1, 0x00, 0x00, 0xe0, 0x31, 0x00,
0x00, 0xe0, 0x8f, 0x00, 0x00, 0xe0, 0xb9, 0x00,
0x00, 0xe1, 0x70, 0x00, 0x00, 0xe3, 0x2e, 0x00,
0x00, 0xe8, 0x7c, 0x00, 0x00, 0xe8, 0xeb, 0x00,
0x00, 0xe9, 0x53, 0x00, 0x00, 0xe9, 0x94, 0x00,
0x00, 0xeb, 0x02, 0x00, 0x00, 0xeb, 0x05, 0x00,
0x00, 0xeb, 0x55, 0x00, 0x00, 0xeb, 0xc5, 0x00,
0x00, 0xed, 0xf7, 0x00, 0x00, 0xf2, 0x97, 0x00,
0x00, 0xf2, 0xfd, 0x00, 0x00, 0xf3, 0x21, 0x00,
0x00, 0xf3, 0xaa, 0x1a, 0x0b, 0x0b, 0xb9, 0x1a,
0x0b, 0x0f, 0xa5, 0x1a, 0x0b, 0x14, 0x57, 0x1a,
0x0b, 0x17, 0x70, 0x1a, 0x0b, 0x22, 0xbb, 0x51,
0x1c, 0x0b, 0xba, 0x51, 0x1c, 0x0b, 0xc3, 0x51,
0x1c, 0x0b, 0xcd, 0x71, 0x94, 0x03, 0x85, 0x71,
0x94, 0xfc, 0x67, 0x90, 0x0e, 0x00, 0x2a, 0x00,
0x02, 0x01, 0x20, 0x20, 0x01, 0x07, 0xf8, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x0b, 0x00,
0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xe6, 0xfc, 0x82, 0xff, 0xfe,
0xa3, 0xbf, 0xc3, 0x00, 0x1d, 0x2a, 0x10, 0xcb,
0xc0
];
parse_build_compare(&raw);
}
#[test]
fn build_invalid_attribute() {
let raw = vec![
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x5e, 0x02, 0x00, 0x00, 0x00, 0x43, 0x40,
0x01, 0x01, 0x00, 0x40, 0x02, 0x16, 0x02, 0x05,
0x00, 0x00, 0xbf, 0xee, 0x00, 0x00, 0xd0, 0x6c,
0x00, 0x00, 0x1b, 0x1b, 0x00, 0x00, 0x1d, 0x97,
0x00, 0x00, 0x5c, 0xa7, 0x40, 0x03, 0x04, 0x17,
0x81, 0x20, 0x3d, 0x40, 0x06, 0x00, 0xc0, 0x07,
0x08, 0x00, 0x00, 0xfe, 0x57, 0x0a, 0x40, 0x01,
0xf6, 0xe0, 0x14, 0x0e, 0x00, 0x01, 0x00, 0x01,
0xac, 0x15, 0x09, 0xf6, 0x00, 0x09, 0x0a, 0x40,
0x01, 0xf6, 0x18, 0xcb, 0x20, 0x6b
];
parse_build_compare(&raw);
}
#[test]
fn build_mp_reach_nlri_ll() {
let raw = vec![
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x9d, 0x02, 0x00, 0x00, 0x00, 0x86, 0x40,
0x01, 0x01, 0x00, 0x40, 0x02, 0x16, 0x02, 0x05,
0x00, 0x00, 0x5f, 0xa2, 0x00, 0x00, 0x19, 0x35,
0x00, 0x00, 0x0d, 0x1c, 0x00, 0x00, 0x4f, 0xf9,
0x00, 0x03, 0x3f, 0xf9, 0x80, 0x04, 0x04, 0x00,
0x01, 0x57, 0xd4, 0xc0, 0x08, 0x24, 0x19, 0x35,
0x00, 0x56, 0x19, 0x35, 0x03, 0xe8, 0x19, 0x35,
0x05, 0x78, 0x19, 0x35, 0x05, 0x7c, 0x5f, 0xa2,
0x00, 0x01, 0x5f, 0xa2, 0x32, 0xdc, 0x5f, 0xa2,
0x32, 0xdd, 0x5f, 0xa2, 0x4f, 0x4c, 0x5f, 0xa2,
0xfc, 0x59, 0xc0, 0x10, 0x08, 0x00, 0x02, 0x5f,
0xa2, 0x00, 0x00, 0x01, 0x36, 0x90, 0x0e, 0x00,
0x2c, 0x00, 0x02, 0x01, 0x20, 0x20, 0x01, 0x07,
0xf8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa5,
0x02, 0x44, 0x82, 0x00, 0x01, 0xfe, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xc3, 0xd6,
0x00, 0x51, 0x03, 0xe7, 0xc0, 0x00, 0x30, 0x2a,
0x0e, 0x9b, 0x43, 0x00, 0x00
];
parse_build_compare(&raw);
}
#[test]
fn build_from_pdu_with_empty_unreach() {
let raw = vec![
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x63, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x80,
0x0f, 0x03, 0x00, 0x02, 0x01, 0x40, 0x01, 0x01,
0x00, 0x40, 0x02, 0x12, 0x02, 0x04, 0x00, 0x00,
0x1b, 0x1b, 0x00, 0x00, 0x95, 0x0e, 0x00, 0x00,
0xe7, 0x8e, 0x00, 0x00, 0x46, 0x88, 0x80, 0x0e,
0x2a, 0x00, 0x02, 0x01, 0x20, 0x20, 0x01, 0x07,
0xf8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa5,
0x00, 0x69, 0x39, 0x00, 0x01, 0xfe, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x88, 0x2f,
0xff, 0xfe, 0xbc, 0x52, 0xdd, 0x00, 0x20, 0x24,
0x02, 0x82, 0xc0
];
let sc = SessionConfig::modern();
let original = UpdateMessage::from_octets(&raw, &sc).unwrap();
let mut builder = UpdateBuilder::<_, Ipv4UnicastNlri>::from_update_message(
&original,
&sc,
Vec::new()
).unwrap();
if let Ok(Some(iter)) =
original.typed_withdrawals::<_, Ipv4UnicastNlri>()
{
for a in iter {
builder.add_withdrawal(a.unwrap()).unwrap();
}
}
if let Ok(Some(iter)) =
original.typed_announcements::<_, Ipv4UnicastNlri>()
{
for a in iter {
builder.add_announcement(a.unwrap()).unwrap();
}
}
let composed = builder.into_message(&SessionConfig::modern()).unwrap();
assert_eq!(original.path_attributes().unwrap().count(), 4);
assert_eq!(composed.path_attributes().unwrap().count(), 2);
}
}