1#[cfg(feature = "fuzz")]
17use chrono::TimeZone;
18use chrono::{DateTime, Utc};
19use std::hash::{Hash, Hasher};
20use std::net::{IpAddr, Ipv4Addr};
21use std::ops::Deref;
22
23use netgauze_bgp_pkt::nlri::RouteDistinguisher;
24
25use crate::iana::{BmpMessageType, BmpPeerTypeCode, BmpVersion};
26
27use serde::{Deserialize, Serialize};
28
29#[cfg(feature = "codec")]
30pub mod codec;
31pub mod iana;
32pub mod v3;
33pub mod v4;
34#[cfg(feature = "serde")]
35pub mod wire;
36
37#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
50pub enum BmpMessage {
51 V3(v3::BmpMessageValue),
52 V4(v4::BmpMessageValue),
53}
54
55impl BmpMessage {
56 pub fn get_version(&self) -> BmpVersion {
59 match self {
60 BmpMessage::V3(_) => BmpVersion::Version3,
61 BmpMessage::V4(_) => BmpVersion::Version4,
62 }
63 }
64
65 pub fn get_type(&self) -> BmpMessageType {
68 match &self {
69 BmpMessage::V3(value) => value.get_type(),
70 BmpMessage::V4(value) => value.get_type(),
71 }
72 }
73}
74
75#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
101#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
102pub struct PeerHeader {
103 peer_type: BmpPeerType,
104 rd: Option<RouteDistinguisher>,
105 #[cfg_attr(feature = "fuzz", arbitrary(with = arbitrary_ext::arbitrary_option(crate::arbitrary_ip)))]
106 address: Option<IpAddr>,
107 peer_as: u32,
108 bgp_id: Ipv4Addr,
109 #[cfg_attr(feature = "fuzz", arbitrary(with = arbitrary_ext::arbitrary_option(crate::arbitrary_datetime)))]
110 timestamp: Option<DateTime<Utc>>,
111}
112
113impl PeerHeader {
114 pub const fn new(
115 peer_type: BmpPeerType,
116 rd: Option<RouteDistinguisher>,
117 address: Option<IpAddr>,
118 peer_as: u32,
119 bgp_id: Ipv4Addr,
120 timestamp: Option<DateTime<Utc>>,
121 ) -> Self {
122 Self {
123 peer_type,
124 rd,
125 address,
126 peer_as,
127 bgp_id,
128 timestamp,
129 }
130 }
131
132 pub const fn peer_type(&self) -> BmpPeerType {
133 self.peer_type
134 }
135
136 pub const fn rd(&self) -> Option<RouteDistinguisher> {
137 self.rd
138 }
139
140 pub const fn address(&self) -> Option<IpAddr> {
141 self.address
142 }
143
144 pub const fn peer_as(&self) -> u32 {
145 self.peer_as
146 }
147
148 pub const fn bgp_id(&self) -> Ipv4Addr {
149 self.bgp_id
150 }
151
152 pub const fn timestamp(&self) -> Option<&DateTime<Utc>> {
153 self.timestamp.as_ref()
154 }
155
156 pub const fn is_asn4(&self) -> bool {
157 match self.peer_type {
158 BmpPeerType::GlobalInstancePeer { asn2, .. } => !asn2,
159 BmpPeerType::RdInstancePeer { asn2, .. } => !asn2,
160 BmpPeerType::LocalInstancePeer { asn2, .. } => !asn2,
161 BmpPeerType::LocRibInstancePeer { .. } => true,
162 BmpPeerType::Experimental251 { .. } => true,
163 BmpPeerType::Experimental252 { .. } => true,
164 BmpPeerType::Experimental253 { .. } => true,
165 BmpPeerType::Experimental254 { .. } => true,
166 }
167 }
168}
169
170#[derive(Debug, Hash, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
194#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
195pub enum BmpPeerType {
196 GlobalInstancePeer {
197 ipv6: bool,
198 post_policy: bool,
199 asn2: bool,
200 adj_rib_out: bool,
201 },
202 RdInstancePeer {
203 ipv6: bool,
204 post_policy: bool,
205 asn2: bool,
206 adj_rib_out: bool,
207 },
208 LocalInstancePeer {
209 ipv6: bool,
210 post_policy: bool,
211 asn2: bool,
212 adj_rib_out: bool,
213 },
214 LocRibInstancePeer {
215 filtered: bool,
216 },
217 Experimental251 {
218 flags: u8,
219 },
220 Experimental252 {
221 flags: u8,
222 },
223 Experimental253 {
224 flags: u8,
225 },
226 Experimental254 {
227 flags: u8,
228 },
229}
230
231impl BmpPeerType {
232 pub const fn get_type(&self) -> BmpPeerTypeCode {
234 match self {
235 Self::GlobalInstancePeer { .. } => BmpPeerTypeCode::GlobalInstancePeer,
236 Self::RdInstancePeer { .. } => BmpPeerTypeCode::RdInstancePeer,
237 Self::LocalInstancePeer { .. } => BmpPeerTypeCode::LocalInstancePeer,
238 Self::LocRibInstancePeer { .. } => BmpPeerTypeCode::LocRibInstancePeer,
239 Self::Experimental251 { .. } => BmpPeerTypeCode::Experimental251,
240 Self::Experimental252 { .. } => BmpPeerTypeCode::Experimental252,
241 Self::Experimental253 { .. } => BmpPeerTypeCode::Experimental253,
242 Self::Experimental254 { .. } => BmpPeerTypeCode::Experimental254,
243 }
244 }
245}
246
247#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
251#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
252pub struct CounterU32(u32);
253
254impl CounterU32 {
255 pub const fn new(value: u32) -> Self {
256 Self(value)
257 }
258
259 pub const fn value(&self) -> u32 {
260 self.0
261 }
262}
263
264impl Deref for CounterU32 {
265 type Target = u32;
266
267 fn deref(&self) -> &Self::Target {
268 &self.0
269 }
270}
271
272#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
283#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
284pub struct GaugeU64(u64);
285
286impl GaugeU64 {
287 pub const fn new(value: u64) -> Self {
288 Self(value)
289 }
290
291 pub const fn value(&self) -> u64 {
292 self.0
293 }
294}
295
296impl Deref for GaugeU64 {
297 type Target = u64;
298
299 fn deref(&self) -> &Self::Target {
300 &self.0
301 }
302}
303
304#[cfg(feature = "fuzz")]
306fn arbitrary_ipv4(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Ipv4Addr> {
307 let value = u.int_in_range(0..=u32::MAX)?;
308 Ok(Ipv4Addr::from(value))
309}
310
311#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
314#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
315pub struct PeerKey {
316 #[cfg_attr(feature = "fuzz", arbitrary(with = arbitrary_ext::arbitrary_option(crate::arbitrary_ip)))]
317 peer_address: Option<IpAddr>,
318 peer_type: BmpPeerType,
319 rd: Option<RouteDistinguisher>,
320 asn: u32,
321 #[cfg_attr(feature = "fuzz", arbitrary(with = arbitrary_ipv4))]
322 bgp_id: Ipv4Addr,
323}
324
325impl Hash for PeerKey {
326 fn hash<H: Hasher>(&self, state: &mut H) {
327 self.peer_address.hash(state);
328 self.peer_type.get_type().hash(state);
329 self.rd.hash(state);
330 self.asn.hash(state);
331 self.bgp_id.hash(state);
332 }
333}
334impl PartialEq<Self> for PeerKey {
335 fn eq(&self, other: &Self) -> bool {
336 self.peer_address.eq(&other.peer_address)
337 && std::mem::discriminant(&self.peer_type) == std::mem::discriminant(&other.peer_type)
338 && self.rd == other.rd
339 && self.asn == other.asn
340 && self.bgp_id == other.bgp_id
341 }
342}
343
344impl Eq for PeerKey {}
345
346impl PeerKey {
347 pub const fn new(
348 peer_address: Option<IpAddr>,
349 peer_type: BmpPeerType,
350 rd: Option<RouteDistinguisher>,
351 asn: u32,
352 bgp_id: Ipv4Addr,
353 ) -> Self {
354 Self {
355 peer_address,
356 peer_type,
357 rd,
358 asn,
359 bgp_id,
360 }
361 }
362
363 pub const fn from_peer_header(header: &PeerHeader) -> Self {
364 Self::new(
365 header.address,
366 header.peer_type,
367 header.rd,
368 header.peer_as,
369 header.bgp_id,
370 )
371 }
372
373 pub const fn peer_address(&self) -> Option<IpAddr> {
374 self.peer_address
375 }
376 pub const fn peer_type(&self) -> BmpPeerType {
377 self.peer_type
378 }
379 pub const fn rd(&self) -> Option<RouteDistinguisher> {
380 self.rd
381 }
382 pub const fn asn(&self) -> u32 {
383 self.asn
384 }
385 pub const fn bgp_id(&self) -> Ipv4Addr {
386 self.bgp_id
387 }
388}
389
390#[cfg(feature = "fuzz")]
392fn arbitrary_ipv6(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<std::net::Ipv6Addr> {
393 let value = u.int_in_range(0..=u128::MAX)?;
394 Ok(std::net::Ipv6Addr::from(value))
395}
396
397#[cfg(feature = "fuzz")]
399fn arbitrary_ip(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<IpAddr> {
400 let ipv4 = arbitrary_ipv4(u)?;
401 let ipv6 = arbitrary_ipv6(u)?;
402 let choices = [IpAddr::V4(ipv4), IpAddr::V6(ipv6)];
403 let addr = u.choose(&choices)?;
404 Ok(*addr)
405}
406
407#[cfg(feature = "fuzz")]
408fn arbitrary_datetime(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<DateTime<Utc>> {
409 loop {
410 let seconds = u.int_in_range(0..=i64::MAX)?;
411 if let chrono::LocalResult::Single(tt) = Utc.timestamp_opt(seconds, 0) {
412 return Ok(tt);
413 }
414 }
415}