Skip to main content

rustbgpd_wire/
capability.rs

1use bytes::{Buf, BufMut, Bytes};
2
3use crate::constants::{capability_code, param_type};
4use crate::error::{DecodeError, EncodeError};
5
6/// Address Family Identifier (IANA).
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[repr(u16)]
9pub enum Afi {
10    /// IPv4 (AFI 1).
11    Ipv4 = 1,
12    /// IPv6 (AFI 2).
13    Ipv6 = 2,
14    /// Layer-2 VPN (AFI 25) — carrier family for EVPN (RFC 7432).
15    L2Vpn = 25,
16}
17
18impl Afi {
19    /// Create from a raw 16-bit AFI value.
20    #[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/// Subsequent Address Family Identifier (IANA).
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33#[repr(u8)]
34pub enum Safi {
35    /// Unicast forwarding (SAFI 1).
36    Unicast = 1,
37    /// Multicast forwarding (SAFI 2).
38    Multicast = 2,
39    /// RFC 7432 EVPN — only valid with [`Afi::L2Vpn`].
40    Evpn = 70,
41    /// RFC 8955 `FlowSpec`.
42    FlowSpec = 133,
43}
44
45impl Safi {
46    /// Create from a raw 8-bit SAFI value.
47    #[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/// Per-AFI/SAFI entry in the Graceful Restart capability (RFC 4724 §3).
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct GracefulRestartFamily {
62    /// Address family.
63    pub afi: Afi,
64    /// Sub-address family.
65    pub safi: Safi,
66    /// Whether the peer preserved forwarding state for this family.
67    pub forwarding_preserved: bool,
68}
69
70/// Add-Path send/receive mode per AFI/SAFI (RFC 7911 §4).
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72#[repr(u8)]
73pub enum AddPathMode {
74    /// Peer can receive multiple paths.
75    Receive = 1,
76    /// Peer can send multiple paths.
77    Send = 2,
78    /// Peer can both send and receive multiple paths.
79    Both = 3,
80}
81
82impl AddPathMode {
83    /// Create from a raw 8-bit mode value.
84    #[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/// Per-AFI/SAFI entry in the Add-Path capability (RFC 7911 §4).
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97pub struct AddPathFamily {
98    /// Address family.
99    pub afi: Afi,
100    /// Sub-address family.
101    pub safi: Safi,
102    /// Send/receive mode for this family.
103    pub send_receive: AddPathMode,
104}
105
106/// Per-family entry in the Extended Next Hop Encoding capability (RFC 8950).
107///
108/// Each tuple advertises that NLRI for `(nlri_afi, nlri_safi)` may use the
109/// specified `next_hop_afi` in `MP_REACH_NLRI`.
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111pub struct ExtendedNextHopFamily {
112    /// AFI of the NLRI.
113    pub nlri_afi: Afi,
114    /// SAFI of the NLRI.
115    pub nlri_safi: Safi,
116    /// AFI of the next-hop encoding.
117    pub next_hop_afi: Afi,
118}
119
120/// Per-AFI/SAFI entry in the Long-Lived Graceful Restart capability (RFC 9494).
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub struct LlgrFamily {
123    /// Address family.
124    pub afi: Afi,
125    /// Sub-address family.
126    pub safi: Safi,
127    /// Whether the peer preserved forwarding state for this family during LLGR.
128    pub forwarding_preserved: bool,
129    /// Long-lived stale time in seconds (24-bit, max `16_777_215` ≈ 194 days).
130    pub stale_time: u32,
131}
132
133/// BGP Role (RFC 9234 §4) — the speaker's role on this eBGP session.
134///
135/// The numeric encoding is the 1-byte capability value carried in the
136/// Role capability (code 9). The compatibility matrix
137/// (Provider↔Customer, RS↔RS-Client, Peer↔Peer) is enforced by the FSM
138/// negotiator, not by this codec.
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140#[repr(u8)]
141pub enum BgpRole {
142    /// Speaker is a transit Provider for the peer.
143    Provider = 0,
144    /// Speaker is a Route Server (RFC 7947).
145    RouteServer = 1,
146    /// Speaker is a client of a Route Server.
147    RouteServerClient = 2,
148    /// Speaker is a Customer of the peer.
149    Customer = 3,
150    /// Speaker is a lateral Peer of the peer.
151    Peer = 4,
152}
153
154impl BgpRole {
155    /// Create from a raw 8-bit role value. Unknown values yield `None` so
156    /// the caller can preserve them via [`Capability::Unknown`].
157    #[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    /// Raw 8-bit encoding for the Role capability value field.
170    #[must_use]
171    pub fn to_u8(self) -> u8 {
172        self as u8
173    }
174}
175
176/// BGP capability as negotiated in OPEN optional parameters.
177#[derive(Debug, Clone, PartialEq, Eq)]
178pub enum Capability {
179    /// RFC 4760: Multi-Protocol Extensions.
180    MultiProtocol {
181        /// Address family.
182        afi: Afi,
183        /// Sub-address family.
184        safi: Safi,
185    },
186    /// RFC 8950: Extended Next Hop Encoding.
187    ExtendedNextHop(Vec<ExtendedNextHopFamily>),
188    /// RFC 4724: Graceful Restart.
189    GracefulRestart {
190        /// R-bit: the sender has restarted and its forwarding state
191        /// may have been preserved.
192        restart_state: bool,
193        /// N-bit (RFC 8538): the sender supports Notification GR — NOTIFICATIONs
194        /// trigger GR unless Cease/Hard Reset (subcode 9) is used.
195        notification: bool,
196        /// Time in seconds the sender will retain stale routes (12-bit, max 4095).
197        restart_time: u16,
198        /// Per-AFI/SAFI forwarding state flags.
199        families: Vec<GracefulRestartFamily>,
200    },
201    /// RFC 2918: Route Refresh.
202    RouteRefresh,
203    /// RFC 7313: Enhanced Route Refresh.
204    EnhancedRouteRefresh,
205    /// RFC 8654: Extended Messages (raise max message length to 65535).
206    ExtendedMessage,
207    /// RFC 9494: Long-Lived Graceful Restart.
208    LongLivedGracefulRestart(Vec<LlgrFamily>),
209    /// RFC 7911: Add-Path — advertise/receive multiple paths per prefix.
210    AddPath(Vec<AddPathFamily>),
211    /// RFC 5291: Outbound Route Filtering — per-family ORF-Type/role blocks.
212    OutboundRouteFilter(Vec<crate::orf::OrfCapEntry>),
213    /// RFC 6793: 4-Byte AS Number.
214    FourOctetAs {
215        /// The 4-byte autonomous system number.
216        asn: u32,
217    },
218    /// RFC 9234 §4: BGP Role. The speaker's role on this eBGP session.
219    Role {
220        /// The role advertised by this speaker.
221        role: BgpRole,
222    },
223    /// Unknown or unrecognized capability, preserved for re-emission.
224    Unknown {
225        /// Capability code.
226        code: u8,
227        /// Raw capability value bytes.
228        data: Bytes,
229    },
230}
231
232impl Capability {
233    /// Decode a single capability TLV from a buffer.
234    ///
235    /// # Errors
236    ///
237    /// Returns [`DecodeError::MalformedOptionalParameter`] if the TLV is
238    /// truncated or the claimed length exceeds the remaining bytes.
239    #[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                    // Store as unknown if length is wrong
266                    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                    // Unrecognized AFI/SAFI — store as unknown
277                    let mut data = bytes::BytesMut::with_capacity(4);
278                    data.put_u16(afi_raw);
279                    data.put_u8(0); // reserved
280                    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                // RFC 8950: repeated tuples of
303                // NLRI AFI (2) | NLRI SAFI (2) | Next Hop AFI (2)
304                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                // Minimum 2 bytes (restart flags/time). Each family is 4 bytes.
350                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                    // Skip unrecognized AFI/SAFI entries silently
374                }
375                Ok(Capability::GracefulRestart {
376                    restart_state,
377                    notification,
378                    restart_time,
379                    families,
380                })
381            }
382            capability_code::LONG_LIVED_GRACEFUL_RESTART => {
383                // RFC 9494: repeated entries of AFI(2) + SAFI(1) + flags(1) + stale_time(3) = 7 bytes each
384                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                    // stale_time is 24-bit (3 bytes, big-endian)
398                    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                // RFC 7911 §4: value is N entries of (AFI:2 + SAFI:1 + mode:1) = 4 bytes each
425                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                // Snapshot the raw bytes before parsing so we can fall back
431                // to Unknown if any entry would be discarded (lossless roundtrip).
432                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                // Preserve as Unknown if any entry was unrecognized, to avoid
455                // silently rewriting malformed capability data on re-encode.
456                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                // RFC 5291 §4: per-family blocks. Preserve as Unknown on a
467                // structural error or unrecognized AFI/SAFI (lossless
468                // round-trip, mirroring Add-Path).
469                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                // RFC 9234 §4.1: Length = 1, value ∈ {0..=4}. Anything else
485                // is preserved as Unknown so the peer's bytes round-trip and
486                // FSM negotiation can decide whether to reject the session.
487                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    /// Encode a single capability TLV into a buffer.
507    ///
508    /// # Errors
509    ///
510    /// Returns [`EncodeError::ValueOutOfRange`] if the capability value
511    /// exceeds the 255-byte limit of the single-octet length field.
512    #[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); // length
521                buf.put_u16(*afi as u16);
522                buf.put_u8(0); // reserved
523                buf.put_u8(*safi as u8);
524            }
525            Capability::RouteRefresh => {
526                buf.put_u8(capability_code::ROUTE_REFRESH);
527                buf.put_u8(0); // zero-length value
528            }
529            Capability::EnhancedRouteRefresh => {
530                buf.put_u8(capability_code::ENHANCED_ROUTE_REFRESH);
531                buf.put_u8(0); // zero-length value
532            }
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); // zero-length value
553            }
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                    // stale_time is 24-bit (3 bytes, big-endian)
606                    #[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                // RFC 5291 §4: the value carries one or more blocks. An empty
630                // list would encode to a zero-length value, which the decoder
631                // (correctly) treats as malformed and round-trips as Unknown —
632                // reject it here so encode/decode stay symmetric.
633                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); // length
654                buf.put_u32(*asn);
655            }
656            Capability::Role { role } => {
657                buf.put_u8(capability_code::BGP_ROLE);
658                buf.put_u8(1); // length
659                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    /// Returns the capability code byte.
678    #[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    /// Encoded size of this capability TLV (code + length + value).
697    #[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
713/// Decode all optional parameters from an OPEN message body.
714/// Returns capabilities found in parameter type 2 TLVs.
715///
716/// # Errors
717///
718/// Returns [`DecodeError::MalformedOptionalParameter`] if any parameter TLV
719/// is truncated or contains an invalid capability.
720pub 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            // Parse capabilities from a bounded sub-buffer so a malformed
751            // capability length cannot consume into the next parameter or
752            // beyond the OPEN body.
753            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            // Skip unknown parameter types
761            buf.advance(usize::from(param_len));
762        }
763
764        remaining = remaining.saturating_sub(usize::from(param_len));
765    }
766
767    Ok(capabilities)
768}
769
770/// Encode capabilities as OPEN optional parameters (parameter type 2).
771///
772/// # Errors
773///
774/// Returns [`EncodeError::ValueOutOfRange`] if the total capabilities size
775/// exceeds 255 bytes or any individual capability is too large.
776///
777/// # Note
778///
779/// On error, partial bytes may have been written to `buf`. Callers should
780/// encode into a staging buffer (as `OpenMessage::encode` does) to ensure
781/// atomicity.
782pub 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    // Calculate total capability TLV size
791    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    // Parameter type 2 header
801    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]; // code=1, len=4, AFI=1, res=0, SAFI=1
818        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]; // code=65, len=4, ASN=65000
832        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]; // code=99, len=3
840        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]; // code=1, len=4, AFI=99 (unknown)
854        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        // Parameter type=2, length=12, containing two capabilities
898        let mut data = bytes::BytesMut::new();
899        data.put_u8(2); // param type = capabilities
900        data.put_u8(12); // param length
901        // Cap 1: MultiProtocol IPv4 Unicast
902        data.put_u8(1);
903        data.put_u8(4);
904        data.put_u16(1); // AFI IPv4
905        data.put_u8(0);
906        data.put_u8(1); // SAFI Unicast
907        // Cap 2: FourOctetAs 65001
908        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]; // FourOctetAs but only 2 bytes of value
935        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        // code=64, len=10 (2 + 2*4), flags=0x80 (R-bit) | time=120
942        // Family 1: IPv4/Unicast, forwarding preserved
943        // Family 2: IPv6/Unicast, forwarding not preserved
944        let mut data = bytes::BytesMut::new();
945        data.put_u8(64); // code
946        data.put_u8(10); // length: 2 + 2*4
947        data.put_u16(0x8078); // R-bit set, restart_time=120
948        data.put_u16(1); // AFI IPv4
949        data.put_u8(1); // SAFI Unicast
950        data.put_u8(0x80); // forwarding preserved
951        data.put_u16(2); // AFI IPv6
952        data.put_u8(1); // SAFI Unicast
953        data.put_u8(0x00); // forwarding not preserved
954
955        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); // 2 + 1*4
984        data.put_u16(0x005A); // R-bit clear, restart_time=90
985        data.put_u16(1); // AFI IPv4
986        data.put_u8(1); // SAFI Unicast
987        data.put_u8(0x00); // forwarding not preserved
988
989        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); // just the flags/time, no families
1011        data.put_u16(0x003C); // time=60
1012
1013        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        // code(1) + length(1) + flags_time(2) + 1 family(4) = 8
1065        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        // Length 3 is invalid (not 2 + N*4)
1082        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        // 64 families → value_len = 2 + 64*4 = 258, exceeds u8
1091        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        // Total capabilities exceeding 255 bytes
1121        let caps: Vec<Capability> = (0..50)
1122            .map(|_| Capability::Unknown {
1123                code: 99,
1124                data: Bytes::from(vec![0u8; 5]),
1125            })
1126            .collect();
1127        // 50 caps * 7 bytes each = 350 > 255
1128        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); // 2 + 1*4
1161        data.put_u16(0xC078); // R-bit + N-bit set, restart_time=120
1162        data.put_u16(1); // AFI IPv4
1163        data.put_u8(1); // SAFI Unicast
1164        data.put_u8(0x80); // forwarding preserved
1165
1166        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        // Build optional parameters where the capability inside claims a
1205        // length that would overrun the parameter boundary.
1206        // Parameter: type=2, len=4 (only 4 bytes of capability data)
1207        // Capability inside: code=65 (FourOctetAs), len=8 (claims 8 but only 2 available)
1208        // Followed by: a valid second parameter that should not be consumed.
1209        let mut data = bytes::BytesMut::new();
1210        // Parameter 1: capabilities, len=4
1211        data.put_u8(2); // param type = capabilities
1212        data.put_u8(4); // param len = 4 bytes
1213        // Capability: code=65, len=8 (overflows the 4-byte parameter)
1214        data.put_u8(65);
1215        data.put_u8(8); // claims 8 bytes but only 2 remain in parameter
1216        data.put_u16(0xBEEF); // 2 bytes of data
1217        // Parameter 2: unknown type, should be untouched
1218        data.put_u8(99); // param type = unknown
1219        data.put_u8(2); // param len = 2
1220        data.put_u16(0xCAFE);
1221
1222        let mut buf = data.freeze();
1223        // Should fail because the capability overflows the parameter slice
1224        // Total is 8 bytes: param1(2+4) + param2(2+2) but we pass the full
1225        // length so the outer parser sees both parameters.
1226        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]; // code=6, len=0
1233        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]; // code=6, len=1 (should be 0)
1258        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    // --- Extended Next Hop capability tests ---
1264
1265    #[test]
1266    fn decode_extended_nexthop_single_family() {
1267        // code=5, len=6,
1268        // NLRI AFI=1 (IPv4), NLRI SAFI=1 (Unicast), NH AFI=2 (IPv6)
1269        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        // code=5, len=4 (must be multiple of 6)
1299        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    // --- Add-Path capability tests ---
1306
1307    #[test]
1308    fn decode_add_path_single_family() {
1309        // code=69, len=4, AFI=1(IPv4), SAFI=1(Unicast), mode=3(Both)
1310        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); // code
1327        data.put_u8(8); // len = 2 * 4
1328        data.put_u16(1); // AFI IPv4
1329        data.put_u8(1); // SAFI Unicast
1330        data.put_u8(1); // Receive
1331        data.put_u16(2); // AFI IPv6
1332        data.put_u8(1); // SAFI Unicast
1333        data.put_u8(2); // Send
1334
1335        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        // code(1) + length(1) + 1 family(4) = 6
1384        assert_eq!(cap.encoded_len(), 6);
1385    }
1386
1387    #[test]
1388    fn add_path_bad_length_stored_as_unknown() {
1389        // code=69, len=3 (not multiple of 4)
1390        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        // code=69, len=0
1399        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        // code=69, len=4, AFI=99(unknown), SAFI=1, mode=3
1408        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        // Unrecognized entry → preserve as Unknown for lossless roundtrip
1412        assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1413    }
1414
1415    #[test]
1416    fn add_path_invalid_mode_preserved_as_unknown() {
1417        // code=69, len=4, AFI=1, SAFI=1, mode=0 (invalid)
1418        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        // Invalid mode → preserve as Unknown for lossless roundtrip
1422        assert!(matches!(cap, Capability::Unknown { code: 69, .. }));
1423    }
1424
1425    #[test]
1426    fn add_path_mixed_valid_and_invalid_preserved_as_unknown() {
1427        // Two entries: valid IPv4/Unicast/Both + invalid AFI=99
1428        let mut data = bytes::BytesMut::new();
1429        data.put_u8(69); // code
1430        data.put_u8(8); // len = 2 * 4
1431        data.put_u16(1); // AFI IPv4
1432        data.put_u8(1); // SAFI Unicast
1433        data.put_u8(3); // Both (valid)
1434        data.put_u16(99); // AFI unknown
1435        data.put_u8(1); // SAFI Unicast
1436        data.put_u8(3); // Both
1437        let mut buf = data.freeze();
1438        let cap = Capability::decode(&mut buf).unwrap();
1439        // One invalid entry → entire capability preserved as Unknown
1440        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, // 24-bit max
1489        }]);
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    // --- BGP Role capability (RFC 9234 §4) tests ---
1518
1519    #[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            // Wire form: code=9, len=1, value=role
1532            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        // 1 (code) + 1 (length) + 1 (value) = 3
1553        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        // RFC 9234 §4.1: Role length MUST be 1. Anything else round-trips as
1565        // Unknown so the negotiator can decide (the codec stays non-fatal).
1566        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        // Role bytes 5..=255 are not defined; preserve the offending byte as
1585        // Unknown so the negotiator can NOTIFICATION 2/11 with the raw value.
1586        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    // --- Outbound Route Filtering capability (RFC 5291 §4) tests ---
1617
1618    #[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        // code(1) + len(1) + AFI(2)+res(1)+SAFI(1)+count(1) + type/sr(2) = 9
1632        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        // An empty block list has no valid wire form (RFC 5291 §4 requires one
1642        // or more), and the decoder treats a zero-length value as Unknown —
1643        // encode must refuse it so the codec stays symmetric.
1644        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        // code=3, len=7, AFI=99(unknown), SAFI=1, count=1, type=64, sr=1
1652        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}