Skip to main content

rustbgpd_wire/
orf.rs

1//! Outbound Route Filtering (ORF) types and codec — RFC 5291 + RFC 5292.
2//!
3//! ORF is a negotiated capability (code 3) that lets a BGP speaker push a
4//! filter to its peer; the peer applies that filter to the routes it
5//! advertises back. rustbgpd implements the **receive** side of the
6//! Address-Prefix ORF-Type (RFC 5292): it advertises that it is willing to
7//! receive ORF entries and applies them to its Adj-RIB-Out for that peer.
8//!
9//! This module is the pure wire codec. Semantic validation (is the ORF-Type
10//! negotiated? is `min_len <= max_len`?) is the caller's responsibility — see
11//! the transport/RIB layers. Only genuine BGP-framing errors (truncation, or a
12//! group length that overruns the message body) return a `DecodeError`; a
13//! malformed Address-Prefix *entry* (undefined Action, or a prefix length
14//! beyond the address family) decodes into [`OrfEntries::Malformed`] so the
15//! caller can apply the RFC 5291 §5.2 reset instead of tearing the session down.
16
17use bytes::{Buf, BufMut, Bytes};
18use std::net::{Ipv4Addr, Ipv6Addr};
19
20use crate::capability::{Afi, Safi};
21use crate::constants::orf;
22use crate::error::DecodeError;
23use crate::nlri::{Ipv4Prefix, Ipv6Prefix, Prefix};
24
25/// ORF-Type (RFC 5291 §5 / RFC 5292). Unknown types are preserved so the
26/// codec round-trips forward-defined types losslessly.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum OrfType {
29    /// Address-Prefix ORF-Type 64 (RFC 5292) — the standard value.
30    AddressPrefix,
31    /// Legacy pre-standard Address-Prefix ORF-Type 128 (Cisco).
32    AddressPrefixLegacy,
33    /// Any other ORF-Type value, preserved verbatim.
34    Unknown(u8),
35}
36
37impl OrfType {
38    /// Create from the raw 8-bit ORF-Type value.
39    #[must_use]
40    pub fn from_u8(value: u8) -> Self {
41        match value {
42            orf::TYPE_ADDRESS_PREFIX => Self::AddressPrefix,
43            orf::TYPE_ADDRESS_PREFIX_LEGACY => Self::AddressPrefixLegacy,
44            other => Self::Unknown(other),
45        }
46    }
47
48    /// Raw 8-bit ORF-Type value.
49    #[must_use]
50    pub fn as_u8(self) -> u8 {
51        match self {
52            Self::AddressPrefix => orf::TYPE_ADDRESS_PREFIX,
53            Self::AddressPrefixLegacy => orf::TYPE_ADDRESS_PREFIX_LEGACY,
54            Self::Unknown(value) => value,
55        }
56    }
57
58    /// Whether this type uses the RFC 5292 Address-Prefix entry encoding.
59    #[must_use]
60    pub fn is_address_prefix(self) -> bool {
61        matches!(self, Self::AddressPrefix | Self::AddressPrefixLegacy)
62    }
63}
64
65/// Capability Send/Receive field (RFC 5291 §4). Unknown values are preserved.
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
67pub enum OrfSendReceive {
68    /// The speaker is willing to receive ORF entries from its peer.
69    Receive,
70    /// The speaker is willing to send ORF entries to its peer.
71    Send,
72    /// The speaker is willing to both send and receive ORF entries.
73    Both,
74    /// Any other value, preserved verbatim.
75    Unknown(u8),
76}
77
78impl OrfSendReceive {
79    /// Create from the raw 8-bit Send/Receive value.
80    #[must_use]
81    pub fn from_u8(value: u8) -> Self {
82        match value {
83            orf::SEND_RECEIVE_RECEIVE => Self::Receive,
84            orf::SEND_RECEIVE_SEND => Self::Send,
85            orf::SEND_RECEIVE_BOTH => Self::Both,
86            other => Self::Unknown(other),
87        }
88    }
89
90    /// Raw 8-bit Send/Receive value.
91    #[must_use]
92    pub fn as_u8(self) -> u8 {
93        match self {
94            Self::Receive => orf::SEND_RECEIVE_RECEIVE,
95            Self::Send => orf::SEND_RECEIVE_SEND,
96            Self::Both => orf::SEND_RECEIVE_BOTH,
97            Self::Unknown(value) => value,
98        }
99    }
100
101    /// Whether the advertiser is willing to **send** ORF entries (Send or Both).
102    #[must_use]
103    pub fn can_send(self) -> bool {
104        matches!(self, Self::Send | Self::Both)
105    }
106
107    /// Whether the advertiser is willing to **receive** ORF entries (Receive or Both).
108    #[must_use]
109    pub fn can_receive(self) -> bool {
110        matches!(self, Self::Receive | Self::Both)
111    }
112}
113
114/// One (ORF-Type, Send/Receive) pair inside a capability block.
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub struct OrfCapType {
117    /// The ORF-Type.
118    pub orf_type: OrfType,
119    /// Send/Receive role advertised for this type.
120    pub send_receive: OrfSendReceive,
121}
122
123/// One per-(AFI,SAFI) block of the ORF capability value (RFC 5291 §4).
124#[derive(Debug, Clone, PartialEq, Eq)]
125pub struct OrfCapEntry {
126    /// Address family.
127    pub afi: Afi,
128    /// Sub-address family.
129    pub safi: Safi,
130    /// The ORF-Types advertised for this family, with their Send/Receive roles.
131    pub orf_types: Vec<OrfCapType>,
132}
133
134/// ROUTE-REFRESH When-to-refresh octet (RFC 5291 §5.2). Unknown values preserved.
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
136pub enum WhenToRefresh {
137    /// Re-run the outbound advertisement sweep immediately.
138    Immediate,
139    /// Install the filter now but defer the sweep to a later ROUTE-REFRESH.
140    Defer,
141    /// Any other value, preserved verbatim.
142    Unknown(u8),
143}
144
145impl WhenToRefresh {
146    /// Create from the raw 8-bit When-to-refresh value.
147    #[must_use]
148    pub fn from_u8(value: u8) -> Self {
149        match value {
150            orf::WHEN_IMMEDIATE => Self::Immediate,
151            orf::WHEN_DEFER => Self::Defer,
152            other => Self::Unknown(other),
153        }
154    }
155
156    /// Raw 8-bit When-to-refresh value.
157    #[must_use]
158    pub fn as_u8(self) -> u8 {
159        match self {
160            Self::Immediate => orf::WHEN_IMMEDIATE,
161            Self::Defer => orf::WHEN_DEFER,
162            Self::Unknown(value) => value,
163        }
164    }
165}
166
167/// Action field of a common ORF entry header (RFC 5291 §5.1.1).
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
169pub enum OrfAction {
170    /// Add this entry to the ORF list.
171    Add,
172    /// Remove the matching entry from the ORF list.
173    Remove,
174    /// Remove all previously installed entries (entry carries only the header).
175    RemoveAll,
176}
177
178/// Match field of a common ORF entry header (RFC 5291 §5.1.1).
179#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
180pub enum OrfMatch {
181    /// Permit routes matching this entry.
182    Permit,
183    /// Deny routes matching this entry.
184    Deny,
185}
186
187/// A single Address-Prefix ORF entry (RFC 5291 §5.1.1 header + RFC 5292 §4).
188///
189/// For [`OrfAction::RemoveAll`], only the action is meaningful — `prefix` is
190/// `None` and the length/sequence fields are zero.
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub struct AddressPrefixOrf {
193    /// Add / Remove / Remove-All.
194    pub action: OrfAction,
195    /// Permit or Deny (ignored for Remove-All).
196    pub match_: OrfMatch,
197    /// Ordering key within the ORF list.
198    pub sequence: u32,
199    /// Minimum prefix length (0 = unspecified, RFC 5292 §4).
200    pub min_len: u8,
201    /// Maximum prefix length (0 = unspecified, RFC 5292 §4).
202    pub max_len: u8,
203    /// The filtered prefix (`None` for Remove-All).
204    pub prefix: Option<Prefix>,
205}
206
207/// Decoded entries of one ORF-Type group inside a ROUTE-REFRESH.
208#[derive(Debug, Clone, PartialEq, Eq)]
209pub enum OrfEntries {
210    /// Parsed Address-Prefix entries (ORF-Type 64 or 128).
211    AddressPrefix(Vec<AddressPrefixOrf>),
212    /// Raw bytes for an ORF-Type this codec does not parse, preserved verbatim
213    /// for lossless round-trip (the caller ignores un-negotiated types).
214    Raw(Bytes),
215    /// An Address-Prefix group whose entries could not be parsed (undefined
216    /// Action, prefix length beyond the family, etc.). Per RFC 5291 §5.2 the
217    /// receiver ignores the malformed entries and removes the previously
218    /// installed ORF list of that type — it does not tear the session down,
219    /// so this is surfaced as data rather than a `DecodeError`. The raw bytes
220    /// are preserved for lossless round-trip.
221    Malformed(Bytes),
222}
223
224/// One ORF-Type group within a ROUTE-REFRESH ORF payload (RFC 5291 §5.2).
225#[derive(Debug, Clone, PartialEq, Eq)]
226pub struct OrfEntryGroup {
227    /// The ORF-Type of this group.
228    pub orf_type: OrfType,
229    /// The group's entries.
230    pub entries: OrfEntries,
231}
232
233/// The ORF section carried in a ROUTE-REFRESH message (RFC 5291 §5.2),
234/// following the standard AFI/Reserved/SAFI header.
235#[derive(Debug, Clone, PartialEq, Eq)]
236pub struct OrfPayload {
237    /// When-to-refresh directive.
238    pub when_to_refresh: WhenToRefresh,
239    /// One or more ORF-Type groups.
240    pub groups: Vec<OrfEntryGroup>,
241}
242
243// ── Capability value codec (RFC 5291 §4) ─────────────────────────────────
244
245/// Encoded length of the ORF capability value (sum over blocks).
246#[must_use]
247pub fn capability_value_len(entries: &[OrfCapEntry]) -> usize {
248    // Per block: AFI(2) + Reserved(1) + SAFI(1) + NumberOfORFs(1) + 2×types.
249    entries.iter().map(|e| 5 + 2 * e.orf_types.len()).sum()
250}
251
252/// Encode the ORF capability value (without the capability code/length header).
253pub fn encode_capability_value(entries: &[OrfCapEntry], buf: &mut impl BufMut) {
254    for entry in entries {
255        buf.put_u16(entry.afi as u16);
256        buf.put_u8(0); // reserved
257        buf.put_u8(entry.safi as u8);
258        // Number of ORFs — bounded by the 255-byte capability value, so a
259        // u8 cast cannot truncate in practice; clamp defensively.
260        let count = u8::try_from(entry.orf_types.len()).unwrap_or(u8::MAX);
261        buf.put_u8(count);
262        for t in &entry.orf_types {
263            buf.put_u8(t.orf_type.as_u8());
264            buf.put_u8(t.send_receive.as_u8());
265        }
266    }
267}
268
269/// Decode the ORF capability value. Returns `None` on a structural error or an
270/// unrecognized AFI/SAFI, so the caller can preserve the capability as
271/// `Capability::Unknown` for a lossless round-trip (mirroring Add-Path).
272#[must_use]
273pub fn decode_capability_value(mut raw: &[u8]) -> Option<Vec<OrfCapEntry>> {
274    // RFC 5291 §4: the value carries one or more blocks. An empty value is
275    // malformed — return None so the caller preserves it as Unknown.
276    if raw.is_empty() {
277        return None;
278    }
279    let mut entries = Vec::new();
280    while !raw.is_empty() {
281        if raw.len() < 5 {
282            return None;
283        }
284        let afi_raw = u16::from_be_bytes([raw[0], raw[1]]);
285        let safi_raw = raw[3];
286        let count = usize::from(raw[4]);
287        raw = &raw[5..];
288        if raw.len() < count * 2 {
289            return None;
290        }
291        let afi = Afi::from_u16(afi_raw)?;
292        let safi = Safi::from_u8(safi_raw)?;
293        let mut orf_types = Vec::with_capacity(count);
294        for _ in 0..count {
295            orf_types.push(OrfCapType {
296                orf_type: OrfType::from_u8(raw[0]),
297                send_receive: OrfSendReceive::from_u8(raw[1]),
298            });
299            raw = &raw[2..];
300        }
301        entries.push(OrfCapEntry {
302            afi,
303            safi,
304            orf_types,
305        });
306    }
307    Some(entries)
308}
309
310// ── ROUTE-REFRESH ORF payload codec (RFC 5291 §5.2) ──────────────────────
311
312/// Decode the ORF section of a ROUTE-REFRESH body, after the AFI/Reserved/SAFI
313/// header. `family` is the resolved AFI/SAFI pair of the message. Address-
314/// Prefix entries are parsed only for IPv4/IPv6 unicast, where their prefix
315/// encoding is defined here; other families are kept as raw bytes.
316///
317/// # Errors
318///
319/// Returns [`DecodeError`] only on genuine framing problems: truncation, or a
320/// group length that overruns the body. A malformed Address-Prefix *entry*
321/// (undefined Action, or a prefix length beyond the address family) does not
322/// error — that group decodes into [`OrfEntries::Malformed`] for the caller to
323/// reset (RFC 5291 §5.2).
324pub fn decode_route_refresh_orf(
325    buf: &mut impl Buf,
326    family: Option<(Afi, Safi)>,
327) -> Result<OrfPayload, DecodeError> {
328    if buf.remaining() < 1 {
329        return Err(DecodeError::Incomplete {
330            needed: 1,
331            available: buf.remaining(),
332        });
333    }
334    let when_to_refresh = WhenToRefresh::from_u8(buf.get_u8());
335
336    let mut groups = Vec::new();
337    while buf.remaining() > 0 {
338        if buf.remaining() < 3 {
339            return Err(DecodeError::Incomplete {
340                needed: 3,
341                available: buf.remaining(),
342            });
343        }
344        let orf_type = OrfType::from_u8(buf.get_u8());
345        let len = usize::from(buf.get_u16());
346        if buf.remaining() < len {
347            return Err(DecodeError::Incomplete {
348                needed: len,
349                available: buf.remaining(),
350            });
351        }
352        let group_bytes = buf.copy_to_bytes(len);
353        let parse_family = match family {
354            Some((afi @ (Afi::Ipv4 | Afi::Ipv6), Safi::Unicast))
355                if orf_type.is_address_prefix() =>
356            {
357                Some(afi)
358            }
359            _ => None,
360        };
361        let entries = if let Some(afi) = parse_family {
362            // A parse failure here is a malformed-but-framed group: surface it
363            // as data (RFC 5291 §5.2 reset semantics), not a session error.
364            match decode_address_prefix_entries(&group_bytes, afi) {
365                Ok(parsed) => OrfEntries::AddressPrefix(parsed),
366                Err(_) => OrfEntries::Malformed(group_bytes),
367            }
368        } else {
369            OrfEntries::Raw(group_bytes)
370        };
371        groups.push(OrfEntryGroup { orf_type, entries });
372    }
373
374    Ok(OrfPayload {
375        when_to_refresh,
376        groups,
377    })
378}
379
380/// Encode an ORF payload after the AFI/Reserved/SAFI header (used for
381/// round-trip tests; rustbgpd is receive-side and does not emit ORF in
382/// production).
383///
384/// # Errors
385///
386/// Returns [`crate::error::EncodeError`] if a group's encoded entries exceed
387/// the 16-bit Length field.
388pub fn encode_route_refresh_orf(
389    payload: &OrfPayload,
390    buf: &mut impl BufMut,
391) -> Result<(), crate::error::EncodeError> {
392    buf.put_u8(payload.when_to_refresh.as_u8());
393    for group in &payload.groups {
394        let mut group_buf = bytes::BytesMut::new();
395        match &group.entries {
396            OrfEntries::AddressPrefix(entries) => {
397                for entry in entries {
398                    encode_address_prefix_entry(entry, &mut group_buf);
399                }
400            }
401            OrfEntries::Raw(raw) | OrfEntries::Malformed(raw) => group_buf.put_slice(raw),
402        }
403        let len = u16::try_from(group_buf.len()).map_err(|_| {
404            crate::error::EncodeError::ValueOutOfRange {
405                field: "orf_group_length",
406                value: group_buf.len().to_string(),
407            }
408        })?;
409        buf.put_u8(group.orf_type.as_u8());
410        buf.put_u16(len);
411        buf.put_slice(&group_buf);
412    }
413    Ok(())
414}
415
416/// Encoded length of an ORF payload: When-to-refresh(1) + per group
417/// `ORF-Type(1) + Length(2) + entry bytes`.
418#[must_use]
419pub fn route_refresh_orf_len(payload: &OrfPayload) -> usize {
420    let mut len = 1;
421    for group in &payload.groups {
422        let entry_bytes = match &group.entries {
423            OrfEntries::AddressPrefix(entries) => {
424                entries.iter().map(address_prefix_entry_len).sum::<usize>()
425            }
426            OrfEntries::Raw(raw) | OrfEntries::Malformed(raw) => raw.len(),
427        };
428        len += 3 + entry_bytes;
429    }
430    len
431}
432
433fn address_prefix_entry_len(entry: &AddressPrefixOrf) -> usize {
434    match entry.action {
435        OrfAction::RemoveAll => 1,
436        OrfAction::Add | OrfAction::Remove => {
437            // header(1) + sequence(4) + min(1) + max(1) + prefixlen(1) + prefix
438            let prefix_bytes = entry
439                .prefix
440                .map_or(0, |p| usize::from(p.prefix_len().div_ceil(8)));
441            8 + prefix_bytes
442        }
443    }
444}
445
446fn decode_address_prefix_entries(
447    mut raw: &[u8],
448    family: Afi,
449) -> Result<Vec<AddressPrefixOrf>, DecodeError> {
450    let mut entries = Vec::new();
451    while !raw.is_empty() {
452        let header = raw[0];
453        raw = &raw[1..];
454        let match_ = if header & orf::MATCH_MASK != 0 {
455            OrfMatch::Deny
456        } else {
457            OrfMatch::Permit
458        };
459        let action = match header & orf::ACTION_MASK {
460            orf::ACTION_ADD => OrfAction::Add,
461            orf::ACTION_REMOVE => OrfAction::Remove,
462            orf::ACTION_REMOVE_ALL => OrfAction::RemoveAll,
463            // The 0x40 bit pattern is undefined — a genuine framing error.
464            _ => {
465                return Err(DecodeError::InvalidNetworkField {
466                    detail: format!("ORF entry has undefined Action in header {header:#x}"),
467                    data: vec![header],
468                });
469            }
470        };
471        if action == OrfAction::RemoveAll {
472            entries.push(AddressPrefixOrf {
473                action,
474                match_,
475                sequence: 0,
476                min_len: 0,
477                max_len: 0,
478                prefix: None,
479            });
480            continue;
481        }
482
483        // Add / Remove: sequence(4) + min(1) + max(1) + prefixlen(1) + prefix.
484        if raw.len() < 7 {
485            return Err(DecodeError::Incomplete {
486                needed: 7,
487                available: raw.len(),
488            });
489        }
490        let sequence = u32::from_be_bytes([raw[0], raw[1], raw[2], raw[3]]);
491        let min_len = raw[4];
492        let max_len = raw[5];
493        let prefix_len = raw[6];
494        raw = &raw[7..];
495
496        let prefix = decode_orf_prefix(&mut raw, family, prefix_len)?;
497        entries.push(AddressPrefixOrf {
498            action,
499            match_,
500            sequence,
501            min_len,
502            max_len,
503            prefix: Some(prefix),
504        });
505    }
506    Ok(entries)
507}
508
509fn decode_orf_prefix(raw: &mut &[u8], family: Afi, prefix_len: u8) -> Result<Prefix, DecodeError> {
510    let max_len = match family {
511        Afi::Ipv4 => 32,
512        Afi::Ipv6 => 128,
513        Afi::L2Vpn => unreachable!("Address-Prefix ORF parser is gated to IP unicast families"),
514    };
515    if prefix_len > max_len {
516        return Err(DecodeError::InvalidNetworkField {
517            detail: format!("ORF prefix length {prefix_len} exceeds {max_len}"),
518            data: vec![prefix_len],
519        });
520    }
521    let byte_count = usize::from(prefix_len.div_ceil(8));
522    if raw.len() < byte_count {
523        return Err(DecodeError::Incomplete {
524            needed: byte_count,
525            available: raw.len(),
526        });
527    }
528    let prefix = match family {
529        Afi::Ipv4 => {
530            let mut octets = [0u8; 4];
531            octets[..byte_count].copy_from_slice(&raw[..byte_count]);
532            Prefix::V4(Ipv4Prefix::new(Ipv4Addr::from(octets), prefix_len))
533        }
534        Afi::Ipv6 => {
535            let mut octets = [0u8; 16];
536            octets[..byte_count].copy_from_slice(&raw[..byte_count]);
537            Prefix::V6(Ipv6Prefix::new(Ipv6Addr::from(octets), prefix_len))
538        }
539        Afi::L2Vpn => unreachable!("Address-Prefix ORF parser is gated to IP unicast families"),
540    };
541    *raw = &raw[byte_count..];
542    Ok(prefix)
543}
544
545fn encode_address_prefix_entry(entry: &AddressPrefixOrf, buf: &mut impl BufMut) {
546    let action_bits = match entry.action {
547        OrfAction::Add => orf::ACTION_ADD,
548        OrfAction::Remove => orf::ACTION_REMOVE,
549        OrfAction::RemoveAll => orf::ACTION_REMOVE_ALL,
550    };
551    let match_bits = match entry.match_ {
552        OrfMatch::Permit => 0,
553        OrfMatch::Deny => orf::MATCH_DENY,
554    };
555    buf.put_u8(action_bits | match_bits);
556    if entry.action == OrfAction::RemoveAll {
557        return;
558    }
559    buf.put_u32(entry.sequence);
560    buf.put_u8(entry.min_len);
561    buf.put_u8(entry.max_len);
562    match entry.prefix {
563        Some(Prefix::V4(p)) => {
564            buf.put_u8(p.len);
565            let n = usize::from(p.len.div_ceil(8));
566            buf.put_slice(&p.addr.octets()[..n]);
567        }
568        Some(Prefix::V6(p)) => {
569            buf.put_u8(p.len);
570            let n = usize::from(p.len.div_ceil(8));
571            buf.put_slice(&p.addr.octets()[..n]);
572        }
573        None => buf.put_u8(0),
574    }
575}
576
577#[cfg(test)]
578mod tests {
579    use super::*;
580    use bytes::BytesMut;
581
582    fn ap(
583        action: OrfAction,
584        match_: OrfMatch,
585        sequence: u32,
586        min_len: u8,
587        max_len: u8,
588        prefix: Option<Prefix>,
589    ) -> AddressPrefixOrf {
590        AddressPrefixOrf {
591            action,
592            match_,
593            sequence,
594            min_len,
595            max_len,
596            prefix,
597        }
598    }
599
600    fn v4(addr: [u8; 4], len: u8) -> Prefix {
601        Prefix::V4(Ipv4Prefix::new(Ipv4Addr::from(addr), len))
602    }
603
604    #[test]
605    fn capability_value_roundtrip_receive() {
606        let entries = vec![
607            OrfCapEntry {
608                afi: Afi::Ipv4,
609                safi: Safi::Unicast,
610                orf_types: vec![OrfCapType {
611                    orf_type: OrfType::AddressPrefix,
612                    send_receive: OrfSendReceive::Receive,
613                }],
614            },
615            OrfCapEntry {
616                afi: Afi::Ipv6,
617                safi: Safi::Unicast,
618                orf_types: vec![
619                    OrfCapType {
620                        orf_type: OrfType::AddressPrefix,
621                        send_receive: OrfSendReceive::Both,
622                    },
623                    OrfCapType {
624                        orf_type: OrfType::AddressPrefixLegacy,
625                        send_receive: OrfSendReceive::Send,
626                    },
627                ],
628            },
629        ];
630        let mut buf = BytesMut::new();
631        encode_capability_value(&entries, &mut buf);
632        assert_eq!(buf.len(), capability_value_len(&entries));
633        let decoded = decode_capability_value(&buf).unwrap();
634        assert_eq!(decoded, entries);
635    }
636
637    #[test]
638    fn capability_value_unknown_type_preserved() {
639        // A future ORF-Type round-trips as Unknown, not a parse failure.
640        let entries = vec![OrfCapEntry {
641            afi: Afi::Ipv4,
642            safi: Safi::Unicast,
643            orf_types: vec![OrfCapType {
644                orf_type: OrfType::Unknown(200),
645                send_receive: OrfSendReceive::Receive,
646            }],
647        }];
648        let mut buf = BytesMut::new();
649        encode_capability_value(&entries, &mut buf);
650        let decoded = decode_capability_value(&buf).unwrap();
651        assert_eq!(decoded, entries);
652    }
653
654    #[test]
655    fn capability_value_unknown_afi_rejected() {
656        // AFI 99 (unknown) → None so the caller preserves Capability::Unknown.
657        let raw = [0u8, 99, 0, 1, 1, 64, 1];
658        assert!(decode_capability_value(&raw).is_none());
659    }
660
661    #[test]
662    fn capability_value_truncated_rejected() {
663        let raw = [0u8, 1, 0, 1]; // missing Number-of-ORFs
664        assert!(decode_capability_value(&raw).is_none());
665    }
666
667    #[test]
668    fn rr_orf_roundtrip_add_permit_and_deny() {
669        let payload = OrfPayload {
670            when_to_refresh: WhenToRefresh::Immediate,
671            groups: vec![OrfEntryGroup {
672                orf_type: OrfType::AddressPrefix,
673                entries: OrfEntries::AddressPrefix(vec![
674                    ap(
675                        OrfAction::Add,
676                        OrfMatch::Permit,
677                        10,
678                        24,
679                        32,
680                        Some(v4([10, 0, 0, 0], 8)),
681                    ),
682                    ap(
683                        OrfAction::Add,
684                        OrfMatch::Deny,
685                        20,
686                        0,
687                        0,
688                        Some(v4([192, 168, 0, 0], 16)),
689                    ),
690                ]),
691            }],
692        };
693        let mut buf = BytesMut::new();
694        encode_route_refresh_orf(&payload, &mut buf).unwrap();
695        assert_eq!(buf.len(), route_refresh_orf_len(&payload));
696        let mut cursor = buf.freeze();
697        let decoded =
698            decode_route_refresh_orf(&mut cursor, Some((Afi::Ipv4, Safi::Unicast))).unwrap();
699        assert_eq!(decoded, payload);
700    }
701
702    #[test]
703    fn rr_orf_roundtrip_remove_and_remove_all() {
704        let payload = OrfPayload {
705            when_to_refresh: WhenToRefresh::Defer,
706            groups: vec![OrfEntryGroup {
707                orf_type: OrfType::AddressPrefix,
708                entries: OrfEntries::AddressPrefix(vec![
709                    ap(OrfAction::RemoveAll, OrfMatch::Permit, 0, 0, 0, None),
710                    ap(
711                        OrfAction::Remove,
712                        OrfMatch::Permit,
713                        5,
714                        0,
715                        0,
716                        Some(v4([172, 16, 0, 0], 12)),
717                    ),
718                ]),
719            }],
720        };
721        let mut buf = BytesMut::new();
722        encode_route_refresh_orf(&payload, &mut buf).unwrap();
723        let mut cursor = buf.freeze();
724        let decoded =
725            decode_route_refresh_orf(&mut cursor, Some((Afi::Ipv4, Safi::Unicast))).unwrap();
726        assert_eq!(decoded, payload);
727    }
728
729    #[test]
730    fn rr_orf_roundtrip_default_route_and_host_route() {
731        let payload = OrfPayload {
732            when_to_refresh: WhenToRefresh::Immediate,
733            groups: vec![OrfEntryGroup {
734                orf_type: OrfType::AddressPrefix,
735                entries: OrfEntries::AddressPrefix(vec![
736                    ap(
737                        OrfAction::Add,
738                        OrfMatch::Permit,
739                        1,
740                        0,
741                        0,
742                        Some(v4([0, 0, 0, 0], 0)),
743                    ),
744                    ap(
745                        OrfAction::Add,
746                        OrfMatch::Permit,
747                        2,
748                        0,
749                        0,
750                        Some(v4([10, 1, 2, 3], 32)),
751                    ),
752                ]),
753            }],
754        };
755        let mut buf = BytesMut::new();
756        encode_route_refresh_orf(&payload, &mut buf).unwrap();
757        let mut cursor = buf.freeze();
758        let decoded =
759            decode_route_refresh_orf(&mut cursor, Some((Afi::Ipv4, Safi::Unicast))).unwrap();
760        assert_eq!(decoded, payload);
761    }
762
763    #[test]
764    fn rr_orf_roundtrip_ipv6_host_route() {
765        let p = Prefix::V6(Ipv6Prefix::new("2001:db8::1".parse().unwrap(), 128));
766        let payload = OrfPayload {
767            when_to_refresh: WhenToRefresh::Immediate,
768            groups: vec![OrfEntryGroup {
769                orf_type: OrfType::AddressPrefix,
770                entries: OrfEntries::AddressPrefix(vec![ap(
771                    OrfAction::Add,
772                    OrfMatch::Deny,
773                    7,
774                    64,
775                    128,
776                    Some(p),
777                )]),
778            }],
779        };
780        let mut buf = BytesMut::new();
781        encode_route_refresh_orf(&payload, &mut buf).unwrap();
782        let mut cursor = buf.freeze();
783        let decoded =
784            decode_route_refresh_orf(&mut cursor, Some((Afi::Ipv6, Safi::Unicast))).unwrap();
785        assert_eq!(decoded, payload);
786    }
787
788    #[test]
789    fn rr_orf_legacy_type_128_parsed() {
790        let payload = OrfPayload {
791            when_to_refresh: WhenToRefresh::Immediate,
792            groups: vec![OrfEntryGroup {
793                orf_type: OrfType::AddressPrefixLegacy,
794                entries: OrfEntries::AddressPrefix(vec![ap(
795                    OrfAction::Add,
796                    OrfMatch::Permit,
797                    1,
798                    0,
799                    0,
800                    Some(v4([10, 0, 0, 0], 8)),
801                )]),
802            }],
803        };
804        let mut buf = BytesMut::new();
805        encode_route_refresh_orf(&payload, &mut buf).unwrap();
806        let mut cursor = buf.freeze();
807        let decoded =
808            decode_route_refresh_orf(&mut cursor, Some((Afi::Ipv4, Safi::Unicast))).unwrap();
809        assert_eq!(decoded, payload);
810    }
811
812    #[test]
813    fn rr_orf_unknown_type_preserved_as_raw() {
814        // ORF-Type 9 (unknown): keep the entry bytes verbatim, don't error.
815        let mut buf = BytesMut::new();
816        buf.put_u8(WhenToRefresh::Immediate.as_u8());
817        buf.put_u8(9); // unknown ORF-Type
818        buf.put_u16(3);
819        buf.put_slice(&[0xAA, 0xBB, 0xCC]);
820        let mut cursor = buf.freeze();
821        let decoded =
822            decode_route_refresh_orf(&mut cursor, Some((Afi::Ipv4, Safi::Unicast))).unwrap();
823        assert_eq!(decoded.groups.len(), 1);
824        assert_eq!(decoded.groups[0].orf_type, OrfType::Unknown(9));
825        assert_eq!(
826            decoded.groups[0].entries,
827            OrfEntries::Raw(Bytes::from_static(&[0xAA, 0xBB, 0xCC]))
828        );
829    }
830
831    #[test]
832    fn rr_orf_address_prefix_without_family_preserved_as_raw() {
833        let mut buf = BytesMut::new();
834        buf.put_u8(WhenToRefresh::Immediate.as_u8());
835        buf.put_u8(OrfType::AddressPrefix.as_u8());
836        buf.put_u16(8);
837        buf.put_u8(orf::ACTION_ADD);
838        buf.put_u32(1);
839        buf.put_u8(0);
840        buf.put_u8(0);
841        buf.put_u8(40); // would be malformed for IPv4, but family is unknown.
842        let mut cursor = buf.freeze();
843        let decoded = decode_route_refresh_orf(&mut cursor, None).unwrap();
844        assert_eq!(
845            decoded.groups[0].entries,
846            OrfEntries::Raw(Bytes::from_static(&[orf::ACTION_ADD, 0, 0, 0, 1, 0, 0, 40]))
847        );
848    }
849
850    #[test]
851    fn rr_orf_address_prefix_non_ip_family_preserved_as_raw() {
852        let mut buf = BytesMut::new();
853        buf.put_u8(WhenToRefresh::Immediate.as_u8());
854        buf.put_u8(OrfType::AddressPrefix.as_u8());
855        buf.put_u16(8);
856        buf.put_u8(orf::ACTION_ADD);
857        buf.put_u32(1);
858        buf.put_u8(0);
859        buf.put_u8(0);
860        buf.put_u8(40);
861        let mut cursor = buf.freeze();
862        let decoded =
863            decode_route_refresh_orf(&mut cursor, Some((Afi::L2Vpn, Safi::Evpn))).unwrap();
864        assert_eq!(
865            decoded.groups[0].entries,
866            OrfEntries::Raw(Bytes::from_static(&[orf::ACTION_ADD, 0, 0, 0, 1, 0, 0, 40]))
867        );
868    }
869
870    #[test]
871    fn rr_orf_address_prefix_non_unicast_family_preserved_as_raw() {
872        let mut buf = BytesMut::new();
873        buf.put_u8(WhenToRefresh::Immediate.as_u8());
874        buf.put_u8(OrfType::AddressPrefix.as_u8());
875        buf.put_u16(8);
876        buf.put_u8(orf::ACTION_ADD);
877        buf.put_u32(1);
878        buf.put_u8(0);
879        buf.put_u8(0);
880        buf.put_u8(40);
881        let mut cursor = buf.freeze();
882        let decoded =
883            decode_route_refresh_orf(&mut cursor, Some((Afi::Ipv4, Safi::FlowSpec))).unwrap();
884        assert_eq!(
885            decoded.groups[0].entries,
886            OrfEntries::Raw(Bytes::from_static(&[orf::ACTION_ADD, 0, 0, 0, 1, 0, 0, 40]))
887        );
888    }
889
890    #[test]
891    fn rr_orf_prefix_len_over_family_max_is_malformed_not_error() {
892        // prefixlen 40 for IPv4 is a malformed-but-framed group: it decodes
893        // into Malformed (RFC 5291 §5.2 reset semantics), not a session error.
894        let mut buf = BytesMut::new();
895        buf.put_u8(WhenToRefresh::Immediate.as_u8());
896        buf.put_u8(OrfType::AddressPrefix.as_u8());
897        buf.put_u16(8);
898        buf.put_u8(orf::ACTION_ADD);
899        buf.put_u32(1);
900        buf.put_u8(0);
901        buf.put_u8(0);
902        buf.put_u8(40); // prefixlen > 32
903        let mut cursor = buf.freeze();
904        let decoded =
905            decode_route_refresh_orf(&mut cursor, Some((Afi::Ipv4, Safi::Unicast))).unwrap();
906        assert!(matches!(
907            decoded.groups[0].entries,
908            OrfEntries::Malformed(_)
909        ));
910    }
911
912    #[test]
913    fn rr_orf_undefined_action_is_malformed_not_error() {
914        let mut buf = BytesMut::new();
915        buf.put_u8(WhenToRefresh::Immediate.as_u8());
916        buf.put_u8(OrfType::AddressPrefix.as_u8());
917        buf.put_u16(1);
918        buf.put_u8(0x40); // undefined Action bit pattern
919        let mut cursor = buf.freeze();
920        let decoded =
921            decode_route_refresh_orf(&mut cursor, Some((Afi::Ipv4, Safi::Unicast))).unwrap();
922        assert!(matches!(
923            decoded.groups[0].entries,
924            OrfEntries::Malformed(_)
925        ));
926    }
927
928    #[test]
929    fn rr_orf_truncated_group_rejected() {
930        let mut buf = BytesMut::new();
931        buf.put_u8(WhenToRefresh::Immediate.as_u8());
932        buf.put_u8(OrfType::AddressPrefix.as_u8());
933        buf.put_u16(20); // claims 20 bytes but none follow
934        let mut cursor = buf.freeze();
935        assert!(decode_route_refresh_orf(&mut cursor, Some((Afi::Ipv4, Safi::Unicast))).is_err());
936    }
937}