1use std::fmt;
10use std::net::{Ipv4Addr, Ipv6Addr};
11
12use crate::error::{DecodeError, EncodeError};
13use crate::evpn::RouteDistinguisher;
14
15pub const VPNV4_AFI: u16 = 1;
17pub const VPNV6_AFI: u16 = 2;
19pub const LABELED_UNICAST_SAFI: u8 = 4;
21pub const MPLS_VPN_SAFI: u8 = 128;
23pub const ROUTE_DISTINGUISHER_LEN: usize = 8;
25pub const ROUTE_DISTINGUISHER_BITS: u8 = 64;
27pub const MPLS_LABEL_ENTRY_BITS: u8 = 24;
29pub const MPLS_LABEL_ENTRY_LEN: usize = 3;
31pub const MAX_MPLS_LABEL: u32 = 0x000F_FFFF;
33pub const MAX_MPLS_TRAFFIC_CLASS: u8 = 0x07;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
38pub enum VpnAddressFamily {
39 V4,
41 V6,
43}
44
45impl VpnAddressFamily {
46 #[must_use]
48 pub const fn afi(self) -> u16 {
49 match self {
50 Self::V4 => VPNV4_AFI,
51 Self::V6 => VPNV6_AFI,
52 }
53 }
54
55 const fn max_prefix_len(self) -> u8 {
56 match self {
57 Self::V4 => 32,
58 Self::V6 => 128,
59 }
60 }
61}
62
63impl fmt::Display for VpnAddressFamily {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 match self {
66 Self::V4 => write!(f, "vpnv4"),
67 Self::V6 => write!(f, "vpnv6"),
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
74pub struct MplsLabelEntry {
75 pub label: u32,
77 pub traffic_class: u8,
79 pub bottom_of_stack: bool,
81}
82
83impl MplsLabelEntry {
84 pub fn try_new(
91 label: u32,
92 traffic_class: u8,
93 bottom_of_stack: bool,
94 ) -> Result<Self, EncodeError> {
95 validate_label(label)?;
96 validate_traffic_class(traffic_class)?;
97 Ok(Self {
98 label,
99 traffic_class,
100 bottom_of_stack,
101 })
102 }
103
104 #[expect(
106 clippy::cast_possible_truncation,
107 reason = "traffic-class value is masked to 3 bits before the cast"
108 )]
109 #[must_use]
110 pub const fn from_raw(raw: u32) -> Self {
111 Self {
112 label: (raw >> 4) & MAX_MPLS_LABEL,
113 traffic_class: ((raw >> 1) & MAX_MPLS_TRAFFIC_CLASS as u32) as u8,
114 bottom_of_stack: (raw & 0x01) != 0,
115 }
116 }
117
118 pub fn raw_value(&self) -> Result<u32, EncodeError> {
125 validate_label(self.label)?;
126 validate_traffic_class(self.traffic_class)?;
127 Ok((self.label << 4)
128 | (u32::from(self.traffic_class) << 1)
129 | u32::from(self.bottom_of_stack))
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
139pub enum VpnPrefix {
140 V4 {
142 addr: Ipv4Addr,
144 len: u8,
146 },
147 V6 {
149 addr: Ipv6Addr,
151 len: u8,
153 },
154}
155
156impl VpnPrefix {
157 pub fn v4(addr: Ipv4Addr, len: u8) -> Result<Self, EncodeError> {
163 if len > 32 {
164 return Err(EncodeError::ValueOutOfRange {
165 field: "VPNv4 prefix length",
166 value: len.to_string(),
167 });
168 }
169 Ok(Self::V4 {
170 addr: Ipv4Addr::from(mask_v4(addr, len)),
171 len,
172 })
173 }
174
175 pub fn v6(addr: Ipv6Addr, len: u8) -> Result<Self, EncodeError> {
181 if len > 128 {
182 return Err(EncodeError::ValueOutOfRange {
183 field: "VPNv6 prefix length",
184 value: len.to_string(),
185 });
186 }
187 Ok(Self::V6 {
188 addr: Ipv6Addr::from(mask_v6(addr, len)),
189 len,
190 })
191 }
192
193 #[must_use]
195 pub const fn family(&self) -> VpnAddressFamily {
196 match self {
197 Self::V4 { .. } => VpnAddressFamily::V4,
198 Self::V6 { .. } => VpnAddressFamily::V6,
199 }
200 }
201
202 #[must_use]
204 pub const fn len(&self) -> u8 {
205 match self {
206 Self::V4 { len, .. } | Self::V6 { len, .. } => *len,
207 }
208 }
209
210 #[must_use]
212 pub const fn is_empty(&self) -> bool {
213 self.len() == 0
214 }
215
216 fn wire_octets(&self) -> [u8; 16] {
217 match self {
218 Self::V4 { addr, .. } => {
219 let mut out = [0u8; 16];
220 out[..4].copy_from_slice(&addr.octets());
221 out
222 }
223 Self::V6 { addr, .. } => addr.octets(),
224 }
225 }
226}
227
228impl fmt::Display for VpnPrefix {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 match self {
231 Self::V4 { addr, len } => write!(f, "{addr}/{len}"),
232 Self::V6 { addr, len } => write!(f, "{addr}/{len}"),
233 }
234 }
235}
236
237#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
242pub struct VpnRouteKey {
243 pub route_distinguisher: RouteDistinguisher,
245 pub prefix: VpnPrefix,
247}
248
249#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
251pub struct VpnNlri {
252 pub labels: Vec<MplsLabelEntry>,
254 pub route_distinguisher: RouteDistinguisher,
256 pub prefix: VpnPrefix,
258}
259
260impl VpnNlri {
261 #[must_use]
263 pub const fn key(&self) -> VpnRouteKey {
264 VpnRouteKey {
265 route_distinguisher: self.route_distinguisher,
266 prefix: self.prefix,
267 }
268 }
269
270 fn validate_for_family(&self, family: VpnAddressFamily) -> Result<(), EncodeError> {
271 if self.prefix.family() != family {
272 return Err(EncodeError::ValueOutOfRange {
273 field: "VPN NLRI family",
274 value: self.prefix.family().to_string(),
275 });
276 }
277 validate_label_stack(&self.labels)?;
278 let total_bits = total_vpn_nlri_bits(self.labels.len(), self.prefix.len());
279 if total_bits > u16::from(u8::MAX) {
280 return Err(EncodeError::ValueOutOfRange {
281 field: "VPN NLRI length bits",
282 value: total_bits.to_string(),
283 });
284 }
285 Ok(())
286 }
287}
288
289pub fn decode_vpnv4_nlri(buf: &[u8]) -> Result<Vec<VpnNlri>, DecodeError> {
296 decode_vpn_nlri(buf, VpnAddressFamily::V4)
297}
298
299pub fn decode_vpnv6_nlri(buf: &[u8]) -> Result<Vec<VpnNlri>, DecodeError> {
306 decode_vpn_nlri(buf, VpnAddressFamily::V6)
307}
308
309pub fn decode_vpn_nlri(
316 mut buf: &[u8],
317 family: VpnAddressFamily,
318) -> Result<Vec<VpnNlri>, DecodeError> {
319 let mut entries = Vec::new();
320
321 while !buf.is_empty() {
322 let field_start = buf;
323 let total_len_bits = buf[0];
324 buf = &buf[1..];
325 let value_len = usize::from(total_len_bits.div_ceil(8));
326
327 if buf.len() < value_len {
328 return invalid_vpn_nlri(
329 format!(
330 "{family} NLRI truncated: length {total_len_bits} bits requires {value_len} bytes, have {}",
331 buf.len()
332 ),
333 field_start,
334 1 + buf.len(),
335 );
336 }
337
338 let value = &buf[..value_len];
339 buf = &buf[value_len..];
340
341 entries.push(decode_one_vpn_nlri(
342 value,
343 total_len_bits,
344 family,
345 field_start,
346 )?);
347 }
348
349 Ok(entries)
350}
351
352pub fn encode_vpnv4_nlri(entries: &[VpnNlri], buf: &mut Vec<u8>) -> Result<(), EncodeError> {
359 encode_vpn_nlri(entries, VpnAddressFamily::V4, buf)
360}
361
362pub fn encode_vpnv6_nlri(entries: &[VpnNlri], buf: &mut Vec<u8>) -> Result<(), EncodeError> {
369 encode_vpn_nlri(entries, VpnAddressFamily::V6, buf)
370}
371
372pub fn encode_vpn_nlri(
382 entries: &[VpnNlri],
383 family: VpnAddressFamily,
384 buf: &mut Vec<u8>,
385) -> Result<(), EncodeError> {
386 let start_len = buf.len();
387
388 for entry in entries {
389 if let Err(err) = encode_one_vpn_nlri(entry, family, buf) {
390 buf.truncate(start_len);
391 return Err(err);
392 }
393 }
394
395 Ok(())
396}
397
398fn decode_one_vpn_nlri(
399 value: &[u8],
400 total_len_bits: u8,
401 family: VpnAddressFamily,
402 field_start: &[u8],
403) -> Result<VpnNlri, DecodeError> {
404 let min_bits = u16::from(MPLS_LABEL_ENTRY_BITS) + u16::from(ROUTE_DISTINGUISHER_BITS);
405 if u16::from(total_len_bits) < min_bits {
406 return invalid_vpn_nlri(
407 format!("{family} NLRI length {total_len_bits} bits is shorter than label+RD"),
408 field_start,
409 1 + value.len(),
410 );
411 }
412
413 let (labels, label_octets, label_bits) =
414 decode_label_stack(value, total_len_bits, family, field_start)?;
415 let rd_offset = label_octets;
416 let rd_end = rd_offset + ROUTE_DISTINGUISHER_LEN;
417 if value.len() < rd_end {
418 return invalid_vpn_nlri(
419 format!("{family} NLRI truncated before Route Distinguisher"),
420 field_start,
421 1 + value.len(),
422 );
423 }
424
425 let mut rd = [0u8; ROUTE_DISTINGUISHER_LEN];
426 rd.copy_from_slice(&value[rd_offset..rd_end]);
427
428 let Some(prefix_len) = total_len_bits
433 .checked_sub(label_bits)
434 .and_then(|rem| rem.checked_sub(ROUTE_DISTINGUISHER_BITS))
435 else {
436 return invalid_vpn_nlri(
437 format!(
438 "{family} NLRI length {total_len_bits} bits cannot hold the {label_bits}-bit label stack plus Route Distinguisher"
439 ),
440 field_start,
441 1 + value.len(),
442 );
443 };
444 if prefix_len > family.max_prefix_len() {
445 return invalid_vpn_nlri(
446 format!(
447 "{family} prefix length {prefix_len} exceeds {}",
448 family.max_prefix_len()
449 ),
450 field_start,
451 1 + value.len(),
452 );
453 }
454
455 let prefix_octets = usize::from(prefix_len.div_ceil(8));
456 let prefix_start = rd_end;
457 let prefix_end = prefix_start + prefix_octets;
458 if value.len() < prefix_end {
459 return invalid_vpn_nlri(
460 format!("{family} NLRI truncated before prefix"),
461 field_start,
462 1 + value.len(),
463 );
464 }
465
466 let prefix = decode_vpn_prefix(family, prefix_len, &value[prefix_start..prefix_end])?;
467 Ok(VpnNlri {
468 labels,
469 route_distinguisher: RouteDistinguisher(rd),
470 prefix,
471 })
472}
473
474fn decode_label_stack(
475 value: &[u8],
476 total_len_bits: u8,
477 family: VpnAddressFamily,
478 field_start: &[u8],
479) -> Result<(Vec<MplsLabelEntry>, usize, u8), DecodeError> {
480 let mut labels = Vec::new();
481 let mut offset = 0usize;
482 let mut label_bits = 0u8;
483
484 loop {
485 if u16::from(label_bits) + u16::from(MPLS_LABEL_ENTRY_BITS) > u16::from(total_len_bits) {
486 return invalid_vpn_nlri(
487 format!("{family} NLRI label stack has no bottom-of-stack marker"),
488 field_start,
489 1 + value.len(),
490 );
491 }
492 if value.len() < offset + MPLS_LABEL_ENTRY_LEN {
493 return invalid_vpn_nlri(
494 format!("{family} NLRI truncated inside label stack"),
495 field_start,
496 1 + value.len(),
497 );
498 }
499
500 let raw = (u32::from(value[offset]) << 16)
501 | (u32::from(value[offset + 1]) << 8)
502 | u32::from(value[offset + 2]);
503 let label = MplsLabelEntry::from_raw(raw);
504 labels.push(label);
505 offset += MPLS_LABEL_ENTRY_LEN;
506 label_bits += MPLS_LABEL_ENTRY_BITS;
507
508 if label.bottom_of_stack {
509 return Ok((labels, offset, label_bits));
510 }
511 }
512}
513
514fn decode_vpn_prefix(
515 family: VpnAddressFamily,
516 len: u8,
517 bytes: &[u8],
518) -> Result<VpnPrefix, DecodeError> {
519 let expected = usize::from(len.div_ceil(8));
520 if bytes.len() != expected {
521 return Err(DecodeError::MalformedField {
522 message_type: "UPDATE",
523 detail: format!(
524 "{family} prefix byte length {} != expected {expected}",
525 bytes.len()
526 ),
527 });
528 }
529
530 match family {
531 VpnAddressFamily::V4 => {
532 let mut octets = [0u8; 4];
533 octets[..bytes.len()].copy_from_slice(bytes);
534 Ok(VpnPrefix::V4 {
535 addr: Ipv4Addr::from(mask_v4(Ipv4Addr::from(octets), len)),
536 len,
537 })
538 }
539 VpnAddressFamily::V6 => {
540 let mut octets = [0u8; 16];
541 octets[..bytes.len()].copy_from_slice(bytes);
542 Ok(VpnPrefix::V6 {
543 addr: Ipv6Addr::from(mask_v6(Ipv6Addr::from(octets), len)),
544 len,
545 })
546 }
547 }
548}
549
550fn encode_one_vpn_nlri(
551 entry: &VpnNlri,
552 family: VpnAddressFamily,
553 buf: &mut Vec<u8>,
554) -> Result<(), EncodeError> {
555 entry.validate_for_family(family)?;
556 let total_bits = total_vpn_nlri_bits(entry.labels.len(), entry.prefix.len());
557 let total_bits_u8 = u8::try_from(total_bits).map_err(|_| EncodeError::ValueOutOfRange {
558 field: "VPN NLRI length bits",
559 value: total_bits.to_string(),
560 })?;
561 buf.push(total_bits_u8);
562
563 for label in &entry.labels {
564 let raw = label.raw_value()?;
565 buf.push(((raw >> 16) & 0xFF) as u8);
566 buf.push(((raw >> 8) & 0xFF) as u8);
567 buf.push((raw & 0xFF) as u8);
568 }
569
570 buf.extend_from_slice(&entry.route_distinguisher.0);
571 let prefix_octets = entry.prefix.wire_octets();
572 let prefix_byte_count = usize::from(entry.prefix.len().div_ceil(8));
573 buf.extend_from_slice(&prefix_octets[..prefix_byte_count]);
574 Ok(())
575}
576
577fn validate_label_stack(labels: &[MplsLabelEntry]) -> Result<(), EncodeError> {
578 if labels.is_empty() {
579 return Err(EncodeError::ValueOutOfRange {
580 field: "VPN label stack",
581 value: "empty".to_string(),
582 });
583 }
584 for (index, label) in labels.iter().enumerate() {
585 validate_label(label.label)?;
586 validate_traffic_class(label.traffic_class)?;
587 if label.bottom_of_stack && index + 1 != labels.len() {
588 return Err(EncodeError::ValueOutOfRange {
589 field: "VPN label stack",
590 value: "bottom-of-stack before final label".to_string(),
591 });
592 }
593 }
594 if !labels.last().is_some_and(|label| label.bottom_of_stack) {
595 return Err(EncodeError::ValueOutOfRange {
596 field: "VPN label stack",
597 value: "missing bottom-of-stack".to_string(),
598 });
599 }
600 Ok(())
601}
602
603fn validate_label(label: u32) -> Result<(), EncodeError> {
604 if label > MAX_MPLS_LABEL {
605 return Err(EncodeError::ValueOutOfRange {
606 field: "MPLS label",
607 value: label.to_string(),
608 });
609 }
610 Ok(())
611}
612
613fn validate_traffic_class(traffic_class: u8) -> Result<(), EncodeError> {
614 if traffic_class > MAX_MPLS_TRAFFIC_CLASS {
615 return Err(EncodeError::ValueOutOfRange {
616 field: "MPLS traffic class",
617 value: traffic_class.to_string(),
618 });
619 }
620 Ok(())
621}
622
623fn total_vpn_nlri_bits(label_count: usize, prefix_len: u8) -> u16 {
624 let label_bits = u16::try_from(label_count)
625 .unwrap_or(u16::MAX)
626 .saturating_mul(u16::from(MPLS_LABEL_ENTRY_BITS));
627 label_bits
632 .saturating_add(u16::from(ROUTE_DISTINGUISHER_BITS))
633 .saturating_add(u16::from(prefix_len))
634}
635
636fn mask_v4(addr: Ipv4Addr, len: u8) -> u32 {
637 let raw = u32::from(addr);
638 if len == 0 {
639 0
640 } else if len >= 32 {
641 raw
642 } else {
643 raw & !((1u32 << (32 - len)) - 1)
644 }
645}
646
647fn mask_v6(addr: Ipv6Addr, len: u8) -> u128 {
648 let raw = u128::from(addr);
649 if len == 0 {
650 0
651 } else if len >= 128 {
652 raw
653 } else {
654 raw & !((1u128 << (128 - len)) - 1)
655 }
656}
657
658fn invalid_vpn_nlri<T>(detail: String, data: &[u8], len: usize) -> Result<T, DecodeError> {
659 Err(DecodeError::InvalidNetworkField {
660 detail,
661 data: data[..len.min(data.len())].to_vec(),
662 })
663}
664
665#[cfg(test)]
666mod tests {
667 use super::*;
668
669 fn rd() -> RouteDistinguisher {
670 RouteDistinguisher([0, 0, 0xFD, 0xE9, 0, 0, 0, 100])
671 }
672
673 fn label(value: u32, bos: bool) -> MplsLabelEntry {
674 MplsLabelEntry::try_new(value, 0, bos).unwrap()
675 }
676
677 #[test]
678 fn constants_match_standards() {
679 assert_eq!(VPNV4_AFI, 1);
680 assert_eq!(VPNV6_AFI, 2);
681 assert_eq!(LABELED_UNICAST_SAFI, 4);
682 assert_eq!(MPLS_VPN_SAFI, 128);
683 assert_eq!(ROUTE_DISTINGUISHER_LEN, 8);
684 assert_eq!(MPLS_LABEL_ENTRY_BITS, 24);
685 }
686
687 #[test]
688 fn label_entry_roundtrip() {
689 let entry = MplsLabelEntry::try_new(100_000, 5, true).unwrap();
690 let raw = entry.raw_value().unwrap();
691 assert_eq!(MplsLabelEntry::from_raw(raw), entry);
692 }
693
694 #[test]
695 fn vpnv4_single_label_roundtrip() {
696 let entry = VpnNlri {
697 labels: vec![label(200, true)],
698 route_distinguisher: rd(),
699 prefix: VpnPrefix::v4(Ipv4Addr::new(10, 0, 1, 99), 24).unwrap(),
700 };
701 let mut buf = Vec::new();
702 encode_vpnv4_nlri(std::slice::from_ref(&entry), &mut buf).unwrap();
703 assert_eq!(buf[0], 24 + 64 + 24);
704
705 let decoded = decode_vpnv4_nlri(&buf).unwrap();
706 assert_eq!(decoded, vec![entry]);
707 assert_eq!(decoded[0].prefix.to_string(), "10.0.1.0/24");
708 }
709
710 #[test]
711 fn vpnv6_two_label_roundtrip() {
712 let prefix = "2001:db8:100::1".parse::<Ipv6Addr>().unwrap();
713 let entry = VpnNlri {
714 labels: vec![label(16_000, false), label(24_000, true)],
715 route_distinguisher: rd(),
716 prefix: VpnPrefix::v6(prefix, 48).unwrap(),
717 };
718 let mut buf = Vec::new();
719 encode_vpnv6_nlri(std::slice::from_ref(&entry), &mut buf).unwrap();
720 assert_eq!(buf[0], 48 + 64 + 48);
721
722 let decoded = decode_vpnv6_nlri(&buf).unwrap();
723 assert_eq!(decoded, vec![entry]);
724 assert_eq!(decoded[0].prefix.to_string(), "2001:db8:100::/48");
725 }
726
727 #[test]
728 fn multiple_nlri_preserve_order() {
729 let a = VpnNlri {
730 labels: vec![label(100, true)],
731 route_distinguisher: rd(),
732 prefix: VpnPrefix::v4(Ipv4Addr::new(10, 0, 0, 0), 8).unwrap(),
733 };
734 let b = VpnNlri {
735 labels: vec![label(101, true)],
736 route_distinguisher: rd(),
737 prefix: VpnPrefix::v4(Ipv4Addr::new(192, 0, 2, 0), 24).unwrap(),
738 };
739 let mut buf = Vec::new();
740 encode_vpnv4_nlri(&[a.clone(), b.clone()], &mut buf).unwrap();
741
742 assert_eq!(decode_vpnv4_nlri(&buf).unwrap(), vec![a, b]);
743 }
744
745 #[test]
746 fn route_key_excludes_label_stack() {
747 let prefix = VpnPrefix::v4(Ipv4Addr::new(203, 0, 113, 0), 24).unwrap();
748 let a = VpnNlri {
749 labels: vec![label(100, true)],
750 route_distinguisher: rd(),
751 prefix,
752 };
753 let b = VpnNlri {
754 labels: vec![label(200, true)],
755 route_distinguisher: rd(),
756 prefix,
757 };
758
759 assert_eq!(a.key(), b.key());
760 assert_ne!(a, b);
761 }
762
763 #[test]
764 fn decode_rejects_nlri_shorter_than_label_plus_rd() {
765 let err = decode_vpnv4_nlri(&[87, 0, 0, 1]).unwrap_err();
766 assert!(matches!(err, DecodeError::InvalidNetworkField { .. }));
767 }
768
769 #[test]
770 fn decode_rejects_truncated_value() {
771 let err = decode_vpnv4_nlri(&[112, 0, 0x0C, 0x81]).unwrap_err();
772 assert!(matches!(err, DecodeError::InvalidNetworkField { .. }));
773 }
774
775 #[test]
776 fn decode_rejects_missing_bottom_of_stack() {
777 let mut buf = vec![24 + 64, 0, 0x0C, 0x80];
778 buf.extend_from_slice(&rd().0);
779 let err = decode_vpnv4_nlri(&buf).unwrap_err();
780 assert!(matches!(err, DecodeError::InvalidNetworkField { .. }));
781 }
782
783 #[test]
784 fn decode_rejects_label_stack_consuming_rd_bits_without_underflow() {
785 let mut buf = vec![105u8, 0, 0, 0, 0, 0, 1];
791 buf.extend_from_slice(&rd().0);
792 let err = decode_vpnv4_nlri(&buf).unwrap_err();
793 assert!(matches!(err, DecodeError::InvalidNetworkField { .. }));
794 }
795
796 #[test]
797 fn decode_rejects_high_bit_length_without_bottom_of_stack() {
798 let mut buf = vec![u8::MAX];
799 for value in 0..10u32 {
800 let raw = MplsLabelEntry::try_new(value + 100, 0, false)
801 .unwrap()
802 .raw_value()
803 .unwrap();
804 buf.push(((raw >> 16) & 0xFF) as u8);
805 buf.push(((raw >> 8) & 0xFF) as u8);
806 buf.push((raw & 0xFF) as u8);
807 }
808 buf.extend_from_slice(&[0, 0]);
809
810 let err = decode_vpnv6_nlri(&buf).unwrap_err();
811 assert!(matches!(err, DecodeError::InvalidNetworkField { .. }));
812 }
813
814 #[test]
815 fn decode_rejects_prefix_too_long_for_family() {
816 let mut buf = vec![24 + 64 + 33, 0, 0x0C, 0x81];
817 buf.extend_from_slice(&rd().0);
818 buf.extend_from_slice(&[10, 0, 0, 0, 0]);
819 let err = decode_vpnv4_nlri(&buf).unwrap_err();
820 assert!(matches!(err, DecodeError::InvalidNetworkField { .. }));
821 }
822
823 #[test]
824 fn encode_rejects_empty_label_stack_and_restores_buffer() {
825 let entry = VpnNlri {
826 labels: vec![],
827 route_distinguisher: rd(),
828 prefix: VpnPrefix::v4(Ipv4Addr::new(10, 0, 0, 0), 8).unwrap(),
829 };
830 let mut buf = vec![0xAA];
831 let err = encode_vpnv4_nlri(&[entry], &mut buf).unwrap_err();
832
833 assert!(matches!(err, EncodeError::ValueOutOfRange { .. }));
834 assert_eq!(buf, vec![0xAA]);
835 }
836
837 #[test]
838 fn encode_rejects_missing_bottom_of_stack() {
839 let entry = VpnNlri {
840 labels: vec![label(100, false)],
841 route_distinguisher: rd(),
842 prefix: VpnPrefix::v4(Ipv4Addr::new(10, 0, 0, 0), 8).unwrap(),
843 };
844
845 assert!(matches!(
846 encode_vpnv4_nlri(&[entry], &mut Vec::new()),
847 Err(EncodeError::ValueOutOfRange { .. })
848 ));
849 }
850
851 #[test]
852 fn encode_rejects_early_bottom_of_stack() {
853 let entry = VpnNlri {
854 labels: vec![label(100, true), label(200, true)],
855 route_distinguisher: rd(),
856 prefix: VpnPrefix::v4(Ipv4Addr::new(10, 0, 0, 0), 8).unwrap(),
857 };
858
859 assert!(matches!(
860 encode_vpnv4_nlri(&[entry], &mut Vec::new()),
861 Err(EncodeError::ValueOutOfRange { .. })
862 ));
863 }
864
865 #[test]
866 fn encode_rejects_label_and_traffic_class_out_of_range() {
867 assert!(MplsLabelEntry::try_new(MAX_MPLS_LABEL + 1, 0, true).is_err());
868 assert!(MplsLabelEntry::try_new(100, MAX_MPLS_TRAFFIC_CLASS + 1, true).is_err());
869 }
870
871 #[test]
872 fn encode_rejects_too_long_vpnv6_nlri() {
873 let entry = VpnNlri {
874 labels: vec![label(100, false), label(200, false), label(300, true)],
875 route_distinguisher: rd(),
876 prefix: VpnPrefix::v6(Ipv6Addr::UNSPECIFIED, 128).unwrap(),
877 };
878
879 let err = encode_vpnv6_nlri(&[entry], &mut Vec::new()).unwrap_err();
880 assert!(matches!(err, EncodeError::ValueOutOfRange { .. }));
881 }
882
883 #[test]
884 fn encode_rejects_wrong_family() {
885 let entry = VpnNlri {
886 labels: vec![label(100, true)],
887 route_distinguisher: rd(),
888 prefix: VpnPrefix::v6(Ipv6Addr::LOCALHOST, 128).unwrap(),
889 };
890
891 let err = encode_vpnv4_nlri(&[entry], &mut Vec::new()).unwrap_err();
892 assert!(matches!(err, EncodeError::ValueOutOfRange { .. }));
893 }
894}