1 use std::{cmp::Ordering, collections::HashSet, fmt, str::FromStr};
2
3 use rpki::repository::x509::Time;
4
5 use crate::commons::{
6 api::{AsNumber, RoaDefinition, TypedPrefix},
7 bgp::{IpRange, TypedPrefixTree, TypedPrefixTreeBuilder},
8 };
9
10 //------------ AnnouncementTree ----------------------------------------------
11
12 pub type AnnouncementTree = TypedPrefixTree<Announcement>;
13
14 //------------ RoaTree -------------------------------------------------------
15
16 pub type RoaTree = TypedPrefixTree<RoaDefinition>;
17
make_roa_tree(roas: &[RoaDefinition]) -> RoaTree18 pub fn make_roa_tree(roas: &[RoaDefinition]) -> RoaTree {
19 make_tree(roas)
20 }
21
22 pub type ValidatedAnnouncementTree = TypedPrefixTree<ValidatedAnnouncement>;
23
make_validated_announcement_tree(validated: &[ValidatedAnnouncement]) -> ValidatedAnnouncementTree24 pub fn make_validated_announcement_tree(validated: &[ValidatedAnnouncement]) -> ValidatedAnnouncementTree {
25 make_tree(validated)
26 }
27
make_tree<V>(els: &[V]) -> TypedPrefixTree<V> where V: AsRef<TypedPrefix> + Clone,28 fn make_tree<V>(els: &[V]) -> TypedPrefixTree<V>
29 where
30 V: AsRef<TypedPrefix> + Clone,
31 {
32 let mut builder = TypedPrefixTreeBuilder::default();
33 for el in els {
34 builder.add(el.clone());
35 }
36 builder.build()
37 }
38
39 //------------ Announcement --------------------------------------------------
40
41 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
42 pub struct Announcement {
43 asn: AsNumber,
44 prefix: TypedPrefix,
45 }
46
47 impl Announcement {
new(asn: AsNumber, prefix: TypedPrefix) -> Self48 pub fn new(asn: AsNumber, prefix: TypedPrefix) -> Self {
49 Announcement { asn, prefix }
50 }
51
asn(&self) -> &AsNumber52 pub fn asn(&self) -> &AsNumber {
53 &self.asn
54 }
55
prefix(&self) -> &TypedPrefix56 pub fn prefix(&self) -> &TypedPrefix {
57 &self.prefix
58 }
59
validate(&self, roas: &RoaTree) -> ValidatedAnnouncement60 pub fn validate(&self, roas: &RoaTree) -> ValidatedAnnouncement {
61 let covering = roas.matching_or_less_specific(&self.prefix);
62 if covering.is_empty() {
63 ValidatedAnnouncement {
64 announcement: *self,
65 validity: AnnouncementValidity::NotFound,
66 authorizing: None,
67 disallowing: vec![],
68 }
69 } else {
70 let mut invalidating = vec![];
71 let mut same_asn_found = false;
72 let mut none_as0_found = false;
73 for roa in covering {
74 if roa.asn() == self.asn {
75 if roa.prefix().matching_or_less_specific(&self.prefix)
76 && roa.effective_max_length() >= self.prefix.addr_len()
77 {
78 return ValidatedAnnouncement {
79 announcement: *self,
80 validity: AnnouncementValidity::Valid,
81 authorizing: Some(*roa),
82 disallowing: vec![],
83 };
84 } else {
85 same_asn_found = true;
86 }
87 }
88 if roa.asn() != AsNumber::zero() {
89 none_as0_found = true;
90 }
91 invalidating.push(*roa);
92 }
93
94 // NOTE: Valid announcements already returned, we only have invalids left
95
96 let validity = if same_asn_found {
97 AnnouncementValidity::InvalidLength
98 } else if none_as0_found {
99 AnnouncementValidity::InvalidAsn
100 } else {
101 AnnouncementValidity::Disallowed
102 };
103
104 ValidatedAnnouncement {
105 announcement: *self,
106 validity,
107 authorizing: None,
108 disallowing: invalidating,
109 }
110 }
111 }
112 }
113
114 impl FromStr for Announcement {
115 type Err = String;
116
from_str(s: &str) -> Result<Self, Self::Err>117 fn from_str(s: &str) -> Result<Self, Self::Err> {
118 let as_roa = RoaDefinition::from_str(s).map_err(|e| format!("Can't parse: {}, Error: {}", s, e))?;
119 if as_roa.max_length().is_some() {
120 Err(format!("Cannot parse announcement (max length not allowed): {}", s))
121 } else {
122 Ok(as_roa.into())
123 }
124 }
125 }
126
127 impl fmt::Display for Announcement {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129 write!(f, "{} => {}", self.prefix, self.asn)
130 }
131 }
132
133 impl Ord for Announcement {
cmp(&self, other: &Self) -> Ordering134 fn cmp(&self, other: &Self) -> Ordering {
135 let mut ordering = self.prefix.cmp(other.prefix());
136 if ordering == Ordering::Equal {
137 ordering = self.asn.cmp(&other.asn);
138 }
139 ordering
140 }
141 }
142
143 impl PartialOrd for Announcement {
partial_cmp(&self, other: &Self) -> Option<Ordering>144 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
145 Some(self.cmp(other))
146 }
147 }
148
149 impl From<Announcement> for RoaDefinition {
from(a: Announcement) -> Self150 fn from(a: Announcement) -> Self {
151 RoaDefinition::new(a.asn, a.prefix, None)
152 }
153 }
154
155 impl From<RoaDefinition> for Announcement {
from(d: RoaDefinition) -> Self156 fn from(d: RoaDefinition) -> Self {
157 Announcement {
158 asn: d.asn(),
159 prefix: d.prefix(),
160 }
161 }
162 }
163
164 impl AsRef<TypedPrefix> for Announcement {
as_ref(&self) -> &TypedPrefix165 fn as_ref(&self) -> &TypedPrefix {
166 &self.prefix
167 }
168 }
169
170 //------------ Announcements -------------------------------------------------
171
172 pub struct Announcements {
173 seen: TypedPrefixTree<Announcement>,
174 last_updated: Option<Time>,
175 last_checked: Option<Time>,
176 }
177
178 impl Announcements {
update(&mut self, announcements: Vec<Announcement>)179 pub fn update(&mut self, announcements: Vec<Announcement>) {
180 let mut builder = TypedPrefixTreeBuilder::default();
181 for a in announcements {
182 builder.add(a);
183 }
184 let tree = builder.build();
185 self.seen = tree;
186 let now = Time::now();
187 self.last_updated = Some(now);
188 self.last_checked = Some(now);
189 }
190
update_checked(&mut self)191 pub fn update_checked(&mut self) {
192 self.last_checked = Some(Time::now())
193 }
194
equivalent(&self, announcements: &[Announcement]) -> bool195 pub fn equivalent(&self, announcements: &[Announcement]) -> bool {
196 let current_set: HashSet<&Announcement> = self.seen.all().into_iter().collect();
197 let new_set: HashSet<&Announcement> = announcements.iter().collect();
198 current_set == new_set
199 }
200
all(&self) -> Vec<&Announcement>201 pub fn all(&self) -> Vec<&Announcement> {
202 self.seen.all()
203 }
204
contained_by(&self, range: impl Into<IpRange>) -> Vec<&Announcement>205 pub fn contained_by(&self, range: impl Into<IpRange>) -> Vec<&Announcement> {
206 self.seen.matching_or_more_specific(range)
207 }
208
size(&self) -> usize209 pub fn size(&self) -> usize {
210 self.seen.size()
211 }
212
is_empty(&self) -> bool213 pub fn is_empty(&self) -> bool {
214 self.size() == 0
215 }
216
last_checked(&self) -> Option<Time>217 pub fn last_checked(&self) -> Option<Time> {
218 self.last_checked
219 }
220
last_updated(&self) -> Option<Time>221 pub fn last_updated(&self) -> Option<Time> {
222 self.last_updated
223 }
224 }
225
226 impl Default for Announcements {
default() -> Self227 fn default() -> Self {
228 Announcements {
229 seen: TypedPrefixTreeBuilder::default().build(),
230 last_updated: None,
231 last_checked: None,
232 }
233 }
234 }
235
236 //------------ ValidatedAnnouncement -----------------------------------------
237
238 #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
239 pub struct ValidatedAnnouncement {
240 announcement: Announcement,
241 validity: AnnouncementValidity,
242 authorizing: Option<RoaDefinition>,
243 disallowing: Vec<RoaDefinition>,
244 }
245
246 impl ValidatedAnnouncement {
validity(&self) -> AnnouncementValidity247 pub fn validity(&self) -> AnnouncementValidity {
248 self.validity
249 }
250
announcement(&self) -> Announcement251 pub fn announcement(&self) -> Announcement {
252 self.announcement
253 }
254
unpack( self, ) -> ( Announcement, AnnouncementValidity, Option<RoaDefinition>, Vec<RoaDefinition>, )255 pub fn unpack(
256 self,
257 ) -> (
258 Announcement,
259 AnnouncementValidity,
260 Option<RoaDefinition>,
261 Vec<RoaDefinition>,
262 ) {
263 (self.announcement, self.validity, self.authorizing, self.disallowing)
264 }
265 }
266
267 impl AsRef<TypedPrefix> for ValidatedAnnouncement {
as_ref(&self) -> &TypedPrefix268 fn as_ref(&self) -> &TypedPrefix {
269 &self.announcement.prefix
270 }
271 }
272
273 //------------ AnnouncementValidity -------------------------------------------
274
275 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
276 pub enum AnnouncementValidity {
277 Valid,
278 InvalidLength,
279 InvalidAsn,
280 Disallowed,
281 NotFound,
282 }
283
284 //------------ Tests --------------------------------------------------------
285
286 #[cfg(test)]
287 mod tests {
288 use super::*;
289 use crate::test::*;
290
291 #[test]
find_contained()292 fn find_contained() {
293 let ann_v4 = Announcement::from_str("1.0.0.0/24 => 13335").unwrap();
294 let ann_v6 = Announcement::from_str("2001:4:112::/48 => 112").unwrap();
295
296 let mut announcements = Announcements::default();
297 announcements.update(vec![ann_v4, ann_v6]);
298
299 let matches = announcements.contained_by(ann_v4.prefix());
300 assert_eq!(1, matches.len());
301 assert!(matches.contains(&&ann_v4));
302
303 let matches = announcements.contained_by(ann_v6.prefix());
304 assert_eq!(1, matches.len());
305 assert!(matches.contains(&&ann_v6));
306 }
307
308 #[test]
validate_announcement()309 fn validate_announcement() {
310 let roa_authorizing_1 = definition("10.0.0.0/23-24 => 64496");
311 let roa_authorizing_2 = definition("10.0.0.0/23 => 64498");
312 let roa_irrelevant = definition("10.1.0.0/23-24 => 64496");
313
314 let ann_v1 = announcement("10.0.0.0/24 => 64496");
315 let ann_v2 = announcement("10.0.1.0/24 => 64496");
316 let ann_ia = announcement("10.0.0.0/24 => 64497");
317 let ann_il = announcement("10.0.1.0/24 => 64498");
318 let ann_nf = announcement("10.2.0.0/24 => 64497");
319
320 let mut roas_builder = TypedPrefixTreeBuilder::default();
321 roas_builder.add(roa_authorizing_1);
322 roas_builder.add(roa_authorizing_2);
323 roas_builder.add(roa_irrelevant);
324 let roas = roas_builder.build();
325
326 fn assert_state(ann: &Announcement, roas: &RoaTree, expected: AnnouncementValidity) {
327 assert_eq!(ann.validate(roas).validity, expected);
328 }
329
330 assert_state(&ann_v1, &roas, AnnouncementValidity::Valid);
331 assert_state(&ann_v2, &roas, AnnouncementValidity::Valid);
332 assert_state(&ann_ia, &roas, AnnouncementValidity::InvalidAsn);
333 assert_state(&ann_il, &roas, AnnouncementValidity::InvalidLength);
334 assert_state(&ann_nf, &roas, AnnouncementValidity::NotFound);
335 }
336 }
337