bgpkit_parser/models/bgp/
mod.rs

1//! BGP messages and relevant structs.
2
3pub mod attributes;
4pub mod capabilities;
5pub mod community;
6pub mod elem;
7pub mod error;
8pub mod flowspec;
9pub mod linkstate;
10pub mod role;
11pub mod tunnel_encap;
12
13pub use attributes::*;
14pub use community::*;
15pub use elem::*;
16pub use error::*;
17pub use flowspec::*;
18pub use linkstate::*;
19pub use role::*;
20pub use tunnel_encap::*;
21
22use crate::models::network::*;
23use capabilities::{
24    AddPathCapability, BgpCapabilityType, BgpExtendedMessageCapability, BgpRoleCapability,
25    ExtendedNextHopCapability, FourOctetAsCapability, GracefulRestartCapability,
26    MultiprotocolExtensionsCapability, RouteRefreshCapability,
27};
28use num_enum::{IntoPrimitive, TryFromPrimitive};
29use std::net::Ipv4Addr;
30
31pub type BgpIdentifier = Ipv4Addr;
32
33#[derive(Debug, TryFromPrimitive, IntoPrimitive, Copy, Clone, PartialEq, Hash)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35#[repr(u8)]
36pub enum BgpMessageType {
37    OPEN = 1,
38    UPDATE = 2,
39    NOTIFICATION = 3,
40    KEEPALIVE = 4,
41}
42
43// https://tools.ietf.org/html/rfc4271#section-4
44#[derive(Debug, Clone, PartialEq, Eq)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub enum BgpMessage {
47    Open(BgpOpenMessage),
48    Update(BgpUpdateMessage),
49    Notification(BgpNotificationMessage),
50    KeepAlive,
51}
52
53impl BgpMessage {
54    pub const fn msg_type(&self) -> BgpMessageType {
55        match self {
56            BgpMessage::Open(_) => BgpMessageType::OPEN,
57            BgpMessage::Update(_) => BgpMessageType::UPDATE,
58            BgpMessage::Notification(_) => BgpMessageType::NOTIFICATION,
59            BgpMessage::KeepAlive => BgpMessageType::KEEPALIVE,
60        }
61    }
62}
63
64/// BGP Open Message
65///
66/// ```text
67///  0                   1                   2                   3
68///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
69///  +-+-+-+-+-+-+-+-+
70///  |    Version    |
71///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72///  |     My Autonomous System      |
73///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74///  |           Hold Time           |
75///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76///  |                         BGP Identifier                        |
77///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
78///  | Opt Parm Len  |
79///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80///  |                                                               |
81///  |             Optional Parameters (variable)                    |
82///  |                                                               |
83///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84/// ```
85#[derive(Debug, Clone, PartialEq, Eq)]
86#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
87pub struct BgpOpenMessage {
88    pub version: u8,
89    pub asn: Asn,
90    pub hold_time: u16,
91    pub sender_ip: Ipv4Addr,
92    pub extended_length: bool,
93    pub opt_params: Vec<OptParam>,
94}
95
96#[derive(Debug, Clone, PartialEq, Eq)]
97#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
98pub struct OptParam {
99    pub param_type: u8,
100    pub param_len: u16,
101    pub param_value: ParamValue,
102}
103
104#[derive(Debug, Clone, PartialEq, Eq)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106pub enum ParamValue {
107    Raw(Vec<u8>),
108    Capacities(Vec<Capability>),
109}
110
111/// BGP Capability.
112///
113/// - RFC3392: <https://datatracker.ietf.org/doc/html/rfc3392>
114/// - Capability codes: <https://www.iana.org/assignments/capability-codes/capability-codes.xhtml#capability-codes-2>
115#[derive(Debug, Clone, PartialEq, Eq)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub struct Capability {
118    pub ty: BgpCapabilityType,
119    pub value: CapabilityValue,
120}
121
122/// Parsed BGP capability values
123#[derive(Debug, Clone, PartialEq, Eq)]
124#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
125pub enum CapabilityValue {
126    /// Raw unparsed capability data
127    Raw(Vec<u8>),
128    /// Multiprotocol Extensions capability - RFC 2858, Section 7
129    MultiprotocolExtensions(MultiprotocolExtensionsCapability),
130    /// Route Refresh capability - RFC 2918
131    RouteRefresh(RouteRefreshCapability),
132    /// Extended Next Hop capability - RFC 8950, Section 3
133    ExtendedNextHop(ExtendedNextHopCapability),
134    /// Graceful Restart capability - RFC 4724
135    GracefulRestart(GracefulRestartCapability),
136    /// 4-octet AS number capability - RFC 6793
137    FourOctetAs(FourOctetAsCapability),
138    /// ADD-PATH capability - RFC 7911
139    AddPath(AddPathCapability),
140    /// BGP Role capability - RFC 9234
141    BgpRole(BgpRoleCapability),
142    /// BGP Extended Message capability - RFC 8654
143    BgpExtendedMessage(BgpExtendedMessageCapability),
144}
145
146#[derive(Debug, Clone, PartialEq, Default, Eq)]
147#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
148/// BGP Update Message.
149///
150/// Corresponding RFC section: <https://datatracker.ietf.org/doc/html/rfc4271#section-4.3>
151pub struct BgpUpdateMessage {
152    /// Withdrawn prefixes in this update message.
153    ///
154    /// **IMPORTANT:** Do **not** access this field directly in order to get all withdrawn prefixes.
155    /// Some withdrawn prefixes may be present in the [`AttributeValue::MpUnreachNlri`] attribute,
156    /// and will **not** be included here. Accessing this field directly may cause you to miss
157    /// IPv6 or multi-protocol prefixes.
158    ///
159    /// Instead, use [`Elementor::bgp_update_to_elems`] to reliably extract all withdrawn prefixes from the update,
160    /// or combine this field with prefixes found in the `MpUnreachNlri` attribute manually.
161    ///
162    /// See
163    /// * RFC4271 Section 4.3: <https://datatracker.ietf.org/doc/html/rfc4271#section-4.3>
164    /// * RFC4760 Section 4: <https://datatracker.ietf.org/doc/html/rfc4760#section-4>
165    pub withdrawn_prefixes: Vec<NetworkPrefix>,
166
167    /// BGP path attributes.
168    pub attributes: Attributes,
169
170    /// Network prefixes that are being advertised in this update message.
171    ///
172    /// **IMPORTANT:** Do **not** access this field directly in order to get all announced prefixes.
173    /// Some advertised prefixes may be present in the [`AttributeValue::MpReachNlri`] attribute,
174    /// and will **not** be included here. Accessing this field directly may cause you to miss
175    /// IPv6 or multi-protocol prefixes.
176    ///
177    /// Instead, use [`Elementor::bgp_update_to_elems`] to reliably extract all announced prefixes from the update,
178    /// or combine this field with prefixes found in the `MpReachNlri` attribute manually.
179    ///
180    /// See
181    ///
182    /// * RFC4271 Section 4.3: <https://datatracker.ietf.org/doc/html/rfc4271#section-4.3>
183    /// * RFC4760 Section 3: <https://datatracker.ietf.org/doc/html/rfc4760#section-3>
184    pub announced_prefixes: Vec<NetworkPrefix>,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq)]
188#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
189pub struct BgpNotificationMessage {
190    pub error: BgpError,
191    pub data: Vec<u8>,
192}
193
194#[cfg(test)]
195mod tests {
196    use super::*;
197
198    #[test]
199    fn test_message_type() {
200        let open = BgpMessage::Open(BgpOpenMessage {
201            version: 4,
202            asn: Asn::new_32bit(1),
203            hold_time: 180,
204            sender_ip: Ipv4Addr::new(192, 0, 2, 1),
205            extended_length: false,
206            opt_params: vec![],
207        });
208        assert_eq!(open.msg_type(), BgpMessageType::OPEN);
209
210        let update = BgpMessage::Update(BgpUpdateMessage::default());
211        assert_eq!(update.msg_type(), BgpMessageType::UPDATE);
212
213        let notification = BgpMessage::Notification(BgpNotificationMessage {
214            error: BgpError::Unknown(0, 0),
215            data: vec![],
216        });
217        assert_eq!(notification.msg_type(), BgpMessageType::NOTIFICATION);
218
219        let keepalive = BgpMessage::KeepAlive;
220        assert_eq!(keepalive.msg_type(), BgpMessageType::KEEPALIVE);
221    }
222
223    #[test]
224    #[cfg(feature = "serde")]
225    fn test_serde() {
226        let open = BgpMessage::Open(BgpOpenMessage {
227            version: 4,
228            asn: Asn::new_32bit(1),
229            hold_time: 180,
230            sender_ip: Ipv4Addr::new(192, 0, 2, 1),
231            extended_length: false,
232            opt_params: vec![],
233        });
234        let serialized = serde_json::to_string(&open).unwrap();
235        let deserialized: BgpMessage = serde_json::from_str(&serialized).unwrap();
236        assert_eq!(open, deserialized);
237
238        let update = BgpMessage::Update(BgpUpdateMessage::default());
239        let serialized = serde_json::to_string(&update).unwrap();
240        let deserialized: BgpMessage = serde_json::from_str(&serialized).unwrap();
241        assert_eq!(update, deserialized);
242
243        let notification = BgpMessage::Notification(BgpNotificationMessage {
244            error: BgpError::Unknown(0, 0),
245            data: vec![],
246        });
247        let serialized = serde_json::to_string(&notification).unwrap();
248        let deserialized: BgpMessage = serde_json::from_str(&serialized).unwrap();
249        assert_eq!(notification, deserialized);
250
251        let keepalive = BgpMessage::KeepAlive;
252        let serialized = serde_json::to_string(&keepalive).unwrap();
253        let deserialized: BgpMessage = serde_json::from_str(&serialized).unwrap();
254        assert_eq!(keepalive, deserialized);
255    }
256}