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}
15
16impl Afi {
17 #[must_use]
19 pub fn from_u16(value: u16) -> Option<Self> {
20 match value {
21 1 => Some(Self::Ipv4),
22 2 => Some(Self::Ipv6),
23 _ => None,
24 }
25 }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30#[repr(u8)]
31pub enum Safi {
32 Unicast = 1,
34 Multicast = 2,
36 FlowSpec = 133,
38}
39
40impl Safi {
41 #[must_use]
43 pub fn from_u8(value: u8) -> Option<Self> {
44 match value {
45 1 => Some(Self::Unicast),
46 2 => Some(Self::Multicast),
47 133 => Some(Self::FlowSpec),
48 _ => None,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub struct GracefulRestartFamily {
56 pub afi: Afi,
58 pub safi: Safi,
60 pub forwarding_preserved: bool,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66#[repr(u8)]
67pub enum AddPathMode {
68 Receive = 1,
70 Send = 2,
72 Both = 3,
74}
75
76impl AddPathMode {
77 #[must_use]
79 pub fn from_u8(value: u8) -> Option<Self> {
80 match value {
81 1 => Some(Self::Receive),
82 2 => Some(Self::Send),
83 3 => Some(Self::Both),
84 _ => None,
85 }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91pub struct AddPathFamily {
92 pub afi: Afi,
94 pub safi: Safi,
96 pub send_receive: AddPathMode,
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105pub struct ExtendedNextHopFamily {
106 pub nlri_afi: Afi,
108 pub nlri_safi: Safi,
110 pub next_hop_afi: Afi,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub struct LlgrFamily {
117 pub afi: Afi,
119 pub safi: Safi,
121 pub forwarding_preserved: bool,
123 pub stale_time: u32,
125}
126
127#[derive(Debug, Clone, PartialEq, Eq)]
129pub enum Capability {
130 MultiProtocol {
132 afi: Afi,
134 safi: Safi,
136 },
137 ExtendedNextHop(Vec<ExtendedNextHopFamily>),
139 GracefulRestart {
141 restart_state: bool,
144 notification: bool,
147 restart_time: u16,
149 families: Vec<GracefulRestartFamily>,
151 },
152 RouteRefresh,
154 EnhancedRouteRefresh,
156 ExtendedMessage,
158 LongLivedGracefulRestart(Vec<LlgrFamily>),
160 AddPath(Vec<AddPathFamily>),
162 FourOctetAs {
164 asn: u32,
166 },
167 Unknown {
169 code: u8,
171 data: Bytes,
173 },
174}
175
176impl Capability {
177 #[expect(clippy::too_many_lines)]
184 pub fn decode(buf: &mut impl Buf) -> Result<Self, DecodeError> {
185 if buf.remaining() < 2 {
186 return Err(DecodeError::MalformedOptionalParameter {
187 offset: 0,
188 detail: "capability TLV too short".into(),
189 });
190 }
191
192 let code = buf.get_u8();
193 let length = buf.get_u8();
194
195 if buf.remaining() < usize::from(length) {
196 return Err(DecodeError::MalformedOptionalParameter {
197 offset: 0,
198 detail: format!(
199 "capability code {code} claims length {length}, \
200 but only {} bytes remain",
201 buf.remaining()
202 ),
203 });
204 }
205
206 match code {
207 capability_code::MULTI_PROTOCOL => {
208 if length != 4 {
209 let data = buf.copy_to_bytes(usize::from(length));
211 return Ok(Capability::Unknown { code, data });
212 }
213 let afi_raw = buf.get_u16();
214 let _reserved = buf.get_u8();
215 let safi_raw = buf.get_u8();
216
217 if let (Some(afi), Some(safi)) = (Afi::from_u16(afi_raw), Safi::from_u8(safi_raw)) {
218 Ok(Capability::MultiProtocol { afi, safi })
219 } else {
220 let mut data = bytes::BytesMut::with_capacity(4);
222 data.put_u16(afi_raw);
223 data.put_u8(0); data.put_u8(safi_raw);
225 Ok(Capability::Unknown {
226 code,
227 data: data.freeze(),
228 })
229 }
230 }
231 capability_code::ROUTE_REFRESH => {
232 if length != 0 {
233 let data = buf.copy_to_bytes(usize::from(length));
234 return Ok(Capability::Unknown { code, data });
235 }
236 Ok(Capability::RouteRefresh)
237 }
238 capability_code::ENHANCED_ROUTE_REFRESH => {
239 if length != 0 {
240 let data = buf.copy_to_bytes(usize::from(length));
241 return Ok(Capability::Unknown { code, data });
242 }
243 Ok(Capability::EnhancedRouteRefresh)
244 }
245 capability_code::EXTENDED_NEXT_HOP => {
246 if !usize::from(length).is_multiple_of(6) {
249 let data = buf.copy_to_bytes(usize::from(length));
250 return Ok(Capability::Unknown { code, data });
251 }
252 let entry_count = usize::from(length) / 6;
253 let raw_data = buf.copy_to_bytes(usize::from(length));
254 let mut cursor = raw_data.clone();
255 let mut families = Vec::with_capacity(entry_count);
256 let mut all_valid = true;
257 for _ in 0..entry_count {
258 let nlri_afi_raw = cursor.get_u16();
259 let nlri_safi_field = cursor.get_u16();
260 let next_hop_afi_raw = cursor.get_u16();
261 let nlri_safi = u8::try_from(nlri_safi_field).ok().and_then(Safi::from_u8);
262 if let (Some(nlri_afi), Some(nlri_safi), Some(next_hop_afi)) = (
263 Afi::from_u16(nlri_afi_raw),
264 nlri_safi,
265 Afi::from_u16(next_hop_afi_raw),
266 ) {
267 families.push(ExtendedNextHopFamily {
268 nlri_afi,
269 nlri_safi,
270 next_hop_afi,
271 });
272 } else {
273 all_valid = false;
274 }
275 }
276 if all_valid {
277 Ok(Capability::ExtendedNextHop(families))
278 } else {
279 Ok(Capability::Unknown {
280 code,
281 data: raw_data,
282 })
283 }
284 }
285 capability_code::EXTENDED_MESSAGE => {
286 if length != 0 {
287 let data = buf.copy_to_bytes(usize::from(length));
288 return Ok(Capability::Unknown { code, data });
289 }
290 Ok(Capability::ExtendedMessage)
291 }
292 capability_code::GRACEFUL_RESTART => {
293 if length < 2 || !(length - 2).is_multiple_of(4) {
295 let data = buf.copy_to_bytes(usize::from(length));
296 return Ok(Capability::Unknown { code, data });
297 }
298 let flags_and_time = buf.get_u16();
299 let restart_state = (flags_and_time & 0x8000) != 0;
300 let notification = (flags_and_time & 0x4000) != 0;
301 let restart_time = flags_and_time & 0x0FFF;
302 let family_count = (length - 2) / 4;
303 let mut families = Vec::with_capacity(usize::from(family_count));
304 for _ in 0..family_count {
305 let afi_raw = buf.get_u16();
306 let safi_raw = buf.get_u8();
307 let flags = buf.get_u8();
308 if let (Some(afi), Some(safi)) =
309 (Afi::from_u16(afi_raw), Safi::from_u8(safi_raw))
310 {
311 families.push(GracefulRestartFamily {
312 afi,
313 safi,
314 forwarding_preserved: (flags & 0x80) != 0,
315 });
316 }
317 }
319 Ok(Capability::GracefulRestart {
320 restart_state,
321 notification,
322 restart_time,
323 families,
324 })
325 }
326 capability_code::LONG_LIVED_GRACEFUL_RESTART => {
327 if !usize::from(length).is_multiple_of(7) {
329 let data = buf.copy_to_bytes(usize::from(length));
330 return Ok(Capability::Unknown { code, data });
331 }
332 let entry_count = usize::from(length) / 7;
333 let raw_data = buf.copy_to_bytes(usize::from(length));
334 let mut cursor = raw_data.clone();
335 let mut families = Vec::with_capacity(entry_count);
336 let mut all_valid = true;
337 for _ in 0..entry_count {
338 let afi_raw = cursor.get_u16();
339 let safi_raw = cursor.get_u8();
340 let flags = cursor.get_u8();
341 let st_hi = cursor.get_u8();
343 let st_lo = cursor.get_u16();
344 let stale_time = (u32::from(st_hi) << 16) | u32::from(st_lo);
345 if let (Some(afi), Some(safi)) =
346 (Afi::from_u16(afi_raw), Safi::from_u8(safi_raw))
347 {
348 families.push(LlgrFamily {
349 afi,
350 safi,
351 forwarding_preserved: (flags & 0x80) != 0,
352 stale_time,
353 });
354 } else {
355 all_valid = false;
356 }
357 }
358 if all_valid {
359 Ok(Capability::LongLivedGracefulRestart(families))
360 } else {
361 Ok(Capability::Unknown {
362 code,
363 data: raw_data,
364 })
365 }
366 }
367 capability_code::ADD_PATH => {
368 if length == 0 || !usize::from(length).is_multiple_of(4) {
370 let data = buf.copy_to_bytes(usize::from(length));
371 return Ok(Capability::Unknown { code, data });
372 }
373 let entry_count = usize::from(length) / 4;
374 let raw_data = buf.copy_to_bytes(usize::from(length));
377 let mut cursor = raw_data.clone();
378 let mut families = Vec::with_capacity(entry_count);
379 let mut all_valid = true;
380 for _ in 0..entry_count {
381 let afi_raw = cursor.get_u16();
382 let safi_raw = cursor.get_u8();
383 let mode_raw = cursor.get_u8();
384 if let (Some(afi), Some(safi), Some(mode)) = (
385 Afi::from_u16(afi_raw),
386 Safi::from_u8(safi_raw),
387 AddPathMode::from_u8(mode_raw),
388 ) {
389 families.push(AddPathFamily {
390 afi,
391 safi,
392 send_receive: mode,
393 });
394 } else {
395 all_valid = false;
396 }
397 }
398 if all_valid {
401 Ok(Capability::AddPath(families))
402 } else {
403 Ok(Capability::Unknown {
404 code,
405 data: raw_data,
406 })
407 }
408 }
409 capability_code::FOUR_OCTET_AS => {
410 if length != 4 {
411 let data = buf.copy_to_bytes(usize::from(length));
412 return Ok(Capability::Unknown { code, data });
413 }
414 let asn = buf.get_u32();
415 Ok(Capability::FourOctetAs { asn })
416 }
417 _ => {
418 let data = buf.copy_to_bytes(usize::from(length));
419 Ok(Capability::Unknown { code, data })
420 }
421 }
422 }
423
424 #[expect(
431 clippy::too_many_lines,
432 reason = "Capability encode keeps all TLV variants in one exhaustive wire encoder"
433 )]
434 pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> {
435 match self {
436 Capability::MultiProtocol { afi, safi } => {
437 buf.put_u8(capability_code::MULTI_PROTOCOL);
438 buf.put_u8(4); buf.put_u16(*afi as u16);
440 buf.put_u8(0); buf.put_u8(*safi as u8);
442 }
443 Capability::RouteRefresh => {
444 buf.put_u8(capability_code::ROUTE_REFRESH);
445 buf.put_u8(0); }
447 Capability::EnhancedRouteRefresh => {
448 buf.put_u8(capability_code::ENHANCED_ROUTE_REFRESH);
449 buf.put_u8(0); }
451 Capability::ExtendedNextHop(families) => {
452 let value_len = families.len() * 6;
453 if value_len > 255 {
454 return Err(EncodeError::ValueOutOfRange {
455 field: "extended_next_hop_capability_length",
456 value: value_len.to_string(),
457 });
458 }
459 buf.put_u8(capability_code::EXTENDED_NEXT_HOP);
460 #[expect(clippy::cast_possible_truncation)]
461 buf.put_u8(value_len as u8);
462 for fam in families {
463 buf.put_u16(fam.nlri_afi as u16);
464 buf.put_u16(u16::from(fam.nlri_safi as u8));
465 buf.put_u16(fam.next_hop_afi as u16);
466 }
467 }
468 Capability::ExtendedMessage => {
469 buf.put_u8(capability_code::EXTENDED_MESSAGE);
470 buf.put_u8(0); }
472 Capability::GracefulRestart {
473 restart_state,
474 notification,
475 restart_time,
476 families,
477 } => {
478 let value_len = 2 + families.len() * 4;
479 if value_len > 255 {
480 return Err(EncodeError::ValueOutOfRange {
481 field: "graceful_restart_capability_length",
482 value: value_len.to_string(),
483 });
484 }
485 if *restart_time > 4095 {
486 return Err(EncodeError::ValueOutOfRange {
487 field: "graceful_restart_time",
488 value: restart_time.to_string(),
489 });
490 }
491 buf.put_u8(capability_code::GRACEFUL_RESTART);
492 #[expect(clippy::cast_possible_truncation)]
493 buf.put_u8(value_len as u8);
494 let mut flags_and_time = *restart_time;
495 if *restart_state {
496 flags_and_time |= 0x8000;
497 }
498 if *notification {
499 flags_and_time |= 0x4000;
500 }
501 buf.put_u16(flags_and_time);
502 for fam in families {
503 buf.put_u16(fam.afi as u16);
504 buf.put_u8(fam.safi as u8);
505 buf.put_u8(if fam.forwarding_preserved { 0x80 } else { 0 });
506 }
507 }
508 Capability::LongLivedGracefulRestart(families) => {
509 let value_len = families.len() * 7;
510 if value_len > 255 {
511 return Err(EncodeError::ValueOutOfRange {
512 field: "llgr_capability_length",
513 value: value_len.to_string(),
514 });
515 }
516 buf.put_u8(capability_code::LONG_LIVED_GRACEFUL_RESTART);
517 #[expect(clippy::cast_possible_truncation)]
518 buf.put_u8(value_len as u8);
519 for fam in families {
520 buf.put_u16(fam.afi as u16);
521 buf.put_u8(fam.safi as u8);
522 buf.put_u8(if fam.forwarding_preserved { 0x80 } else { 0 });
523 #[expect(clippy::cast_possible_truncation)]
525 buf.put_u8((fam.stale_time >> 16) as u8);
526 buf.put_u16((fam.stale_time & 0xFFFF) as u16);
527 }
528 }
529 Capability::AddPath(families) => {
530 let value_len = families.len() * 4;
531 if value_len > 255 {
532 return Err(EncodeError::ValueOutOfRange {
533 field: "add_path_capability_length",
534 value: value_len.to_string(),
535 });
536 }
537 buf.put_u8(capability_code::ADD_PATH);
538 #[expect(clippy::cast_possible_truncation)]
539 buf.put_u8(value_len as u8);
540 for fam in families {
541 buf.put_u16(fam.afi as u16);
542 buf.put_u8(fam.safi as u8);
543 buf.put_u8(fam.send_receive as u8);
544 }
545 }
546 Capability::FourOctetAs { asn } => {
547 buf.put_u8(capability_code::FOUR_OCTET_AS);
548 buf.put_u8(4); buf.put_u32(*asn);
550 }
551 Capability::Unknown { code, data } => {
552 if data.len() > 255 {
553 return Err(EncodeError::ValueOutOfRange {
554 field: "unknown_capability_length",
555 value: data.len().to_string(),
556 });
557 }
558 buf.put_u8(*code);
559 #[expect(clippy::cast_possible_truncation)]
560 buf.put_u8(data.len() as u8);
561 buf.put_slice(data);
562 }
563 }
564 Ok(())
565 }
566
567 #[must_use]
569 pub fn code(&self) -> u8 {
570 match self {
571 Self::MultiProtocol { .. } => capability_code::MULTI_PROTOCOL,
572 Self::RouteRefresh => capability_code::ROUTE_REFRESH,
573 Self::EnhancedRouteRefresh => capability_code::ENHANCED_ROUTE_REFRESH,
574 Self::ExtendedNextHop(_) => capability_code::EXTENDED_NEXT_HOP,
575 Self::ExtendedMessage => capability_code::EXTENDED_MESSAGE,
576 Self::LongLivedGracefulRestart(_) => capability_code::LONG_LIVED_GRACEFUL_RESTART,
577 Self::AddPath(_) => capability_code::ADD_PATH,
578 Self::GracefulRestart { .. } => capability_code::GRACEFUL_RESTART,
579 Self::FourOctetAs { .. } => capability_code::FOUR_OCTET_AS,
580 Self::Unknown { code, .. } => *code,
581 }
582 }
583
584 #[must_use]
586 pub fn encoded_len(&self) -> usize {
587 2 + match self {
588 Self::MultiProtocol { .. } | Self::FourOctetAs { .. } => 4,
589 Self::RouteRefresh | Self::EnhancedRouteRefresh | Self::ExtendedMessage => 0,
590 Self::ExtendedNextHop(families) => families.len() * 6,
591 Self::LongLivedGracefulRestart(families) => families.len() * 7,
592 Self::AddPath(families) => families.len() * 4,
593 Self::GracefulRestart { families, .. } => 2 + families.len() * 4,
594 Self::Unknown { data, .. } => data.len(),
595 }
596 }
597}
598
599pub fn decode_optional_parameters(
607 buf: &mut impl Buf,
608 opt_params_len: u8,
609) -> Result<Vec<Capability>, DecodeError> {
610 let mut capabilities = Vec::new();
611 let mut remaining = usize::from(opt_params_len);
612
613 while remaining > 0 {
614 if buf.remaining() < 2 {
615 return Err(DecodeError::MalformedOptionalParameter {
616 offset: usize::from(opt_params_len) - remaining,
617 detail: "optional parameter TLV too short".into(),
618 });
619 }
620
621 let param_type = buf.get_u8();
622 let param_len = buf.get_u8();
623 remaining = remaining.saturating_sub(2);
624
625 if usize::from(param_len) > remaining || buf.remaining() < usize::from(param_len) {
626 return Err(DecodeError::MalformedOptionalParameter {
627 offset: usize::from(opt_params_len) - remaining,
628 detail: format!(
629 "parameter type {param_type} claims length {param_len}, \
630 but only {remaining} bytes remain"
631 ),
632 });
633 }
634
635 if param_type == param_type::CAPABILITIES {
636 let param_bytes = buf.copy_to_bytes(usize::from(param_len));
640 let mut cap_buf = param_bytes;
641 while cap_buf.has_remaining() {
642 let cap = Capability::decode(&mut cap_buf)?;
643 capabilities.push(cap);
644 }
645 } else {
646 buf.advance(usize::from(param_len));
648 }
649
650 remaining = remaining.saturating_sub(usize::from(param_len));
651 }
652
653 Ok(capabilities)
654}
655
656pub fn encode_optional_parameters(
669 capabilities: &[Capability],
670 buf: &mut impl BufMut,
671) -> Result<(), EncodeError> {
672 if capabilities.is_empty() {
673 return Ok(());
674 }
675
676 let cap_total: usize = capabilities.iter().map(Capability::encoded_len).sum();
678
679 if cap_total > 255 {
680 return Err(EncodeError::ValueOutOfRange {
681 field: "capabilities_parameter_length",
682 value: cap_total.to_string(),
683 });
684 }
685
686 buf.put_u8(param_type::CAPABILITIES);
688 #[expect(clippy::cast_possible_truncation)]
689 buf.put_u8(cap_total as u8);
690
691 for cap in capabilities {
692 cap.encode(buf)?;
693 }
694 Ok(())
695}
696
697#[cfg(test)]
698mod tests {
699 use super::*;
700
701 #[test]
702 fn decode_multi_protocol_ipv4_unicast() {
703 let data: &[u8] = &[1, 4, 0, 1, 0, 1]; let mut buf = Bytes::copy_from_slice(data);
705 let cap = Capability::decode(&mut buf).unwrap();
706 assert_eq!(
707 cap,
708 Capability::MultiProtocol {
709 afi: Afi::Ipv4,
710 safi: Safi::Unicast
711 }
712 );
713 }
714
715 #[test]
716 fn decode_four_octet_as() {
717 let data: &[u8] = &[65, 4, 0, 0, 0xFD, 0xE8]; let mut buf = Bytes::copy_from_slice(data);
719 let cap = Capability::decode(&mut buf).unwrap();
720 assert_eq!(cap, Capability::FourOctetAs { asn: 65000 });
721 }
722
723 #[test]
724 fn decode_unknown_capability_preserved() {
725 let data: &[u8] = &[99, 3, 0xAA, 0xBB, 0xCC]; let mut buf = Bytes::copy_from_slice(data);
727 let cap = Capability::decode(&mut buf).unwrap();
728 match cap {
729 Capability::Unknown { code, data } => {
730 assert_eq!(code, 99);
731 assert_eq!(data.as_ref(), &[0xAA, 0xBB, 0xCC]);
732 }
733 _ => panic!("expected Unknown"),
734 }
735 }
736
737 #[test]
738 fn unrecognized_afi_safi_stored_as_unknown() {
739 let data: &[u8] = &[1, 4, 0, 99, 0, 1]; let mut buf = Bytes::copy_from_slice(data);
741 let cap = Capability::decode(&mut buf).unwrap();
742 assert!(matches!(cap, Capability::Unknown { code: 1, .. }));
743 }
744
745 #[test]
746 fn roundtrip_multi_protocol() {
747 let original = Capability::MultiProtocol {
748 afi: Afi::Ipv6,
749 safi: Safi::Unicast,
750 };
751 let mut encoded = bytes::BytesMut::with_capacity(6);
752 original.encode(&mut encoded).unwrap();
753 let mut buf = encoded.freeze();
754 let decoded = Capability::decode(&mut buf).unwrap();
755 assert_eq!(original, decoded);
756 }
757
758 #[test]
759 fn roundtrip_four_octet_as() {
760 let original = Capability::FourOctetAs { asn: 4_200_000_000 };
761 let mut encoded = bytes::BytesMut::with_capacity(6);
762 original.encode(&mut encoded).unwrap();
763 let mut buf = encoded.freeze();
764 let decoded = Capability::decode(&mut buf).unwrap();
765 assert_eq!(original, decoded);
766 }
767
768 #[test]
769 fn roundtrip_unknown() {
770 let original = Capability::Unknown {
771 code: 42,
772 data: Bytes::from_static(&[1, 2, 3]),
773 };
774 let mut encoded = bytes::BytesMut::with_capacity(5);
775 original.encode(&mut encoded).unwrap();
776 let mut buf = encoded.freeze();
777 let decoded = Capability::decode(&mut buf).unwrap();
778 assert_eq!(original, decoded);
779 }
780
781 #[test]
782 fn decode_optional_params_multiple_caps() {
783 let mut data = bytes::BytesMut::new();
785 data.put_u8(2); data.put_u8(12); data.put_u8(1);
789 data.put_u8(4);
790 data.put_u16(1); data.put_u8(0);
792 data.put_u8(1); data.put_u8(65);
795 data.put_u8(4);
796 data.put_u32(65001);
797
798 let mut buf = data.freeze();
799 let caps = decode_optional_parameters(&mut buf, 14).unwrap();
800 assert_eq!(caps.len(), 2);
801 assert_eq!(
802 caps[0],
803 Capability::MultiProtocol {
804 afi: Afi::Ipv4,
805 safi: Safi::Unicast
806 }
807 );
808 assert_eq!(caps[1], Capability::FourOctetAs { asn: 65001 });
809 }
810
811 #[test]
812 fn decode_empty_optional_params() {
813 let mut buf = Bytes::new();
814 let caps = decode_optional_parameters(&mut buf, 0).unwrap();
815 assert!(caps.is_empty());
816 }
817
818 #[test]
819 fn reject_truncated_capability() {
820 let data: &[u8] = &[65, 4, 0, 0]; let mut buf = Bytes::copy_from_slice(data);
822 assert!(Capability::decode(&mut buf).is_err());
823 }
824
825 #[test]
826 fn decode_graceful_restart_with_families() {
827 let mut data = bytes::BytesMut::new();
831 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();
842 let cap = Capability::decode(&mut buf).unwrap();
843 assert_eq!(
844 cap,
845 Capability::GracefulRestart {
846 restart_state: true,
847 notification: false,
848 restart_time: 120,
849 families: vec![
850 GracefulRestartFamily {
851 afi: Afi::Ipv4,
852 safi: Safi::Unicast,
853 forwarding_preserved: true,
854 },
855 GracefulRestartFamily {
856 afi: Afi::Ipv6,
857 safi: Safi::Unicast,
858 forwarding_preserved: false,
859 },
860 ],
861 }
862 );
863 }
864
865 #[test]
866 fn decode_graceful_restart_no_r_bit() {
867 let mut data = bytes::BytesMut::new();
868 data.put_u8(64);
869 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();
876 let cap = Capability::decode(&mut buf).unwrap();
877 assert_eq!(
878 cap,
879 Capability::GracefulRestart {
880 restart_state: false,
881 notification: false,
882 restart_time: 90,
883 families: vec![GracefulRestartFamily {
884 afi: Afi::Ipv4,
885 safi: Safi::Unicast,
886 forwarding_preserved: false,
887 }],
888 }
889 );
890 }
891
892 #[test]
893 fn decode_graceful_restart_empty_families() {
894 let mut data = bytes::BytesMut::new();
895 data.put_u8(64);
896 data.put_u8(2); data.put_u16(0x003C); let mut buf = data.freeze();
900 let cap = Capability::decode(&mut buf).unwrap();
901 assert_eq!(
902 cap,
903 Capability::GracefulRestart {
904 restart_state: false,
905 notification: false,
906 restart_time: 60,
907 families: vec![],
908 }
909 );
910 }
911
912 #[test]
913 fn roundtrip_graceful_restart() {
914 let original = Capability::GracefulRestart {
915 restart_state: true,
916 notification: false,
917 restart_time: 120,
918 families: vec![
919 GracefulRestartFamily {
920 afi: Afi::Ipv4,
921 safi: Safi::Unicast,
922 forwarding_preserved: true,
923 },
924 GracefulRestartFamily {
925 afi: Afi::Ipv6,
926 safi: Safi::Unicast,
927 forwarding_preserved: false,
928 },
929 ],
930 };
931 let mut encoded = bytes::BytesMut::with_capacity(12);
932 original.encode(&mut encoded).unwrap();
933 let mut buf = encoded.freeze();
934 let decoded = Capability::decode(&mut buf).unwrap();
935 assert_eq!(original, decoded);
936 }
937
938 #[test]
939 fn graceful_restart_encoded_len() {
940 let cap = Capability::GracefulRestart {
941 restart_state: false,
942 notification: false,
943 restart_time: 120,
944 families: vec![GracefulRestartFamily {
945 afi: Afi::Ipv4,
946 safi: Safi::Unicast,
947 forwarding_preserved: true,
948 }],
949 };
950 assert_eq!(cap.encoded_len(), 8);
952 }
953
954 #[test]
955 fn graceful_restart_code() {
956 let cap = Capability::GracefulRestart {
957 restart_state: false,
958 notification: false,
959 restart_time: 0,
960 families: vec![],
961 };
962 assert_eq!(cap.code(), 64);
963 }
964
965 #[test]
966 fn graceful_restart_bad_length_stored_as_unknown() {
967 let data: &[u8] = &[64, 3, 0x00, 0x3C, 0xFF];
969 let mut buf = Bytes::copy_from_slice(data);
970 let cap = Capability::decode(&mut buf).unwrap();
971 assert!(matches!(cap, Capability::Unknown { code: 64, .. }));
972 }
973
974 #[test]
975 fn encode_rejects_oversized_gr_families() {
976 let families: Vec<GracefulRestartFamily> = (0..64)
978 .map(|_| GracefulRestartFamily {
979 afi: Afi::Ipv4,
980 safi: Safi::Unicast,
981 forwarding_preserved: false,
982 })
983 .collect();
984 let cap = Capability::GracefulRestart {
985 restart_state: false,
986 notification: false,
987 restart_time: 120,
988 families,
989 };
990 let mut buf = bytes::BytesMut::new();
991 assert!(cap.encode(&mut buf).is_err());
992 }
993
994 #[test]
995 fn encode_rejects_oversized_unknown_data() {
996 let cap = Capability::Unknown {
997 code: 99,
998 data: Bytes::from(vec![0u8; 256]),
999 };
1000 let mut buf = bytes::BytesMut::new();
1001 assert!(cap.encode(&mut buf).is_err());
1002 }
1003
1004 #[test]
1005 fn encode_optional_params_rejects_overflow() {
1006 let caps: Vec<Capability> = (0..50)
1008 .map(|_| Capability::Unknown {
1009 code: 99,
1010 data: Bytes::from(vec![0u8; 5]),
1011 })
1012 .collect();
1013 let mut buf = bytes::BytesMut::new();
1015 assert!(encode_optional_parameters(&caps, &mut buf).is_err());
1016 }
1017
1018 #[test]
1019 fn encode_rejects_restart_time_over_4095() {
1020 let cap = Capability::GracefulRestart {
1021 restart_state: false,
1022 notification: false,
1023 restart_time: 4096,
1024 families: vec![],
1025 };
1026 let mut buf = bytes::BytesMut::new();
1027 assert!(cap.encode(&mut buf).is_err());
1028 }
1029
1030 #[test]
1031 fn encode_accepts_restart_time_at_4095() {
1032 let cap = Capability::GracefulRestart {
1033 restart_state: false,
1034 notification: false,
1035 restart_time: 4095,
1036 families: vec![],
1037 };
1038 let mut buf = bytes::BytesMut::new();
1039 assert!(cap.encode(&mut buf).is_ok());
1040 }
1041
1042 #[test]
1043 fn decode_graceful_restart_n_bit() {
1044 let mut data = bytes::BytesMut::new();
1045 data.put_u8(64);
1046 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();
1053 let cap = Capability::decode(&mut buf).unwrap();
1054 assert_eq!(
1055 cap,
1056 Capability::GracefulRestart {
1057 restart_state: true,
1058 notification: true,
1059 restart_time: 120,
1060 families: vec![GracefulRestartFamily {
1061 afi: Afi::Ipv4,
1062 safi: Safi::Unicast,
1063 forwarding_preserved: true,
1064 }],
1065 }
1066 );
1067 }
1068
1069 #[test]
1070 fn roundtrip_graceful_restart_with_n_bit() {
1071 let original = Capability::GracefulRestart {
1072 restart_state: true,
1073 notification: true,
1074 restart_time: 120,
1075 families: vec![GracefulRestartFamily {
1076 afi: Afi::Ipv4,
1077 safi: Safi::Unicast,
1078 forwarding_preserved: true,
1079 }],
1080 };
1081 let mut encoded = bytes::BytesMut::with_capacity(12);
1082 original.encode(&mut encoded).unwrap();
1083 let mut buf = encoded.freeze();
1084 let decoded = Capability::decode(&mut buf).unwrap();
1085 assert_eq!(original, decoded);
1086 }
1087
1088 #[test]
1089 fn decode_capability_bounded_to_parameter_slice() {
1090 let mut data = bytes::BytesMut::new();
1096 data.put_u8(2); data.put_u8(4); data.put_u8(65);
1101 data.put_u8(8); data.put_u16(0xBEEF); data.put_u8(99); data.put_u8(2); data.put_u16(0xCAFE);
1107
1108 let mut buf = data.freeze();
1109 let result = decode_optional_parameters(&mut buf, 8);
1113 assert!(result.is_err());
1114 }
1115
1116 #[test]
1117 fn decode_extended_message() {
1118 let data: &[u8] = &[6, 0]; let mut buf = Bytes::copy_from_slice(data);
1120 let cap = Capability::decode(&mut buf).unwrap();
1121 assert_eq!(cap, Capability::ExtendedMessage);
1122 }
1123
1124 #[test]
1125 fn roundtrip_extended_message() {
1126 let original = Capability::ExtendedMessage;
1127 let mut encoded = bytes::BytesMut::with_capacity(2);
1128 original.encode(&mut encoded).unwrap();
1129 let mut buf = encoded.freeze();
1130 let decoded = Capability::decode(&mut buf).unwrap();
1131 assert_eq!(original, decoded);
1132 }
1133
1134 #[test]
1135 fn extended_message_code_and_len() {
1136 let cap = Capability::ExtendedMessage;
1137 assert_eq!(cap.code(), 6);
1138 assert_eq!(cap.encoded_len(), 2);
1139 }
1140
1141 #[test]
1142 fn extended_message_bad_length_stored_as_unknown() {
1143 let data: &[u8] = &[6, 1, 0xFF]; let mut buf = Bytes::copy_from_slice(data);
1145 let cap = Capability::decode(&mut buf).unwrap();
1146 assert!(matches!(cap, Capability::Unknown { code: 6, .. }));
1147 }
1148
1149 #[test]
1152 fn decode_extended_nexthop_single_family() {
1153 let data: &[u8] = &[5, 6, 0, 1, 0, 1, 0, 2];
1156 let mut buf = Bytes::copy_from_slice(data);
1157 let cap = Capability::decode(&mut buf).unwrap();
1158 assert_eq!(
1159 cap,
1160 Capability::ExtendedNextHop(vec![ExtendedNextHopFamily {
1161 nlri_afi: Afi::Ipv4,
1162 nlri_safi: Safi::Unicast,
1163 next_hop_afi: Afi::Ipv6,
1164 }])
1165 );
1166 }
1167
1168 #[test]
1169 fn roundtrip_extended_nexthop() {
1170 let original = Capability::ExtendedNextHop(vec![ExtendedNextHopFamily {
1171 nlri_afi: Afi::Ipv4,
1172 nlri_safi: Safi::Unicast,
1173 next_hop_afi: Afi::Ipv6,
1174 }]);
1175 let mut encoded = bytes::BytesMut::with_capacity(8);
1176 original.encode(&mut encoded).unwrap();
1177 let mut buf = encoded.freeze();
1178 let decoded = Capability::decode(&mut buf).unwrap();
1179 assert_eq!(original, decoded);
1180 }
1181
1182 #[test]
1183 fn extended_nexthop_bad_length_stored_as_unknown() {
1184 let data: &[u8] = &[5, 4, 0, 1, 0, 1];
1186 let mut buf = Bytes::copy_from_slice(data);
1187 let cap = Capability::decode(&mut buf).unwrap();
1188 assert!(matches!(cap, Capability::Unknown { code: 5, .. }));
1189 }
1190
1191 #[test]
1194 fn decode_add_path_single_family() {
1195 let data: &[u8] = &[69, 4, 0, 1, 1, 3];
1197 let mut buf = Bytes::copy_from_slice(data);
1198 let cap = Capability::decode(&mut buf).unwrap();
1199 assert_eq!(
1200 cap,
1201 Capability::AddPath(vec![AddPathFamily {
1202 afi: Afi::Ipv4,
1203 safi: Safi::Unicast,
1204 send_receive: AddPathMode::Both,
1205 }])
1206 );
1207 }
1208
1209 #[test]
1210 fn decode_add_path_multiple_families() {
1211 let mut data = bytes::BytesMut::new();
1212 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();
1222 let cap = Capability::decode(&mut buf).unwrap();
1223 assert_eq!(
1224 cap,
1225 Capability::AddPath(vec![
1226 AddPathFamily {
1227 afi: Afi::Ipv4,
1228 safi: Safi::Unicast,
1229 send_receive: AddPathMode::Receive,
1230 },
1231 AddPathFamily {
1232 afi: Afi::Ipv6,
1233 safi: Safi::Unicast,
1234 send_receive: AddPathMode::Send,
1235 },
1236 ])
1237 );
1238 }
1239
1240 #[test]
1241 fn roundtrip_add_path() {
1242 let original = Capability::AddPath(vec![
1243 AddPathFamily {
1244 afi: Afi::Ipv4,
1245 safi: Safi::Unicast,
1246 send_receive: AddPathMode::Both,
1247 },
1248 AddPathFamily {
1249 afi: Afi::Ipv6,
1250 safi: Safi::Unicast,
1251 send_receive: AddPathMode::Receive,
1252 },
1253 ]);
1254 let mut encoded = bytes::BytesMut::with_capacity(10);
1255 original.encode(&mut encoded).unwrap();
1256 let mut buf = encoded.freeze();
1257 let decoded = Capability::decode(&mut buf).unwrap();
1258 assert_eq!(original, decoded);
1259 }
1260
1261 #[test]
1262 fn add_path_code_and_len() {
1263 let cap = Capability::AddPath(vec![AddPathFamily {
1264 afi: Afi::Ipv4,
1265 safi: Safi::Unicast,
1266 send_receive: AddPathMode::Receive,
1267 }]);
1268 assert_eq!(cap.code(), 69);
1269 assert_eq!(cap.encoded_len(), 6);
1271 }
1272
1273 #[test]
1274 fn add_path_bad_length_stored_as_unknown() {
1275 let data: &[u8] = &[69, 3, 0, 1, 1];
1277 let mut buf = Bytes::copy_from_slice(data);
1278 let cap = Capability::decode(&mut buf).unwrap();
1279 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1280 }
1281
1282 #[test]
1283 fn add_path_zero_length_stored_as_unknown() {
1284 let data: &[u8] = &[69, 0];
1286 let mut buf = Bytes::copy_from_slice(data);
1287 let cap = Capability::decode(&mut buf).unwrap();
1288 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1289 }
1290
1291 #[test]
1292 fn add_path_unknown_afi_preserved_as_unknown() {
1293 let data: &[u8] = &[69, 4, 0, 99, 1, 3];
1295 let mut buf = Bytes::copy_from_slice(data);
1296 let cap = Capability::decode(&mut buf).unwrap();
1297 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1299 }
1300
1301 #[test]
1302 fn add_path_invalid_mode_preserved_as_unknown() {
1303 let data: &[u8] = &[69, 4, 0, 1, 1, 0];
1305 let mut buf = Bytes::copy_from_slice(data);
1306 let cap = Capability::decode(&mut buf).unwrap();
1307 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1309 }
1310
1311 #[test]
1312 fn add_path_mixed_valid_and_invalid_preserved_as_unknown() {
1313 let mut data = bytes::BytesMut::new();
1315 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();
1324 let cap = Capability::decode(&mut buf).unwrap();
1325 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1327 }
1328
1329 #[test]
1330 fn llgr_capability_roundtrip() {
1331 let families = vec![
1332 LlgrFamily {
1333 afi: Afi::Ipv4,
1334 safi: Safi::Unicast,
1335 forwarding_preserved: true,
1336 stale_time: 86400,
1337 },
1338 LlgrFamily {
1339 afi: Afi::Ipv6,
1340 safi: Safi::Unicast,
1341 forwarding_preserved: false,
1342 stale_time: 3600,
1343 },
1344 ];
1345 let cap = Capability::LongLivedGracefulRestart(families);
1346
1347 let mut buf = bytes::BytesMut::new();
1348 cap.encode(&mut buf).unwrap();
1349 let mut frozen = buf.freeze();
1350 let decoded = Capability::decode(&mut frozen).unwrap();
1351
1352 match decoded {
1353 Capability::LongLivedGracefulRestart(fams) => {
1354 assert_eq!(fams.len(), 2);
1355 assert_eq!(fams[0].afi, Afi::Ipv4);
1356 assert_eq!(fams[0].safi, Safi::Unicast);
1357 assert!(fams[0].forwarding_preserved);
1358 assert_eq!(fams[0].stale_time, 86400);
1359 assert_eq!(fams[1].afi, Afi::Ipv6);
1360 assert_eq!(fams[1].safi, Safi::Unicast);
1361 assert!(!fams[1].forwarding_preserved);
1362 assert_eq!(fams[1].stale_time, 3600);
1363 }
1364 other => panic!("expected LongLivedGracefulRestart, got {other:?}"),
1365 }
1366 }
1367
1368 #[test]
1369 fn llgr_capability_max_stale_time() {
1370 let cap = Capability::LongLivedGracefulRestart(vec![LlgrFamily {
1371 afi: Afi::Ipv4,
1372 safi: Safi::Unicast,
1373 forwarding_preserved: false,
1374 stale_time: 0x00FF_FFFF, }]);
1376
1377 let mut buf = bytes::BytesMut::new();
1378 cap.encode(&mut buf).unwrap();
1379 let mut frozen = buf.freeze();
1380 let decoded = Capability::decode(&mut frozen).unwrap();
1381
1382 match decoded {
1383 Capability::LongLivedGracefulRestart(fams) => {
1384 assert_eq!(fams[0].stale_time, 0x00FF_FFFF);
1385 }
1386 other => panic!("expected LongLivedGracefulRestart, got {other:?}"),
1387 }
1388 }
1389
1390 #[test]
1391 fn llgr_capability_empty() {
1392 let cap = Capability::LongLivedGracefulRestart(vec![]);
1393 let mut buf = bytes::BytesMut::new();
1394 cap.encode(&mut buf).unwrap();
1395 let mut frozen = buf.freeze();
1396 let decoded = Capability::decode(&mut frozen).unwrap();
1397 assert!(matches!(
1398 decoded,
1399 Capability::LongLivedGracefulRestart(fams) if fams.is_empty()
1400 ));
1401 }
1402}