1use crate::models::*;
56use crate::parser::ComparableRegex;
57use crate::ParserError;
58use crate::ParserError::FilterError;
59use ipnet::IpNet;
60use std::net::IpAddr;
61use std::str::FromStr;
62
63#[derive(Debug, Clone, PartialEq)]
77pub enum Filter {
78 OriginAsn(u32),
79 Prefix(IpNet, PrefixMatchType),
80 PeerIp(IpAddr),
81 PeerIps(Vec<IpAddr>),
82 PeerAsn(u32),
83 Type(ElemType),
84 IpVersion(IpVersion),
85 TsStart(f64),
86 TsEnd(f64),
87 AsPath(ComparableRegex),
88 Community(ComparableRegex),
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub enum IpVersion {
93 Ipv4,
94 Ipv6,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub enum PrefixMatchType {
99 Exact,
100 IncludeSuper,
101 IncludeSub,
102 IncludeSuperSub,
103}
104
105fn parse_time_str(time_str: &str) -> Option<chrono::NaiveDateTime> {
106 if let Ok(t) = time_str.parse::<f64>() {
107 return chrono::DateTime::from_timestamp(t as i64, 0).map(|t| t.naive_utc());
108 }
109 if let Ok(t) = chrono::DateTime::parse_from_rfc3339(time_str) {
110 return Some(t.naive_utc());
111 }
112 None
113}
114
115impl Filter {
116 pub fn new(filter_type: &str, filter_value: &str) -> Result<Filter, ParserError> {
117 match filter_type {
118 "origin_asn" => match u32::from_str(filter_value) {
119 Ok(v) => Ok(Filter::OriginAsn(v)),
120 Err(_) => Err(FilterError(format!(
121 "cannot parse origin asn from {}",
122 filter_value
123 ))),
124 },
125 "prefix" => match IpNet::from_str(filter_value) {
126 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::Exact)),
127 Err(_) => Err(FilterError(format!(
128 "cannot parse prefix from {}",
129 filter_value
130 ))),
131 },
132 "prefix_super" => match IpNet::from_str(filter_value) {
133 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::IncludeSuper)),
134 Err(_) => Err(FilterError(format!(
135 "cannot parse prefix from {}",
136 filter_value
137 ))),
138 },
139 "prefix_sub" => match IpNet::from_str(filter_value) {
140 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::IncludeSub)),
141 Err(_) => Err(FilterError(format!(
142 "cannot parse prefix from {}",
143 filter_value
144 ))),
145 },
146 "prefix_super_sub" => match IpNet::from_str(filter_value) {
147 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::IncludeSuperSub)),
148 Err(_) => Err(FilterError(format!(
149 "cannot parse prefix from {}",
150 filter_value
151 ))),
152 },
153 "peer_ip" => match IpAddr::from_str(filter_value) {
154 Ok(v) => Ok(Filter::PeerIp(v)),
155 Err(_) => Err(FilterError(format!(
156 "cannot parse peer IP from {}",
157 filter_value
158 ))),
159 },
160 "peer_ips" => {
161 let mut ips = vec![];
162 for ip_str in filter_value.replace(' ', "").split(',') {
163 match IpAddr::from_str(ip_str) {
164 Ok(v) => ips.push(v),
165 Err(_) => {
166 return Err(FilterError(format!(
167 "cannot parse peer IP from {}",
168 ip_str
169 )))
170 }
171 }
172 }
173 Ok(Filter::PeerIps(ips))
174 }
175 "peer_asn" => match u32::from_str(filter_value) {
176 Ok(v) => Ok(Filter::PeerAsn(v)),
177 Err(_) => Err(FilterError(format!(
178 "cannot parse peer asn from {}",
179 filter_value
180 ))),
181 },
182 "type" => match filter_value {
183 "w" | "withdraw" | "withdrawal" => Ok(Filter::Type(ElemType::WITHDRAW)),
184 "a" | "announce" | "announcement" => Ok(Filter::Type(ElemType::ANNOUNCE)),
185 _ => Err(FilterError(format!(
186 "cannot parse elem type from {}",
187 filter_value
188 ))),
189 },
190 "ts_start" | "start_ts" => match parse_time_str(filter_value) {
191 Some(t) => Ok(Filter::TsStart(t.and_utc().timestamp() as f64)),
192 None => Err(FilterError(format!(
193 "cannot parse TsStart filter from {}",
194 filter_value
195 ))),
196 },
197 "ts_end" | "end_ts" => match parse_time_str(filter_value) {
198 Some(t) => Ok(Filter::TsEnd(t.and_utc().timestamp() as f64)),
199 None => Err(FilterError(format!(
200 "cannot parse TsEnd filter from {}",
201 filter_value
202 ))),
203 },
204 "as_path" => match ComparableRegex::new(filter_value) {
205 Ok(v) => Ok(Filter::AsPath(v)),
206 Err(_) => Err(FilterError(format!(
207 "cannot parse AS path regex from {}",
208 filter_value
209 ))),
210 },
211 "community" => match ComparableRegex::new(filter_value) {
212 Ok(v) => Ok(Filter::Community(v)),
213 Err(_) => Err(FilterError(format!(
214 "cannot parse Community regex from {}",
215 filter_value
216 ))),
217 },
218 "ip_version" | "ip" => match filter_value {
219 "4" | "v4" | "ipv4" => Ok(Filter::IpVersion(IpVersion::Ipv4)),
220 "6" | "v6" | "ipv6" => Ok(Filter::IpVersion(IpVersion::Ipv6)),
221 _ => Err(FilterError(format!(
222 "cannot parse IP version from {}",
223 filter_value
224 ))),
225 },
226 _ => Err(FilterError(format!("unknown filter type: {}", filter_type))),
227 }
228 }
229}
230
231pub trait Filterable {
232 fn match_filter(&self, filter: &Filter) -> bool;
233 fn match_filters(&self, filters: &[Filter]) -> bool;
234}
235
236const fn same_family(prefix_1: &IpNet, prefix_2: &IpNet) -> bool {
237 matches!(
238 (prefix_1, prefix_2),
239 (IpNet::V4(_), IpNet::V4(_)) | (IpNet::V6(_), IpNet::V6(_))
240 )
241}
242
243fn prefix_match(match_prefix: &IpNet, input_prefix: &IpNet, t: &PrefixMatchType) -> bool {
244 let exact = input_prefix.eq(match_prefix);
245 match t {
246 PrefixMatchType::Exact => exact,
247 PrefixMatchType::IncludeSuper => {
248 if exact {
249 exact
250 } else if !same_family(match_prefix, input_prefix) {
251 false
253 } else {
254 match_prefix.addr() >= input_prefix.addr()
256 && match_prefix.broadcast() <= input_prefix.broadcast()
257 }
258 }
259 PrefixMatchType::IncludeSub => {
260 if exact {
261 exact
262 } else if !same_family(match_prefix, input_prefix) {
263 false
265 } else {
266 match_prefix.addr() <= input_prefix.addr()
268 && match_prefix.broadcast() >= input_prefix.broadcast()
269 }
270 }
271 PrefixMatchType::IncludeSuperSub => {
272 if exact {
273 exact
274 } else if !same_family(match_prefix, input_prefix) {
275 false
277 } else {
278 (match_prefix.addr() >= input_prefix.addr()
280 && match_prefix.broadcast() <= input_prefix.broadcast())
281 || (match_prefix.addr() <= input_prefix.addr()
282 && match_prefix.broadcast() >= input_prefix.broadcast())
283 }
284 }
285 }
286}
287
288impl Filterable for BgpElem {
289 fn match_filter(&self, filter: &Filter) -> bool {
290 match filter {
291 Filter::OriginAsn(v) => {
292 let asn: Asn = (*v).into();
293 if let Some(origins) = &self.origin_asns {
294 origins.contains(&asn)
295 } else {
296 false
297 }
298 }
299 Filter::Prefix(v, t) => prefix_match(v, &self.prefix.prefix, t),
300 Filter::PeerIp(v) => self.peer_ip == *v,
301 Filter::PeerIps(v) => v.contains(&self.peer_ip),
302 Filter::PeerAsn(v) => self.peer_asn.eq(v),
303 Filter::Type(v) => self.elem_type.eq(v),
304 Filter::TsStart(v) => self.timestamp >= *v,
305 Filter::TsEnd(v) => self.timestamp <= *v,
306 Filter::AsPath(v) => {
307 if let Some(path) = &self.as_path {
308 v.is_match(path.to_string().as_str())
309 } else {
310 false
311 }
312 }
313 Filter::Community(r) => {
314 if let Some(communities) = &self.communities {
315 communities.iter().any(|c| r.is_match(c.to_string()))
316 } else {
317 false
318 }
319 }
320 Filter::IpVersion(version) => match version {
321 IpVersion::Ipv4 => self.prefix.prefix.addr().is_ipv4(),
322 IpVersion::Ipv6 => self.prefix.prefix.addr().is_ipv6(),
323 },
324 }
325 }
326
327 fn match_filters(&self, filters: &[Filter]) -> bool {
328 filters.iter().all(|f| self.match_filter(f))
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335 use crate::BgpkitParser;
336 use anyhow::Result;
337 use std::str::FromStr;
338
339 #[test]
340 fn test_filters_on_mrt_file() {
341 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
342 let parser = BgpkitParser::new(url).unwrap();
343 let elems = parser.into_elem_iter().collect::<Vec<BgpElem>>();
344
345 let filters = vec![Filter::PeerIp(IpAddr::from_str("185.1.8.65").unwrap())];
346 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
347 assert_eq!(count, 3393);
348
349 let filters = vec![
350 Filter::PeerIp(IpAddr::from_str("185.1.8.65").unwrap()),
351 Filter::Prefix(
352 IpNet::from_str("190.115.192.0/22").unwrap(),
353 PrefixMatchType::Exact,
354 ),
355 ];
356 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
357 assert_eq!(count, 5);
358
359 let filters = vec![Filter::Prefix(
360 IpNet::from_str("190.115.192.0/24").unwrap(),
361 PrefixMatchType::IncludeSuper,
362 )];
363 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
364 assert_eq!(count, 18);
365
366 let filters = vec![Filter::Prefix(
367 IpNet::from_str("190.115.192.0/22").unwrap(),
368 PrefixMatchType::IncludeSub,
369 )];
370 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
371 assert_eq!(count, 42);
372
373 let filters = vec![Filter::Prefix(
374 IpNet::from_str("190.115.192.0/23").unwrap(),
375 PrefixMatchType::IncludeSuperSub,
376 )];
377 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
378 assert_eq!(count, 24);
379
380 let filters = vec![Filter::new("as_path", r" ?174 1916 52888$").unwrap()];
381 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
382 assert_eq!(count, 12);
383
384 let filters = vec![Filter::new("community", r"60924:.*").unwrap()];
386 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
387 assert_eq!(count, 4243);
388
389 let filters = vec![Filter::new("community", r".+:784$").unwrap()];
391 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
392 assert_eq!(count, 107);
393
394 let filters = vec![Filter::new("community", r"\d+:\d+:\d+$").unwrap()];
396 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
397 assert_eq!(count, 4397);
398
399 let filters = vec![
400 Filter::TsStart(1637437798_f64),
401 Filter::TsEnd(1637437798_f64),
402 ];
403 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
404 assert_eq!(count, 13);
405
406 let filters = vec![Filter::Type(ElemType::WITHDRAW)];
407 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
408 assert_eq!(count, 379);
409
410 let filters = vec![
411 Filter::Type(ElemType::WITHDRAW),
412 Filter::Prefix(
413 IpNet::from_str("2804:100::/32").unwrap(),
414 PrefixMatchType::Exact,
415 ),
416 ];
417 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
418 assert_eq!(count, 1);
419
420 let filters = vec![Filter::PeerIps(vec![
431 IpAddr::from_str("185.1.8.65").unwrap(),
432 IpAddr::from_str("2001:7f8:73:0:3:fa4:0:1").unwrap(),
433 ])];
434 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
435 assert_eq!(count, 3393 + 834);
436 }
437
438 #[test]
439 fn test_filter_incorrect_filters() {
440 let incorrect_filters = [
442 Filter::new("community", r"[abc"),
443 Filter::new("as_path", r"[0-9"),
444 Filter::new("prefix_super_sub", "-192.-168.-1.1/24"),
445 ];
446 assert!(incorrect_filters
447 .iter()
448 .all(|f| matches!(f, Err(FilterError(_)))));
449 }
450
451 #[test]
452 fn test_parsing_time_str() {
453 let ts = chrono::NaiveDateTime::from_str("2021-11-20T19:49:58").unwrap();
454 assert_eq!(parse_time_str("1637437798"), Some(ts));
455 assert_eq!(parse_time_str("2021-11-20T19:49:58Z"), Some(ts));
456 assert_eq!(parse_time_str("2021-11-20T19:49:58+00:00"), Some(ts));
457
458 assert_eq!(parse_time_str("2021-11-20T19:49:58"), None);
459 assert_eq!(parse_time_str("2021-11-20T19:49:58ZDXV"), None);
460 assert_eq!(parse_time_str("2021-11-20 19:49:58"), None);
461 assert_eq!(parse_time_str("2021-11-20"), None);
462 }
463
464 #[test]
465 fn test_filter_iter() -> Result<()> {
466 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
467 let parser = BgpkitParser::new(url)?
468 .add_filter("peer_ip", "185.1.8.50")?
469 .add_filter("type", "w")?;
470 let count = parser.into_elem_iter().count();
471 assert_eq!(count, 39);
472
473 let parser = BgpkitParser::new(url)?
474 .add_filter("ts_start", "1637437798")?
475 .add_filter("ts_end", "2021-11-20T19:49:58Z")?;
476 let count = parser.into_elem_iter().count();
477 assert_eq!(count, 13);
478 Ok(())
479 }
480
481 #[test]
482 fn test_filter_iter_multi_peers() {
483 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
484 let parser = BgpkitParser::new(url)
485 .unwrap()
486 .add_filter("peer_ips", "185.1.8.65, 2001:7f8:73:0:3:fa4:0:1")
487 .unwrap();
488 let count = parser.into_elem_iter().count();
489 assert_eq!(count, 3393 + 834);
490 }
491
492 #[test]
493 fn test_prefix_match() {
494 let p1 = IpNet::from_str("10.1.1.0/24").unwrap();
496 let p1_exact = IpNet::from_str("10.1.1.0/24").unwrap();
497 let p1_super = IpNet::from_str("10.1.0.0/16").unwrap();
498 let p1_sub = IpNet::from_str("10.1.1.0/25").unwrap();
499
500 let p2 = IpNet::from_str("2001:0DB8:0000:000b::/64").unwrap();
501
502 assert!(prefix_match(&p1, &p1_exact, &PrefixMatchType::Exact));
504 assert!(!prefix_match(&p1, &p1_sub, &PrefixMatchType::Exact));
505 assert!(!prefix_match(&p1, &p1_super, &PrefixMatchType::Exact));
506 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::Exact));
507
508 assert!(prefix_match(&p1, &p1_exact, &PrefixMatchType::IncludeSuper));
510 assert!(!prefix_match(&p1, &p1_sub, &PrefixMatchType::IncludeSuper));
511 assert!(prefix_match(&p1, &p1_super, &PrefixMatchType::IncludeSuper));
512 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::IncludeSuper));
513
514 assert!(prefix_match(&p1, &p1_exact, &PrefixMatchType::IncludeSub));
516 assert!(prefix_match(&p1, &p1_sub, &PrefixMatchType::IncludeSub));
517 assert!(!prefix_match(&p1, &p1_super, &PrefixMatchType::IncludeSub));
518 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::IncludeSub));
519
520 assert!(prefix_match(
522 &p1,
523 &p1_exact,
524 &PrefixMatchType::IncludeSuperSub
525 ));
526 assert!(prefix_match(
527 &p1,
528 &p1_sub,
529 &PrefixMatchType::IncludeSuperSub
530 ));
531 assert!(prefix_match(
532 &p1,
533 &p1_super,
534 &PrefixMatchType::IncludeSuperSub
535 ));
536 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::IncludeSuperSub));
537 }
538
539 #[test]
540 fn test_filter_new() {
541 let filter = Filter::new("origin_asn", "12345").unwrap();
542 assert_eq!(filter, Filter::OriginAsn(12345));
543
544 let filter = Filter::new("prefix", "192.168.1.0/24").unwrap();
545 assert_eq!(
546 filter,
547 Filter::Prefix(
548 IpNet::from_str("192.168.1.0/24").unwrap(),
549 PrefixMatchType::Exact
550 )
551 );
552 let filter = Filter::new("prefix_super", "192.168.1.0/24").unwrap();
553 assert_eq!(
554 filter,
555 Filter::Prefix(
556 IpNet::from_str("192.168.1.0/24").unwrap(),
557 PrefixMatchType::IncludeSuper
558 )
559 );
560 let filter = Filter::new("prefix_sub", "192.168.1.0/24").unwrap();
561 assert_eq!(
562 filter,
563 Filter::Prefix(
564 IpNet::from_str("192.168.1.0/24").unwrap(),
565 PrefixMatchType::IncludeSub
566 )
567 );
568 let filter = Filter::new("prefix_super_sub", "192.168.1.0/24").unwrap();
569 assert_eq!(
570 filter,
571 Filter::Prefix(
572 IpNet::from_str("192.168.1.0/24").unwrap(),
573 PrefixMatchType::IncludeSuperSub
574 )
575 );
576
577 let filter = Filter::new("peer_ip", "192.168.1.1").unwrap();
578 assert_eq!(
579 filter,
580 Filter::PeerIp(IpAddr::from_str("192.168.1.1").unwrap())
581 );
582
583 let filter = Filter::new("peer_asn", "12345").unwrap();
584 assert_eq!(filter, Filter::PeerAsn(12345));
585
586 let filter = Filter::new("type", "w").unwrap();
587 assert_eq!(filter, Filter::Type(ElemType::WITHDRAW));
588
589 let filter = Filter::new("ts_start", "1637437798").unwrap();
590 assert_eq!(filter, Filter::TsStart(1637437798_f64));
591
592 let filter = Filter::new("ts_end", "1637437798").unwrap();
593 assert_eq!(filter, Filter::TsEnd(1637437798_f64));
594
595 let filter = Filter::new("as_path", r" ?174 1916 52888$").unwrap();
596 assert_eq!(
597 filter,
598 Filter::AsPath(ComparableRegex::new(r" ?174 1916 52888$").unwrap())
599 );
600
601 assert!(Filter::new("origin_asn", "not a number").is_err());
602 assert!(Filter::new("peer_asn", "not a number").is_err());
603 assert!(Filter::new("ts_start", "not a number").is_err());
604 assert!(Filter::new("ts_end", "not a number").is_err());
605 assert!(Filter::new("prefix", "not a prefix").is_err());
606 assert!(Filter::new("prefix_super", "not a prefix").is_err());
607 assert!(Filter::new("prefix_sub", "not a prefix").is_err());
608 assert!(Filter::new("peer_ip", "not a IP").is_err());
609 assert!(Filter::new("peer_ips", "not,a,IP").is_err());
610 assert!(Filter::new("type", "not a type").is_err());
611 assert!(Filter::new("as_path", "[abc").is_err());
612 assert!(Filter::new("ip_version", "5").is_err());
613 assert!(Filter::new("unknown_filter", "some_value").is_err());
614 }
615
616 #[test]
617 fn test_filterable_match_filter() {
618 let elem = BgpElem {
619 timestamp: 1637437798_f64,
620 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
621 peer_asn: Asn::new_32bit(12345),
622 prefix: NetworkPrefix::new(IpNet::from_str("192.168.1.0/24").unwrap(), 0),
623 next_hop: None,
624 as_path: Some(AsPath::from_sequence(vec![174, 1916, 52888])),
625 origin_asns: Some(vec![Asn::new_16bit(12345)]),
626 origin: None,
627 local_pref: None,
628 med: None,
629 communities: Some(vec![MetaCommunity::Large(LargeCommunity::new(
630 12345,
631 [678910, 111213],
632 ))]),
633 atomic: false,
634 aggr_asn: None,
635 aggr_ip: None,
636 only_to_customer: None,
637 unknown: None,
638 elem_type: ElemType::ANNOUNCE,
639 deprecated: None,
640 };
641
642 let mut filters = vec![];
643
644 let filter = Filter::new("origin_asn", "12345").unwrap();
645 filters.push(filter.clone());
646 assert!(elem.match_filter(&filter));
647
648 let filter = Filter::new("origin_asn", "678910").unwrap();
649 assert!(!elem.match_filter(&filter));
650
651 let filter = Filter::new("prefix", "192.168.1.0/24").unwrap();
652 filters.push(filter.clone());
653 assert!(elem.match_filter(&filter));
654
655 let filter = Filter::new("peer_ip", "192.168.1.1").unwrap();
656 filters.push(filter.clone());
657 assert!(elem.match_filter(&filter));
658
659 let filter = Filter::new("peer_asn", "12345").unwrap();
660 filters.push(filter.clone());
661 assert!(elem.match_filter(&filter));
662
663 let filter = Filter::new("type", "a").unwrap();
664 filters.push(filter.clone());
665 assert!(elem.match_filter(&filter));
666
667 let filter = Filter::new("ts_start", "1637437798").unwrap();
668 filters.push(filter.clone());
669 assert!(elem.match_filter(&filter));
670
671 let filter = Filter::new("ts_end", "1637437798").unwrap();
672 filters.push(filter.clone());
673 assert!(elem.match_filter(&filter));
674
675 let filter = Filter::new("as_path", r" ?174 1916 52888$").unwrap();
676 filters.push(filter.clone());
677 assert!(elem.match_filter(&filter));
678
679 let filter = Filter::new("ip_version", "4").unwrap();
680 filters.push(filter.clone());
681 assert!(elem.match_filter(&filter));
682
683 let filter = Filter::new("ip", "ipv6").unwrap();
684 assert!(!elem.match_filter(&filter));
685
686 let filter = Filter::new("community", r"12345:678910:111213$").unwrap();
687 filters.push(filter.clone());
688 assert!(elem.match_filter(&filter));
689
690 assert!(elem.match_filters(&filters));
691 }
692}