1use bytes::{Buf, BufMut, Bytes};
2
3use crate::constants::{capability_code, param_type};
4use crate::error::{DecodeError, EncodeError};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[repr(u16)]
9pub enum Afi {
10 Ipv4 = 1,
12 Ipv6 = 2,
14 L2Vpn = 25,
16}
17
18impl Afi {
19 #[must_use]
21 pub fn from_u16(value: u16) -> Option<Self> {
22 match value {
23 1 => Some(Self::Ipv4),
24 2 => Some(Self::Ipv6),
25 25 => Some(Self::L2Vpn),
26 _ => None,
27 }
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33#[repr(u8)]
34pub enum Safi {
35 Unicast = 1,
37 Multicast = 2,
39 Evpn = 70,
41 FlowSpec = 133,
43}
44
45impl Safi {
46 #[must_use]
48 pub fn from_u8(value: u8) -> Option<Self> {
49 match value {
50 1 => Some(Self::Unicast),
51 2 => Some(Self::Multicast),
52 70 => Some(Self::Evpn),
53 133 => Some(Self::FlowSpec),
54 _ => None,
55 }
56 }
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct GracefulRestartFamily {
62 pub afi: Afi,
64 pub safi: Safi,
66 pub forwarding_preserved: bool,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72#[repr(u8)]
73pub enum AddPathMode {
74 Receive = 1,
76 Send = 2,
78 Both = 3,
80}
81
82impl AddPathMode {
83 #[must_use]
85 pub fn from_u8(value: u8) -> Option<Self> {
86 match value {
87 1 => Some(Self::Receive),
88 2 => Some(Self::Send),
89 3 => Some(Self::Both),
90 _ => None,
91 }
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97pub struct AddPathFamily {
98 pub afi: Afi,
100 pub safi: Safi,
102 pub send_receive: AddPathMode,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111pub struct ExtendedNextHopFamily {
112 pub nlri_afi: Afi,
114 pub nlri_safi: Safi,
116 pub next_hop_afi: Afi,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub struct LlgrFamily {
123 pub afi: Afi,
125 pub safi: Safi,
127 pub forwarding_preserved: bool,
129 pub stale_time: u32,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140#[repr(u8)]
141pub enum BgpRole {
142 Provider = 0,
144 RouteServer = 1,
146 RouteServerClient = 2,
148 Customer = 3,
150 Peer = 4,
152}
153
154impl BgpRole {
155 #[must_use]
158 pub fn from_u8(value: u8) -> Option<Self> {
159 match value {
160 0 => Some(Self::Provider),
161 1 => Some(Self::RouteServer),
162 2 => Some(Self::RouteServerClient),
163 3 => Some(Self::Customer),
164 4 => Some(Self::Peer),
165 _ => None,
166 }
167 }
168
169 #[must_use]
171 pub fn to_u8(self) -> u8 {
172 self as u8
173 }
174}
175
176#[derive(Debug, Clone, PartialEq, Eq)]
178pub enum Capability {
179 MultiProtocol {
181 afi: Afi,
183 safi: Safi,
185 },
186 ExtendedNextHop(Vec<ExtendedNextHopFamily>),
188 GracefulRestart {
190 restart_state: bool,
193 notification: bool,
196 restart_time: u16,
198 families: Vec<GracefulRestartFamily>,
200 },
201 RouteRefresh,
203 EnhancedRouteRefresh,
205 ExtendedMessage,
207 LongLivedGracefulRestart(Vec<LlgrFamily>),
209 AddPath(Vec<AddPathFamily>),
211 OutboundRouteFilter(Vec<crate::orf::OrfCapEntry>),
213 FourOctetAs {
215 asn: u32,
217 },
218 Role {
220 role: BgpRole,
222 },
223 Unknown {
225 code: u8,
227 data: Bytes,
229 },
230}
231
232impl Capability {
233 #[expect(clippy::too_many_lines)]
240 pub fn decode(buf: &mut impl Buf) -> Result<Self, DecodeError> {
241 if buf.remaining() < 2 {
242 return Err(DecodeError::MalformedOptionalParameter {
243 offset: 0,
244 detail: "capability TLV too short".into(),
245 });
246 }
247
248 let code = buf.get_u8();
249 let length = buf.get_u8();
250
251 if buf.remaining() < usize::from(length) {
252 return Err(DecodeError::MalformedOptionalParameter {
253 offset: 0,
254 detail: format!(
255 "capability code {code} claims length {length}, \
256 but only {} bytes remain",
257 buf.remaining()
258 ),
259 });
260 }
261
262 match code {
263 capability_code::MULTI_PROTOCOL => {
264 if length != 4 {
265 let data = buf.copy_to_bytes(usize::from(length));
267 return Ok(Capability::Unknown { code, data });
268 }
269 let afi_raw = buf.get_u16();
270 let _reserved = buf.get_u8();
271 let safi_raw = buf.get_u8();
272
273 if let (Some(afi), Some(safi)) = (Afi::from_u16(afi_raw), Safi::from_u8(safi_raw)) {
274 Ok(Capability::MultiProtocol { afi, safi })
275 } else {
276 let mut data = bytes::BytesMut::with_capacity(4);
278 data.put_u16(afi_raw);
279 data.put_u8(0); data.put_u8(safi_raw);
281 Ok(Capability::Unknown {
282 code,
283 data: data.freeze(),
284 })
285 }
286 }
287 capability_code::ROUTE_REFRESH => {
288 if length != 0 {
289 let data = buf.copy_to_bytes(usize::from(length));
290 return Ok(Capability::Unknown { code, data });
291 }
292 Ok(Capability::RouteRefresh)
293 }
294 capability_code::ENHANCED_ROUTE_REFRESH => {
295 if length != 0 {
296 let data = buf.copy_to_bytes(usize::from(length));
297 return Ok(Capability::Unknown { code, data });
298 }
299 Ok(Capability::EnhancedRouteRefresh)
300 }
301 capability_code::EXTENDED_NEXT_HOP => {
302 if !usize::from(length).is_multiple_of(6) {
305 let data = buf.copy_to_bytes(usize::from(length));
306 return Ok(Capability::Unknown { code, data });
307 }
308 let entry_count = usize::from(length) / 6;
309 let raw_data = buf.copy_to_bytes(usize::from(length));
310 let mut cursor = raw_data.clone();
311 let mut families = Vec::with_capacity(entry_count);
312 let mut all_valid = true;
313 for _ in 0..entry_count {
314 let nlri_afi_raw = cursor.get_u16();
315 let nlri_safi_field = cursor.get_u16();
316 let next_hop_afi_raw = cursor.get_u16();
317 let nlri_safi = u8::try_from(nlri_safi_field).ok().and_then(Safi::from_u8);
318 if let (Some(nlri_afi), Some(nlri_safi), Some(next_hop_afi)) = (
319 Afi::from_u16(nlri_afi_raw),
320 nlri_safi,
321 Afi::from_u16(next_hop_afi_raw),
322 ) {
323 families.push(ExtendedNextHopFamily {
324 nlri_afi,
325 nlri_safi,
326 next_hop_afi,
327 });
328 } else {
329 all_valid = false;
330 }
331 }
332 if all_valid {
333 Ok(Capability::ExtendedNextHop(families))
334 } else {
335 Ok(Capability::Unknown {
336 code,
337 data: raw_data,
338 })
339 }
340 }
341 capability_code::EXTENDED_MESSAGE => {
342 if length != 0 {
343 let data = buf.copy_to_bytes(usize::from(length));
344 return Ok(Capability::Unknown { code, data });
345 }
346 Ok(Capability::ExtendedMessage)
347 }
348 capability_code::GRACEFUL_RESTART => {
349 if length < 2 || !(length - 2).is_multiple_of(4) {
351 let data = buf.copy_to_bytes(usize::from(length));
352 return Ok(Capability::Unknown { code, data });
353 }
354 let flags_and_time = buf.get_u16();
355 let restart_state = (flags_and_time & 0x8000) != 0;
356 let notification = (flags_and_time & 0x4000) != 0;
357 let restart_time = flags_and_time & 0x0FFF;
358 let family_count = (length - 2) / 4;
359 let mut families = Vec::with_capacity(usize::from(family_count));
360 for _ in 0..family_count {
361 let afi_raw = buf.get_u16();
362 let safi_raw = buf.get_u8();
363 let flags = buf.get_u8();
364 if let (Some(afi), Some(safi)) =
365 (Afi::from_u16(afi_raw), Safi::from_u8(safi_raw))
366 {
367 families.push(GracefulRestartFamily {
368 afi,
369 safi,
370 forwarding_preserved: (flags & 0x80) != 0,
371 });
372 }
373 }
375 Ok(Capability::GracefulRestart {
376 restart_state,
377 notification,
378 restart_time,
379 families,
380 })
381 }
382 capability_code::LONG_LIVED_GRACEFUL_RESTART => {
383 if !usize::from(length).is_multiple_of(7) {
385 let data = buf.copy_to_bytes(usize::from(length));
386 return Ok(Capability::Unknown { code, data });
387 }
388 let entry_count = usize::from(length) / 7;
389 let raw_data = buf.copy_to_bytes(usize::from(length));
390 let mut cursor = raw_data.clone();
391 let mut families = Vec::with_capacity(entry_count);
392 let mut all_valid = true;
393 for _ in 0..entry_count {
394 let afi_raw = cursor.get_u16();
395 let safi_raw = cursor.get_u8();
396 let flags = cursor.get_u8();
397 let st_hi = cursor.get_u8();
399 let st_lo = cursor.get_u16();
400 let stale_time = (u32::from(st_hi) << 16) | u32::from(st_lo);
401 if let (Some(afi), Some(safi)) =
402 (Afi::from_u16(afi_raw), Safi::from_u8(safi_raw))
403 {
404 families.push(LlgrFamily {
405 afi,
406 safi,
407 forwarding_preserved: (flags & 0x80) != 0,
408 stale_time,
409 });
410 } else {
411 all_valid = false;
412 }
413 }
414 if all_valid {
415 Ok(Capability::LongLivedGracefulRestart(families))
416 } else {
417 Ok(Capability::Unknown {
418 code,
419 data: raw_data,
420 })
421 }
422 }
423 capability_code::ADD_PATH => {
424 if length == 0 || !usize::from(length).is_multiple_of(4) {
426 let data = buf.copy_to_bytes(usize::from(length));
427 return Ok(Capability::Unknown { code, data });
428 }
429 let entry_count = usize::from(length) / 4;
430 let raw_data = buf.copy_to_bytes(usize::from(length));
433 let mut cursor = raw_data.clone();
434 let mut families = Vec::with_capacity(entry_count);
435 let mut all_valid = true;
436 for _ in 0..entry_count {
437 let afi_raw = cursor.get_u16();
438 let safi_raw = cursor.get_u8();
439 let mode_raw = cursor.get_u8();
440 if let (Some(afi), Some(safi), Some(mode)) = (
441 Afi::from_u16(afi_raw),
442 Safi::from_u8(safi_raw),
443 AddPathMode::from_u8(mode_raw),
444 ) {
445 families.push(AddPathFamily {
446 afi,
447 safi,
448 send_receive: mode,
449 });
450 } else {
451 all_valid = false;
452 }
453 }
454 if all_valid {
457 Ok(Capability::AddPath(families))
458 } else {
459 Ok(Capability::Unknown {
460 code,
461 data: raw_data,
462 })
463 }
464 }
465 capability_code::OUTBOUND_ROUTE_FILTERING => {
466 let raw = buf.copy_to_bytes(usize::from(length));
470 match crate::orf::decode_capability_value(&raw) {
471 Some(entries) => Ok(Capability::OutboundRouteFilter(entries)),
472 None => Ok(Capability::Unknown { code, data: raw }),
473 }
474 }
475 capability_code::FOUR_OCTET_AS => {
476 if length != 4 {
477 let data = buf.copy_to_bytes(usize::from(length));
478 return Ok(Capability::Unknown { code, data });
479 }
480 let asn = buf.get_u32();
481 Ok(Capability::FourOctetAs { asn })
482 }
483 capability_code::BGP_ROLE => {
484 if length != 1 {
488 let data = buf.copy_to_bytes(usize::from(length));
489 return Ok(Capability::Unknown { code, data });
490 }
491 let role_byte = buf.get_u8();
492 if let Some(role) = BgpRole::from_u8(role_byte) {
493 Ok(Capability::Role { role })
494 } else {
495 let data = Bytes::copy_from_slice(&[role_byte]);
496 Ok(Capability::Unknown { code, data })
497 }
498 }
499 _ => {
500 let data = buf.copy_to_bytes(usize::from(length));
501 Ok(Capability::Unknown { code, data })
502 }
503 }
504 }
505
506 #[expect(
513 clippy::too_many_lines,
514 reason = "Capability encode keeps all TLV variants in one exhaustive wire encoder"
515 )]
516 pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> {
517 match self {
518 Capability::MultiProtocol { afi, safi } => {
519 buf.put_u8(capability_code::MULTI_PROTOCOL);
520 buf.put_u8(4); buf.put_u16(*afi as u16);
522 buf.put_u8(0); buf.put_u8(*safi as u8);
524 }
525 Capability::RouteRefresh => {
526 buf.put_u8(capability_code::ROUTE_REFRESH);
527 buf.put_u8(0); }
529 Capability::EnhancedRouteRefresh => {
530 buf.put_u8(capability_code::ENHANCED_ROUTE_REFRESH);
531 buf.put_u8(0); }
533 Capability::ExtendedNextHop(families) => {
534 let value_len = families.len() * 6;
535 if value_len > 255 {
536 return Err(EncodeError::ValueOutOfRange {
537 field: "extended_next_hop_capability_length",
538 value: value_len.to_string(),
539 });
540 }
541 buf.put_u8(capability_code::EXTENDED_NEXT_HOP);
542 #[expect(clippy::cast_possible_truncation)]
543 buf.put_u8(value_len as u8);
544 for fam in families {
545 buf.put_u16(fam.nlri_afi as u16);
546 buf.put_u16(u16::from(fam.nlri_safi as u8));
547 buf.put_u16(fam.next_hop_afi as u16);
548 }
549 }
550 Capability::ExtendedMessage => {
551 buf.put_u8(capability_code::EXTENDED_MESSAGE);
552 buf.put_u8(0); }
554 Capability::GracefulRestart {
555 restart_state,
556 notification,
557 restart_time,
558 families,
559 } => {
560 let value_len = 2 + families.len() * 4;
561 if value_len > 255 {
562 return Err(EncodeError::ValueOutOfRange {
563 field: "graceful_restart_capability_length",
564 value: value_len.to_string(),
565 });
566 }
567 if *restart_time > 4095 {
568 return Err(EncodeError::ValueOutOfRange {
569 field: "graceful_restart_time",
570 value: restart_time.to_string(),
571 });
572 }
573 buf.put_u8(capability_code::GRACEFUL_RESTART);
574 #[expect(clippy::cast_possible_truncation)]
575 buf.put_u8(value_len as u8);
576 let mut flags_and_time = *restart_time;
577 if *restart_state {
578 flags_and_time |= 0x8000;
579 }
580 if *notification {
581 flags_and_time |= 0x4000;
582 }
583 buf.put_u16(flags_and_time);
584 for fam in families {
585 buf.put_u16(fam.afi as u16);
586 buf.put_u8(fam.safi as u8);
587 buf.put_u8(if fam.forwarding_preserved { 0x80 } else { 0 });
588 }
589 }
590 Capability::LongLivedGracefulRestart(families) => {
591 let value_len = families.len() * 7;
592 if value_len > 255 {
593 return Err(EncodeError::ValueOutOfRange {
594 field: "llgr_capability_length",
595 value: value_len.to_string(),
596 });
597 }
598 buf.put_u8(capability_code::LONG_LIVED_GRACEFUL_RESTART);
599 #[expect(clippy::cast_possible_truncation)]
600 buf.put_u8(value_len as u8);
601 for fam in families {
602 buf.put_u16(fam.afi as u16);
603 buf.put_u8(fam.safi as u8);
604 buf.put_u8(if fam.forwarding_preserved { 0x80 } else { 0 });
605 #[expect(clippy::cast_possible_truncation)]
607 buf.put_u8((fam.stale_time >> 16) as u8);
608 buf.put_u16((fam.stale_time & 0xFFFF) as u16);
609 }
610 }
611 Capability::AddPath(families) => {
612 let value_len = families.len() * 4;
613 if value_len > 255 {
614 return Err(EncodeError::ValueOutOfRange {
615 field: "add_path_capability_length",
616 value: value_len.to_string(),
617 });
618 }
619 buf.put_u8(capability_code::ADD_PATH);
620 #[expect(clippy::cast_possible_truncation)]
621 buf.put_u8(value_len as u8);
622 for fam in families {
623 buf.put_u16(fam.afi as u16);
624 buf.put_u8(fam.safi as u8);
625 buf.put_u8(fam.send_receive as u8);
626 }
627 }
628 Capability::OutboundRouteFilter(entries) => {
629 if entries.is_empty() {
634 return Err(EncodeError::ValueOutOfRange {
635 field: "outbound_route_filter_capability_blocks",
636 value: "0".to_string(),
637 });
638 }
639 let value_len = crate::orf::capability_value_len(entries);
640 if value_len > 255 {
641 return Err(EncodeError::ValueOutOfRange {
642 field: "outbound_route_filter_capability_length",
643 value: value_len.to_string(),
644 });
645 }
646 buf.put_u8(capability_code::OUTBOUND_ROUTE_FILTERING);
647 #[expect(clippy::cast_possible_truncation)]
648 buf.put_u8(value_len as u8);
649 crate::orf::encode_capability_value(entries, buf);
650 }
651 Capability::FourOctetAs { asn } => {
652 buf.put_u8(capability_code::FOUR_OCTET_AS);
653 buf.put_u8(4); buf.put_u32(*asn);
655 }
656 Capability::Role { role } => {
657 buf.put_u8(capability_code::BGP_ROLE);
658 buf.put_u8(1); buf.put_u8(role.to_u8());
660 }
661 Capability::Unknown { code, data } => {
662 if data.len() > 255 {
663 return Err(EncodeError::ValueOutOfRange {
664 field: "unknown_capability_length",
665 value: data.len().to_string(),
666 });
667 }
668 buf.put_u8(*code);
669 #[expect(clippy::cast_possible_truncation)]
670 buf.put_u8(data.len() as u8);
671 buf.put_slice(data);
672 }
673 }
674 Ok(())
675 }
676
677 #[must_use]
679 pub fn code(&self) -> u8 {
680 match self {
681 Self::MultiProtocol { .. } => capability_code::MULTI_PROTOCOL,
682 Self::RouteRefresh => capability_code::ROUTE_REFRESH,
683 Self::EnhancedRouteRefresh => capability_code::ENHANCED_ROUTE_REFRESH,
684 Self::ExtendedNextHop(_) => capability_code::EXTENDED_NEXT_HOP,
685 Self::ExtendedMessage => capability_code::EXTENDED_MESSAGE,
686 Self::LongLivedGracefulRestart(_) => capability_code::LONG_LIVED_GRACEFUL_RESTART,
687 Self::AddPath(_) => capability_code::ADD_PATH,
688 Self::OutboundRouteFilter(_) => capability_code::OUTBOUND_ROUTE_FILTERING,
689 Self::GracefulRestart { .. } => capability_code::GRACEFUL_RESTART,
690 Self::FourOctetAs { .. } => capability_code::FOUR_OCTET_AS,
691 Self::Role { .. } => capability_code::BGP_ROLE,
692 Self::Unknown { code, .. } => *code,
693 }
694 }
695
696 #[must_use]
698 pub fn encoded_len(&self) -> usize {
699 2 + match self {
700 Self::MultiProtocol { .. } | Self::FourOctetAs { .. } => 4,
701 Self::RouteRefresh | Self::EnhancedRouteRefresh | Self::ExtendedMessage => 0,
702 Self::Role { .. } => 1,
703 Self::ExtendedNextHop(families) => families.len() * 6,
704 Self::LongLivedGracefulRestart(families) => families.len() * 7,
705 Self::AddPath(families) => families.len() * 4,
706 Self::OutboundRouteFilter(entries) => crate::orf::capability_value_len(entries),
707 Self::GracefulRestart { families, .. } => 2 + families.len() * 4,
708 Self::Unknown { data, .. } => data.len(),
709 }
710 }
711}
712
713pub fn decode_optional_parameters(
721 buf: &mut impl Buf,
722 opt_params_len: u8,
723) -> Result<Vec<Capability>, DecodeError> {
724 let mut capabilities = Vec::new();
725 let mut remaining = usize::from(opt_params_len);
726
727 while remaining > 0 {
728 if buf.remaining() < 2 {
729 return Err(DecodeError::MalformedOptionalParameter {
730 offset: usize::from(opt_params_len) - remaining,
731 detail: "optional parameter TLV too short".into(),
732 });
733 }
734
735 let param_type = buf.get_u8();
736 let param_len = buf.get_u8();
737 remaining = remaining.saturating_sub(2);
738
739 if usize::from(param_len) > remaining || buf.remaining() < usize::from(param_len) {
740 return Err(DecodeError::MalformedOptionalParameter {
741 offset: usize::from(opt_params_len) - remaining,
742 detail: format!(
743 "parameter type {param_type} claims length {param_len}, \
744 but only {remaining} bytes remain"
745 ),
746 });
747 }
748
749 if param_type == param_type::CAPABILITIES {
750 let param_bytes = buf.copy_to_bytes(usize::from(param_len));
754 let mut cap_buf = param_bytes;
755 while cap_buf.has_remaining() {
756 let cap = Capability::decode(&mut cap_buf)?;
757 capabilities.push(cap);
758 }
759 } else {
760 buf.advance(usize::from(param_len));
762 }
763
764 remaining = remaining.saturating_sub(usize::from(param_len));
765 }
766
767 Ok(capabilities)
768}
769
770pub fn encode_optional_parameters(
783 capabilities: &[Capability],
784 buf: &mut impl BufMut,
785) -> Result<(), EncodeError> {
786 if capabilities.is_empty() {
787 return Ok(());
788 }
789
790 let cap_total: usize = capabilities.iter().map(Capability::encoded_len).sum();
792
793 if cap_total > 255 {
794 return Err(EncodeError::ValueOutOfRange {
795 field: "capabilities_parameter_length",
796 value: cap_total.to_string(),
797 });
798 }
799
800 buf.put_u8(param_type::CAPABILITIES);
802 #[expect(clippy::cast_possible_truncation)]
803 buf.put_u8(cap_total as u8);
804
805 for cap in capabilities {
806 cap.encode(buf)?;
807 }
808 Ok(())
809}
810
811#[cfg(test)]
812mod tests {
813 use super::*;
814
815 #[test]
816 fn decode_multi_protocol_ipv4_unicast() {
817 let data: &[u8] = &[1, 4, 0, 1, 0, 1]; let mut buf = Bytes::copy_from_slice(data);
819 let cap = Capability::decode(&mut buf).unwrap();
820 assert_eq!(
821 cap,
822 Capability::MultiProtocol {
823 afi: Afi::Ipv4,
824 safi: Safi::Unicast
825 }
826 );
827 }
828
829 #[test]
830 fn decode_four_octet_as() {
831 let data: &[u8] = &[65, 4, 0, 0, 0xFD, 0xE8]; let mut buf = Bytes::copy_from_slice(data);
833 let cap = Capability::decode(&mut buf).unwrap();
834 assert_eq!(cap, Capability::FourOctetAs { asn: 65000 });
835 }
836
837 #[test]
838 fn decode_unknown_capability_preserved() {
839 let data: &[u8] = &[99, 3, 0xAA, 0xBB, 0xCC]; let mut buf = Bytes::copy_from_slice(data);
841 let cap = Capability::decode(&mut buf).unwrap();
842 match cap {
843 Capability::Unknown { code, data } => {
844 assert_eq!(code, 99);
845 assert_eq!(data.as_ref(), &[0xAA, 0xBB, 0xCC]);
846 }
847 _ => panic!("expected Unknown"),
848 }
849 }
850
851 #[test]
852 fn unrecognized_afi_safi_stored_as_unknown() {
853 let data: &[u8] = &[1, 4, 0, 99, 0, 1]; let mut buf = Bytes::copy_from_slice(data);
855 let cap = Capability::decode(&mut buf).unwrap();
856 assert!(matches!(cap, Capability::Unknown { code: 1, .. }));
857 }
858
859 #[test]
860 fn roundtrip_multi_protocol() {
861 let original = Capability::MultiProtocol {
862 afi: Afi::Ipv6,
863 safi: Safi::Unicast,
864 };
865 let mut encoded = bytes::BytesMut::with_capacity(6);
866 original.encode(&mut encoded).unwrap();
867 let mut buf = encoded.freeze();
868 let decoded = Capability::decode(&mut buf).unwrap();
869 assert_eq!(original, decoded);
870 }
871
872 #[test]
873 fn roundtrip_four_octet_as() {
874 let original = Capability::FourOctetAs { asn: 4_200_000_000 };
875 let mut encoded = bytes::BytesMut::with_capacity(6);
876 original.encode(&mut encoded).unwrap();
877 let mut buf = encoded.freeze();
878 let decoded = Capability::decode(&mut buf).unwrap();
879 assert_eq!(original, decoded);
880 }
881
882 #[test]
883 fn roundtrip_unknown() {
884 let original = Capability::Unknown {
885 code: 42,
886 data: Bytes::from_static(&[1, 2, 3]),
887 };
888 let mut encoded = bytes::BytesMut::with_capacity(5);
889 original.encode(&mut encoded).unwrap();
890 let mut buf = encoded.freeze();
891 let decoded = Capability::decode(&mut buf).unwrap();
892 assert_eq!(original, decoded);
893 }
894
895 #[test]
896 fn decode_optional_params_multiple_caps() {
897 let mut data = bytes::BytesMut::new();
899 data.put_u8(2); data.put_u8(12); data.put_u8(1);
903 data.put_u8(4);
904 data.put_u16(1); data.put_u8(0);
906 data.put_u8(1); data.put_u8(65);
909 data.put_u8(4);
910 data.put_u32(65001);
911
912 let mut buf = data.freeze();
913 let caps = decode_optional_parameters(&mut buf, 14).unwrap();
914 assert_eq!(caps.len(), 2);
915 assert_eq!(
916 caps[0],
917 Capability::MultiProtocol {
918 afi: Afi::Ipv4,
919 safi: Safi::Unicast
920 }
921 );
922 assert_eq!(caps[1], Capability::FourOctetAs { asn: 65001 });
923 }
924
925 #[test]
926 fn decode_empty_optional_params() {
927 let mut buf = Bytes::new();
928 let caps = decode_optional_parameters(&mut buf, 0).unwrap();
929 assert!(caps.is_empty());
930 }
931
932 #[test]
933 fn reject_truncated_capability() {
934 let data: &[u8] = &[65, 4, 0, 0]; let mut buf = Bytes::copy_from_slice(data);
936 assert!(Capability::decode(&mut buf).is_err());
937 }
938
939 #[test]
940 fn decode_graceful_restart_with_families() {
941 let mut data = bytes::BytesMut::new();
945 data.put_u8(64); data.put_u8(10); data.put_u16(0x8078); data.put_u16(1); data.put_u8(1); data.put_u8(0x80); data.put_u16(2); data.put_u8(1); data.put_u8(0x00); let mut buf = data.freeze();
956 let cap = Capability::decode(&mut buf).unwrap();
957 assert_eq!(
958 cap,
959 Capability::GracefulRestart {
960 restart_state: true,
961 notification: false,
962 restart_time: 120,
963 families: vec![
964 GracefulRestartFamily {
965 afi: Afi::Ipv4,
966 safi: Safi::Unicast,
967 forwarding_preserved: true,
968 },
969 GracefulRestartFamily {
970 afi: Afi::Ipv6,
971 safi: Safi::Unicast,
972 forwarding_preserved: false,
973 },
974 ],
975 }
976 );
977 }
978
979 #[test]
980 fn decode_graceful_restart_no_r_bit() {
981 let mut data = bytes::BytesMut::new();
982 data.put_u8(64);
983 data.put_u8(6); data.put_u16(0x005A); data.put_u16(1); data.put_u8(1); data.put_u8(0x00); let mut buf = data.freeze();
990 let cap = Capability::decode(&mut buf).unwrap();
991 assert_eq!(
992 cap,
993 Capability::GracefulRestart {
994 restart_state: false,
995 notification: false,
996 restart_time: 90,
997 families: vec![GracefulRestartFamily {
998 afi: Afi::Ipv4,
999 safi: Safi::Unicast,
1000 forwarding_preserved: false,
1001 }],
1002 }
1003 );
1004 }
1005
1006 #[test]
1007 fn decode_graceful_restart_empty_families() {
1008 let mut data = bytes::BytesMut::new();
1009 data.put_u8(64);
1010 data.put_u8(2); data.put_u16(0x003C); let mut buf = data.freeze();
1014 let cap = Capability::decode(&mut buf).unwrap();
1015 assert_eq!(
1016 cap,
1017 Capability::GracefulRestart {
1018 restart_state: false,
1019 notification: false,
1020 restart_time: 60,
1021 families: vec![],
1022 }
1023 );
1024 }
1025
1026 #[test]
1027 fn roundtrip_graceful_restart() {
1028 let original = Capability::GracefulRestart {
1029 restart_state: true,
1030 notification: false,
1031 restart_time: 120,
1032 families: vec![
1033 GracefulRestartFamily {
1034 afi: Afi::Ipv4,
1035 safi: Safi::Unicast,
1036 forwarding_preserved: true,
1037 },
1038 GracefulRestartFamily {
1039 afi: Afi::Ipv6,
1040 safi: Safi::Unicast,
1041 forwarding_preserved: false,
1042 },
1043 ],
1044 };
1045 let mut encoded = bytes::BytesMut::with_capacity(12);
1046 original.encode(&mut encoded).unwrap();
1047 let mut buf = encoded.freeze();
1048 let decoded = Capability::decode(&mut buf).unwrap();
1049 assert_eq!(original, decoded);
1050 }
1051
1052 #[test]
1053 fn graceful_restart_encoded_len() {
1054 let cap = Capability::GracefulRestart {
1055 restart_state: false,
1056 notification: false,
1057 restart_time: 120,
1058 families: vec![GracefulRestartFamily {
1059 afi: Afi::Ipv4,
1060 safi: Safi::Unicast,
1061 forwarding_preserved: true,
1062 }],
1063 };
1064 assert_eq!(cap.encoded_len(), 8);
1066 }
1067
1068 #[test]
1069 fn graceful_restart_code() {
1070 let cap = Capability::GracefulRestart {
1071 restart_state: false,
1072 notification: false,
1073 restart_time: 0,
1074 families: vec![],
1075 };
1076 assert_eq!(cap.code(), 64);
1077 }
1078
1079 #[test]
1080 fn graceful_restart_bad_length_stored_as_unknown() {
1081 let data: &[u8] = &[64, 3, 0x00, 0x3C, 0xFF];
1083 let mut buf = Bytes::copy_from_slice(data);
1084 let cap = Capability::decode(&mut buf).unwrap();
1085 assert!(matches!(cap, Capability::Unknown { code: 64, .. }));
1086 }
1087
1088 #[test]
1089 fn encode_rejects_oversized_gr_families() {
1090 let families: Vec<GracefulRestartFamily> = (0..64)
1092 .map(|_| GracefulRestartFamily {
1093 afi: Afi::Ipv4,
1094 safi: Safi::Unicast,
1095 forwarding_preserved: false,
1096 })
1097 .collect();
1098 let cap = Capability::GracefulRestart {
1099 restart_state: false,
1100 notification: false,
1101 restart_time: 120,
1102 families,
1103 };
1104 let mut buf = bytes::BytesMut::new();
1105 assert!(cap.encode(&mut buf).is_err());
1106 }
1107
1108 #[test]
1109 fn encode_rejects_oversized_unknown_data() {
1110 let cap = Capability::Unknown {
1111 code: 99,
1112 data: Bytes::from(vec![0u8; 256]),
1113 };
1114 let mut buf = bytes::BytesMut::new();
1115 assert!(cap.encode(&mut buf).is_err());
1116 }
1117
1118 #[test]
1119 fn encode_optional_params_rejects_overflow() {
1120 let caps: Vec<Capability> = (0..50)
1122 .map(|_| Capability::Unknown {
1123 code: 99,
1124 data: Bytes::from(vec![0u8; 5]),
1125 })
1126 .collect();
1127 let mut buf = bytes::BytesMut::new();
1129 assert!(encode_optional_parameters(&caps, &mut buf).is_err());
1130 }
1131
1132 #[test]
1133 fn encode_rejects_restart_time_over_4095() {
1134 let cap = Capability::GracefulRestart {
1135 restart_state: false,
1136 notification: false,
1137 restart_time: 4096,
1138 families: vec![],
1139 };
1140 let mut buf = bytes::BytesMut::new();
1141 assert!(cap.encode(&mut buf).is_err());
1142 }
1143
1144 #[test]
1145 fn encode_accepts_restart_time_at_4095() {
1146 let cap = Capability::GracefulRestart {
1147 restart_state: false,
1148 notification: false,
1149 restart_time: 4095,
1150 families: vec![],
1151 };
1152 let mut buf = bytes::BytesMut::new();
1153 assert!(cap.encode(&mut buf).is_ok());
1154 }
1155
1156 #[test]
1157 fn decode_graceful_restart_n_bit() {
1158 let mut data = bytes::BytesMut::new();
1159 data.put_u8(64);
1160 data.put_u8(6); data.put_u16(0xC078); data.put_u16(1); data.put_u8(1); data.put_u8(0x80); let mut buf = data.freeze();
1167 let cap = Capability::decode(&mut buf).unwrap();
1168 assert_eq!(
1169 cap,
1170 Capability::GracefulRestart {
1171 restart_state: true,
1172 notification: true,
1173 restart_time: 120,
1174 families: vec![GracefulRestartFamily {
1175 afi: Afi::Ipv4,
1176 safi: Safi::Unicast,
1177 forwarding_preserved: true,
1178 }],
1179 }
1180 );
1181 }
1182
1183 #[test]
1184 fn roundtrip_graceful_restart_with_n_bit() {
1185 let original = Capability::GracefulRestart {
1186 restart_state: true,
1187 notification: true,
1188 restart_time: 120,
1189 families: vec![GracefulRestartFamily {
1190 afi: Afi::Ipv4,
1191 safi: Safi::Unicast,
1192 forwarding_preserved: true,
1193 }],
1194 };
1195 let mut encoded = bytes::BytesMut::with_capacity(12);
1196 original.encode(&mut encoded).unwrap();
1197 let mut buf = encoded.freeze();
1198 let decoded = Capability::decode(&mut buf).unwrap();
1199 assert_eq!(original, decoded);
1200 }
1201
1202 #[test]
1203 fn decode_capability_bounded_to_parameter_slice() {
1204 let mut data = bytes::BytesMut::new();
1210 data.put_u8(2); data.put_u8(4); data.put_u8(65);
1215 data.put_u8(8); data.put_u16(0xBEEF); data.put_u8(99); data.put_u8(2); data.put_u16(0xCAFE);
1221
1222 let mut buf = data.freeze();
1223 let result = decode_optional_parameters(&mut buf, 8);
1227 assert!(result.is_err());
1228 }
1229
1230 #[test]
1231 fn decode_extended_message() {
1232 let data: &[u8] = &[6, 0]; let mut buf = Bytes::copy_from_slice(data);
1234 let cap = Capability::decode(&mut buf).unwrap();
1235 assert_eq!(cap, Capability::ExtendedMessage);
1236 }
1237
1238 #[test]
1239 fn roundtrip_extended_message() {
1240 let original = Capability::ExtendedMessage;
1241 let mut encoded = bytes::BytesMut::with_capacity(2);
1242 original.encode(&mut encoded).unwrap();
1243 let mut buf = encoded.freeze();
1244 let decoded = Capability::decode(&mut buf).unwrap();
1245 assert_eq!(original, decoded);
1246 }
1247
1248 #[test]
1249 fn extended_message_code_and_len() {
1250 let cap = Capability::ExtendedMessage;
1251 assert_eq!(cap.code(), 6);
1252 assert_eq!(cap.encoded_len(), 2);
1253 }
1254
1255 #[test]
1256 fn extended_message_bad_length_stored_as_unknown() {
1257 let data: &[u8] = &[6, 1, 0xFF]; let mut buf = Bytes::copy_from_slice(data);
1259 let cap = Capability::decode(&mut buf).unwrap();
1260 assert!(matches!(cap, Capability::Unknown { code: 6, .. }));
1261 }
1262
1263 #[test]
1266 fn decode_extended_nexthop_single_family() {
1267 let data: &[u8] = &[5, 6, 0, 1, 0, 1, 0, 2];
1270 let mut buf = Bytes::copy_from_slice(data);
1271 let cap = Capability::decode(&mut buf).unwrap();
1272 assert_eq!(
1273 cap,
1274 Capability::ExtendedNextHop(vec![ExtendedNextHopFamily {
1275 nlri_afi: Afi::Ipv4,
1276 nlri_safi: Safi::Unicast,
1277 next_hop_afi: Afi::Ipv6,
1278 }])
1279 );
1280 }
1281
1282 #[test]
1283 fn roundtrip_extended_nexthop() {
1284 let original = Capability::ExtendedNextHop(vec![ExtendedNextHopFamily {
1285 nlri_afi: Afi::Ipv4,
1286 nlri_safi: Safi::Unicast,
1287 next_hop_afi: Afi::Ipv6,
1288 }]);
1289 let mut encoded = bytes::BytesMut::with_capacity(8);
1290 original.encode(&mut encoded).unwrap();
1291 let mut buf = encoded.freeze();
1292 let decoded = Capability::decode(&mut buf).unwrap();
1293 assert_eq!(original, decoded);
1294 }
1295
1296 #[test]
1297 fn extended_nexthop_bad_length_stored_as_unknown() {
1298 let data: &[u8] = &[5, 4, 0, 1, 0, 1];
1300 let mut buf = Bytes::copy_from_slice(data);
1301 let cap = Capability::decode(&mut buf).unwrap();
1302 assert!(matches!(cap, Capability::Unknown { code: 5, .. }));
1303 }
1304
1305 #[test]
1308 fn decode_add_path_single_family() {
1309 let data: &[u8] = &[69, 4, 0, 1, 1, 3];
1311 let mut buf = Bytes::copy_from_slice(data);
1312 let cap = Capability::decode(&mut buf).unwrap();
1313 assert_eq!(
1314 cap,
1315 Capability::AddPath(vec![AddPathFamily {
1316 afi: Afi::Ipv4,
1317 safi: Safi::Unicast,
1318 send_receive: AddPathMode::Both,
1319 }])
1320 );
1321 }
1322
1323 #[test]
1324 fn decode_add_path_multiple_families() {
1325 let mut data = bytes::BytesMut::new();
1326 data.put_u8(69); data.put_u8(8); data.put_u16(1); data.put_u8(1); data.put_u8(1); data.put_u16(2); data.put_u8(1); data.put_u8(2); let mut buf = data.freeze();
1336 let cap = Capability::decode(&mut buf).unwrap();
1337 assert_eq!(
1338 cap,
1339 Capability::AddPath(vec![
1340 AddPathFamily {
1341 afi: Afi::Ipv4,
1342 safi: Safi::Unicast,
1343 send_receive: AddPathMode::Receive,
1344 },
1345 AddPathFamily {
1346 afi: Afi::Ipv6,
1347 safi: Safi::Unicast,
1348 send_receive: AddPathMode::Send,
1349 },
1350 ])
1351 );
1352 }
1353
1354 #[test]
1355 fn roundtrip_add_path() {
1356 let original = Capability::AddPath(vec![
1357 AddPathFamily {
1358 afi: Afi::Ipv4,
1359 safi: Safi::Unicast,
1360 send_receive: AddPathMode::Both,
1361 },
1362 AddPathFamily {
1363 afi: Afi::Ipv6,
1364 safi: Safi::Unicast,
1365 send_receive: AddPathMode::Receive,
1366 },
1367 ]);
1368 let mut encoded = bytes::BytesMut::with_capacity(10);
1369 original.encode(&mut encoded).unwrap();
1370 let mut buf = encoded.freeze();
1371 let decoded = Capability::decode(&mut buf).unwrap();
1372 assert_eq!(original, decoded);
1373 }
1374
1375 #[test]
1376 fn add_path_code_and_len() {
1377 let cap = Capability::AddPath(vec![AddPathFamily {
1378 afi: Afi::Ipv4,
1379 safi: Safi::Unicast,
1380 send_receive: AddPathMode::Receive,
1381 }]);
1382 assert_eq!(cap.code(), 69);
1383 assert_eq!(cap.encoded_len(), 6);
1385 }
1386
1387 #[test]
1388 fn add_path_bad_length_stored_as_unknown() {
1389 let data: &[u8] = &[69, 3, 0, 1, 1];
1391 let mut buf = Bytes::copy_from_slice(data);
1392 let cap = Capability::decode(&mut buf).unwrap();
1393 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1394 }
1395
1396 #[test]
1397 fn add_path_zero_length_stored_as_unknown() {
1398 let data: &[u8] = &[69, 0];
1400 let mut buf = Bytes::copy_from_slice(data);
1401 let cap = Capability::decode(&mut buf).unwrap();
1402 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1403 }
1404
1405 #[test]
1406 fn add_path_unknown_afi_preserved_as_unknown() {
1407 let data: &[u8] = &[69, 4, 0, 99, 1, 3];
1409 let mut buf = Bytes::copy_from_slice(data);
1410 let cap = Capability::decode(&mut buf).unwrap();
1411 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1413 }
1414
1415 #[test]
1416 fn add_path_invalid_mode_preserved_as_unknown() {
1417 let data: &[u8] = &[69, 4, 0, 1, 1, 0];
1419 let mut buf = Bytes::copy_from_slice(data);
1420 let cap = Capability::decode(&mut buf).unwrap();
1421 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1423 }
1424
1425 #[test]
1426 fn add_path_mixed_valid_and_invalid_preserved_as_unknown() {
1427 let mut data = bytes::BytesMut::new();
1429 data.put_u8(69); data.put_u8(8); data.put_u16(1); data.put_u8(1); data.put_u8(3); data.put_u16(99); data.put_u8(1); data.put_u8(3); let mut buf = data.freeze();
1438 let cap = Capability::decode(&mut buf).unwrap();
1439 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1441 }
1442
1443 #[test]
1444 fn llgr_capability_roundtrip() {
1445 let families = vec![
1446 LlgrFamily {
1447 afi: Afi::Ipv4,
1448 safi: Safi::Unicast,
1449 forwarding_preserved: true,
1450 stale_time: 86400,
1451 },
1452 LlgrFamily {
1453 afi: Afi::Ipv6,
1454 safi: Safi::Unicast,
1455 forwarding_preserved: false,
1456 stale_time: 3600,
1457 },
1458 ];
1459 let cap = Capability::LongLivedGracefulRestart(families);
1460
1461 let mut buf = bytes::BytesMut::new();
1462 cap.encode(&mut buf).unwrap();
1463 let mut frozen = buf.freeze();
1464 let decoded = Capability::decode(&mut frozen).unwrap();
1465
1466 match decoded {
1467 Capability::LongLivedGracefulRestart(fams) => {
1468 assert_eq!(fams.len(), 2);
1469 assert_eq!(fams[0].afi, Afi::Ipv4);
1470 assert_eq!(fams[0].safi, Safi::Unicast);
1471 assert!(fams[0].forwarding_preserved);
1472 assert_eq!(fams[0].stale_time, 86400);
1473 assert_eq!(fams[1].afi, Afi::Ipv6);
1474 assert_eq!(fams[1].safi, Safi::Unicast);
1475 assert!(!fams[1].forwarding_preserved);
1476 assert_eq!(fams[1].stale_time, 3600);
1477 }
1478 other => panic!("expected LongLivedGracefulRestart, got {other:?}"),
1479 }
1480 }
1481
1482 #[test]
1483 fn llgr_capability_max_stale_time() {
1484 let cap = Capability::LongLivedGracefulRestart(vec![LlgrFamily {
1485 afi: Afi::Ipv4,
1486 safi: Safi::Unicast,
1487 forwarding_preserved: false,
1488 stale_time: 0x00FF_FFFF, }]);
1490
1491 let mut buf = bytes::BytesMut::new();
1492 cap.encode(&mut buf).unwrap();
1493 let mut frozen = buf.freeze();
1494 let decoded = Capability::decode(&mut frozen).unwrap();
1495
1496 match decoded {
1497 Capability::LongLivedGracefulRestart(fams) => {
1498 assert_eq!(fams[0].stale_time, 0x00FF_FFFF);
1499 }
1500 other => panic!("expected LongLivedGracefulRestart, got {other:?}"),
1501 }
1502 }
1503
1504 #[test]
1505 fn llgr_capability_empty() {
1506 let cap = Capability::LongLivedGracefulRestart(vec![]);
1507 let mut buf = bytes::BytesMut::new();
1508 cap.encode(&mut buf).unwrap();
1509 let mut frozen = buf.freeze();
1510 let decoded = Capability::decode(&mut frozen).unwrap();
1511 assert!(matches!(
1512 decoded,
1513 Capability::LongLivedGracefulRestart(fams) if fams.is_empty()
1514 ));
1515 }
1516
1517 #[test]
1520 fn bgp_role_capability_encode_decode_roundtrip() {
1521 for role in [
1522 BgpRole::Provider,
1523 BgpRole::RouteServer,
1524 BgpRole::RouteServerClient,
1525 BgpRole::Customer,
1526 BgpRole::Peer,
1527 ] {
1528 let original = Capability::Role { role };
1529 let mut buf = bytes::BytesMut::new();
1530 original.encode(&mut buf).unwrap();
1531 assert_eq!(buf.as_ref(), &[9, 1, role.to_u8()][..]);
1533 let mut frozen = buf.freeze();
1534 let decoded = Capability::decode(&mut frozen).unwrap();
1535 assert_eq!(decoded, original);
1536 }
1537 }
1538
1539 #[test]
1540 fn bgp_role_capability_code_returns_nine() {
1541 assert_eq!(
1542 Capability::Role {
1543 role: BgpRole::Provider
1544 }
1545 .code(),
1546 9
1547 );
1548 }
1549
1550 #[test]
1551 fn bgp_role_capability_encoded_len_is_three() {
1552 assert_eq!(
1554 Capability::Role {
1555 role: BgpRole::Customer
1556 }
1557 .encoded_len(),
1558 3
1559 );
1560 }
1561
1562 #[test]
1563 fn bgp_role_capability_bad_length_stored_as_unknown() {
1564 for (len, payload) in [
1567 (0u8, &[][..]),
1568 (2u8, &[0x00, 0x00][..]),
1569 (3u8, &[0x00, 0x00, 0x03][..]),
1570 ] {
1571 let mut wire = vec![9, len];
1572 wire.extend_from_slice(payload);
1573 let mut buf = Bytes::copy_from_slice(&wire);
1574 let cap = Capability::decode(&mut buf).unwrap();
1575 assert!(
1576 matches!(cap, Capability::Unknown { code: 9, .. }),
1577 "len {len}: expected Unknown, got {cap:?}"
1578 );
1579 }
1580 }
1581
1582 #[test]
1583 fn bgp_role_capability_invalid_role_byte_stored_as_unknown() {
1584 for invalid in [5u8, 99u8, 200u8, 255u8] {
1587 let wire = [9u8, 1, invalid];
1588 let mut buf = Bytes::copy_from_slice(&wire);
1589 let cap = Capability::decode(&mut buf).unwrap();
1590 match cap {
1591 Capability::Unknown { code, data } => {
1592 assert_eq!(code, 9);
1593 assert_eq!(data.as_ref(), &[invalid][..]);
1594 }
1595 other => panic!("invalid role byte {invalid}: expected Unknown, got {other:?}"),
1596 }
1597 }
1598 }
1599
1600 #[test]
1601 fn bgp_role_from_u8_roundtrip() {
1602 for role in [
1603 BgpRole::Provider,
1604 BgpRole::RouteServer,
1605 BgpRole::RouteServerClient,
1606 BgpRole::Customer,
1607 BgpRole::Peer,
1608 ] {
1609 assert_eq!(BgpRole::from_u8(role.to_u8()), Some(role));
1610 }
1611 for invalid in [5u8, 9, 99, 255] {
1612 assert_eq!(BgpRole::from_u8(invalid), None);
1613 }
1614 }
1615
1616 #[test]
1619 fn roundtrip_outbound_route_filter() {
1620 use crate::orf::{OrfCapEntry, OrfCapType, OrfSendReceive, OrfType};
1621 let original = Capability::OutboundRouteFilter(vec![OrfCapEntry {
1622 afi: Afi::Ipv4,
1623 safi: Safi::Unicast,
1624 orf_types: vec![OrfCapType {
1625 orf_type: OrfType::AddressPrefix,
1626 send_receive: OrfSendReceive::Receive,
1627 }],
1628 }]);
1629 let mut encoded = bytes::BytesMut::new();
1630 original.encode(&mut encoded).unwrap();
1631 assert_eq!(encoded.len(), original.encoded_len());
1633 assert_eq!(original.code(), 3);
1634 let mut buf = encoded.freeze();
1635 let decoded = Capability::decode(&mut buf).unwrap();
1636 assert_eq!(original, decoded);
1637 }
1638
1639 #[test]
1640 fn outbound_route_filter_rejects_empty_entries() {
1641 let cap = Capability::OutboundRouteFilter(vec![]);
1645 let mut buf = bytes::BytesMut::new();
1646 assert!(cap.encode(&mut buf).is_err());
1647 }
1648
1649 #[test]
1650 fn outbound_route_filter_unknown_afi_preserved_as_unknown() {
1651 let data: &[u8] = &[3, 7, 0, 99, 0, 1, 1, 64, 1];
1653 let mut buf = Bytes::copy_from_slice(data);
1654 let cap = Capability::decode(&mut buf).unwrap();
1655 assert!(matches!(cap, Capability::Unknown { code: 3, .. }));
1656 }
1657}