1use crate::bgp::attributes::{AsPath, AtomicAggregate, Origin};
2use crate::bgp::community::*;
3use crate::network::{Asn, NetworkPrefix};
4use itertools::Itertools;
5use serde::{Serialize, Serializer};
6use std::cmp::Ordering;
7use std::fmt::{Display, Formatter};
8use std::net::IpAddr;
9use std::str::FromStr;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum ElemType {
17 ANNOUNCE,
18 WITHDRAW,
19}
20
21impl Serialize for ElemType {
22 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
23 where
24 S: Serializer,
25 {
26 serializer.serialize_str(match self {
27 ElemType::ANNOUNCE => "announce",
28 ElemType::WITHDRAW => "withdraw",
29 })
30 }
31}
32
33impl ElemType {
34 pub fn is_announce(&self) -> bool {
35 match self {
36 ElemType::ANNOUNCE => true,
37 ElemType::WITHDRAW => false,
38 }
39 }
40}
41
42#[derive(Debug, Clone, Serialize, PartialEq)]
49pub struct BgpElem {
50 pub timestamp: f64,
51 #[serde(rename = "type")]
52 pub elem_type: ElemType,
53 pub peer_ip: IpAddr,
54 pub peer_asn: Asn,
55 pub prefix: NetworkPrefix,
56 pub next_hop: Option<IpAddr>,
57 pub as_path: Option<AsPath>,
58 pub origin_asns: Option<Vec<Asn>>,
59 pub origin: Option<Origin>,
60 pub local_pref: Option<u32>,
61 pub med: Option<u32>,
62 pub communities: Option<Vec<MetaCommunity>>,
63 pub atomic: Option<AtomicAggregate>,
64 pub aggr_asn: Option<Asn>,
65 pub aggr_ip: Option<IpAddr>,
66 pub only_to_customer: Option<u32>,
67}
68
69impl Eq for BgpElem {}
70
71impl PartialOrd<Self> for BgpElem {
72 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
73 Some(self.cmp(other))
74 }
75}
76
77impl Ord for BgpElem {
78 fn cmp(&self, other: &Self) -> Ordering {
79 self.timestamp
80 .partial_cmp(&other.timestamp)
81 .unwrap()
82 .then_with(|| self.peer_ip.cmp(&other.peer_ip))
83 }
84}
85
86#[derive(Debug, Clone, Serialize)]
88pub struct BgpElemRef<'a> {
89 pub timestamp: &'a f64,
90 pub elem_type: &'a ElemType,
91 pub peer_ip: &'a IpAddr,
92 pub peer_asn: &'a Asn,
93 pub prefix: &'a NetworkPrefix,
94 pub next_hop: &'a Option<IpAddr>,
95 pub as_path: &'a Option<AsPath>,
96 pub origin_asns: &'a Option<Vec<Asn>>,
97 pub origin: &'a Option<Origin>,
98 pub local_pref: &'a Option<u32>,
99 pub med: &'a Option<u32>,
100 pub communities: &'a Option<Vec<MetaCommunity>>,
101 pub atomic: &'a Option<AtomicAggregate>,
102 pub aggr_asn: &'a Option<Asn>,
103 pub aggr_ip: &'a Option<IpAddr>,
104}
105
106impl Default for BgpElem {
107 fn default() -> Self {
108 BgpElem {
109 timestamp: 0.0,
110 elem_type: ElemType::ANNOUNCE,
111 peer_ip: IpAddr::from_str("0.0.0.0").unwrap(),
112 peer_asn: 0.into(),
113 prefix: NetworkPrefix::from_str("0.0.0.0/0").unwrap(),
114 next_hop: None,
115 as_path: None,
116 origin_asns: None,
117 origin: None,
118 local_pref: None,
119 med: None,
120 communities: None,
121 atomic: None,
122 aggr_asn: None,
123 aggr_ip: None,
124 only_to_customer: None,
125 }
126 }
127}
128
129macro_rules! option_to_string {
130 ($a:expr) => {
131 if let Some(v) = $a {
132 v.to_string()
133 } else {
134 String::new()
135 }
136 };
137}
138
139#[inline(always)]
140pub fn option_to_string_communities(o: &Option<Vec<MetaCommunity>>) -> String {
141 if let Some(v) = o {
142 v.iter().join(" ")
143 } else {
144 String::new()
145 }
146}
147
148impl Display for BgpElem {
149 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
150 let t = match self.elem_type {
151 ElemType::ANNOUNCE => "A",
152 ElemType::WITHDRAW => "W",
153 };
154 let format = format!(
155 "{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}",
156 t,
157 &self.timestamp,
158 &self.peer_ip,
159 &self.peer_asn,
160 &self.prefix,
161 option_to_string!(&self.as_path),
162 option_to_string!(&self.origin),
163 option_to_string!(&self.next_hop),
164 option_to_string!(&self.local_pref),
165 option_to_string!(&self.med),
166 option_to_string_communities(&self.communities),
167 option_to_string!(&self.atomic),
168 option_to_string!(&self.aggr_asn),
169 option_to_string!(&self.aggr_ip),
170 );
171 write!(f, "{}", format)
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use std::default::Default;
179 use std::str::FromStr;
180
181 #[test]
182 fn test_default() {
183 let elem = BgpElem {
184 timestamp: 0.0,
185 elem_type: ElemType::ANNOUNCE,
186 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
187 peer_asn: 0.into(),
188 prefix: NetworkPrefix::from_str("8.8.8.0/24").unwrap(),
189 ..Default::default()
190 };
191 println!("{}", serde_json::json!(elem).to_string());
192 }
193
194 #[test]
195 fn test_sorting() {
196 let elem1 = BgpElem {
197 timestamp: 1.1,
198 elem_type: ElemType::ANNOUNCE,
199 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
200 peer_asn: 0.into(),
201 prefix: NetworkPrefix::from_str("8.8.8.0/24").unwrap(),
202 ..Default::default()
203 };
204 let elem2 = BgpElem {
205 timestamp: 1.2,
206 elem_type: ElemType::ANNOUNCE,
207 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
208 peer_asn: 0.into(),
209 prefix: NetworkPrefix::from_str("8.8.8.0/24").unwrap(),
210 ..Default::default()
211 };
212 let elem3 = BgpElem {
213 timestamp: 1.2,
214 elem_type: ElemType::ANNOUNCE,
215 peer_ip: IpAddr::from_str("192.168.1.2").unwrap(),
216 peer_asn: 0.into(),
217 prefix: NetworkPrefix::from_str("8.8.8.0/24").unwrap(),
218 ..Default::default()
219 };
220
221 assert_eq!(elem1 < elem2, true);
222 assert_eq!(elem2 < elem3, true);
223 }
224}