use crate::models::network::{Afi, NetworkPrefix};
#[cfg(feature = "parser")]
use bytes::{Buf, Bytes};
use ipnet::IpNet;
use smallvec::SmallVec;
use std::fmt::{Debug, Formatter};
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct MplsLabel(u32);
impl MplsLabel {
pub const MAX_VALUE: u32 = 0x000F_FFFF;
pub const IPV4_EXPLICIT_NULL: u32 = 0;
pub const IPV6_EXPLICIT_NULL: u32 = 2;
pub const IMPLICIT_NULL: u32 = 3;
pub fn try_new(value: u32) -> Result<Self, MplsLabelError> {
if value > Self::MAX_VALUE {
return Err(MplsLabelError::LabelValueTooLarge(value));
}
Ok(Self(value))
}
pub(crate) fn new_masked(value: u32) -> Self {
Self(value & Self::MAX_VALUE)
}
pub fn value(&self) -> u32 {
self.0
}
pub fn is_reserved(&self) -> bool {
self.0 <= 15
}
pub fn is_implicit_null(&self) -> bool {
self.0 == Self::IMPLICIT_NULL
}
pub fn is_ipv4_explicit_null(&self) -> bool {
self.0 == Self::IPV4_EXPLICIT_NULL
}
pub fn is_ipv6_explicit_null(&self) -> bool {
self.0 == Self::IPV6_EXPLICIT_NULL
}
pub fn encode(&self, is_bottom: bool) -> [u8; 3] {
let raw = (self.0 << 4) | (if is_bottom { 1 } else { 0 });
[(raw >> 16) as u8, (raw >> 8) as u8, raw as u8]
}
pub fn decode(bytes: [u8; 3]) -> (Self, bool) {
let raw = ((bytes[0] as u32) << 16) | ((bytes[1] as u32) << 8) | (bytes[2] as u32);
let label_value = raw >> 4;
let bos = (raw & 0x01) != 0;
(Self::new_masked(label_value), bos)
}
}
impl Debug for MplsLabel {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "MplsLabel({})", self.0)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for MplsLabel {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_u32(self.0)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for MplsLabel {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = u32::deserialize(deserializer)?;
Self::try_new(value).map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MplsLabelError {
LabelValueTooLarge(u32),
}
impl std::fmt::Display for MplsLabelError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
MplsLabelError::LabelValueTooLarge(v) => {
write!(
f,
"MPLS label value {} exceeds maximum 0x{:X}",
v,
MplsLabel::MAX_VALUE
)
}
}
}
}
impl std::error::Error for MplsLabelError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum LabeledNlriMode {
SingleLabel,
#[default]
MultiLabel,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LabeledNlriConfig {
pub add_path: bool,
pub mode: LabeledNlriMode,
pub max_labels: u8,
pub peer_max_labels: Option<u8>,
}
impl LabeledNlriConfig {
pub fn try_new(
add_path: bool,
mode: LabeledNlriMode,
max_labels: u8,
peer_max_labels: Option<u8>,
) -> Result<Self, LabeledNlriConfigError> {
if max_labels == 0 || max_labels > 254 {
return Err(LabeledNlriConfigError::InvalidMaxLabels(max_labels));
}
if let Some(peer) = peer_max_labels {
if peer == 0 {
return Err(LabeledNlriConfigError::InvalidPeerMaxLabels(peer));
}
}
Ok(Self {
add_path,
mode,
max_labels,
peer_max_labels,
})
}
}
impl Default for LabeledNlriConfig {
fn default() -> Self {
Self {
add_path: false,
mode: LabeledNlriMode::default(),
max_labels: 16,
peer_max_labels: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LabeledNlriConfigError {
InvalidMaxLabels(u8),
InvalidPeerMaxLabels(u8),
}
impl std::fmt::Display for LabeledNlriConfigError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
LabeledNlriConfigError::InvalidMaxLabels(v) => {
write!(f, "max_labels {} is invalid, must be 1-254", v)
}
LabeledNlriConfigError::InvalidPeerMaxLabels(v) => {
write!(f, "peer_max_labels {} is invalid, must be 2-254 or None", v)
}
}
}
}
impl std::error::Error for LabeledNlriConfigError {}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LabeledNetworkPrefix {
pub prefix: IpNet,
pub labels: SmallVec<[MplsLabel; 2]>,
pub path_id: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LabeledNetworkPrefixError {
EmptyLabelStack,
PrefixLengthOverflow { total_bits: usize, max: usize },
}
impl std::fmt::Display for LabeledNetworkPrefixError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
LabeledNetworkPrefixError::EmptyLabelStack => {
write!(f, "labeled prefix must have at least one label")
}
LabeledNetworkPrefixError::PrefixLengthOverflow { total_bits, max } => {
write!(
f,
"total NLRI length {} bits exceeds maximum {} bits",
total_bits, max
)
}
}
}
}
impl std::error::Error for LabeledNetworkPrefixError {}
impl LabeledNetworkPrefix {
pub fn try_new(
prefix: IpNet,
labels: SmallVec<[MplsLabel; 2]>,
path_id: Option<u32>,
) -> Result<Self, LabeledNetworkPrefixError> {
if labels.is_empty() {
return Err(LabeledNetworkPrefixError::EmptyLabelStack);
}
let label_bits = labels.len().checked_mul(24).ok_or(
LabeledNetworkPrefixError::PrefixLengthOverflow {
total_bits: usize::MAX,
max: 255,
},
)?;
let prefix_bits = prefix.prefix_len() as usize;
let total_bits = label_bits.checked_add(prefix_bits).ok_or(
LabeledNetworkPrefixError::PrefixLengthOverflow {
total_bits: usize::MAX,
max: 255,
},
)?;
if total_bits > 255 {
return Err(LabeledNetworkPrefixError::PrefixLengthOverflow {
total_bits,
max: 255,
});
}
Ok(Self {
prefix,
labels,
path_id,
})
}
pub fn top_label(&self) -> Option<&MplsLabel> {
self.labels.first()
}
pub fn bottom_label(&self) -> Option<&MplsLabel> {
self.labels.last()
}
pub fn has_multiple_labels(&self) -> bool {
self.labels.len() > 1
}
pub fn label_count(&self) -> usize {
self.labels.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LabeledNlriEncodeError {
EmptyLabelStack,
TotalBitsOverflow {
total_bits: usize,
max: usize,
},
SingleLabelModeWithMultipleLabels {
label_count: usize,
},
LabelCountExceedsPeerLimit {
actual: usize,
peer_max: u8,
},
AddPathNotNegotiated,
}
impl std::fmt::Display for LabeledNlriEncodeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
LabeledNlriEncodeError::EmptyLabelStack => {
write!(f, "cannot encode labeled prefix with empty label stack")
}
LabeledNlriEncodeError::TotalBitsOverflow { total_bits, max } => {
write!(
f,
"total NLRI length {} bits exceeds maximum {} bits",
total_bits, max
)
}
LabeledNlriEncodeError::SingleLabelModeWithMultipleLabels { label_count } => {
write!(f, "single-label mode cannot encode {} labels", label_count)
}
LabeledNlriEncodeError::LabelCountExceedsPeerLimit { actual, peer_max } => {
write!(f, "label count {} exceeds peer limit {}", actual, peer_max)
}
LabeledNlriEncodeError::AddPathNotNegotiated => {
write!(f, "ADD-PATH not negotiated but path_id is present")
}
}
}
}
impl std::error::Error for LabeledNlriEncodeError {}
#[cfg(feature = "parser")]
pub fn parse_labeled_nlri(
input: &mut Bytes,
afi: Afi,
config: &LabeledNlriConfig,
) -> Result<Vec<LabeledNetworkPrefix>, crate::error::ParserError> {
use crate::error::ParserError;
let mut result = Vec::new();
while input.has_remaining() {
let path_id = if config.add_path {
if input.remaining() < 4 {
return Err(ParserError::TruncatedLabeledNlri);
}
Some(input.get_u32())
} else {
None
};
if input.remaining() < 1 {
return Err(ParserError::TruncatedLabeledNlri);
}
let total_bits = input.get_u8() as usize;
if total_bits < 24 {
return Err(ParserError::InvalidLabeledNlriLength);
}
let nlri_bytes = total_bits.div_ceil(8);
if input.remaining() < nlri_bytes {
return Err(ParserError::TruncatedLabeledNlri);
}
let nlri_data = input.copy_to_bytes(nlri_bytes);
let mut nlri_input = nlri_data;
let mut labels: SmallVec<[MplsLabel; 2]> = SmallVec::new();
match config.mode {
LabeledNlriMode::SingleLabel => {
if nlri_input.remaining() < 3 {
return Err(ParserError::TruncatedLabeledNlri);
}
let label_bytes = [
nlri_input.get_u8(),
nlri_input.get_u8(),
nlri_input.get_u8(),
];
let (label, _bos) = MplsLabel::decode(label_bytes);
labels.push(label);
}
LabeledNlriMode::MultiLabel => {
loop {
if labels.len() >= config.max_labels as usize {
return Err(ParserError::MaxLabelStackDepthExceeded);
}
if let Some(peer_max) = config.peer_max_labels {
if labels.len() >= peer_max as usize {
return Err(ParserError::PeerMaxLabelsExceeded);
}
}
if nlri_input.remaining() < 3 {
return Err(ParserError::TruncatedLabeledNlri);
}
let label_bytes = [
nlri_input.get_u8(),
nlri_input.get_u8(),
nlri_input.get_u8(),
];
let (label, bos) = MplsLabel::decode(label_bytes);
labels.push(label);
if bos {
break;
}
}
}
}
let label_bits = labels
.len()
.checked_mul(24)
.ok_or(ParserError::InvalidLabeledNlriLength)?;
let prefix_bits = total_bits
.checked_sub(label_bits)
.ok_or(ParserError::InvalidLabeledNlriLength)?;
let max_prefix_bits = match afi {
Afi::Ipv4 => 32,
Afi::Ipv6 => 128,
_ => return Err(ParserError::InvalidLabeledNlriLength),
};
if prefix_bits > max_prefix_bits {
return Err(ParserError::InvalidLabeledNlriLength);
}
let prefix_bytes = prefix_bits.div_ceil(8);
if nlri_input.remaining() < prefix_bytes {
return Err(ParserError::TruncatedPrefix);
}
let prefix_data = nlri_input.copy_to_bytes(prefix_bytes);
let prefix = parse_prefix_with_masking(afi, &prefix_data, prefix_bits as u8)?;
if nlri_input.has_remaining() {
return Err(ParserError::InvalidLabeledNlriLength);
}
result.push(LabeledNetworkPrefix {
prefix,
labels,
path_id,
});
}
Ok(result)
}
#[cfg(feature = "parser")]
fn parse_prefix_with_masking(
afi: Afi,
data: &[u8],
prefix_bits: u8,
) -> Result<IpNet, crate::error::ParserError> {
use crate::error::ParserError;
use std::net::{Ipv4Addr, Ipv6Addr};
let full_bytes = (prefix_bits as usize) / 8;
let remainder_bits = prefix_bits % 8;
match afi {
Afi::Ipv4 => {
let mut octets = [0u8; 4];
let copy_len = data.len().min(4);
octets[..copy_len].copy_from_slice(&data[..copy_len]);
if remainder_bits > 0 && copy_len > full_bytes {
let mask = 0xFF << (8 - remainder_bits);
octets[full_bytes] &= mask;
}
let addr = Ipv4Addr::from(octets);
Ok(IpNet::V4(
ipnet::Ipv4Net::new(addr, prefix_bits).map_err(|_| ParserError::InvalidPrefix)?,
))
}
Afi::Ipv6 => {
let mut octets = [0u8; 16];
let copy_len = data.len().min(16);
octets[..copy_len].copy_from_slice(&data[..copy_len]);
if remainder_bits > 0 && copy_len > full_bytes {
let mask = 0xFF << (8 - remainder_bits);
octets[full_bytes] &= mask;
}
let addr = Ipv6Addr::from(octets);
Ok(IpNet::V6(
ipnet::Ipv6Net::new(addr, prefix_bits).map_err(|_| ParserError::InvalidPrefix)?,
))
}
_ => Err(ParserError::InvalidLabeledNlriLength),
}
}
#[cfg(feature = "parser")]
pub fn parse_labeled_withdrawal_nlri(
input: &mut Bytes,
afi: Afi,
config: &LabeledNlriConfig,
) -> Result<Vec<NetworkPrefix>, crate::error::ParserError> {
use crate::error::ParserError;
let mut result = Vec::new();
if !input.has_remaining() {
return Ok(result);
}
while input.has_remaining() {
let path_id = if config.add_path {
if input.remaining() < 4 {
return Err(ParserError::TruncatedLabeledNlri);
}
Some(input.get_u32())
} else {
None
};
if input.remaining() < 1 {
return Err(ParserError::TruncatedLabeledNlri);
}
let total_bits = input.get_u8() as usize;
if total_bits < 24 {
return Err(ParserError::InvalidLabeledNlriLength);
}
let nlri_bytes = total_bits.div_ceil(8);
if input.remaining() < nlri_bytes {
return Err(ParserError::TruncatedLabeledNlri);
}
let nlri_data = input.copy_to_bytes(nlri_bytes);
let mut nlri_input = nlri_data;
if nlri_input.remaining() < 3 {
return Err(ParserError::TruncatedLabeledNlri);
}
let _compatibility_field = [
nlri_input.get_u8(),
nlri_input.get_u8(),
nlri_input.get_u8(),
];
let prefix_bits = total_bits
.checked_sub(24) .ok_or(ParserError::InvalidLabeledNlriLength)?;
let max_prefix_bits = match afi {
Afi::Ipv4 => 32,
Afi::Ipv6 => 128,
_ => return Err(ParserError::InvalidLabeledNlriLength),
};
if prefix_bits > max_prefix_bits {
return Err(ParserError::InvalidLabeledNlriLength);
}
let prefix_bytes = prefix_bits.div_ceil(8);
if nlri_input.remaining() < prefix_bytes {
return Err(ParserError::TruncatedPrefix);
}
let prefix_data = nlri_input.copy_to_bytes(prefix_bytes);
let prefix = parse_prefix_with_masking(afi, &prefix_data, prefix_bits as u8)?;
if nlri_input.has_remaining() {
return Err(ParserError::InvalidLabeledNlriLength);
}
result.push(NetworkPrefix::new(prefix, path_id));
}
Ok(result)
}
pub fn encode_labeled_prefix(
prefix: &LabeledNetworkPrefix,
mode: LabeledNlriMode,
add_path: bool,
peer_max_labels: Option<u8>,
) -> Result<Vec<u8>, LabeledNlriEncodeError> {
if prefix.labels.is_empty() {
return Err(LabeledNlriEncodeError::EmptyLabelStack);
}
if prefix.path_id.is_some() && !add_path {
return Err(LabeledNlriEncodeError::AddPathNotNegotiated);
}
let mut output = Vec::new();
if let Some(path_id) = prefix.path_id {
output.extend_from_slice(&path_id.to_be_bytes());
}
let label_bits =
prefix
.labels
.len()
.checked_mul(24)
.ok_or(LabeledNlriEncodeError::TotalBitsOverflow {
total_bits: usize::MAX,
max: 255,
})?;
let prefix_bits = prefix.prefix.prefix_len() as usize;
let total_bits =
label_bits
.checked_add(prefix_bits)
.ok_or(LabeledNlriEncodeError::TotalBitsOverflow {
total_bits: usize::MAX,
max: 255,
})?;
if total_bits > 255 {
return Err(LabeledNlriEncodeError::TotalBitsOverflow {
total_bits,
max: 255,
});
}
output.push(total_bits as u8);
match mode {
LabeledNlriMode::SingleLabel => {
if prefix.labels.len() > 1 {
return Err(LabeledNlriEncodeError::SingleLabelModeWithMultipleLabels {
label_count: prefix.labels.len(),
});
}
let label = &prefix.labels[0];
let encoded = label.encode(true); output.extend_from_slice(&encoded);
}
LabeledNlriMode::MultiLabel => {
if let Some(peer_max) = peer_max_labels {
if prefix.labels.len() > peer_max as usize {
return Err(LabeledNlriEncodeError::LabelCountExceedsPeerLimit {
actual: prefix.labels.len(),
peer_max,
});
}
}
for (i, label) in prefix.labels.iter().enumerate() {
let is_bottom = i == prefix.labels.len() - 1;
let encoded = label.encode(is_bottom);
output.extend_from_slice(&encoded);
}
}
}
let prefix_bytes = prefix_bits.div_ceil(8);
let prefix_octets = match prefix.prefix {
IpNet::V4(p) => p.addr().octets().to_vec(),
IpNet::V6(p) => p.addr().octets().to_vec(),
};
output.extend_from_slice(&prefix_octets[..prefix_bytes]);
Ok(output)
}
pub fn encode_labeled_withdrawal(
prefix: &NetworkPrefix,
) -> Result<Vec<u8>, LabeledNlriEncodeError> {
let mut output = Vec::new();
if let Some(path_id) = prefix.path_id {
output.extend_from_slice(&path_id.to_be_bytes());
}
let prefix_bits = prefix.prefix.prefix_len() as usize;
let total_bits =
24usize
.checked_add(prefix_bits)
.ok_or(LabeledNlriEncodeError::TotalBitsOverflow {
total_bits: prefix_bits.saturating_add(24),
max: 255,
})?;
if total_bits > 255 {
return Err(LabeledNlriEncodeError::TotalBitsOverflow {
total_bits,
max: 255,
});
}
output.push(total_bits as u8);
output.extend_from_slice(&[0x80, 0x00, 0x00]);
let prefix_bytes = prefix_bits.div_ceil(8);
let prefix_octets = match prefix.prefix {
IpNet::V4(p) => p.addr().octets().to_vec(),
IpNet::V6(p) => p.addr().octets().to_vec(),
};
output.extend_from_slice(&prefix_octets[..prefix_bytes]);
Ok(output)
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_mpls_label_new() {
let label = MplsLabel::try_new(100).unwrap();
assert_eq!(label.value(), 100);
assert!(!label.is_reserved());
}
#[test]
fn test_mpls_label_too_large() {
let result = MplsLabel::try_new(0x0010_0000); assert!(result.is_err());
}
#[test]
fn test_mpls_label_reserved() {
let label = MplsLabel::try_new(0).unwrap();
assert!(label.is_reserved());
assert!(label.is_ipv4_explicit_null());
let label = MplsLabel::try_new(2).unwrap();
assert!(label.is_reserved());
assert!(label.is_ipv6_explicit_null());
let label = MplsLabel::try_new(3).unwrap();
assert!(label.is_reserved());
assert!(label.is_implicit_null());
let label = MplsLabel::try_new(15).unwrap();
assert!(label.is_reserved());
let label = MplsLabel::try_new(16).unwrap();
assert!(!label.is_reserved());
}
#[test]
fn test_mpls_label_encode_decode() {
let label = MplsLabel::try_new(24001).unwrap();
let encoded = label.encode(true);
assert_eq!(encoded, [0x05, 0xDC, 0x11]);
let (decoded, bos) = MplsLabel::decode(encoded);
assert_eq!(decoded.value(), 24001);
assert!(bos);
let encoded = label.encode(false);
assert_eq!(encoded, [0x05, 0xDC, 0x10]);
let (decoded, bos) = MplsLabel::decode(encoded);
assert_eq!(decoded.value(), 24001);
assert!(!bos);
}
#[test]
fn test_labeled_network_prefix_new() {
let prefix = IpNet::from_str("192.0.2.0/24").unwrap();
let labels = SmallVec::from_vec(vec![MplsLabel::try_new(100).unwrap()]);
let labeled = LabeledNetworkPrefix::try_new(prefix, labels, None).unwrap();
assert_eq!(labeled.label_count(), 1);
assert!(!labeled.has_multiple_labels());
}
#[test]
fn test_labeled_network_prefix_empty_labels() {
let prefix = IpNet::from_str("192.0.2.0/24").unwrap();
let labels: SmallVec<[MplsLabel; 2]> = SmallVec::new();
let result = LabeledNetworkPrefix::try_new(prefix, labels, None);
assert!(matches!(
result,
Err(LabeledNetworkPrefixError::EmptyLabelStack)
));
}
#[test]
fn test_labeled_nlri_config_validation() {
let config = LabeledNlriConfig::try_new(false, LabeledNlriMode::SingleLabel, 16, None);
assert!(config.is_ok());
let result = LabeledNlriConfig::try_new(false, LabeledNlriMode::SingleLabel, 0, None);
assert!(matches!(
result,
Err(LabeledNlriConfigError::InvalidMaxLabels(0))
));
let result = LabeledNlriConfig::try_new(false, LabeledNlriMode::SingleLabel, 255, None);
assert!(matches!(
result,
Err(LabeledNlriConfigError::InvalidMaxLabels(255))
));
let result = LabeledNlriConfig::try_new(false, LabeledNlriMode::MultiLabel, 16, Some(0));
assert!(matches!(
result,
Err(LabeledNlriConfigError::InvalidPeerMaxLabels(0))
));
let result = LabeledNlriConfig::try_new(false, LabeledNlriMode::MultiLabel, 16, Some(1));
assert!(result.is_ok());
}
#[test]
fn test_encode_labeled_prefix_single_label_mode() {
let prefix = IpNet::from_str("192.0.2.0/24").unwrap();
let labels = SmallVec::from_vec(vec![MplsLabel::try_new(24001).unwrap()]);
let labeled = LabeledNetworkPrefix::try_new(prefix, labels, None).unwrap();
let result = encode_labeled_prefix(&labeled, LabeledNlriMode::SingleLabel, false, None);
assert!(result.is_ok());
let encoded = result.unwrap();
assert_eq!(encoded, vec![0x30, 0x05, 0xDC, 0x11, 0xC0, 0x00, 0x02]);
}
#[test]
fn test_encode_labeled_prefix_single_label_mode_multiple_labels() {
let prefix = IpNet::from_str("192.0.2.0/24").unwrap();
let labels = SmallVec::from_vec(vec![
MplsLabel::try_new(24001).unwrap(),
MplsLabel::try_new(24002).unwrap(),
]);
let labeled = LabeledNetworkPrefix::try_new(prefix, labels, None).unwrap();
let result = encode_labeled_prefix(&labeled, LabeledNlriMode::SingleLabel, false, None);
assert!(matches!(
result,
Err(LabeledNlriEncodeError::SingleLabelModeWithMultipleLabels { label_count: 2 })
));
}
#[test]
fn test_encode_labeled_prefix_multi_label_mode() {
let prefix = IpNet::from_str("192.0.2.0/24").unwrap();
let labels = SmallVec::from_vec(vec![
MplsLabel::try_new(24001).unwrap(),
MplsLabel::try_new(24002).unwrap(),
]);
let labeled = LabeledNetworkPrefix::try_new(prefix, labels, None).unwrap();
let result = encode_labeled_prefix(&labeled, LabeledNlriMode::MultiLabel, false, None);
assert!(result.is_ok());
let encoded = result.unwrap();
assert_eq!(
encoded,
vec![0x48, 0x05, 0xDC, 0x10, 0x05, 0xDC, 0x21, 0xC0, 0x00, 0x02]
);
}
#[test]
fn test_encode_labeled_prefix_multi_label_mode_with_path_id() {
let prefix = IpNet::from_str("192.0.2.0/24").unwrap();
let labels = SmallVec::from_vec(vec![
MplsLabel::try_new(24001).unwrap(),
MplsLabel::try_new(24002).unwrap(),
]);
let labeled = LabeledNetworkPrefix::try_new(prefix, labels, Some(123)).unwrap();
let result = encode_labeled_prefix(&labeled, LabeledNlriMode::MultiLabel, true, None);
assert!(result.is_ok());
let encoded = result.unwrap();
assert_eq!(
encoded,
vec![
0x00, 0x00, 0x00, 0x7B, 0x48, 0x05, 0xDC, 0x10, 0x05, 0xDC, 0x21, 0xC0, 0x00, 0x02
]
);
}
#[test]
fn test_encode_labeled_withdrawal() {
let prefix = NetworkPrefix::new(IpNet::from_str("192.0.2.0/24").unwrap(), None);
let result = encode_labeled_withdrawal(&prefix);
assert!(result.is_ok());
let encoded = result.unwrap();
assert_eq!(encoded, vec![0x30, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x02]);
}
#[test]
fn test_encode_labeled_withdrawal_with_path_id() {
let prefix = NetworkPrefix::new(IpNet::from_str("192.0.2.0/24").unwrap(), Some(123));
let result = encode_labeled_withdrawal(&prefix);
assert!(result.is_ok());
let encoded = result.unwrap();
assert_eq!(
encoded,
vec![0x00, 0x00, 0x00, 0x7B, 0x30, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x02]
);
}
#[test]
fn test_encode_labeled_prefix_add_path_not_negotiated() {
let prefix = IpNet::from_str("192.0.2.0/24").unwrap();
let labels = SmallVec::from_vec(vec![MplsLabel::try_new(100).unwrap()]);
let labeled = LabeledNetworkPrefix::try_new(prefix, labels, Some(123)).unwrap();
let result = encode_labeled_prefix(&labeled, LabeledNlriMode::SingleLabel, false, None);
assert!(matches!(
result,
Err(LabeledNlriEncodeError::AddPathNotNegotiated)
));
let result = encode_labeled_prefix(&labeled, LabeledNlriMode::SingleLabel, true, None);
assert!(result.is_ok());
}
}