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, PartialEq, Eq)]
135pub enum Capability {
136 MultiProtocol {
138 afi: Afi,
140 safi: Safi,
142 },
143 ExtendedNextHop(Vec<ExtendedNextHopFamily>),
145 GracefulRestart {
147 restart_state: bool,
150 notification: bool,
153 restart_time: u16,
155 families: Vec<GracefulRestartFamily>,
157 },
158 RouteRefresh,
160 EnhancedRouteRefresh,
162 ExtendedMessage,
164 LongLivedGracefulRestart(Vec<LlgrFamily>),
166 AddPath(Vec<AddPathFamily>),
168 FourOctetAs {
170 asn: u32,
172 },
173 Unknown {
175 code: u8,
177 data: Bytes,
179 },
180}
181
182impl Capability {
183 #[expect(clippy::too_many_lines)]
190 pub fn decode(buf: &mut impl Buf) -> Result<Self, DecodeError> {
191 if buf.remaining() < 2 {
192 return Err(DecodeError::MalformedOptionalParameter {
193 offset: 0,
194 detail: "capability TLV too short".into(),
195 });
196 }
197
198 let code = buf.get_u8();
199 let length = buf.get_u8();
200
201 if buf.remaining() < usize::from(length) {
202 return Err(DecodeError::MalformedOptionalParameter {
203 offset: 0,
204 detail: format!(
205 "capability code {code} claims length {length}, \
206 but only {} bytes remain",
207 buf.remaining()
208 ),
209 });
210 }
211
212 match code {
213 capability_code::MULTI_PROTOCOL => {
214 if length != 4 {
215 let data = buf.copy_to_bytes(usize::from(length));
217 return Ok(Capability::Unknown { code, data });
218 }
219 let afi_raw = buf.get_u16();
220 let _reserved = buf.get_u8();
221 let safi_raw = buf.get_u8();
222
223 if let (Some(afi), Some(safi)) = (Afi::from_u16(afi_raw), Safi::from_u8(safi_raw)) {
224 Ok(Capability::MultiProtocol { afi, safi })
225 } else {
226 let mut data = bytes::BytesMut::with_capacity(4);
228 data.put_u16(afi_raw);
229 data.put_u8(0); data.put_u8(safi_raw);
231 Ok(Capability::Unknown {
232 code,
233 data: data.freeze(),
234 })
235 }
236 }
237 capability_code::ROUTE_REFRESH => {
238 if length != 0 {
239 let data = buf.copy_to_bytes(usize::from(length));
240 return Ok(Capability::Unknown { code, data });
241 }
242 Ok(Capability::RouteRefresh)
243 }
244 capability_code::ENHANCED_ROUTE_REFRESH => {
245 if length != 0 {
246 let data = buf.copy_to_bytes(usize::from(length));
247 return Ok(Capability::Unknown { code, data });
248 }
249 Ok(Capability::EnhancedRouteRefresh)
250 }
251 capability_code::EXTENDED_NEXT_HOP => {
252 if !usize::from(length).is_multiple_of(6) {
255 let data = buf.copy_to_bytes(usize::from(length));
256 return Ok(Capability::Unknown { code, data });
257 }
258 let entry_count = usize::from(length) / 6;
259 let raw_data = buf.copy_to_bytes(usize::from(length));
260 let mut cursor = raw_data.clone();
261 let mut families = Vec::with_capacity(entry_count);
262 let mut all_valid = true;
263 for _ in 0..entry_count {
264 let nlri_afi_raw = cursor.get_u16();
265 let nlri_safi_field = cursor.get_u16();
266 let next_hop_afi_raw = cursor.get_u16();
267 let nlri_safi = u8::try_from(nlri_safi_field).ok().and_then(Safi::from_u8);
268 if let (Some(nlri_afi), Some(nlri_safi), Some(next_hop_afi)) = (
269 Afi::from_u16(nlri_afi_raw),
270 nlri_safi,
271 Afi::from_u16(next_hop_afi_raw),
272 ) {
273 families.push(ExtendedNextHopFamily {
274 nlri_afi,
275 nlri_safi,
276 next_hop_afi,
277 });
278 } else {
279 all_valid = false;
280 }
281 }
282 if all_valid {
283 Ok(Capability::ExtendedNextHop(families))
284 } else {
285 Ok(Capability::Unknown {
286 code,
287 data: raw_data,
288 })
289 }
290 }
291 capability_code::EXTENDED_MESSAGE => {
292 if length != 0 {
293 let data = buf.copy_to_bytes(usize::from(length));
294 return Ok(Capability::Unknown { code, data });
295 }
296 Ok(Capability::ExtendedMessage)
297 }
298 capability_code::GRACEFUL_RESTART => {
299 if length < 2 || !(length - 2).is_multiple_of(4) {
301 let data = buf.copy_to_bytes(usize::from(length));
302 return Ok(Capability::Unknown { code, data });
303 }
304 let flags_and_time = buf.get_u16();
305 let restart_state = (flags_and_time & 0x8000) != 0;
306 let notification = (flags_and_time & 0x4000) != 0;
307 let restart_time = flags_and_time & 0x0FFF;
308 let family_count = (length - 2) / 4;
309 let mut families = Vec::with_capacity(usize::from(family_count));
310 for _ in 0..family_count {
311 let afi_raw = buf.get_u16();
312 let safi_raw = buf.get_u8();
313 let flags = buf.get_u8();
314 if let (Some(afi), Some(safi)) =
315 (Afi::from_u16(afi_raw), Safi::from_u8(safi_raw))
316 {
317 families.push(GracefulRestartFamily {
318 afi,
319 safi,
320 forwarding_preserved: (flags & 0x80) != 0,
321 });
322 }
323 }
325 Ok(Capability::GracefulRestart {
326 restart_state,
327 notification,
328 restart_time,
329 families,
330 })
331 }
332 capability_code::LONG_LIVED_GRACEFUL_RESTART => {
333 if !usize::from(length).is_multiple_of(7) {
335 let data = buf.copy_to_bytes(usize::from(length));
336 return Ok(Capability::Unknown { code, data });
337 }
338 let entry_count = usize::from(length) / 7;
339 let raw_data = buf.copy_to_bytes(usize::from(length));
340 let mut cursor = raw_data.clone();
341 let mut families = Vec::with_capacity(entry_count);
342 let mut all_valid = true;
343 for _ in 0..entry_count {
344 let afi_raw = cursor.get_u16();
345 let safi_raw = cursor.get_u8();
346 let flags = cursor.get_u8();
347 let st_hi = cursor.get_u8();
349 let st_lo = cursor.get_u16();
350 let stale_time = (u32::from(st_hi) << 16) | u32::from(st_lo);
351 if let (Some(afi), Some(safi)) =
352 (Afi::from_u16(afi_raw), Safi::from_u8(safi_raw))
353 {
354 families.push(LlgrFamily {
355 afi,
356 safi,
357 forwarding_preserved: (flags & 0x80) != 0,
358 stale_time,
359 });
360 } else {
361 all_valid = false;
362 }
363 }
364 if all_valid {
365 Ok(Capability::LongLivedGracefulRestart(families))
366 } else {
367 Ok(Capability::Unknown {
368 code,
369 data: raw_data,
370 })
371 }
372 }
373 capability_code::ADD_PATH => {
374 if length == 0 || !usize::from(length).is_multiple_of(4) {
376 let data = buf.copy_to_bytes(usize::from(length));
377 return Ok(Capability::Unknown { code, data });
378 }
379 let entry_count = usize::from(length) / 4;
380 let raw_data = buf.copy_to_bytes(usize::from(length));
383 let mut cursor = raw_data.clone();
384 let mut families = Vec::with_capacity(entry_count);
385 let mut all_valid = true;
386 for _ in 0..entry_count {
387 let afi_raw = cursor.get_u16();
388 let safi_raw = cursor.get_u8();
389 let mode_raw = cursor.get_u8();
390 if let (Some(afi), Some(safi), Some(mode)) = (
391 Afi::from_u16(afi_raw),
392 Safi::from_u8(safi_raw),
393 AddPathMode::from_u8(mode_raw),
394 ) {
395 families.push(AddPathFamily {
396 afi,
397 safi,
398 send_receive: mode,
399 });
400 } else {
401 all_valid = false;
402 }
403 }
404 if all_valid {
407 Ok(Capability::AddPath(families))
408 } else {
409 Ok(Capability::Unknown {
410 code,
411 data: raw_data,
412 })
413 }
414 }
415 capability_code::FOUR_OCTET_AS => {
416 if length != 4 {
417 let data = buf.copy_to_bytes(usize::from(length));
418 return Ok(Capability::Unknown { code, data });
419 }
420 let asn = buf.get_u32();
421 Ok(Capability::FourOctetAs { asn })
422 }
423 _ => {
424 let data = buf.copy_to_bytes(usize::from(length));
425 Ok(Capability::Unknown { code, data })
426 }
427 }
428 }
429
430 #[expect(
437 clippy::too_many_lines,
438 reason = "Capability encode keeps all TLV variants in one exhaustive wire encoder"
439 )]
440 pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> {
441 match self {
442 Capability::MultiProtocol { afi, safi } => {
443 buf.put_u8(capability_code::MULTI_PROTOCOL);
444 buf.put_u8(4); buf.put_u16(*afi as u16);
446 buf.put_u8(0); buf.put_u8(*safi as u8);
448 }
449 Capability::RouteRefresh => {
450 buf.put_u8(capability_code::ROUTE_REFRESH);
451 buf.put_u8(0); }
453 Capability::EnhancedRouteRefresh => {
454 buf.put_u8(capability_code::ENHANCED_ROUTE_REFRESH);
455 buf.put_u8(0); }
457 Capability::ExtendedNextHop(families) => {
458 let value_len = families.len() * 6;
459 if value_len > 255 {
460 return Err(EncodeError::ValueOutOfRange {
461 field: "extended_next_hop_capability_length",
462 value: value_len.to_string(),
463 });
464 }
465 buf.put_u8(capability_code::EXTENDED_NEXT_HOP);
466 #[expect(clippy::cast_possible_truncation)]
467 buf.put_u8(value_len as u8);
468 for fam in families {
469 buf.put_u16(fam.nlri_afi as u16);
470 buf.put_u16(u16::from(fam.nlri_safi as u8));
471 buf.put_u16(fam.next_hop_afi as u16);
472 }
473 }
474 Capability::ExtendedMessage => {
475 buf.put_u8(capability_code::EXTENDED_MESSAGE);
476 buf.put_u8(0); }
478 Capability::GracefulRestart {
479 restart_state,
480 notification,
481 restart_time,
482 families,
483 } => {
484 let value_len = 2 + families.len() * 4;
485 if value_len > 255 {
486 return Err(EncodeError::ValueOutOfRange {
487 field: "graceful_restart_capability_length",
488 value: value_len.to_string(),
489 });
490 }
491 if *restart_time > 4095 {
492 return Err(EncodeError::ValueOutOfRange {
493 field: "graceful_restart_time",
494 value: restart_time.to_string(),
495 });
496 }
497 buf.put_u8(capability_code::GRACEFUL_RESTART);
498 #[expect(clippy::cast_possible_truncation)]
499 buf.put_u8(value_len as u8);
500 let mut flags_and_time = *restart_time;
501 if *restart_state {
502 flags_and_time |= 0x8000;
503 }
504 if *notification {
505 flags_and_time |= 0x4000;
506 }
507 buf.put_u16(flags_and_time);
508 for fam in families {
509 buf.put_u16(fam.afi as u16);
510 buf.put_u8(fam.safi as u8);
511 buf.put_u8(if fam.forwarding_preserved { 0x80 } else { 0 });
512 }
513 }
514 Capability::LongLivedGracefulRestart(families) => {
515 let value_len = families.len() * 7;
516 if value_len > 255 {
517 return Err(EncodeError::ValueOutOfRange {
518 field: "llgr_capability_length",
519 value: value_len.to_string(),
520 });
521 }
522 buf.put_u8(capability_code::LONG_LIVED_GRACEFUL_RESTART);
523 #[expect(clippy::cast_possible_truncation)]
524 buf.put_u8(value_len as u8);
525 for fam in families {
526 buf.put_u16(fam.afi as u16);
527 buf.put_u8(fam.safi as u8);
528 buf.put_u8(if fam.forwarding_preserved { 0x80 } else { 0 });
529 #[expect(clippy::cast_possible_truncation)]
531 buf.put_u8((fam.stale_time >> 16) as u8);
532 buf.put_u16((fam.stale_time & 0xFFFF) as u16);
533 }
534 }
535 Capability::AddPath(families) => {
536 let value_len = families.len() * 4;
537 if value_len > 255 {
538 return Err(EncodeError::ValueOutOfRange {
539 field: "add_path_capability_length",
540 value: value_len.to_string(),
541 });
542 }
543 buf.put_u8(capability_code::ADD_PATH);
544 #[expect(clippy::cast_possible_truncation)]
545 buf.put_u8(value_len as u8);
546 for fam in families {
547 buf.put_u16(fam.afi as u16);
548 buf.put_u8(fam.safi as u8);
549 buf.put_u8(fam.send_receive as u8);
550 }
551 }
552 Capability::FourOctetAs { asn } => {
553 buf.put_u8(capability_code::FOUR_OCTET_AS);
554 buf.put_u8(4); buf.put_u32(*asn);
556 }
557 Capability::Unknown { code, data } => {
558 if data.len() > 255 {
559 return Err(EncodeError::ValueOutOfRange {
560 field: "unknown_capability_length",
561 value: data.len().to_string(),
562 });
563 }
564 buf.put_u8(*code);
565 #[expect(clippy::cast_possible_truncation)]
566 buf.put_u8(data.len() as u8);
567 buf.put_slice(data);
568 }
569 }
570 Ok(())
571 }
572
573 #[must_use]
575 pub fn code(&self) -> u8 {
576 match self {
577 Self::MultiProtocol { .. } => capability_code::MULTI_PROTOCOL,
578 Self::RouteRefresh => capability_code::ROUTE_REFRESH,
579 Self::EnhancedRouteRefresh => capability_code::ENHANCED_ROUTE_REFRESH,
580 Self::ExtendedNextHop(_) => capability_code::EXTENDED_NEXT_HOP,
581 Self::ExtendedMessage => capability_code::EXTENDED_MESSAGE,
582 Self::LongLivedGracefulRestart(_) => capability_code::LONG_LIVED_GRACEFUL_RESTART,
583 Self::AddPath(_) => capability_code::ADD_PATH,
584 Self::GracefulRestart { .. } => capability_code::GRACEFUL_RESTART,
585 Self::FourOctetAs { .. } => capability_code::FOUR_OCTET_AS,
586 Self::Unknown { code, .. } => *code,
587 }
588 }
589
590 #[must_use]
592 pub fn encoded_len(&self) -> usize {
593 2 + match self {
594 Self::MultiProtocol { .. } | Self::FourOctetAs { .. } => 4,
595 Self::RouteRefresh | Self::EnhancedRouteRefresh | Self::ExtendedMessage => 0,
596 Self::ExtendedNextHop(families) => families.len() * 6,
597 Self::LongLivedGracefulRestart(families) => families.len() * 7,
598 Self::AddPath(families) => families.len() * 4,
599 Self::GracefulRestart { families, .. } => 2 + families.len() * 4,
600 Self::Unknown { data, .. } => data.len(),
601 }
602 }
603}
604
605pub fn decode_optional_parameters(
613 buf: &mut impl Buf,
614 opt_params_len: u8,
615) -> Result<Vec<Capability>, DecodeError> {
616 let mut capabilities = Vec::new();
617 let mut remaining = usize::from(opt_params_len);
618
619 while remaining > 0 {
620 if buf.remaining() < 2 {
621 return Err(DecodeError::MalformedOptionalParameter {
622 offset: usize::from(opt_params_len) - remaining,
623 detail: "optional parameter TLV too short".into(),
624 });
625 }
626
627 let param_type = buf.get_u8();
628 let param_len = buf.get_u8();
629 remaining = remaining.saturating_sub(2);
630
631 if usize::from(param_len) > remaining || buf.remaining() < usize::from(param_len) {
632 return Err(DecodeError::MalformedOptionalParameter {
633 offset: usize::from(opt_params_len) - remaining,
634 detail: format!(
635 "parameter type {param_type} claims length {param_len}, \
636 but only {remaining} bytes remain"
637 ),
638 });
639 }
640
641 if param_type == param_type::CAPABILITIES {
642 let param_bytes = buf.copy_to_bytes(usize::from(param_len));
646 let mut cap_buf = param_bytes;
647 while cap_buf.has_remaining() {
648 let cap = Capability::decode(&mut cap_buf)?;
649 capabilities.push(cap);
650 }
651 } else {
652 buf.advance(usize::from(param_len));
654 }
655
656 remaining = remaining.saturating_sub(usize::from(param_len));
657 }
658
659 Ok(capabilities)
660}
661
662pub fn encode_optional_parameters(
675 capabilities: &[Capability],
676 buf: &mut impl BufMut,
677) -> Result<(), EncodeError> {
678 if capabilities.is_empty() {
679 return Ok(());
680 }
681
682 let cap_total: usize = capabilities.iter().map(Capability::encoded_len).sum();
684
685 if cap_total > 255 {
686 return Err(EncodeError::ValueOutOfRange {
687 field: "capabilities_parameter_length",
688 value: cap_total.to_string(),
689 });
690 }
691
692 buf.put_u8(param_type::CAPABILITIES);
694 #[expect(clippy::cast_possible_truncation)]
695 buf.put_u8(cap_total as u8);
696
697 for cap in capabilities {
698 cap.encode(buf)?;
699 }
700 Ok(())
701}
702
703#[cfg(test)]
704mod tests {
705 use super::*;
706
707 #[test]
708 fn decode_multi_protocol_ipv4_unicast() {
709 let data: &[u8] = &[1, 4, 0, 1, 0, 1]; let mut buf = Bytes::copy_from_slice(data);
711 let cap = Capability::decode(&mut buf).unwrap();
712 assert_eq!(
713 cap,
714 Capability::MultiProtocol {
715 afi: Afi::Ipv4,
716 safi: Safi::Unicast
717 }
718 );
719 }
720
721 #[test]
722 fn decode_four_octet_as() {
723 let data: &[u8] = &[65, 4, 0, 0, 0xFD, 0xE8]; let mut buf = Bytes::copy_from_slice(data);
725 let cap = Capability::decode(&mut buf).unwrap();
726 assert_eq!(cap, Capability::FourOctetAs { asn: 65000 });
727 }
728
729 #[test]
730 fn decode_unknown_capability_preserved() {
731 let data: &[u8] = &[99, 3, 0xAA, 0xBB, 0xCC]; let mut buf = Bytes::copy_from_slice(data);
733 let cap = Capability::decode(&mut buf).unwrap();
734 match cap {
735 Capability::Unknown { code, data } => {
736 assert_eq!(code, 99);
737 assert_eq!(data.as_ref(), &[0xAA, 0xBB, 0xCC]);
738 }
739 _ => panic!("expected Unknown"),
740 }
741 }
742
743 #[test]
744 fn unrecognized_afi_safi_stored_as_unknown() {
745 let data: &[u8] = &[1, 4, 0, 99, 0, 1]; let mut buf = Bytes::copy_from_slice(data);
747 let cap = Capability::decode(&mut buf).unwrap();
748 assert!(matches!(cap, Capability::Unknown { code: 1, .. }));
749 }
750
751 #[test]
752 fn roundtrip_multi_protocol() {
753 let original = Capability::MultiProtocol {
754 afi: Afi::Ipv6,
755 safi: Safi::Unicast,
756 };
757 let mut encoded = bytes::BytesMut::with_capacity(6);
758 original.encode(&mut encoded).unwrap();
759 let mut buf = encoded.freeze();
760 let decoded = Capability::decode(&mut buf).unwrap();
761 assert_eq!(original, decoded);
762 }
763
764 #[test]
765 fn roundtrip_four_octet_as() {
766 let original = Capability::FourOctetAs { asn: 4_200_000_000 };
767 let mut encoded = bytes::BytesMut::with_capacity(6);
768 original.encode(&mut encoded).unwrap();
769 let mut buf = encoded.freeze();
770 let decoded = Capability::decode(&mut buf).unwrap();
771 assert_eq!(original, decoded);
772 }
773
774 #[test]
775 fn roundtrip_unknown() {
776 let original = Capability::Unknown {
777 code: 42,
778 data: Bytes::from_static(&[1, 2, 3]),
779 };
780 let mut encoded = bytes::BytesMut::with_capacity(5);
781 original.encode(&mut encoded).unwrap();
782 let mut buf = encoded.freeze();
783 let decoded = Capability::decode(&mut buf).unwrap();
784 assert_eq!(original, decoded);
785 }
786
787 #[test]
788 fn decode_optional_params_multiple_caps() {
789 let mut data = bytes::BytesMut::new();
791 data.put_u8(2); data.put_u8(12); data.put_u8(1);
795 data.put_u8(4);
796 data.put_u16(1); data.put_u8(0);
798 data.put_u8(1); data.put_u8(65);
801 data.put_u8(4);
802 data.put_u32(65001);
803
804 let mut buf = data.freeze();
805 let caps = decode_optional_parameters(&mut buf, 14).unwrap();
806 assert_eq!(caps.len(), 2);
807 assert_eq!(
808 caps[0],
809 Capability::MultiProtocol {
810 afi: Afi::Ipv4,
811 safi: Safi::Unicast
812 }
813 );
814 assert_eq!(caps[1], Capability::FourOctetAs { asn: 65001 });
815 }
816
817 #[test]
818 fn decode_empty_optional_params() {
819 let mut buf = Bytes::new();
820 let caps = decode_optional_parameters(&mut buf, 0).unwrap();
821 assert!(caps.is_empty());
822 }
823
824 #[test]
825 fn reject_truncated_capability() {
826 let data: &[u8] = &[65, 4, 0, 0]; let mut buf = Bytes::copy_from_slice(data);
828 assert!(Capability::decode(&mut buf).is_err());
829 }
830
831 #[test]
832 fn decode_graceful_restart_with_families() {
833 let mut data = bytes::BytesMut::new();
837 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();
848 let cap = Capability::decode(&mut buf).unwrap();
849 assert_eq!(
850 cap,
851 Capability::GracefulRestart {
852 restart_state: true,
853 notification: false,
854 restart_time: 120,
855 families: vec![
856 GracefulRestartFamily {
857 afi: Afi::Ipv4,
858 safi: Safi::Unicast,
859 forwarding_preserved: true,
860 },
861 GracefulRestartFamily {
862 afi: Afi::Ipv6,
863 safi: Safi::Unicast,
864 forwarding_preserved: false,
865 },
866 ],
867 }
868 );
869 }
870
871 #[test]
872 fn decode_graceful_restart_no_r_bit() {
873 let mut data = bytes::BytesMut::new();
874 data.put_u8(64);
875 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();
882 let cap = Capability::decode(&mut buf).unwrap();
883 assert_eq!(
884 cap,
885 Capability::GracefulRestart {
886 restart_state: false,
887 notification: false,
888 restart_time: 90,
889 families: vec![GracefulRestartFamily {
890 afi: Afi::Ipv4,
891 safi: Safi::Unicast,
892 forwarding_preserved: false,
893 }],
894 }
895 );
896 }
897
898 #[test]
899 fn decode_graceful_restart_empty_families() {
900 let mut data = bytes::BytesMut::new();
901 data.put_u8(64);
902 data.put_u8(2); data.put_u16(0x003C); let mut buf = data.freeze();
906 let cap = Capability::decode(&mut buf).unwrap();
907 assert_eq!(
908 cap,
909 Capability::GracefulRestart {
910 restart_state: false,
911 notification: false,
912 restart_time: 60,
913 families: vec![],
914 }
915 );
916 }
917
918 #[test]
919 fn roundtrip_graceful_restart() {
920 let original = Capability::GracefulRestart {
921 restart_state: true,
922 notification: false,
923 restart_time: 120,
924 families: vec![
925 GracefulRestartFamily {
926 afi: Afi::Ipv4,
927 safi: Safi::Unicast,
928 forwarding_preserved: true,
929 },
930 GracefulRestartFamily {
931 afi: Afi::Ipv6,
932 safi: Safi::Unicast,
933 forwarding_preserved: false,
934 },
935 ],
936 };
937 let mut encoded = bytes::BytesMut::with_capacity(12);
938 original.encode(&mut encoded).unwrap();
939 let mut buf = encoded.freeze();
940 let decoded = Capability::decode(&mut buf).unwrap();
941 assert_eq!(original, decoded);
942 }
943
944 #[test]
945 fn graceful_restart_encoded_len() {
946 let cap = Capability::GracefulRestart {
947 restart_state: false,
948 notification: false,
949 restart_time: 120,
950 families: vec![GracefulRestartFamily {
951 afi: Afi::Ipv4,
952 safi: Safi::Unicast,
953 forwarding_preserved: true,
954 }],
955 };
956 assert_eq!(cap.encoded_len(), 8);
958 }
959
960 #[test]
961 fn graceful_restart_code() {
962 let cap = Capability::GracefulRestart {
963 restart_state: false,
964 notification: false,
965 restart_time: 0,
966 families: vec![],
967 };
968 assert_eq!(cap.code(), 64);
969 }
970
971 #[test]
972 fn graceful_restart_bad_length_stored_as_unknown() {
973 let data: &[u8] = &[64, 3, 0x00, 0x3C, 0xFF];
975 let mut buf = Bytes::copy_from_slice(data);
976 let cap = Capability::decode(&mut buf).unwrap();
977 assert!(matches!(cap, Capability::Unknown { code: 64, .. }));
978 }
979
980 #[test]
981 fn encode_rejects_oversized_gr_families() {
982 let families: Vec<GracefulRestartFamily> = (0..64)
984 .map(|_| GracefulRestartFamily {
985 afi: Afi::Ipv4,
986 safi: Safi::Unicast,
987 forwarding_preserved: false,
988 })
989 .collect();
990 let cap = Capability::GracefulRestart {
991 restart_state: false,
992 notification: false,
993 restart_time: 120,
994 families,
995 };
996 let mut buf = bytes::BytesMut::new();
997 assert!(cap.encode(&mut buf).is_err());
998 }
999
1000 #[test]
1001 fn encode_rejects_oversized_unknown_data() {
1002 let cap = Capability::Unknown {
1003 code: 99,
1004 data: Bytes::from(vec![0u8; 256]),
1005 };
1006 let mut buf = bytes::BytesMut::new();
1007 assert!(cap.encode(&mut buf).is_err());
1008 }
1009
1010 #[test]
1011 fn encode_optional_params_rejects_overflow() {
1012 let caps: Vec<Capability> = (0..50)
1014 .map(|_| Capability::Unknown {
1015 code: 99,
1016 data: Bytes::from(vec![0u8; 5]),
1017 })
1018 .collect();
1019 let mut buf = bytes::BytesMut::new();
1021 assert!(encode_optional_parameters(&caps, &mut buf).is_err());
1022 }
1023
1024 #[test]
1025 fn encode_rejects_restart_time_over_4095() {
1026 let cap = Capability::GracefulRestart {
1027 restart_state: false,
1028 notification: false,
1029 restart_time: 4096,
1030 families: vec![],
1031 };
1032 let mut buf = bytes::BytesMut::new();
1033 assert!(cap.encode(&mut buf).is_err());
1034 }
1035
1036 #[test]
1037 fn encode_accepts_restart_time_at_4095() {
1038 let cap = Capability::GracefulRestart {
1039 restart_state: false,
1040 notification: false,
1041 restart_time: 4095,
1042 families: vec![],
1043 };
1044 let mut buf = bytes::BytesMut::new();
1045 assert!(cap.encode(&mut buf).is_ok());
1046 }
1047
1048 #[test]
1049 fn decode_graceful_restart_n_bit() {
1050 let mut data = bytes::BytesMut::new();
1051 data.put_u8(64);
1052 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();
1059 let cap = Capability::decode(&mut buf).unwrap();
1060 assert_eq!(
1061 cap,
1062 Capability::GracefulRestart {
1063 restart_state: true,
1064 notification: true,
1065 restart_time: 120,
1066 families: vec![GracefulRestartFamily {
1067 afi: Afi::Ipv4,
1068 safi: Safi::Unicast,
1069 forwarding_preserved: true,
1070 }],
1071 }
1072 );
1073 }
1074
1075 #[test]
1076 fn roundtrip_graceful_restart_with_n_bit() {
1077 let original = Capability::GracefulRestart {
1078 restart_state: true,
1079 notification: true,
1080 restart_time: 120,
1081 families: vec![GracefulRestartFamily {
1082 afi: Afi::Ipv4,
1083 safi: Safi::Unicast,
1084 forwarding_preserved: true,
1085 }],
1086 };
1087 let mut encoded = bytes::BytesMut::with_capacity(12);
1088 original.encode(&mut encoded).unwrap();
1089 let mut buf = encoded.freeze();
1090 let decoded = Capability::decode(&mut buf).unwrap();
1091 assert_eq!(original, decoded);
1092 }
1093
1094 #[test]
1095 fn decode_capability_bounded_to_parameter_slice() {
1096 let mut data = bytes::BytesMut::new();
1102 data.put_u8(2); data.put_u8(4); data.put_u8(65);
1107 data.put_u8(8); data.put_u16(0xBEEF); data.put_u8(99); data.put_u8(2); data.put_u16(0xCAFE);
1113
1114 let mut buf = data.freeze();
1115 let result = decode_optional_parameters(&mut buf, 8);
1119 assert!(result.is_err());
1120 }
1121
1122 #[test]
1123 fn decode_extended_message() {
1124 let data: &[u8] = &[6, 0]; let mut buf = Bytes::copy_from_slice(data);
1126 let cap = Capability::decode(&mut buf).unwrap();
1127 assert_eq!(cap, Capability::ExtendedMessage);
1128 }
1129
1130 #[test]
1131 fn roundtrip_extended_message() {
1132 let original = Capability::ExtendedMessage;
1133 let mut encoded = bytes::BytesMut::with_capacity(2);
1134 original.encode(&mut encoded).unwrap();
1135 let mut buf = encoded.freeze();
1136 let decoded = Capability::decode(&mut buf).unwrap();
1137 assert_eq!(original, decoded);
1138 }
1139
1140 #[test]
1141 fn extended_message_code_and_len() {
1142 let cap = Capability::ExtendedMessage;
1143 assert_eq!(cap.code(), 6);
1144 assert_eq!(cap.encoded_len(), 2);
1145 }
1146
1147 #[test]
1148 fn extended_message_bad_length_stored_as_unknown() {
1149 let data: &[u8] = &[6, 1, 0xFF]; let mut buf = Bytes::copy_from_slice(data);
1151 let cap = Capability::decode(&mut buf).unwrap();
1152 assert!(matches!(cap, Capability::Unknown { code: 6, .. }));
1153 }
1154
1155 #[test]
1158 fn decode_extended_nexthop_single_family() {
1159 let data: &[u8] = &[5, 6, 0, 1, 0, 1, 0, 2];
1162 let mut buf = Bytes::copy_from_slice(data);
1163 let cap = Capability::decode(&mut buf).unwrap();
1164 assert_eq!(
1165 cap,
1166 Capability::ExtendedNextHop(vec![ExtendedNextHopFamily {
1167 nlri_afi: Afi::Ipv4,
1168 nlri_safi: Safi::Unicast,
1169 next_hop_afi: Afi::Ipv6,
1170 }])
1171 );
1172 }
1173
1174 #[test]
1175 fn roundtrip_extended_nexthop() {
1176 let original = Capability::ExtendedNextHop(vec![ExtendedNextHopFamily {
1177 nlri_afi: Afi::Ipv4,
1178 nlri_safi: Safi::Unicast,
1179 next_hop_afi: Afi::Ipv6,
1180 }]);
1181 let mut encoded = bytes::BytesMut::with_capacity(8);
1182 original.encode(&mut encoded).unwrap();
1183 let mut buf = encoded.freeze();
1184 let decoded = Capability::decode(&mut buf).unwrap();
1185 assert_eq!(original, decoded);
1186 }
1187
1188 #[test]
1189 fn extended_nexthop_bad_length_stored_as_unknown() {
1190 let data: &[u8] = &[5, 4, 0, 1, 0, 1];
1192 let mut buf = Bytes::copy_from_slice(data);
1193 let cap = Capability::decode(&mut buf).unwrap();
1194 assert!(matches!(cap, Capability::Unknown { code: 5, .. }));
1195 }
1196
1197 #[test]
1200 fn decode_add_path_single_family() {
1201 let data: &[u8] = &[69, 4, 0, 1, 1, 3];
1203 let mut buf = Bytes::copy_from_slice(data);
1204 let cap = Capability::decode(&mut buf).unwrap();
1205 assert_eq!(
1206 cap,
1207 Capability::AddPath(vec![AddPathFamily {
1208 afi: Afi::Ipv4,
1209 safi: Safi::Unicast,
1210 send_receive: AddPathMode::Both,
1211 }])
1212 );
1213 }
1214
1215 #[test]
1216 fn decode_add_path_multiple_families() {
1217 let mut data = bytes::BytesMut::new();
1218 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();
1228 let cap = Capability::decode(&mut buf).unwrap();
1229 assert_eq!(
1230 cap,
1231 Capability::AddPath(vec![
1232 AddPathFamily {
1233 afi: Afi::Ipv4,
1234 safi: Safi::Unicast,
1235 send_receive: AddPathMode::Receive,
1236 },
1237 AddPathFamily {
1238 afi: Afi::Ipv6,
1239 safi: Safi::Unicast,
1240 send_receive: AddPathMode::Send,
1241 },
1242 ])
1243 );
1244 }
1245
1246 #[test]
1247 fn roundtrip_add_path() {
1248 let original = Capability::AddPath(vec![
1249 AddPathFamily {
1250 afi: Afi::Ipv4,
1251 safi: Safi::Unicast,
1252 send_receive: AddPathMode::Both,
1253 },
1254 AddPathFamily {
1255 afi: Afi::Ipv6,
1256 safi: Safi::Unicast,
1257 send_receive: AddPathMode::Receive,
1258 },
1259 ]);
1260 let mut encoded = bytes::BytesMut::with_capacity(10);
1261 original.encode(&mut encoded).unwrap();
1262 let mut buf = encoded.freeze();
1263 let decoded = Capability::decode(&mut buf).unwrap();
1264 assert_eq!(original, decoded);
1265 }
1266
1267 #[test]
1268 fn add_path_code_and_len() {
1269 let cap = Capability::AddPath(vec![AddPathFamily {
1270 afi: Afi::Ipv4,
1271 safi: Safi::Unicast,
1272 send_receive: AddPathMode::Receive,
1273 }]);
1274 assert_eq!(cap.code(), 69);
1275 assert_eq!(cap.encoded_len(), 6);
1277 }
1278
1279 #[test]
1280 fn add_path_bad_length_stored_as_unknown() {
1281 let data: &[u8] = &[69, 3, 0, 1, 1];
1283 let mut buf = Bytes::copy_from_slice(data);
1284 let cap = Capability::decode(&mut buf).unwrap();
1285 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1286 }
1287
1288 #[test]
1289 fn add_path_zero_length_stored_as_unknown() {
1290 let data: &[u8] = &[69, 0];
1292 let mut buf = Bytes::copy_from_slice(data);
1293 let cap = Capability::decode(&mut buf).unwrap();
1294 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1295 }
1296
1297 #[test]
1298 fn add_path_unknown_afi_preserved_as_unknown() {
1299 let data: &[u8] = &[69, 4, 0, 99, 1, 3];
1301 let mut buf = Bytes::copy_from_slice(data);
1302 let cap = Capability::decode(&mut buf).unwrap();
1303 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1305 }
1306
1307 #[test]
1308 fn add_path_invalid_mode_preserved_as_unknown() {
1309 let data: &[u8] = &[69, 4, 0, 1, 1, 0];
1311 let mut buf = Bytes::copy_from_slice(data);
1312 let cap = Capability::decode(&mut buf).unwrap();
1313 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1315 }
1316
1317 #[test]
1318 fn add_path_mixed_valid_and_invalid_preserved_as_unknown() {
1319 let mut data = bytes::BytesMut::new();
1321 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();
1330 let cap = Capability::decode(&mut buf).unwrap();
1331 assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1333 }
1334
1335 #[test]
1336 fn llgr_capability_roundtrip() {
1337 let families = vec![
1338 LlgrFamily {
1339 afi: Afi::Ipv4,
1340 safi: Safi::Unicast,
1341 forwarding_preserved: true,
1342 stale_time: 86400,
1343 },
1344 LlgrFamily {
1345 afi: Afi::Ipv6,
1346 safi: Safi::Unicast,
1347 forwarding_preserved: false,
1348 stale_time: 3600,
1349 },
1350 ];
1351 let cap = Capability::LongLivedGracefulRestart(families);
1352
1353 let mut buf = bytes::BytesMut::new();
1354 cap.encode(&mut buf).unwrap();
1355 let mut frozen = buf.freeze();
1356 let decoded = Capability::decode(&mut frozen).unwrap();
1357
1358 match decoded {
1359 Capability::LongLivedGracefulRestart(fams) => {
1360 assert_eq!(fams.len(), 2);
1361 assert_eq!(fams[0].afi, Afi::Ipv4);
1362 assert_eq!(fams[0].safi, Safi::Unicast);
1363 assert!(fams[0].forwarding_preserved);
1364 assert_eq!(fams[0].stale_time, 86400);
1365 assert_eq!(fams[1].afi, Afi::Ipv6);
1366 assert_eq!(fams[1].safi, Safi::Unicast);
1367 assert!(!fams[1].forwarding_preserved);
1368 assert_eq!(fams[1].stale_time, 3600);
1369 }
1370 other => panic!("expected LongLivedGracefulRestart, got {other:?}"),
1371 }
1372 }
1373
1374 #[test]
1375 fn llgr_capability_max_stale_time() {
1376 let cap = Capability::LongLivedGracefulRestart(vec![LlgrFamily {
1377 afi: Afi::Ipv4,
1378 safi: Safi::Unicast,
1379 forwarding_preserved: false,
1380 stale_time: 0x00FF_FFFF, }]);
1382
1383 let mut buf = bytes::BytesMut::new();
1384 cap.encode(&mut buf).unwrap();
1385 let mut frozen = buf.freeze();
1386 let decoded = Capability::decode(&mut frozen).unwrap();
1387
1388 match decoded {
1389 Capability::LongLivedGracefulRestart(fams) => {
1390 assert_eq!(fams[0].stale_time, 0x00FF_FFFF);
1391 }
1392 other => panic!("expected LongLivedGracefulRestart, got {other:?}"),
1393 }
1394 }
1395
1396 #[test]
1397 fn llgr_capability_empty() {
1398 let cap = Capability::LongLivedGracefulRestart(vec![]);
1399 let mut buf = bytes::BytesMut::new();
1400 cap.encode(&mut buf).unwrap();
1401 let mut frozen = buf.freeze();
1402 let decoded = Capability::decode(&mut frozen).unwrap();
1403 assert!(matches!(
1404 decoded,
1405 Capability::LongLivedGracefulRestart(fams) if fams.is_empty()
1406 ));
1407 }
1408}