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