bgpkit_parser/models/rpki/
rtr.rs

1//! RPKI-to-Router (RTR) Protocol Data Structures
2//!
3//! This module defines the data structures for the RTR protocol as specified in:
4//! - RTR v0: [RFC 6810](https://www.rfc-editor.org/rfc/rfc6810.txt)
5//! - RTR v1: [RFC 8210](https://www.rfc-editor.org/rfc/rfc8210.txt)
6//!
7//! The RTR protocol is used to deliver validated RPKI data from a cache server
8//! to a router. This module provides PDU definitions that can be used by
9//! downstream clients to implement RTR protocol communication.
10//!
11//! # Example
12//!
13//! ```rust
14//! use bgpkit_parser::models::rpki::rtr::*;
15//!
16//! // Create a reset query to request the full database
17//! let query = RtrResetQuery {
18//!     version: RtrProtocolVersion::V1,
19//! };
20//!
21//! // Check if a prefix PDU is an announcement or withdrawal
22//! let prefix = RtrIPv4Prefix {
23//!     version: RtrProtocolVersion::V1,
24//!     flags: 1, // announcement
25//!     prefix_length: 24,
26//!     max_length: 24,
27//!     prefix: std::net::Ipv4Addr::new(192, 0, 2, 0),
28//!     asn: 65001.into(),
29//! };
30//! assert!(prefix.is_announcement());
31//! ```
32
33use crate::models::Asn;
34use std::net::{Ipv4Addr, Ipv6Addr};
35
36// =============================================================================
37// Core Enums
38// =============================================================================
39
40/// RTR Protocol Version
41///
42/// The RTR protocol has two versions:
43/// - V0 (RFC 6810): Original protocol specification
44/// - V1 (RFC 8210): Adds Router Key PDU and timing parameters in End of Data
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47#[repr(u8)]
48pub enum RtrProtocolVersion {
49    /// RTR Protocol Version 0 (RFC 6810)
50    V0 = 0,
51    /// RTR Protocol Version 1 (RFC 8210)
52    #[default]
53    V1 = 1,
54}
55
56impl RtrProtocolVersion {
57    /// Create from a raw byte value
58    pub fn from_u8(value: u8) -> Option<Self> {
59        match value {
60            0 => Some(RtrProtocolVersion::V0),
61            1 => Some(RtrProtocolVersion::V1),
62            _ => None,
63        }
64    }
65
66    /// Convert to raw byte value
67    pub fn to_u8(self) -> u8 {
68        self as u8
69    }
70}
71
72impl From<RtrProtocolVersion> for u8 {
73    fn from(v: RtrProtocolVersion) -> Self {
74        v as u8
75    }
76}
77
78impl TryFrom<u8> for RtrProtocolVersion {
79    type Error = u8;
80
81    fn try_from(value: u8) -> Result<Self, Self::Error> {
82        RtrProtocolVersion::from_u8(value).ok_or(value)
83    }
84}
85
86/// RTR PDU Type
87///
88/// Identifies the type of RTR Protocol Data Unit.
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91#[repr(u8)]
92pub enum RtrPduType {
93    /// Serial Notify - Server notifies client of new data available
94    SerialNotify = 0,
95    /// Serial Query - Client requests incremental update
96    SerialQuery = 1,
97    /// Reset Query - Client requests full database
98    ResetQuery = 2,
99    /// Cache Response - Server begins sending data
100    CacheResponse = 3,
101    /// IPv4 Prefix - ROA for IPv4
102    IPv4Prefix = 4,
103    /// IPv6 Prefix - ROA for IPv6
104    IPv6Prefix = 6,
105    /// End of Data - Server finished sending data
106    EndOfData = 7,
107    /// Cache Reset - Server cannot provide incremental update
108    CacheReset = 8,
109    /// Router Key - BGPsec router key (v1 only)
110    RouterKey = 9,
111    /// Error Report - Error notification
112    ErrorReport = 10,
113}
114
115impl RtrPduType {
116    /// Create from a raw byte value
117    pub fn from_u8(value: u8) -> Option<Self> {
118        match value {
119            0 => Some(RtrPduType::SerialNotify),
120            1 => Some(RtrPduType::SerialQuery),
121            2 => Some(RtrPduType::ResetQuery),
122            3 => Some(RtrPduType::CacheResponse),
123            4 => Some(RtrPduType::IPv4Prefix),
124            6 => Some(RtrPduType::IPv6Prefix),
125            7 => Some(RtrPduType::EndOfData),
126            8 => Some(RtrPduType::CacheReset),
127            9 => Some(RtrPduType::RouterKey),
128            10 => Some(RtrPduType::ErrorReport),
129            _ => None,
130        }
131    }
132
133    /// Convert to raw byte value
134    pub fn to_u8(self) -> u8 {
135        self as u8
136    }
137}
138
139impl From<RtrPduType> for u8 {
140    fn from(v: RtrPduType) -> Self {
141        v as u8
142    }
143}
144
145impl TryFrom<u8> for RtrPduType {
146    type Error = u8;
147
148    fn try_from(value: u8) -> Result<Self, Self::Error> {
149        RtrPduType::from_u8(value).ok_or(value)
150    }
151}
152
153/// RTR Error Code
154///
155/// Error codes used in Error Report PDUs.
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
157#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
158#[repr(u16)]
159pub enum RtrErrorCode {
160    /// Corrupt Data - PDU could not be parsed
161    CorruptData = 0,
162    /// Internal Error - Cache experienced an internal error
163    InternalError = 1,
164    /// No Data Available - Cache has no data yet
165    NoDataAvailable = 2,
166    /// Invalid Request - Request was invalid
167    InvalidRequest = 3,
168    /// Unsupported Protocol Version - Protocol version not supported
169    UnsupportedProtocolVersion = 4,
170    /// Unsupported PDU Type - PDU type not supported
171    UnsupportedPduType = 5,
172    /// Withdrawal of Unknown Record - Tried to withdraw non-existent record
173    WithdrawalOfUnknownRecord = 6,
174    /// Duplicate Announcement Received - Same record announced twice
175    DuplicateAnnouncementReceived = 7,
176    /// Unexpected Protocol Version - Version mismatch mid-session (v1 only)
177    UnexpectedProtocolVersion = 8,
178}
179
180impl RtrErrorCode {
181    /// Create from a raw u16 value
182    pub fn from_u16(value: u16) -> Option<Self> {
183        match value {
184            0 => Some(RtrErrorCode::CorruptData),
185            1 => Some(RtrErrorCode::InternalError),
186            2 => Some(RtrErrorCode::NoDataAvailable),
187            3 => Some(RtrErrorCode::InvalidRequest),
188            4 => Some(RtrErrorCode::UnsupportedProtocolVersion),
189            5 => Some(RtrErrorCode::UnsupportedPduType),
190            6 => Some(RtrErrorCode::WithdrawalOfUnknownRecord),
191            7 => Some(RtrErrorCode::DuplicateAnnouncementReceived),
192            8 => Some(RtrErrorCode::UnexpectedProtocolVersion),
193            _ => None,
194        }
195    }
196
197    /// Convert to raw u16 value
198    pub fn to_u16(self) -> u16 {
199        self as u16
200    }
201}
202
203impl From<RtrErrorCode> for u16 {
204    fn from(v: RtrErrorCode) -> Self {
205        v as u16
206    }
207}
208
209impl TryFrom<u16> for RtrErrorCode {
210    type Error = u16;
211
212    fn try_from(value: u16) -> Result<Self, Self::Error> {
213        RtrErrorCode::from_u16(value).ok_or(value)
214    }
215}
216
217// =============================================================================
218// PDU Structs
219// =============================================================================
220
221/// Serial Notify PDU (Type 0)
222///
223/// Sent by server to notify client that new data is available.
224/// This is a hint that the client should send a Serial Query.
225///
226/// Direction: Server → Client
227#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229pub struct RtrSerialNotify {
230    /// Protocol version
231    pub version: RtrProtocolVersion,
232    /// Session identifier
233    pub session_id: u16,
234    /// Current serial number
235    pub serial_number: u32,
236}
237
238/// Serial Query PDU (Type 1)
239///
240/// Sent by client to request incremental update from a known serial number.
241///
242/// Direction: Client → Server
243#[derive(Debug, Clone, PartialEq, Eq, Hash)]
244#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
245pub struct RtrSerialQuery {
246    /// Protocol version
247    pub version: RtrProtocolVersion,
248    /// Session identifier from previous session
249    pub session_id: u16,
250    /// Last known serial number
251    pub serial_number: u32,
252}
253
254impl RtrSerialQuery {
255    /// Create a new Serial Query PDU
256    pub fn new(version: RtrProtocolVersion, session_id: u16, serial_number: u32) -> Self {
257        Self {
258            version,
259            session_id,
260            serial_number,
261        }
262    }
263}
264
265/// Reset Query PDU (Type 2)
266///
267/// Sent by client to request the full database from the server.
268///
269/// Direction: Client → Server
270#[derive(Debug, Clone, PartialEq, Eq, Hash)]
271#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
272pub struct RtrResetQuery {
273    /// Protocol version
274    pub version: RtrProtocolVersion,
275}
276
277impl RtrResetQuery {
278    /// Create a new Reset Query PDU with the specified version
279    pub fn new(version: RtrProtocolVersion) -> Self {
280        Self { version }
281    }
282
283    /// Create a new Reset Query PDU with version 1
284    pub fn new_v1() -> Self {
285        Self::new(RtrProtocolVersion::V1)
286    }
287
288    /// Create a new Reset Query PDU with version 0
289    pub fn new_v0() -> Self {
290        Self::new(RtrProtocolVersion::V0)
291    }
292}
293
294impl Default for RtrResetQuery {
295    fn default() -> Self {
296        Self::new_v1()
297    }
298}
299
300/// Cache Response PDU (Type 3)
301///
302/// Sent by server to indicate the start of a data transfer.
303/// Followed by zero or more IPv4/IPv6 Prefix and Router Key PDUs,
304/// and terminated by an End of Data PDU.
305///
306/// Direction: Server → Client
307#[derive(Debug, Clone, PartialEq, Eq, Hash)]
308#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
309pub struct RtrCacheResponse {
310    /// Protocol version
311    pub version: RtrProtocolVersion,
312    /// Session identifier
313    pub session_id: u16,
314}
315
316/// IPv4 Prefix PDU (Type 4)
317///
318/// Contains a single ROA for an IPv4 prefix.
319///
320/// Direction: Server → Client
321#[derive(Debug, Clone, PartialEq, Eq, Hash)]
322#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
323pub struct RtrIPv4Prefix {
324    /// Protocol version
325    pub version: RtrProtocolVersion,
326    /// Flags (bit 0: 1=announcement, 0=withdrawal)
327    pub flags: u8,
328    /// Prefix length in bits
329    pub prefix_length: u8,
330    /// Maximum prefix length for this ROA
331    pub max_length: u8,
332    /// IPv4 prefix
333    pub prefix: Ipv4Addr,
334    /// Origin AS number
335    pub asn: Asn,
336}
337
338impl RtrIPv4Prefix {
339    /// Check if this is an announcement (not a withdrawal)
340    #[inline]
341    pub fn is_announcement(&self) -> bool {
342        self.flags & 0x01 != 0
343    }
344
345    /// Check if this is a withdrawal
346    #[inline]
347    pub fn is_withdrawal(&self) -> bool {
348        !self.is_announcement()
349    }
350}
351
352/// IPv6 Prefix PDU (Type 6)
353///
354/// Contains a single ROA for an IPv6 prefix.
355///
356/// Direction: Server → Client
357#[derive(Debug, Clone, PartialEq, Eq, Hash)]
358#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
359pub struct RtrIPv6Prefix {
360    /// Protocol version
361    pub version: RtrProtocolVersion,
362    /// Flags (bit 0: 1=announcement, 0=withdrawal)
363    pub flags: u8,
364    /// Prefix length in bits
365    pub prefix_length: u8,
366    /// Maximum prefix length for this ROA
367    pub max_length: u8,
368    /// IPv6 prefix
369    pub prefix: Ipv6Addr,
370    /// Origin AS number
371    pub asn: Asn,
372}
373
374impl RtrIPv6Prefix {
375    /// Check if this is an announcement (not a withdrawal)
376    #[inline]
377    pub fn is_announcement(&self) -> bool {
378        self.flags & 0x01 != 0
379    }
380
381    /// Check if this is a withdrawal
382    #[inline]
383    pub fn is_withdrawal(&self) -> bool {
384        !self.is_announcement()
385    }
386}
387
388/// End of Data PDU (Type 7)
389///
390/// Sent by server to indicate the end of a data transfer.
391///
392/// Direction: Server → Client
393///
394/// Note: In v1, this PDU includes timing parameters. In v0, these are absent.
395#[derive(Debug, Clone, PartialEq, Eq, Hash)]
396#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
397pub struct RtrEndOfData {
398    /// Protocol version
399    pub version: RtrProtocolVersion,
400    /// Session identifier
401    pub session_id: u16,
402    /// Current serial number
403    pub serial_number: u32,
404    /// Refresh interval in seconds (v1 only)
405    pub refresh_interval: Option<u32>,
406    /// Retry interval in seconds (v1 only)
407    pub retry_interval: Option<u32>,
408    /// Expire interval in seconds (v1 only)
409    pub expire_interval: Option<u32>,
410}
411
412impl RtrEndOfData {
413    /// Default refresh interval (1 hour) as recommended by RFC 8210
414    pub const DEFAULT_REFRESH: u32 = 3600;
415    /// Default retry interval (10 minutes) as recommended by RFC 8210
416    pub const DEFAULT_RETRY: u32 = 600;
417    /// Default expire interval (2 hours) as recommended by RFC 8210
418    pub const DEFAULT_EXPIRE: u32 = 7200;
419
420    /// Get the refresh interval, using the default if not specified
421    pub fn refresh_interval_or_default(&self) -> u32 {
422        self.refresh_interval.unwrap_or(Self::DEFAULT_REFRESH)
423    }
424
425    /// Get the retry interval, using the default if not specified
426    pub fn retry_interval_or_default(&self) -> u32 {
427        self.retry_interval.unwrap_or(Self::DEFAULT_RETRY)
428    }
429
430    /// Get the expire interval, using the default if not specified
431    pub fn expire_interval_or_default(&self) -> u32 {
432        self.expire_interval.unwrap_or(Self::DEFAULT_EXPIRE)
433    }
434}
435
436/// Cache Reset PDU (Type 8)
437///
438/// Sent by server in response to a Serial Query when the server
439/// cannot provide an incremental update (e.g., serial is too old).
440/// The client should send a Reset Query.
441///
442/// Direction: Server → Client
443#[derive(Debug, Clone, PartialEq, Eq, Hash)]
444#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
445pub struct RtrCacheReset {
446    /// Protocol version
447    pub version: RtrProtocolVersion,
448}
449
450/// Router Key PDU (Type 9, v1 only)
451///
452/// Contains a BGPsec router key.
453///
454/// Direction: Server → Client
455#[derive(Debug, Clone, PartialEq, Eq, Hash)]
456#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
457pub struct RtrRouterKey {
458    /// Protocol version (always V1)
459    pub version: RtrProtocolVersion,
460    /// Flags (bit 0: 1=announcement, 0=withdrawal)
461    pub flags: u8,
462    /// Subject Key Identifier (SKI) - 20 bytes
463    pub subject_key_identifier: [u8; 20],
464    /// AS number
465    pub asn: Asn,
466    /// Subject Public Key Info (SPKI) - variable length
467    pub subject_public_key_info: Vec<u8>,
468}
469
470impl RtrRouterKey {
471    /// Check if this is an announcement (not a withdrawal)
472    #[inline]
473    pub fn is_announcement(&self) -> bool {
474        self.flags & 0x01 != 0
475    }
476
477    /// Check if this is a withdrawal
478    #[inline]
479    pub fn is_withdrawal(&self) -> bool {
480        !self.is_announcement()
481    }
482}
483
484/// Error Report PDU (Type 10)
485///
486/// Sent by either client or server to report an error.
487///
488/// Direction: Bidirectional (Client ↔ Server)
489#[derive(Debug, Clone, PartialEq, Eq, Hash)]
490#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
491pub struct RtrErrorReport {
492    /// Protocol version
493    pub version: RtrProtocolVersion,
494    /// Error code
495    pub error_code: RtrErrorCode,
496    /// The erroneous PDU that caused the error (may be empty)
497    pub erroneous_pdu: Vec<u8>,
498    /// Human-readable error text (UTF-8)
499    pub error_text: String,
500}
501
502impl RtrErrorReport {
503    /// Create a new Error Report PDU
504    pub fn new(
505        version: RtrProtocolVersion,
506        error_code: RtrErrorCode,
507        erroneous_pdu: Vec<u8>,
508        error_text: String,
509    ) -> Self {
510        Self {
511            version,
512            error_code,
513            erroneous_pdu,
514            error_text,
515        }
516    }
517
518    /// Create an error report for unsupported protocol version
519    pub fn unsupported_version(version: RtrProtocolVersion, erroneous_pdu: Vec<u8>) -> Self {
520        Self::new(
521            version,
522            RtrErrorCode::UnsupportedProtocolVersion,
523            erroneous_pdu,
524            "Unsupported protocol version".to_string(),
525        )
526    }
527
528    /// Create an error report for unsupported PDU type
529    pub fn unsupported_pdu_type(version: RtrProtocolVersion, erroneous_pdu: Vec<u8>) -> Self {
530        Self::new(
531            version,
532            RtrErrorCode::UnsupportedPduType,
533            erroneous_pdu,
534            "Unsupported PDU type".to_string(),
535        )
536    }
537
538    /// Create an error report for corrupt data
539    pub fn corrupt_data(
540        version: RtrProtocolVersion,
541        erroneous_pdu: Vec<u8>,
542        message: &str,
543    ) -> Self {
544        Self::new(
545            version,
546            RtrErrorCode::CorruptData,
547            erroneous_pdu,
548            message.to_string(),
549        )
550    }
551}
552
553// =============================================================================
554// Unified PDU Enum
555// =============================================================================
556
557/// Unified RTR PDU Enum
558///
559/// This enum represents any RTR Protocol Data Unit and is useful for
560/// generic PDU handling when reading from a stream.
561#[derive(Debug, Clone, PartialEq, Eq)]
562#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
563pub enum RtrPdu {
564    /// Serial Notify (Type 0)
565    SerialNotify(RtrSerialNotify),
566    /// Serial Query (Type 1)
567    SerialQuery(RtrSerialQuery),
568    /// Reset Query (Type 2)
569    ResetQuery(RtrResetQuery),
570    /// Cache Response (Type 3)
571    CacheResponse(RtrCacheResponse),
572    /// IPv4 Prefix (Type 4)
573    IPv4Prefix(RtrIPv4Prefix),
574    /// IPv6 Prefix (Type 6)
575    IPv6Prefix(RtrIPv6Prefix),
576    /// End of Data (Type 7)
577    EndOfData(RtrEndOfData),
578    /// Cache Reset (Type 8)
579    CacheReset(RtrCacheReset),
580    /// Router Key (Type 9, v1 only)
581    RouterKey(RtrRouterKey),
582    /// Error Report (Type 10)
583    ErrorReport(RtrErrorReport),
584}
585
586impl RtrPdu {
587    /// Get the PDU type
588    pub fn pdu_type(&self) -> RtrPduType {
589        match self {
590            RtrPdu::SerialNotify(_) => RtrPduType::SerialNotify,
591            RtrPdu::SerialQuery(_) => RtrPduType::SerialQuery,
592            RtrPdu::ResetQuery(_) => RtrPduType::ResetQuery,
593            RtrPdu::CacheResponse(_) => RtrPduType::CacheResponse,
594            RtrPdu::IPv4Prefix(_) => RtrPduType::IPv4Prefix,
595            RtrPdu::IPv6Prefix(_) => RtrPduType::IPv6Prefix,
596            RtrPdu::EndOfData(_) => RtrPduType::EndOfData,
597            RtrPdu::CacheReset(_) => RtrPduType::CacheReset,
598            RtrPdu::RouterKey(_) => RtrPduType::RouterKey,
599            RtrPdu::ErrorReport(_) => RtrPduType::ErrorReport,
600        }
601    }
602
603    /// Get the protocol version
604    pub fn version(&self) -> RtrProtocolVersion {
605        match self {
606            RtrPdu::SerialNotify(p) => p.version,
607            RtrPdu::SerialQuery(p) => p.version,
608            RtrPdu::ResetQuery(p) => p.version,
609            RtrPdu::CacheResponse(p) => p.version,
610            RtrPdu::IPv4Prefix(p) => p.version,
611            RtrPdu::IPv6Prefix(p) => p.version,
612            RtrPdu::EndOfData(p) => p.version,
613            RtrPdu::CacheReset(p) => p.version,
614            RtrPdu::RouterKey(p) => p.version,
615            RtrPdu::ErrorReport(p) => p.version,
616        }
617    }
618}
619
620impl From<RtrSerialNotify> for RtrPdu {
621    fn from(pdu: RtrSerialNotify) -> Self {
622        RtrPdu::SerialNotify(pdu)
623    }
624}
625
626impl From<RtrSerialQuery> for RtrPdu {
627    fn from(pdu: RtrSerialQuery) -> Self {
628        RtrPdu::SerialQuery(pdu)
629    }
630}
631
632impl From<RtrResetQuery> for RtrPdu {
633    fn from(pdu: RtrResetQuery) -> Self {
634        RtrPdu::ResetQuery(pdu)
635    }
636}
637
638impl From<RtrCacheResponse> for RtrPdu {
639    fn from(pdu: RtrCacheResponse) -> Self {
640        RtrPdu::CacheResponse(pdu)
641    }
642}
643
644impl From<RtrIPv4Prefix> for RtrPdu {
645    fn from(pdu: RtrIPv4Prefix) -> Self {
646        RtrPdu::IPv4Prefix(pdu)
647    }
648}
649
650impl From<RtrIPv6Prefix> for RtrPdu {
651    fn from(pdu: RtrIPv6Prefix) -> Self {
652        RtrPdu::IPv6Prefix(pdu)
653    }
654}
655
656impl From<RtrEndOfData> for RtrPdu {
657    fn from(pdu: RtrEndOfData) -> Self {
658        RtrPdu::EndOfData(pdu)
659    }
660}
661
662impl From<RtrCacheReset> for RtrPdu {
663    fn from(pdu: RtrCacheReset) -> Self {
664        RtrPdu::CacheReset(pdu)
665    }
666}
667
668impl From<RtrRouterKey> for RtrPdu {
669    fn from(pdu: RtrRouterKey) -> Self {
670        RtrPdu::RouterKey(pdu)
671    }
672}
673
674impl From<RtrErrorReport> for RtrPdu {
675    fn from(pdu: RtrErrorReport) -> Self {
676        RtrPdu::ErrorReport(pdu)
677    }
678}
679
680// =============================================================================
681// Tests
682// =============================================================================
683
684#[cfg(test)]
685mod tests {
686    use super::*;
687
688    #[test]
689    fn test_protocol_version_conversion() {
690        assert_eq!(RtrProtocolVersion::from_u8(0), Some(RtrProtocolVersion::V0));
691        assert_eq!(RtrProtocolVersion::from_u8(1), Some(RtrProtocolVersion::V1));
692        assert_eq!(RtrProtocolVersion::from_u8(2), None);
693
694        assert_eq!(RtrProtocolVersion::V0.to_u8(), 0);
695        assert_eq!(RtrProtocolVersion::V1.to_u8(), 1);
696    }
697
698    #[test]
699    fn test_pdu_type_conversion() {
700        assert_eq!(RtrPduType::from_u8(0), Some(RtrPduType::SerialNotify));
701        assert_eq!(RtrPduType::from_u8(1), Some(RtrPduType::SerialQuery));
702        assert_eq!(RtrPduType::from_u8(4), Some(RtrPduType::IPv4Prefix));
703        assert_eq!(RtrPduType::from_u8(5), None); // No type 5
704        assert_eq!(RtrPduType::from_u8(6), Some(RtrPduType::IPv6Prefix));
705        assert_eq!(RtrPduType::from_u8(9), Some(RtrPduType::RouterKey));
706
707        assert_eq!(RtrPduType::SerialNotify.to_u8(), 0);
708        assert_eq!(RtrPduType::IPv6Prefix.to_u8(), 6);
709    }
710
711    #[test]
712    fn test_error_code_conversion() {
713        assert_eq!(RtrErrorCode::from_u16(0), Some(RtrErrorCode::CorruptData));
714        assert_eq!(
715            RtrErrorCode::from_u16(4),
716            Some(RtrErrorCode::UnsupportedProtocolVersion)
717        );
718        assert_eq!(
719            RtrErrorCode::from_u16(8),
720            Some(RtrErrorCode::UnexpectedProtocolVersion)
721        );
722        assert_eq!(RtrErrorCode::from_u16(9), None);
723
724        assert_eq!(RtrErrorCode::CorruptData.to_u16(), 0);
725        assert_eq!(RtrErrorCode::UnsupportedProtocolVersion.to_u16(), 4);
726    }
727
728    #[test]
729    fn test_ipv4_prefix_flags() {
730        let announcement = RtrIPv4Prefix {
731            version: RtrProtocolVersion::V1,
732            flags: 1,
733            prefix_length: 24,
734            max_length: 24,
735            prefix: Ipv4Addr::new(192, 0, 2, 0),
736            asn: 65001.into(),
737        };
738        assert!(announcement.is_announcement());
739        assert!(!announcement.is_withdrawal());
740
741        let withdrawal = RtrIPv4Prefix {
742            version: RtrProtocolVersion::V1,
743            flags: 0,
744            prefix_length: 24,
745            max_length: 24,
746            prefix: Ipv4Addr::new(192, 0, 2, 0),
747            asn: 65001.into(),
748        };
749        assert!(!withdrawal.is_announcement());
750        assert!(withdrawal.is_withdrawal());
751    }
752
753    #[test]
754    fn test_ipv6_prefix_flags() {
755        let announcement = RtrIPv6Prefix {
756            version: RtrProtocolVersion::V1,
757            flags: 1,
758            prefix_length: 48,
759            max_length: 48,
760            prefix: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
761            asn: 65001.into(),
762        };
763        assert!(announcement.is_announcement());
764        assert!(!announcement.is_withdrawal());
765    }
766
767    #[test]
768    fn test_router_key_flags() {
769        let announcement = RtrRouterKey {
770            version: RtrProtocolVersion::V1,
771            flags: 1,
772            subject_key_identifier: [0; 20],
773            asn: 65001.into(),
774            subject_public_key_info: vec![1, 2, 3],
775        };
776        assert!(announcement.is_announcement());
777        assert!(!announcement.is_withdrawal());
778    }
779
780    #[test]
781    fn test_end_of_data_defaults() {
782        assert_eq!(RtrEndOfData::DEFAULT_REFRESH, 3600);
783        assert_eq!(RtrEndOfData::DEFAULT_RETRY, 600);
784        assert_eq!(RtrEndOfData::DEFAULT_EXPIRE, 7200);
785
786        let eod = RtrEndOfData {
787            version: RtrProtocolVersion::V0,
788            session_id: 1,
789            serial_number: 100,
790            refresh_interval: None,
791            retry_interval: None,
792            expire_interval: None,
793        };
794        assert_eq!(eod.refresh_interval_or_default(), 3600);
795        assert_eq!(eod.retry_interval_or_default(), 600);
796        assert_eq!(eod.expire_interval_or_default(), 7200);
797
798        let eod_v1 = RtrEndOfData {
799            version: RtrProtocolVersion::V1,
800            session_id: 1,
801            serial_number: 100,
802            refresh_interval: Some(1800),
803            retry_interval: Some(300),
804            expire_interval: Some(3600),
805        };
806        assert_eq!(eod_v1.refresh_interval_or_default(), 1800);
807        assert_eq!(eod_v1.retry_interval_or_default(), 300);
808        assert_eq!(eod_v1.expire_interval_or_default(), 3600);
809    }
810
811    #[test]
812    fn test_reset_query_constructors() {
813        let v1 = RtrResetQuery::new_v1();
814        assert_eq!(v1.version, RtrProtocolVersion::V1);
815
816        let v0 = RtrResetQuery::new_v0();
817        assert_eq!(v0.version, RtrProtocolVersion::V0);
818
819        let default = RtrResetQuery::default();
820        assert_eq!(default.version, RtrProtocolVersion::V1);
821    }
822
823    #[test]
824    fn test_pdu_enum_type() {
825        let pdu = RtrPdu::SerialNotify(RtrSerialNotify {
826            version: RtrProtocolVersion::V1,
827            session_id: 1,
828            serial_number: 100,
829        });
830        assert_eq!(pdu.pdu_type(), RtrPduType::SerialNotify);
831        assert_eq!(pdu.version(), RtrProtocolVersion::V1);
832    }
833
834    #[test]
835    fn test_pdu_enum_all_types_and_versions() {
836        // Test pdu_type() and version() for all PDU variants
837        let pdus = vec![
838            (
839                RtrPdu::SerialNotify(RtrSerialNotify {
840                    version: RtrProtocolVersion::V0,
841                    session_id: 1,
842                    serial_number: 100,
843                }),
844                RtrPduType::SerialNotify,
845                RtrProtocolVersion::V0,
846            ),
847            (
848                RtrPdu::SerialQuery(RtrSerialQuery {
849                    version: RtrProtocolVersion::V1,
850                    session_id: 2,
851                    serial_number: 200,
852                }),
853                RtrPduType::SerialQuery,
854                RtrProtocolVersion::V1,
855            ),
856            (
857                RtrPdu::ResetQuery(RtrResetQuery {
858                    version: RtrProtocolVersion::V0,
859                }),
860                RtrPduType::ResetQuery,
861                RtrProtocolVersion::V0,
862            ),
863            (
864                RtrPdu::CacheResponse(RtrCacheResponse {
865                    version: RtrProtocolVersion::V1,
866                    session_id: 3,
867                }),
868                RtrPduType::CacheResponse,
869                RtrProtocolVersion::V1,
870            ),
871            (
872                RtrPdu::IPv4Prefix(RtrIPv4Prefix {
873                    version: RtrProtocolVersion::V0,
874                    flags: 1,
875                    prefix_length: 24,
876                    max_length: 24,
877                    prefix: Ipv4Addr::new(10, 0, 0, 0),
878                    asn: 65000.into(),
879                }),
880                RtrPduType::IPv4Prefix,
881                RtrProtocolVersion::V0,
882            ),
883            (
884                RtrPdu::IPv6Prefix(RtrIPv6Prefix {
885                    version: RtrProtocolVersion::V1,
886                    flags: 0,
887                    prefix_length: 48,
888                    max_length: 64,
889                    prefix: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
890                    asn: 65001.into(),
891                }),
892                RtrPduType::IPv6Prefix,
893                RtrProtocolVersion::V1,
894            ),
895            (
896                RtrPdu::EndOfData(RtrEndOfData {
897                    version: RtrProtocolVersion::V0,
898                    session_id: 4,
899                    serial_number: 300,
900                    refresh_interval: None,
901                    retry_interval: None,
902                    expire_interval: None,
903                }),
904                RtrPduType::EndOfData,
905                RtrProtocolVersion::V0,
906            ),
907            (
908                RtrPdu::CacheReset(RtrCacheReset {
909                    version: RtrProtocolVersion::V1,
910                }),
911                RtrPduType::CacheReset,
912                RtrProtocolVersion::V1,
913            ),
914            (
915                RtrPdu::RouterKey(RtrRouterKey {
916                    version: RtrProtocolVersion::V1,
917                    flags: 1,
918                    subject_key_identifier: [0; 20],
919                    asn: 65002.into(),
920                    subject_public_key_info: vec![],
921                }),
922                RtrPduType::RouterKey,
923                RtrProtocolVersion::V1,
924            ),
925            (
926                RtrPdu::ErrorReport(RtrErrorReport {
927                    version: RtrProtocolVersion::V0,
928                    error_code: RtrErrorCode::InternalError,
929                    erroneous_pdu: vec![],
930                    error_text: String::new(),
931                }),
932                RtrPduType::ErrorReport,
933                RtrProtocolVersion::V0,
934            ),
935        ];
936
937        for (pdu, expected_type, expected_version) in pdus {
938            assert_eq!(pdu.pdu_type(), expected_type);
939            assert_eq!(pdu.version(), expected_version);
940        }
941    }
942
943    #[test]
944    fn test_pdu_from_impls() {
945        let query = RtrResetQuery::new_v1();
946        let pdu: RtrPdu = query.into();
947        assert_eq!(pdu.pdu_type(), RtrPduType::ResetQuery);
948    }
949
950    #[test]
951    fn test_all_pdu_from_impls() {
952        // Test From impl for all PDU types
953        let notify = RtrSerialNotify {
954            version: RtrProtocolVersion::V1,
955            session_id: 1,
956            serial_number: 100,
957        };
958        let pdu: RtrPdu = notify.into();
959        assert!(matches!(pdu, RtrPdu::SerialNotify(_)));
960
961        let query = RtrSerialQuery::new(RtrProtocolVersion::V1, 1, 100);
962        let pdu: RtrPdu = query.into();
963        assert!(matches!(pdu, RtrPdu::SerialQuery(_)));
964
965        let response = RtrCacheResponse {
966            version: RtrProtocolVersion::V1,
967            session_id: 1,
968        };
969        let pdu: RtrPdu = response.into();
970        assert!(matches!(pdu, RtrPdu::CacheResponse(_)));
971
972        let prefix4 = RtrIPv4Prefix {
973            version: RtrProtocolVersion::V1,
974            flags: 1,
975            prefix_length: 24,
976            max_length: 24,
977            prefix: Ipv4Addr::new(10, 0, 0, 0),
978            asn: 65000.into(),
979        };
980        let pdu: RtrPdu = prefix4.into();
981        assert!(matches!(pdu, RtrPdu::IPv4Prefix(_)));
982
983        let prefix6 = RtrIPv6Prefix {
984            version: RtrProtocolVersion::V1,
985            flags: 1,
986            prefix_length: 48,
987            max_length: 48,
988            prefix: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
989            asn: 65000.into(),
990        };
991        let pdu: RtrPdu = prefix6.into();
992        assert!(matches!(pdu, RtrPdu::IPv6Prefix(_)));
993
994        let eod = RtrEndOfData {
995            version: RtrProtocolVersion::V1,
996            session_id: 1,
997            serial_number: 100,
998            refresh_interval: Some(3600),
999            retry_interval: Some(600),
1000            expire_interval: Some(7200),
1001        };
1002        let pdu: RtrPdu = eod.into();
1003        assert!(matches!(pdu, RtrPdu::EndOfData(_)));
1004
1005        let reset = RtrCacheReset {
1006            version: RtrProtocolVersion::V1,
1007        };
1008        let pdu: RtrPdu = reset.into();
1009        assert!(matches!(pdu, RtrPdu::CacheReset(_)));
1010
1011        let key = RtrRouterKey {
1012            version: RtrProtocolVersion::V1,
1013            flags: 1,
1014            subject_key_identifier: [0; 20],
1015            asn: 65000.into(),
1016            subject_public_key_info: vec![],
1017        };
1018        let pdu: RtrPdu = key.into();
1019        assert!(matches!(pdu, RtrPdu::RouterKey(_)));
1020
1021        let error = RtrErrorReport::new(
1022            RtrProtocolVersion::V1,
1023            RtrErrorCode::InternalError,
1024            vec![],
1025            String::new(),
1026        );
1027        let pdu: RtrPdu = error.into();
1028        assert!(matches!(pdu, RtrPdu::ErrorReport(_)));
1029    }
1030
1031    #[test]
1032    fn test_error_report_constructors() {
1033        let err = RtrErrorReport::unsupported_version(RtrProtocolVersion::V0, vec![1, 2, 3]);
1034        assert_eq!(err.error_code, RtrErrorCode::UnsupportedProtocolVersion);
1035        assert_eq!(err.error_text, "Unsupported protocol version");
1036
1037        let err = RtrErrorReport::unsupported_pdu_type(RtrProtocolVersion::V1, vec![4, 5, 6]);
1038        assert_eq!(err.error_code, RtrErrorCode::UnsupportedPduType);
1039        assert_eq!(err.error_text, "Unsupported PDU type");
1040
1041        let err = RtrErrorReport::corrupt_data(RtrProtocolVersion::V1, vec![], "test error");
1042        assert_eq!(err.error_code, RtrErrorCode::CorruptData);
1043        assert_eq!(err.error_text, "test error");
1044
1045        let err = RtrErrorReport::new(
1046            RtrProtocolVersion::V0,
1047            RtrErrorCode::NoDataAvailable,
1048            vec![7, 8, 9],
1049            "Custom error".to_string(),
1050        );
1051        assert_eq!(err.version, RtrProtocolVersion::V0);
1052        assert_eq!(err.error_code, RtrErrorCode::NoDataAvailable);
1053        assert_eq!(err.erroneous_pdu, vec![7, 8, 9]);
1054        assert_eq!(err.error_text, "Custom error");
1055    }
1056
1057    #[test]
1058    #[cfg(feature = "serde")]
1059    fn test_serde_roundtrip() {
1060        let prefix = RtrIPv4Prefix {
1061            version: RtrProtocolVersion::V1,
1062            flags: 1,
1063            prefix_length: 24,
1064            max_length: 24,
1065            prefix: Ipv4Addr::new(192, 0, 2, 0),
1066            asn: 65001.into(),
1067        };
1068
1069        let json = serde_json::to_string(&prefix).unwrap();
1070        let decoded: RtrIPv4Prefix = serde_json::from_str(&json).unwrap();
1071        assert_eq!(prefix, decoded);
1072    }
1073
1074    #[test]
1075    fn test_try_from_protocol_version() {
1076        // Test TryFrom<u8> for RtrProtocolVersion
1077        let v0: Result<RtrProtocolVersion, u8> = 0u8.try_into();
1078        assert_eq!(v0, Ok(RtrProtocolVersion::V0));
1079
1080        let v1: Result<RtrProtocolVersion, u8> = 1u8.try_into();
1081        assert_eq!(v1, Ok(RtrProtocolVersion::V1));
1082
1083        let invalid: Result<RtrProtocolVersion, u8> = 99u8.try_into();
1084        assert_eq!(invalid, Err(99u8));
1085
1086        // Test From<RtrProtocolVersion> for u8
1087        let byte: u8 = RtrProtocolVersion::V0.into();
1088        assert_eq!(byte, 0);
1089        let byte: u8 = RtrProtocolVersion::V1.into();
1090        assert_eq!(byte, 1);
1091    }
1092
1093    #[test]
1094    fn test_try_from_pdu_type() {
1095        // Test TryFrom<u8> for RtrPduType
1096        let serial_notify: Result<RtrPduType, u8> = 0u8.try_into();
1097        assert_eq!(serial_notify, Ok(RtrPduType::SerialNotify));
1098
1099        let error_report: Result<RtrPduType, u8> = 10u8.try_into();
1100        assert_eq!(error_report, Ok(RtrPduType::ErrorReport));
1101
1102        let invalid: Result<RtrPduType, u8> = 5u8.try_into(); // Type 5 doesn't exist
1103        assert_eq!(invalid, Err(5u8));
1104
1105        let invalid: Result<RtrPduType, u8> = 255u8.try_into();
1106        assert_eq!(invalid, Err(255u8));
1107
1108        // Test From<RtrPduType> for u8
1109        let byte: u8 = RtrPduType::SerialNotify.into();
1110        assert_eq!(byte, 0);
1111        let byte: u8 = RtrPduType::IPv6Prefix.into();
1112        assert_eq!(byte, 6);
1113        let byte: u8 = RtrPduType::ErrorReport.into();
1114        assert_eq!(byte, 10);
1115    }
1116
1117    #[test]
1118    fn test_try_from_error_code() {
1119        // Test TryFrom<u16> for RtrErrorCode
1120        let corrupt: Result<RtrErrorCode, u16> = 0u16.try_into();
1121        assert_eq!(corrupt, Ok(RtrErrorCode::CorruptData));
1122
1123        let unexpected: Result<RtrErrorCode, u16> = 8u16.try_into();
1124        assert_eq!(unexpected, Ok(RtrErrorCode::UnexpectedProtocolVersion));
1125
1126        let invalid: Result<RtrErrorCode, u16> = 9u16.try_into();
1127        assert_eq!(invalid, Err(9u16));
1128
1129        let invalid: Result<RtrErrorCode, u16> = 1000u16.try_into();
1130        assert_eq!(invalid, Err(1000u16));
1131
1132        // Test From<RtrErrorCode> for u16
1133        let code: u16 = RtrErrorCode::CorruptData.into();
1134        assert_eq!(code, 0);
1135        let code: u16 = RtrErrorCode::UnexpectedProtocolVersion.into();
1136        assert_eq!(code, 8);
1137    }
1138
1139    #[test]
1140    fn test_ipv6_prefix_withdrawal() {
1141        let withdrawal = RtrIPv6Prefix {
1142            version: RtrProtocolVersion::V1,
1143            flags: 0, // withdrawal
1144            prefix_length: 48,
1145            max_length: 64,
1146            prefix: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
1147            asn: 65001.into(),
1148        };
1149        assert!(!withdrawal.is_announcement());
1150        assert!(withdrawal.is_withdrawal());
1151    }
1152
1153    #[test]
1154    fn test_router_key_withdrawal() {
1155        let withdrawal = RtrRouterKey {
1156            version: RtrProtocolVersion::V1,
1157            flags: 0, // withdrawal
1158            subject_key_identifier: [1; 20],
1159            asn: 65001.into(),
1160            subject_public_key_info: vec![0xAB, 0xCD],
1161        };
1162        assert!(!withdrawal.is_announcement());
1163        assert!(withdrawal.is_withdrawal());
1164    }
1165
1166    #[test]
1167    fn test_serial_query_new() {
1168        let query = RtrSerialQuery::new(RtrProtocolVersion::V0, 12345, 67890);
1169        assert_eq!(query.version, RtrProtocolVersion::V0);
1170        assert_eq!(query.session_id, 12345);
1171        assert_eq!(query.serial_number, 67890);
1172    }
1173
1174    #[test]
1175    fn test_reset_query_new() {
1176        let v0 = RtrResetQuery::new(RtrProtocolVersion::V0);
1177        assert_eq!(v0.version, RtrProtocolVersion::V0);
1178
1179        let v1 = RtrResetQuery::new(RtrProtocolVersion::V1);
1180        assert_eq!(v1.version, RtrProtocolVersion::V1);
1181    }
1182
1183    #[test]
1184    fn test_protocol_version_default() {
1185        let default = RtrProtocolVersion::default();
1186        assert_eq!(default, RtrProtocolVersion::V1);
1187    }
1188
1189    #[test]
1190    fn test_all_error_codes() {
1191        // Test all error code conversions
1192        let codes = [
1193            (0u16, RtrErrorCode::CorruptData),
1194            (1u16, RtrErrorCode::InternalError),
1195            (2u16, RtrErrorCode::NoDataAvailable),
1196            (3u16, RtrErrorCode::InvalidRequest),
1197            (4u16, RtrErrorCode::UnsupportedProtocolVersion),
1198            (5u16, RtrErrorCode::UnsupportedPduType),
1199            (6u16, RtrErrorCode::WithdrawalOfUnknownRecord),
1200            (7u16, RtrErrorCode::DuplicateAnnouncementReceived),
1201            (8u16, RtrErrorCode::UnexpectedProtocolVersion),
1202        ];
1203
1204        for (value, expected) in codes {
1205            assert_eq!(RtrErrorCode::from_u16(value), Some(expected));
1206            assert_eq!(expected.to_u16(), value);
1207        }
1208    }
1209
1210    #[test]
1211    fn test_all_pdu_types() {
1212        // Test all PDU type conversions
1213        let types = [
1214            (0u8, RtrPduType::SerialNotify),
1215            (1u8, RtrPduType::SerialQuery),
1216            (2u8, RtrPduType::ResetQuery),
1217            (3u8, RtrPduType::CacheResponse),
1218            (4u8, RtrPduType::IPv4Prefix),
1219            (6u8, RtrPduType::IPv6Prefix),
1220            (7u8, RtrPduType::EndOfData),
1221            (8u8, RtrPduType::CacheReset),
1222            (9u8, RtrPduType::RouterKey),
1223            (10u8, RtrPduType::ErrorReport),
1224        ];
1225
1226        for (value, expected) in types {
1227            assert_eq!(RtrPduType::from_u8(value), Some(expected));
1228            assert_eq!(expected.to_u8(), value);
1229        }
1230
1231        // Test that type 5 doesn't exist
1232        assert_eq!(RtrPduType::from_u8(5), None);
1233    }
1234}