1use crate::models::*;
117use crate::parser::ComparableRegex;
118use crate::ParserError;
119use crate::ParserError::FilterError;
120use ipnet::IpNet;
121use std::net::IpAddr;
122use std::str::FromStr;
123
124#[derive(Debug, Clone, PartialEq)]
145pub enum Filter {
146 OriginAsn(u32),
147 OriginAsns(Vec<u32>),
148 Prefix(IpNet, PrefixMatchType),
149 Prefixes(Vec<IpNet>, PrefixMatchType),
150 PeerIp(IpAddr),
151 PeerIps(Vec<IpAddr>),
152 PeerAsn(u32),
153 PeerAsns(Vec<u32>),
154 Type(ElemType),
155 IpVersion(IpVersion),
156 TsStart(f64),
157 TsEnd(f64),
158 AsPath(ComparableRegex),
159 Community(ComparableRegex),
160 Negated(Box<Filter>),
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub enum IpVersion {
166 Ipv4,
167 Ipv6,
168}
169
170#[derive(Debug, Clone, PartialEq, Eq)]
171pub enum PrefixMatchType {
172 Exact,
173 IncludeSuper,
174 IncludeSub,
175 IncludeSuperSub,
176}
177
178fn parse_time_str(time_str: &str) -> Option<chrono::NaiveDateTime> {
179 if let Ok(t) = time_str.parse::<f64>() {
180 return chrono::DateTime::from_timestamp(t as i64, 0).map(|t| t.naive_utc());
181 }
182 if let Ok(t) = chrono::DateTime::parse_from_rfc3339(time_str) {
183 return Some(t.naive_utc());
184 }
185 None
186}
187
188fn parse_asn_list(filter_value: &str) -> Result<(Vec<u32>, bool), ParserError> {
189 let mut asns = vec![];
190 let mut all_negated: Option<bool> = None;
191
192 for asn_str in filter_value.replace(' ', "").split(',') {
193 if asn_str.is_empty() {
195 continue;
196 }
197
198 let (is_negated, actual_value) = if let Some(stripped) = asn_str.strip_prefix('!') {
199 (true, stripped)
200 } else {
201 (false, asn_str)
202 };
203
204 match all_negated {
206 None => all_negated = Some(is_negated),
207 Some(prev) if prev != is_negated => {
208 return Err(FilterError(
209 "cannot mix positive and negative values in the same filter".to_string(),
210 ));
211 }
212 _ => {}
213 }
214
215 match u32::from_str(actual_value) {
216 Ok(v) => asns.push(v),
217 Err(_) => return Err(FilterError(format!("cannot parse ASN from {actual_value}"))),
218 }
219 }
220 if asns.is_empty() {
222 return Err(FilterError(
223 "ASN list filter requires at least one ASN".to_string(),
224 ));
225 }
226 Ok((asns, all_negated.unwrap_or(false)))
227}
228
229fn parse_prefix_list(filter_value: &str) -> Result<(Vec<IpNet>, bool), ParserError> {
230 let mut prefixes = vec![];
231 let mut all_negated: Option<bool> = None;
232
233 for prefix_str in filter_value.replace(' ', "").split(',') {
234 if prefix_str.is_empty() {
236 continue;
237 }
238
239 let (is_negated, actual_value) = if let Some(stripped) = prefix_str.strip_prefix('!') {
240 (true, stripped)
241 } else {
242 (false, prefix_str)
243 };
244
245 match all_negated {
247 None => all_negated = Some(is_negated),
248 Some(prev) if prev != is_negated => {
249 return Err(FilterError(
250 "cannot mix positive and negative values in the same filter".to_string(),
251 ));
252 }
253 _ => {}
254 }
255
256 match IpNet::from_str(actual_value) {
257 Ok(v) => prefixes.push(v),
258 Err(_) => {
259 return Err(FilterError(format!(
260 "cannot parse prefix from {actual_value}"
261 )))
262 }
263 }
264 }
265 if prefixes.is_empty() {
267 return Err(FilterError(
268 "prefix list filter requires at least one prefix".to_string(),
269 ));
270 }
271 Ok((prefixes, all_negated.unwrap_or(false)))
272}
273
274fn parse_ip_list(filter_value: &str) -> Result<(Vec<IpAddr>, bool), ParserError> {
275 let mut ips = vec![];
276 let mut all_negated: Option<bool> = None;
277
278 for ip_str in filter_value.replace(' ', "").split(',') {
279 if ip_str.is_empty() {
281 continue;
282 }
283
284 let (is_negated, actual_value) = if let Some(stripped) = ip_str.strip_prefix('!') {
285 (true, stripped)
286 } else {
287 (false, ip_str)
288 };
289
290 match all_negated {
292 None => all_negated = Some(is_negated),
293 Some(prev) if prev != is_negated => {
294 return Err(FilterError(
295 "cannot mix positive and negative values in the same filter".to_string(),
296 ));
297 }
298 _ => {}
299 }
300
301 match IpAddr::from_str(actual_value) {
302 Ok(v) => ips.push(v),
303 Err(_) => {
304 return Err(FilterError(format!(
305 "cannot parse IP address from {actual_value}"
306 )))
307 }
308 }
309 }
310 if ips.is_empty() {
312 return Err(FilterError(
313 "IP list filter requires at least one IP address".to_string(),
314 ));
315 }
316 Ok((ips, all_negated.unwrap_or(false)))
317}
318
319impl Filter {
320 pub fn new(filter_type: &str, filter_value: &str) -> Result<Filter, ParserError> {
321 let multi_value_filters = [
324 "origin_asns",
325 "prefixes",
326 "prefixes_super",
327 "prefixes_sub",
328 "prefixes_super_sub",
329 "peer_ips",
330 "peer_asns",
331 ];
332
333 if multi_value_filters.contains(&filter_type) {
334 return Self::new_base(filter_type, filter_value);
336 }
337
338 let (negated, actual_value) = if let Some(stripped) = filter_value.strip_prefix('!') {
340 if stripped.starts_with('!') {
342 return Err(FilterError(format!(
343 "invalid filter value '{}': double negation is not allowed",
344 filter_value
345 )));
346 }
347 (true, stripped)
348 } else {
349 (false, filter_value)
350 };
351
352 if negated
354 && (filter_type == "ts_start"
355 || filter_type == "start_ts"
356 || filter_type == "ts_end"
357 || filter_type == "end_ts")
358 {
359 return Err(FilterError(format!(
360 "timestamp filter '{}' does not support negation",
361 filter_type
362 )));
363 }
364
365 let base_filter = Self::new_base(filter_type, actual_value)?;
366
367 if negated {
368 Ok(Filter::Negated(Box::new(base_filter)))
369 } else {
370 Ok(base_filter)
371 }
372 }
373
374 fn new_base(filter_type: &str, filter_value: &str) -> Result<Filter, ParserError> {
375 match filter_type {
376 "origin_asn" => match u32::from_str(filter_value) {
377 Ok(v) => Ok(Filter::OriginAsn(v)),
378 Err(_) => Err(FilterError(format!(
379 "cannot parse origin asn from {filter_value}"
380 ))),
381 },
382 "origin_asns" => {
383 let (asns, negated) = parse_asn_list(filter_value)?;
384 let filter = Filter::OriginAsns(asns);
385 if negated {
386 Ok(Filter::Negated(Box::new(filter)))
387 } else {
388 Ok(filter)
389 }
390 }
391 "prefix" => match IpNet::from_str(filter_value) {
392 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::Exact)),
393 Err(_) => Err(FilterError(format!(
394 "cannot parse prefix from {filter_value}"
395 ))),
396 },
397 "prefix_super" => match IpNet::from_str(filter_value) {
398 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::IncludeSuper)),
399 Err(_) => Err(FilterError(format!(
400 "cannot parse prefix from {filter_value}"
401 ))),
402 },
403 "prefix_sub" => match IpNet::from_str(filter_value) {
404 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::IncludeSub)),
405 Err(_) => Err(FilterError(format!(
406 "cannot parse prefix from {filter_value}"
407 ))),
408 },
409 "prefix_super_sub" => match IpNet::from_str(filter_value) {
410 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::IncludeSuperSub)),
411 Err(_) => Err(FilterError(format!(
412 "cannot parse prefix from {filter_value}"
413 ))),
414 },
415 "prefixes" => {
416 let (prefixes, negated) = parse_prefix_list(filter_value)?;
417 let filter = Filter::Prefixes(prefixes, PrefixMatchType::Exact);
418 if negated {
419 Ok(Filter::Negated(Box::new(filter)))
420 } else {
421 Ok(filter)
422 }
423 }
424 "prefixes_super" => {
425 let (prefixes, negated) = parse_prefix_list(filter_value)?;
426 let filter = Filter::Prefixes(prefixes, PrefixMatchType::IncludeSuper);
427 if negated {
428 Ok(Filter::Negated(Box::new(filter)))
429 } else {
430 Ok(filter)
431 }
432 }
433 "prefixes_sub" => {
434 let (prefixes, negated) = parse_prefix_list(filter_value)?;
435 let filter = Filter::Prefixes(prefixes, PrefixMatchType::IncludeSub);
436 if negated {
437 Ok(Filter::Negated(Box::new(filter)))
438 } else {
439 Ok(filter)
440 }
441 }
442 "prefixes_super_sub" => {
443 let (prefixes, negated) = parse_prefix_list(filter_value)?;
444 let filter = Filter::Prefixes(prefixes, PrefixMatchType::IncludeSuperSub);
445 if negated {
446 Ok(Filter::Negated(Box::new(filter)))
447 } else {
448 Ok(filter)
449 }
450 }
451 "peer_ip" => match IpAddr::from_str(filter_value) {
452 Ok(v) => Ok(Filter::PeerIp(v)),
453 Err(_) => Err(FilterError(format!(
454 "cannot parse peer IP from {filter_value}"
455 ))),
456 },
457 "peer_ips" => {
458 let (ips, negated) = parse_ip_list(filter_value)?;
459 let filter = Filter::PeerIps(ips);
460 if negated {
461 Ok(Filter::Negated(Box::new(filter)))
462 } else {
463 Ok(filter)
464 }
465 }
466 "peer_asn" => match u32::from_str(filter_value) {
467 Ok(v) => Ok(Filter::PeerAsn(v)),
468 Err(_) => Err(FilterError(format!(
469 "cannot parse peer asn from {filter_value}"
470 ))),
471 },
472 "peer_asns" => {
473 let (asns, negated) = parse_asn_list(filter_value)?;
474 let filter = Filter::PeerAsns(asns);
475 if negated {
476 Ok(Filter::Negated(Box::new(filter)))
477 } else {
478 Ok(filter)
479 }
480 }
481 "type" => match filter_value {
482 "w" | "withdraw" | "withdrawal" => Ok(Filter::Type(ElemType::WITHDRAW)),
483 "a" | "announce" | "announcement" => Ok(Filter::Type(ElemType::ANNOUNCE)),
484 _ => Err(FilterError(format!(
485 "cannot parse elem type from {filter_value}"
486 ))),
487 },
488 "ts_start" | "start_ts" => match parse_time_str(filter_value) {
489 Some(t) => Ok(Filter::TsStart(t.and_utc().timestamp() as f64)),
490 None => Err(FilterError(format!(
491 "cannot parse TsStart filter from {filter_value}"
492 ))),
493 },
494 "ts_end" | "end_ts" => match parse_time_str(filter_value) {
495 Some(t) => Ok(Filter::TsEnd(t.and_utc().timestamp() as f64)),
496 None => Err(FilterError(format!(
497 "cannot parse TsEnd filter from {filter_value}"
498 ))),
499 },
500 "as_path" => match ComparableRegex::new(filter_value) {
501 Ok(v) => Ok(Filter::AsPath(v)),
502 Err(_) => Err(FilterError(format!(
503 "cannot parse AS path regex from {filter_value}"
504 ))),
505 },
506 "community" => match ComparableRegex::new(filter_value) {
507 Ok(v) => Ok(Filter::Community(v)),
508 Err(_) => Err(FilterError(format!(
509 "cannot parse Community regex from {filter_value}"
510 ))),
511 },
512 "ip_version" | "ip" => match filter_value {
513 "4" | "v4" | "ipv4" => Ok(Filter::IpVersion(IpVersion::Ipv4)),
514 "6" | "v6" | "ipv6" => Ok(Filter::IpVersion(IpVersion::Ipv6)),
515 _ => Err(FilterError(format!(
516 "cannot parse IP version from {filter_value}"
517 ))),
518 },
519 _ => Err(FilterError(format!("unknown filter type: {filter_type}"))),
520 }
521 }
522}
523
524pub trait Filterable {
525 fn match_filter(&self, filter: &Filter) -> bool;
526 fn match_filters(&self, filters: &[Filter]) -> bool {
527 filters.iter().all(|f| self.match_filter(f))
528 }
529}
530
531trait RouteFilterView {
532 fn timestamp(&self) -> f64;
533 fn elem_type(&self) -> ElemType;
534 fn peer_ip(&self) -> IpAddr;
535 fn peer_asn(&self) -> Asn;
536 fn prefix(&self) -> &NetworkPrefix;
537 fn as_path(&self) -> Option<&AsPath>;
538 fn matches_origin_asn(&self, asn: Asn) -> bool;
539
540 fn matches_community(&self, _regex: &ComparableRegex) -> bool {
541 false
542 }
543
544 fn supports_community_filter(&self) -> bool {
545 false
546 }
547}
548
549const fn same_family(prefix_1: &IpNet, prefix_2: &IpNet) -> bool {
550 matches!(
551 (prefix_1, prefix_2),
552 (IpNet::V4(_), IpNet::V4(_)) | (IpNet::V6(_), IpNet::V6(_))
553 )
554}
555
556fn prefix_match(match_prefix: &IpNet, input_prefix: &IpNet, t: &PrefixMatchType) -> bool {
557 let exact = input_prefix.eq(match_prefix);
558 match t {
559 PrefixMatchType::Exact => exact,
560 PrefixMatchType::IncludeSuper => {
561 if exact {
562 exact
563 } else if !same_family(match_prefix, input_prefix) {
564 false
566 } else {
567 match_prefix.addr() >= input_prefix.addr()
569 && match_prefix.broadcast() <= input_prefix.broadcast()
570 }
571 }
572 PrefixMatchType::IncludeSub => {
573 if exact {
574 exact
575 } else if !same_family(match_prefix, input_prefix) {
576 false
578 } else {
579 match_prefix.addr() <= input_prefix.addr()
581 && match_prefix.broadcast() >= input_prefix.broadcast()
582 }
583 }
584 PrefixMatchType::IncludeSuperSub => {
585 if exact {
586 exact
587 } else if !same_family(match_prefix, input_prefix) {
588 false
590 } else {
591 (match_prefix.addr() >= input_prefix.addr()
593 && match_prefix.broadcast() <= input_prefix.broadcast())
594 || (match_prefix.addr() <= input_prefix.addr()
595 && match_prefix.broadcast() >= input_prefix.broadcast())
596 }
597 }
598 }
599}
600
601fn match_route_view_filter<T: RouteFilterView>(view: &T, filter: &Filter) -> bool {
602 match filter {
603 Filter::Negated(inner)
604 if matches!(inner.as_ref(), Filter::Community(_))
605 && !view.supports_community_filter() =>
606 {
607 false
608 }
609 Filter::Negated(inner) => !match_route_view_filter(view, inner),
610 Filter::OriginAsn(v) => view.matches_origin_asn((*v).into()),
611 Filter::OriginAsns(v) => v.iter().any(|asn| view.matches_origin_asn((*asn).into())),
612 Filter::Prefix(v, t) => prefix_match(v, &view.prefix().prefix, t),
613 Filter::Prefixes(v, t) => v
614 .iter()
615 .any(|prefix| prefix_match(prefix, &view.prefix().prefix, t)),
616 Filter::PeerIp(v) => view.peer_ip() == *v,
617 Filter::PeerIps(v) => v.contains(&view.peer_ip()),
618 Filter::PeerAsn(v) => view.peer_asn().eq(v),
619 Filter::PeerAsns(v) => v.iter().any(|asn| view.peer_asn().eq(asn)),
620 Filter::Type(v) => view.elem_type().eq(v),
621 Filter::TsStart(v) => view.timestamp() >= *v,
622 Filter::TsEnd(v) => view.timestamp() <= *v,
623 Filter::AsPath(v) => view
624 .as_path()
625 .map(|path| v.is_match(path.to_string().as_str()))
626 .unwrap_or(false),
627 Filter::Community(r) => view.supports_community_filter() && view.matches_community(r),
628 Filter::IpVersion(version) => match version {
629 IpVersion::Ipv4 => view.prefix().prefix.addr().is_ipv4(),
630 IpVersion::Ipv6 => view.prefix().prefix.addr().is_ipv6(),
631 },
632 }
633}
634
635impl RouteFilterView for BgpElem {
636 fn timestamp(&self) -> f64 {
637 self.timestamp
638 }
639
640 fn elem_type(&self) -> ElemType {
641 self.elem_type
642 }
643
644 fn peer_ip(&self) -> IpAddr {
645 self.peer_ip
646 }
647
648 fn peer_asn(&self) -> Asn {
649 self.peer_asn
650 }
651
652 fn prefix(&self) -> &NetworkPrefix {
653 &self.prefix
654 }
655
656 fn as_path(&self) -> Option<&AsPath> {
657 self.as_path.as_ref()
658 }
659
660 fn matches_origin_asn(&self, asn: Asn) -> bool {
661 self.origin_asns
662 .as_ref()
663 .map(|origins| origins.contains(&asn))
664 .unwrap_or(false)
665 }
666
667 fn matches_community(&self, regex: &ComparableRegex) -> bool {
668 self.communities
669 .as_ref()
670 .map(|communities| communities.iter().any(|c| regex.is_match(c.to_string())))
671 .unwrap_or(false)
672 }
673
674 fn supports_community_filter(&self) -> bool {
675 true
676 }
677}
678
679impl RouteFilterView for BgpRouteElem {
680 fn timestamp(&self) -> f64 {
681 self.timestamp
682 }
683
684 fn elem_type(&self) -> ElemType {
685 self.elem_type
686 }
687
688 fn peer_ip(&self) -> IpAddr {
689 self.peer_ip
690 }
691
692 fn peer_asn(&self) -> Asn {
693 self.peer_asn
694 }
695
696 fn prefix(&self) -> &NetworkPrefix {
697 &self.prefix
698 }
699
700 fn as_path(&self) -> Option<&AsPath> {
701 self.as_path.as_deref()
702 }
703
704 fn matches_origin_asn(&self, asn: Asn) -> bool {
705 self.as_path
706 .as_ref()
707 .map(|path| path.iter_origins().any(|origin| origin == asn))
708 .unwrap_or(false)
709 }
710}
711
712impl Filterable for BgpElem {
713 fn match_filter(&self, filter: &Filter) -> bool {
714 match_route_view_filter(self, filter)
715 }
716}
717
718impl Filterable for BgpRouteElem {
719 fn match_filter(&self, filter: &Filter) -> bool {
720 match_route_view_filter(self, filter)
721 }
722}
723
724#[cfg(test)]
725mod tests {
726 use super::*;
727 use crate::BgpkitParser;
728 use anyhow::Result;
729 use std::str::FromStr;
730 use std::sync::Arc;
731
732 fn filter_test_elem() -> BgpElem {
733 BgpElem {
734 timestamp: 1637437798_f64,
735 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
736 peer_asn: Asn::new_32bit(12345),
737 peer_bgp_id: None,
738 prefix: NetworkPrefix::new(IpNet::from_str("192.168.1.0/24").unwrap(), None),
739 next_hop: None,
740 as_path: Some(AsPath::from_sequence(vec![174, 1916, 52888])),
741 origin_asns: Some(vec![Asn::new_32bit(52888)]),
742 origin: None,
743 local_pref: None,
744 med: None,
745 communities: Some(vec![MetaCommunity::Large(LargeCommunity::new(
746 12345,
747 [678910, 111213],
748 ))]),
749 atomic: false,
750 aggr_asn: None,
751 aggr_ip: None,
752 only_to_customer: None,
753 unknown: None,
754 elem_type: ElemType::ANNOUNCE,
755 deprecated: None,
756 }
757 }
758
759 fn route_projection(elem: &BgpElem) -> BgpRouteElem {
760 BgpRouteElem {
761 timestamp: elem.timestamp,
762 elem_type: elem.elem_type,
763 peer_ip: elem.peer_ip,
764 peer_asn: elem.peer_asn,
765 prefix: elem.prefix,
766 as_path: elem.as_path.clone().map(Arc::new),
767 }
768 }
769
770 #[test]
771 fn test_route_community_filters_fail_closed() {
772 let elem = filter_test_elem();
773 let route = route_projection(&elem);
774 let community = Filter::new("community", r"12345:.*").unwrap();
775 let negated_community = Filter::new("community", r"!12345:.*").unwrap();
776
777 assert!(elem.match_filter(&community));
778 assert!(!elem.match_filter(&negated_community));
779 assert!(!route.match_filter(&community));
780 assert!(!route.match_filter(&negated_community));
781 }
782
783 #[test]
784 fn test_filters_on_mrt_file() {
785 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
786 let parser = BgpkitParser::new(url).unwrap();
787 let elems = parser.into_elem_iter().collect::<Vec<BgpElem>>();
788
789 let filters = vec![Filter::PeerIp(IpAddr::from_str("185.1.8.65").unwrap())];
790 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
791 assert_eq!(count, 3393);
792
793 let filters = vec![
794 Filter::PeerIp(IpAddr::from_str("185.1.8.65").unwrap()),
795 Filter::Prefix(
796 IpNet::from_str("190.115.192.0/22").unwrap(),
797 PrefixMatchType::Exact,
798 ),
799 ];
800 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
801 assert_eq!(count, 5);
802
803 let filters = vec![Filter::Prefix(
804 IpNet::from_str("190.115.192.0/24").unwrap(),
805 PrefixMatchType::IncludeSuper,
806 )];
807 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
808 assert_eq!(count, 18);
809
810 let filters = vec![Filter::Prefix(
811 IpNet::from_str("190.115.192.0/22").unwrap(),
812 PrefixMatchType::IncludeSub,
813 )];
814 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
815 assert_eq!(count, 42);
816
817 let filters = vec![Filter::Prefix(
818 IpNet::from_str("190.115.192.0/23").unwrap(),
819 PrefixMatchType::IncludeSuperSub,
820 )];
821 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
822 assert_eq!(count, 24);
823
824 let filters = vec![Filter::new("as_path", r" ?174 1916 52888$").unwrap()];
825 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
826 assert_eq!(count, 12);
827
828 let filters = vec![Filter::new("community", r"60924:.*").unwrap()];
830 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
831 assert_eq!(count, 4243);
832
833 let filters = vec![Filter::new("community", r".+:784$").unwrap()];
835 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
836 assert_eq!(count, 107);
837
838 let filters = vec![Filter::new("community", r"\d+:\d+:\d+$").unwrap()];
840 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
841 assert_eq!(count, 4397);
842
843 let filters = vec![
844 Filter::TsStart(1637437798_f64),
845 Filter::TsEnd(1637437798_f64),
846 ];
847 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
848 assert_eq!(count, 13);
849
850 let filters = vec![Filter::Type(ElemType::WITHDRAW)];
851 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
852 assert_eq!(count, 379);
853
854 let filters = vec![
855 Filter::Type(ElemType::WITHDRAW),
856 Filter::Prefix(
857 IpNet::from_str("2804:100::/32").unwrap(),
858 PrefixMatchType::Exact,
859 ),
860 ];
861 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
862 assert_eq!(count, 1);
863
864 let filters = vec![Filter::PeerIps(vec![
875 IpAddr::from_str("185.1.8.65").unwrap(),
876 IpAddr::from_str("2001:7f8:73:0:3:fa4:0:1").unwrap(),
877 ])];
878 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
879 assert_eq!(count, 3393 + 834);
880 }
881
882 #[test]
883 fn test_filter_incorrect_filters() {
884 let incorrect_filters = [
886 Filter::new("community", r"[abc"),
887 Filter::new("as_path", r"[0-9"),
888 Filter::new("prefix_super_sub", "-192.-168.-1.1/24"),
889 ];
890 assert!(incorrect_filters
891 .iter()
892 .all(|f| matches!(f, Err(FilterError(_)))));
893 }
894
895 #[test]
896 fn test_parsing_time_str() {
897 let ts = chrono::NaiveDateTime::from_str("2021-11-20T19:49:58").unwrap();
898 assert_eq!(parse_time_str("1637437798"), Some(ts));
899 assert_eq!(parse_time_str("2021-11-20T19:49:58Z"), Some(ts));
900 assert_eq!(parse_time_str("2021-11-20T19:49:58+00:00"), Some(ts));
901
902 assert_eq!(parse_time_str("2021-11-20T19:49:58"), None);
903 assert_eq!(parse_time_str("2021-11-20T19:49:58ZDXV"), None);
904 assert_eq!(parse_time_str("2021-11-20 19:49:58"), None);
905 assert_eq!(parse_time_str("2021-11-20"), None);
906 }
907
908 #[test]
909 fn test_filter_iter() -> Result<()> {
910 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
911 let parser = BgpkitParser::new(url)?
912 .add_filter("peer_ip", "185.1.8.50")?
913 .add_filter("type", "w")?;
914 let count = parser.into_elem_iter().count();
915 assert_eq!(count, 39);
916
917 let parser = BgpkitParser::new(url)?
918 .add_filter("ts_start", "1637437798")?
919 .add_filter("ts_end", "2021-11-20T19:49:58Z")?;
920 let count = parser.into_elem_iter().count();
921 assert_eq!(count, 13);
922 Ok(())
923 }
924
925 #[test]
926 fn test_filter_iter_with_negation() -> Result<()> {
927 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
928
929 let parser = BgpkitParser::new(url)?.add_filter("peer_ip", "!185.1.8.65")?;
932 let count = parser.into_elem_iter().count();
933 assert_eq!(count, 8160 - 3393);
934
935 let parser = BgpkitParser::new(url)?.add_filter("type", "!w")?;
938 let count = parser.into_elem_iter().count();
939 assert_eq!(count, 8160 - 379);
940
941 let parser = BgpkitParser::new(url)?
944 .add_filter("peer_ip", "185.1.8.50")?
945 .add_filter("type", "!w")?;
946 let count = parser.into_elem_iter().count();
947 assert_eq!(count, 1563 - 39);
949
950 Ok(())
951 }
952
953 #[test]
954 fn test_filter_iter_multi_peers() {
955 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
956 let parser = BgpkitParser::new(url)
957 .unwrap()
958 .add_filter("peer_ips", "185.1.8.65, 2001:7f8:73:0:3:fa4:0:1")
959 .unwrap();
960 let count = parser.into_elem_iter().count();
961 assert_eq!(count, 3393 + 834);
962 }
963
964 #[test]
965 fn test_prefix_match() {
966 let p1 = IpNet::from_str("10.1.1.0/24").unwrap();
968 let p1_exact = IpNet::from_str("10.1.1.0/24").unwrap();
969 let p1_super = IpNet::from_str("10.1.0.0/16").unwrap();
970 let p1_sub = IpNet::from_str("10.1.1.0/25").unwrap();
971
972 let p2 = IpNet::from_str("2001:0DB8:0000:000b::/64").unwrap();
973
974 assert!(prefix_match(&p1, &p1_exact, &PrefixMatchType::Exact));
976 assert!(!prefix_match(&p1, &p1_sub, &PrefixMatchType::Exact));
977 assert!(!prefix_match(&p1, &p1_super, &PrefixMatchType::Exact));
978 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::Exact));
979
980 assert!(prefix_match(&p1, &p1_exact, &PrefixMatchType::IncludeSuper));
982 assert!(!prefix_match(&p1, &p1_sub, &PrefixMatchType::IncludeSuper));
983 assert!(prefix_match(&p1, &p1_super, &PrefixMatchType::IncludeSuper));
984 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::IncludeSuper));
985
986 assert!(prefix_match(&p1, &p1_exact, &PrefixMatchType::IncludeSub));
988 assert!(prefix_match(&p1, &p1_sub, &PrefixMatchType::IncludeSub));
989 assert!(!prefix_match(&p1, &p1_super, &PrefixMatchType::IncludeSub));
990 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::IncludeSub));
991
992 assert!(prefix_match(
994 &p1,
995 &p1_exact,
996 &PrefixMatchType::IncludeSuperSub
997 ));
998 assert!(prefix_match(
999 &p1,
1000 &p1_sub,
1001 &PrefixMatchType::IncludeSuperSub
1002 ));
1003 assert!(prefix_match(
1004 &p1,
1005 &p1_super,
1006 &PrefixMatchType::IncludeSuperSub
1007 ));
1008 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::IncludeSuperSub));
1009 }
1010
1011 #[test]
1012 fn test_filter_new() {
1013 let filter = Filter::new("origin_asn", "12345").unwrap();
1014 assert_eq!(filter, Filter::OriginAsn(12345));
1015
1016 let filter = Filter::new("origin_asn", "!12345").unwrap();
1018 assert_eq!(filter, Filter::Negated(Box::new(Filter::OriginAsn(12345))));
1019
1020 let filter = Filter::new("prefix", "!192.168.1.0/24").unwrap();
1021 assert_eq!(
1022 filter,
1023 Filter::Negated(Box::new(Filter::Prefix(
1024 IpNet::from_str("192.168.1.0/24").unwrap(),
1025 PrefixMatchType::Exact
1026 )))
1027 );
1028
1029 let filter = Filter::new("peer_ip", "!192.168.1.1").unwrap();
1030 assert_eq!(
1031 filter,
1032 Filter::Negated(Box::new(Filter::PeerIp(
1033 IpAddr::from_str("192.168.1.1").unwrap()
1034 )))
1035 );
1036
1037 let filter = Filter::new("peer_asn", "!12345").unwrap();
1038 assert_eq!(filter, Filter::Negated(Box::new(Filter::PeerAsn(12345))));
1039
1040 let filter = Filter::new("type", "!w").unwrap();
1041 assert_eq!(
1042 filter,
1043 Filter::Negated(Box::new(Filter::Type(ElemType::WITHDRAW)))
1044 );
1045
1046 let filter = Filter::new("ip_version", "!4").unwrap();
1047 assert_eq!(
1048 filter,
1049 Filter::Negated(Box::new(Filter::IpVersion(IpVersion::Ipv4)))
1050 );
1051
1052 let filter = Filter::new("prefix", "192.168.1.0/24").unwrap();
1053 assert_eq!(
1054 filter,
1055 Filter::Prefix(
1056 IpNet::from_str("192.168.1.0/24").unwrap(),
1057 PrefixMatchType::Exact
1058 )
1059 );
1060 let filter = Filter::new("prefix_super", "192.168.1.0/24").unwrap();
1061 assert_eq!(
1062 filter,
1063 Filter::Prefix(
1064 IpNet::from_str("192.168.1.0/24").unwrap(),
1065 PrefixMatchType::IncludeSuper
1066 )
1067 );
1068 let filter = Filter::new("prefix_sub", "192.168.1.0/24").unwrap();
1069 assert_eq!(
1070 filter,
1071 Filter::Prefix(
1072 IpNet::from_str("192.168.1.0/24").unwrap(),
1073 PrefixMatchType::IncludeSub
1074 )
1075 );
1076 let filter = Filter::new("prefix_super_sub", "192.168.1.0/24").unwrap();
1077 assert_eq!(
1078 filter,
1079 Filter::Prefix(
1080 IpNet::from_str("192.168.1.0/24").unwrap(),
1081 PrefixMatchType::IncludeSuperSub
1082 )
1083 );
1084
1085 let filter = Filter::new("peer_ip", "192.168.1.1").unwrap();
1086 assert_eq!(
1087 filter,
1088 Filter::PeerIp(IpAddr::from_str("192.168.1.1").unwrap())
1089 );
1090
1091 let filter = Filter::new("peer_asn", "12345").unwrap();
1092 assert_eq!(filter, Filter::PeerAsn(12345));
1093
1094 let filter = Filter::new("type", "w").unwrap();
1095 assert_eq!(filter, Filter::Type(ElemType::WITHDRAW));
1096
1097 let filter = Filter::new("ts_start", "1637437798").unwrap();
1098 assert_eq!(filter, Filter::TsStart(1637437798_f64));
1099
1100 let filter = Filter::new("ts_end", "1637437798").unwrap();
1101 assert_eq!(filter, Filter::TsEnd(1637437798_f64));
1102
1103 let filter = Filter::new("as_path", r" ?174 1916 52888$").unwrap();
1104 assert_eq!(
1105 filter,
1106 Filter::AsPath(ComparableRegex::new(r" ?174 1916 52888$").unwrap())
1107 );
1108
1109 assert!(Filter::new("origin_asn", "not a number").is_err());
1110 assert!(Filter::new("peer_asn", "not a number").is_err());
1111 assert!(Filter::new("ts_start", "not a number").is_err());
1112 assert!(Filter::new("ts_end", "not a number").is_err());
1113 assert!(Filter::new("prefix", "not a prefix").is_err());
1114 assert!(Filter::new("prefix_super", "not a prefix").is_err());
1115 assert!(Filter::new("prefix_sub", "not a prefix").is_err());
1116 assert!(Filter::new("peer_ip", "not a IP").is_err());
1117 assert!(Filter::new("peer_ips", "not,a,IP").is_err());
1118 assert!(Filter::new("type", "not a type").is_err());
1119 assert!(Filter::new("as_path", "[abc").is_err());
1120 assert!(Filter::new("ip_version", "5").is_err());
1121 assert!(Filter::new("unknown_filter", "some_value").is_err());
1122 }
1123
1124 #[test]
1125 fn test_filterable_match_filter() {
1126 let elem = filter_test_elem();
1127
1128 let mut filters = vec![];
1129
1130 let filter = Filter::new("origin_asn", "52888").unwrap();
1131 filters.push(filter.clone());
1132 assert!(elem.match_filter(&filter));
1133
1134 let filter = Filter::new("origin_asn", "678910").unwrap();
1135 assert!(!elem.match_filter(&filter));
1136
1137 let filter = Filter::new("prefix", "192.168.1.0/24").unwrap();
1138 filters.push(filter.clone());
1139 assert!(elem.match_filter(&filter));
1140
1141 let filter = Filter::new("peer_ip", "192.168.1.1").unwrap();
1142 filters.push(filter.clone());
1143 assert!(elem.match_filter(&filter));
1144
1145 let filter = Filter::new("peer_asn", "12345").unwrap();
1146 filters.push(filter.clone());
1147 assert!(elem.match_filter(&filter));
1148
1149 let filter = Filter::new("type", "a").unwrap();
1150 filters.push(filter.clone());
1151 assert!(elem.match_filter(&filter));
1152
1153 let filter = Filter::new("ts_start", "1637437798").unwrap();
1154 filters.push(filter.clone());
1155 assert!(elem.match_filter(&filter));
1156
1157 let filter = Filter::new("ts_end", "1637437798").unwrap();
1158 filters.push(filter.clone());
1159 assert!(elem.match_filter(&filter));
1160
1161 let filter = Filter::new("as_path", r" ?174 1916 52888$").unwrap();
1162 filters.push(filter.clone());
1163 assert!(elem.match_filter(&filter));
1164
1165 let filter = Filter::new("ip_version", "4").unwrap();
1166 filters.push(filter.clone());
1167 assert!(elem.match_filter(&filter));
1168
1169 let filter = Filter::new("ip", "ipv6").unwrap();
1170 assert!(!elem.match_filter(&filter));
1171
1172 let filter = Filter::new("community", r"12345:678910:111213$").unwrap();
1173 filters.push(filter.clone());
1174 assert!(elem.match_filter(&filter));
1175
1176 assert!(elem.match_filters(&filters));
1177 }
1178
1179 #[test]
1180 fn test_route_filterable_matches_elem_for_route_level_filters() {
1181 let elem = filter_test_elem();
1182 let route = route_projection(&elem);
1183
1184 let cases = vec![
1185 (
1186 "origin_asn matches",
1187 Filter::new("origin_asn", "52888").unwrap(),
1188 true,
1189 ),
1190 (
1191 "origin_asn misses",
1192 Filter::new("origin_asn", "64496").unwrap(),
1193 false,
1194 ),
1195 (
1196 "origin_asns matches",
1197 Filter::new("origin_asns", "64496,52888").unwrap(),
1198 true,
1199 ),
1200 (
1201 "origin_asns misses",
1202 Filter::new("origin_asns", "64496,64497").unwrap(),
1203 false,
1204 ),
1205 (
1206 "prefix exact matches",
1207 Filter::new("prefix", "192.168.1.0/24").unwrap(),
1208 true,
1209 ),
1210 (
1211 "prefix exact misses",
1212 Filter::new("prefix", "192.168.2.0/24").unwrap(),
1213 false,
1214 ),
1215 (
1216 "prefix_super matches",
1217 Filter::new("prefix_super", "192.168.1.128/25").unwrap(),
1218 true,
1219 ),
1220 (
1221 "prefix_sub matches",
1222 Filter::new("prefix_sub", "192.168.0.0/23").unwrap(),
1223 true,
1224 ),
1225 (
1226 "prefix_super_sub matches",
1227 Filter::new("prefix_super_sub", "192.168.1.128/25").unwrap(),
1228 true,
1229 ),
1230 (
1231 "prefixes matches",
1232 Filter::new("prefixes", "10.0.0.0/8,192.168.1.0/24").unwrap(),
1233 true,
1234 ),
1235 (
1236 "peer_ip matches",
1237 Filter::new("peer_ip", "192.168.1.1").unwrap(),
1238 true,
1239 ),
1240 (
1241 "peer_ips matches",
1242 Filter::new("peer_ips", "192.168.1.2,192.168.1.1").unwrap(),
1243 true,
1244 ),
1245 (
1246 "peer_asn matches",
1247 Filter::new("peer_asn", "12345").unwrap(),
1248 true,
1249 ),
1250 (
1251 "peer_asns matches",
1252 Filter::new("peer_asns", "12346,12345").unwrap(),
1253 true,
1254 ),
1255 ("type matches", Filter::new("type", "a").unwrap(), true),
1256 ("type misses", Filter::new("type", "w").unwrap(), false),
1257 (
1258 "ts_start matches",
1259 Filter::new("ts_start", "1637437797").unwrap(),
1260 true,
1261 ),
1262 (
1263 "ts_end matches",
1264 Filter::new("ts_end", "1637437799").unwrap(),
1265 true,
1266 ),
1267 (
1268 "as_path matches",
1269 Filter::new("as_path", r"174 1916 52888$").unwrap(),
1270 true,
1271 ),
1272 (
1273 "as_path misses",
1274 Filter::new("as_path", r"64496$").unwrap(),
1275 false,
1276 ),
1277 (
1278 "ip_version matches",
1279 Filter::new("ip_version", "4").unwrap(),
1280 true,
1281 ),
1282 (
1283 "ip_version misses",
1284 Filter::new("ip_version", "6").unwrap(),
1285 false,
1286 ),
1287 (
1288 "negated origin_asn matches",
1289 Filter::new("origin_asn", "!64496").unwrap(),
1290 true,
1291 ),
1292 (
1293 "negated origin_asn misses",
1294 Filter::new("origin_asn", "!52888").unwrap(),
1295 false,
1296 ),
1297 ];
1298
1299 for (name, filter, expected) in cases {
1300 assert_eq!(elem.match_filter(&filter), expected, "{name} BgpElem");
1301 assert_eq!(route.match_filter(&filter), expected, "{name} BgpRouteElem");
1302 assert_eq!(
1303 elem.match_filter(&filter),
1304 route.match_filter(&filter),
1305 "{name} parity"
1306 );
1307 }
1308
1309 let filters = vec![
1310 Filter::new("origin_asn", "52888").unwrap(),
1311 Filter::new("peer_asn", "!64496").unwrap(),
1312 Filter::new("prefix_super", "192.168.1.128/25").unwrap(),
1313 ];
1314 assert!(elem.match_filters(&filters));
1315 assert_eq!(elem.match_filters(&filters), route.match_filters(&filters));
1316 }
1317
1318 #[test]
1319 fn test_route_filterable_does_not_match_community_filters() {
1320 let elem = filter_test_elem();
1321 let route = route_projection(&elem);
1322 let filter = Filter::new("community", r"12345:678910:111213$").unwrap();
1323
1324 assert!(elem.match_filter(&filter));
1325 assert!(!route.match_filter(&filter));
1326 }
1327
1328 #[test]
1329 fn test_negated_filters() {
1330 let elem = BgpElem {
1331 timestamp: 1637437798_f64,
1332 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
1333 peer_asn: Asn::new_32bit(12345),
1334 peer_bgp_id: None,
1335 prefix: NetworkPrefix::new(IpNet::from_str("192.168.1.0/24").unwrap(), None),
1336 next_hop: None,
1337 as_path: Some(AsPath::from_sequence(vec![174, 1916, 52888])),
1338 origin_asns: Some(vec![Asn::new_16bit(12345)]),
1339 origin: None,
1340 local_pref: None,
1341 med: None,
1342 communities: Some(vec![MetaCommunity::Large(LargeCommunity::new(
1343 12345,
1344 [678910, 111213],
1345 ))]),
1346 atomic: false,
1347 aggr_asn: None,
1348 aggr_ip: None,
1349 only_to_customer: None,
1350 unknown: None,
1351 elem_type: ElemType::ANNOUNCE,
1352 deprecated: None,
1353 };
1354
1355 let filter = Filter::new("origin_asn", "!12345").unwrap();
1358 assert!(!elem.match_filter(&filter));
1359
1360 let filter = Filter::new("origin_asn", "!99999").unwrap();
1362 assert!(elem.match_filter(&filter));
1363
1364 let filter = Filter::new("prefix", "!192.168.1.0/24").unwrap();
1367 assert!(!elem.match_filter(&filter));
1368
1369 let filter = Filter::new("prefix", "!10.0.0.0/8").unwrap();
1371 assert!(elem.match_filter(&filter));
1372
1373 let filter = Filter::new("peer_ip", "!192.168.1.1").unwrap();
1376 assert!(!elem.match_filter(&filter));
1377
1378 let filter = Filter::new("peer_ip", "!10.0.0.1").unwrap();
1380 assert!(elem.match_filter(&filter));
1381
1382 let filter = Filter::new("peer_asn", "!12345").unwrap();
1385 assert!(!elem.match_filter(&filter));
1386
1387 let filter = Filter::new("peer_asn", "!99999").unwrap();
1389 assert!(elem.match_filter(&filter));
1390
1391 let filter = Filter::new("type", "!a").unwrap();
1394 assert!(!elem.match_filter(&filter));
1395
1396 let filter = Filter::new("type", "!w").unwrap();
1398 assert!(elem.match_filter(&filter));
1399
1400 let filter = Filter::new("ip_version", "!4").unwrap();
1403 assert!(!elem.match_filter(&filter));
1404
1405 let filter = Filter::new("ip_version", "!6").unwrap();
1407 assert!(elem.match_filter(&filter));
1408
1409 let filter = Filter::new("as_path", r"!174 1916 52888$").unwrap();
1412 assert!(!elem.match_filter(&filter));
1413
1414 let filter = Filter::new("as_path", r"!99999$").unwrap();
1416 assert!(elem.match_filter(&filter));
1417
1418 let filter = Filter::new("community", r"!12345:678910:111213$").unwrap();
1420 assert!(!elem.match_filter(&filter));
1421
1422 let filter = Filter::new("community", r"!99999:99999$").unwrap();
1423 assert!(elem.match_filter(&filter));
1424
1425 let filter = Filter::new("peer_ips", "!192.168.1.1, !10.0.0.1").unwrap();
1427 assert!(!elem.match_filter(&filter)); let filter = Filter::new("peer_ips", "!10.0.0.1, !10.0.0.2").unwrap();
1430 assert!(elem.match_filter(&filter)); let filters = vec![
1434 Filter::new("origin_asn", "12345").unwrap(), Filter::new("peer_asn", "!99999").unwrap(), Filter::new("prefix", "!10.0.0.0/8").unwrap(), ];
1438 assert!(elem.match_filters(&filters));
1439
1440 let filters = vec![
1442 Filter::new("origin_asn", "12345").unwrap(), Filter::new("origin_asn", "!12345").unwrap(), ];
1445 assert!(!elem.match_filters(&filters));
1446 }
1447
1448 #[test]
1449 fn test_negated_filters_on_mrt_file() {
1450 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
1451 let parser = BgpkitParser::new(url).unwrap();
1452 let elems = parser.into_elem_iter().collect::<Vec<BgpElem>>();
1453
1454 let filters = vec![Filter::PeerIp(IpAddr::from_str("185.1.8.65").unwrap())];
1456 let count_with_peer = elems.iter().filter(|e| e.match_filters(&filters)).count();
1457 assert_eq!(count_with_peer, 3393);
1458
1459 let filters = vec![Filter::new("peer_ip", "!185.1.8.65").unwrap()];
1461 let count_without_peer = elems.iter().filter(|e| e.match_filters(&filters)).count();
1462 assert_eq!(count_without_peer, elems.len() - 3393);
1463
1464 assert_eq!(count_with_peer + count_without_peer, elems.len());
1466
1467 let filters = vec![Filter::Type(ElemType::WITHDRAW)];
1469 let count_withdrawals = elems.iter().filter(|e| e.match_filters(&filters)).count();
1470 assert_eq!(count_withdrawals, 379);
1471
1472 let filters = vec![Filter::new("type", "!w").unwrap()];
1473 let count_not_withdrawals = elems.iter().filter(|e| e.match_filters(&filters)).count();
1474 assert_eq!(count_not_withdrawals, elems.len() - 379);
1475
1476 let filters = vec![Filter::Prefix(
1478 IpNet::from_str("190.115.192.0/22").unwrap(),
1479 PrefixMatchType::Exact,
1480 )];
1481 let count_with_prefix = elems.iter().filter(|e| e.match_filters(&filters)).count();
1482
1483 let filters = vec![Filter::new("prefix", "!190.115.192.0/22").unwrap()];
1484 let count_without_prefix = elems.iter().filter(|e| e.match_filters(&filters)).count();
1485 assert_eq!(count_with_prefix + count_without_prefix, elems.len());
1486
1487 let filters = vec![Filter::Prefix(
1489 IpNet::from_str("190.115.192.0/24").unwrap(),
1490 PrefixMatchType::IncludeSuper,
1491 )];
1492 let count_with_super = elems.iter().filter(|e| e.match_filters(&filters)).count();
1493
1494 let filters = vec![Filter::new("prefix_super", "!190.115.192.0/24").unwrap()];
1495 let count_without_super = elems.iter().filter(|e| e.match_filters(&filters)).count();
1496 assert_eq!(count_with_super + count_without_super, elems.len());
1497
1498 let filters = vec![Filter::Prefix(
1500 IpNet::from_str("190.115.192.0/22").unwrap(),
1501 PrefixMatchType::IncludeSub,
1502 )];
1503 let count_with_sub = elems.iter().filter(|e| e.match_filters(&filters)).count();
1504
1505 let filters = vec![Filter::new("prefix_sub", "!190.115.192.0/22").unwrap()];
1506 let count_without_sub = elems.iter().filter(|e| e.match_filters(&filters)).count();
1507 assert_eq!(count_with_sub + count_without_sub, elems.len());
1508
1509 let filters = vec![Filter::Prefix(
1511 IpNet::from_str("190.115.192.0/23").unwrap(),
1512 PrefixMatchType::IncludeSuperSub,
1513 )];
1514 let count_with_super_sub = elems.iter().filter(|e| e.match_filters(&filters)).count();
1515
1516 let filters = vec![Filter::new("prefix_super_sub", "!190.115.192.0/23").unwrap()];
1517 let count_without_super_sub = elems.iter().filter(|e| e.match_filters(&filters)).count();
1518 assert_eq!(count_with_super_sub + count_without_super_sub, elems.len());
1519 }
1520
1521 #[test]
1522 fn test_double_negation_rejected() {
1523 let result = Filter::new("origin_asn", "!!13335");
1526 assert!(result.is_err());
1527 let err = result.unwrap_err();
1528 assert!(err.to_string().contains("double negation"));
1529
1530 let result = Filter::new("prefix", "!!!10.0.0.0/8");
1531 assert!(result.is_err());
1532 let err = result.unwrap_err();
1533 assert!(err.to_string().contains("double negation"));
1534 }
1535
1536 #[test]
1537 fn test_timestamp_negation_rejected() {
1538 let result = Filter::new("ts_start", "!1637437798");
1540 assert!(result.is_err());
1541 let err = result.unwrap_err();
1542 assert!(err
1543 .to_string()
1544 .contains("timestamp filter 'ts_start' does not support negation"));
1545
1546 let result = Filter::new("ts_end", "!1637437798");
1547 assert!(result.is_err());
1548 let err = result.unwrap_err();
1549 assert!(err
1550 .to_string()
1551 .contains("timestamp filter 'ts_end' does not support negation"));
1552
1553 let result = Filter::new("start_ts", "!1637437798");
1554 assert!(result.is_err());
1555
1556 let result = Filter::new("end_ts", "!1637437798");
1557 assert!(result.is_err());
1558 }
1559
1560 #[test]
1561 fn test_multiple_origin_asns() -> Result<()> {
1562 let filter = Filter::new("origin_asns", "12345,67890,13335").unwrap();
1564 match filter {
1565 Filter::OriginAsns(asns) => {
1566 assert_eq!(asns.len(), 3);
1567 assert!(asns.contains(&12345));
1568 assert!(asns.contains(&67890));
1569 assert!(asns.contains(&13335));
1570 }
1571 _ => panic!("Expected OriginAsns filter"),
1572 }
1573
1574 let filter = Filter::new("origin_asns", "12345, 67890, 13335").unwrap();
1576 match filter {
1577 Filter::OriginAsns(asns) => {
1578 assert_eq!(asns.len(), 3);
1579 }
1580 _ => panic!("Expected OriginAsns filter"),
1581 }
1582
1583 Ok(())
1584 }
1585
1586 #[test]
1587 fn test_multiple_prefixes() -> Result<()> {
1588 let prefix1 = IpNet::from_str("190.115.192.0/22").unwrap();
1590 let prefix2 = IpNet::from_str("2804:100::/32").unwrap();
1591
1592 let filter = Filter::new("prefixes", "190.115.192.0/22,2804:100::/32").unwrap();
1593 match filter {
1594 Filter::Prefixes(prefixes, match_type) => {
1595 assert_eq!(prefixes.len(), 2);
1596 assert!(prefixes.contains(&prefix1));
1597 assert!(prefixes.contains(&prefix2));
1598 assert_eq!(match_type, PrefixMatchType::Exact);
1599 }
1600 _ => panic!("Expected Prefixes filter"),
1601 }
1602
1603 let filter = Filter::new("prefixes", "190.115.192.0/22, 2804:100::/32").unwrap();
1605 match filter {
1606 Filter::Prefixes(prefixes, _) => {
1607 assert_eq!(prefixes.len(), 2);
1608 }
1609 _ => panic!("Expected Prefixes filter"),
1610 }
1611
1612 Ok(())
1613 }
1614
1615 #[test]
1616 fn test_multiple_prefixes_with_match_types() -> Result<()> {
1617 let filter = Filter::new("prefixes_super", "190.115.192.0/24,2804:100::/32").unwrap();
1619 match filter {
1620 Filter::Prefixes(prefixes, match_type) => {
1621 assert_eq!(prefixes.len(), 2);
1622 assert_eq!(match_type, PrefixMatchType::IncludeSuper);
1623 }
1624 _ => panic!("Expected Prefixes filter with IncludeSuper"),
1625 }
1626
1627 let filter = Filter::new("prefixes_sub", "190.115.192.0/22,2804:100::/32").unwrap();
1629 match filter {
1630 Filter::Prefixes(prefixes, match_type) => {
1631 assert_eq!(prefixes.len(), 2);
1632 assert_eq!(match_type, PrefixMatchType::IncludeSub);
1633 }
1634 _ => panic!("Expected Prefixes filter with IncludeSub"),
1635 }
1636
1637 let filter = Filter::new("prefixes_super_sub", "190.115.192.0/23,2804:100::/32").unwrap();
1639 match filter {
1640 Filter::Prefixes(prefixes, match_type) => {
1641 assert_eq!(prefixes.len(), 2);
1642 assert_eq!(match_type, PrefixMatchType::IncludeSuperSub);
1643 }
1644 _ => panic!("Expected Prefixes filter with IncludeSuperSub"),
1645 }
1646
1647 Ok(())
1648 }
1649
1650 #[test]
1651 fn test_multiple_peer_asns() -> Result<()> {
1652 let filter = Filter::new("peer_asns", "12345,67890,13335").unwrap();
1654 match filter {
1655 Filter::PeerAsns(asns) => {
1656 assert_eq!(asns.len(), 3);
1657 assert!(asns.contains(&12345));
1658 assert!(asns.contains(&67890));
1659 assert!(asns.contains(&13335));
1660 }
1661 _ => panic!("Expected PeerAsns filter"),
1662 }
1663
1664 Ok(())
1665 }
1666
1667 #[test]
1668 fn test_negated_multiple_filters() -> Result<()> {
1669 let filter = Filter::new("origin_asns", "!13335,!15169").unwrap();
1671 assert!(matches!(filter, Filter::Negated(_)));
1672
1673 let filter = Filter::new("prefixes", "!1.1.1.0/24,!8.8.8.0/24").unwrap();
1675 assert!(matches!(filter, Filter::Negated(_)));
1676
1677 let filter = Filter::new("peer_asns", "!12345,!67890").unwrap();
1679 assert!(matches!(filter, Filter::Negated(_)));
1680
1681 Ok(())
1682 }
1683
1684 #[test]
1685 fn test_invalid_multiple_filters() {
1686 let result = Filter::new("origin_asns", "12345,not_a_number,67890");
1688 assert!(result.is_err());
1689
1690 let result = Filter::new("prefixes", "1.1.1.0/24,invalid_prefix");
1692 assert!(result.is_err());
1693
1694 let result = Filter::new("peer_asns", "12345,invalid,67890");
1696 assert!(result.is_err());
1697
1698 let result = Filter::new("peer_ips", "192.168.1.1,invalid_ip");
1700 assert!(result.is_err());
1701
1702 let result = Filter::new("origin_asns", "12345,!67890");
1704 assert!(result.is_err());
1705 assert!(result
1706 .unwrap_err()
1707 .to_string()
1708 .contains("cannot mix positive and negative values"));
1709
1710 let result = Filter::new("prefixes", "1.1.1.0/24,!8.8.8.0/24");
1711 assert!(result.is_err());
1712 assert!(result
1713 .unwrap_err()
1714 .to_string()
1715 .contains("cannot mix positive and negative values"));
1716
1717 let result = Filter::new("peer_ips", "192.168.1.1,!10.0.0.1");
1718 assert!(result.is_err());
1719 assert!(result
1720 .unwrap_err()
1721 .to_string()
1722 .contains("cannot mix positive and negative values"));
1723
1724 let result = Filter::new("peer_asns", "!12345,67890");
1725 assert!(result.is_err());
1726 assert!(result
1727 .unwrap_err()
1728 .to_string()
1729 .contains("cannot mix positive and negative values"));
1730
1731 let result = Filter::new("origin_asns", "");
1733 assert!(result.is_err());
1734 assert!(result.unwrap_err().to_string().contains("at least one ASN"));
1735
1736 let result = Filter::new("prefixes", "");
1738 assert!(result.is_err());
1739 assert!(result
1740 .unwrap_err()
1741 .to_string()
1742 .contains("at least one prefix"));
1743
1744 let result = Filter::new("peer_ips", "");
1746 assert!(result.is_err());
1747 assert!(result.unwrap_err().to_string().contains("at least one IP"));
1748
1749 let result = Filter::new("origin_asns", ",,,");
1751 assert!(result.is_err());
1752 assert!(result.unwrap_err().to_string().contains("at least one ASN"));
1753
1754 let result = Filter::new("prefixes", ",,,");
1756 assert!(result.is_err());
1757 assert!(result
1758 .unwrap_err()
1759 .to_string()
1760 .contains("at least one prefix"));
1761
1762 let result = Filter::new("peer_ips", ",,,");
1764 assert!(result.is_err());
1765 assert!(result.unwrap_err().to_string().contains("at least one IP"));
1766
1767 let result = Filter::new("origin_asns", "12345,67890,");
1769 assert!(result.is_ok());
1770
1771 let result = Filter::new("origin_asns", "12345,,67890");
1773 assert!(result.is_ok());
1774
1775 let result = Filter::new("peer_ips", "192.168.1.1,192.168.1.2,");
1777 assert!(result.is_ok());
1778 }
1779
1780 #[test]
1781 fn test_multiple_filters_or_logic_behavior() {
1782 let elem = BgpElem {
1784 timestamp: 1637437798_f64,
1785 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
1786 peer_asn: Asn::new_32bit(12345),
1787 peer_bgp_id: None,
1788 prefix: NetworkPrefix::new(IpNet::from_str("192.168.1.0/24").unwrap(), None),
1789 next_hop: None,
1790 as_path: Some(AsPath::from_sequence(vec![174, 1916, 52888])),
1791 origin_asns: Some(vec![Asn::new_16bit(12345)]),
1792 origin: None,
1793 local_pref: None,
1794 med: None,
1795 communities: None,
1796 atomic: false,
1797 aggr_asn: None,
1798 aggr_ip: None,
1799 only_to_customer: None,
1800 unknown: None,
1801 elem_type: ElemType::ANNOUNCE,
1802 deprecated: None,
1803 };
1804
1805 let filter = Filter::new("origin_asns", "12345,67890,99999").unwrap();
1807 assert!(elem.match_filter(&filter)); let filter = Filter::new("origin_asns", "67890,99999").unwrap();
1810 assert!(!elem.match_filter(&filter)); let filter = Filter::new("prefixes", "192.168.1.0/24,10.0.0.0/8,172.16.0.0/12").unwrap();
1814 assert!(elem.match_filter(&filter)); let filter = Filter::new("prefixes", "10.0.0.0/8,172.16.0.0/12").unwrap();
1817 assert!(!elem.match_filter(&filter)); let filter = Filter::new("peer_asns", "12345,67890").unwrap();
1821 assert!(elem.match_filter(&filter)); let filter = Filter::new("peer_asns", "67890,99999").unwrap();
1824 assert!(!elem.match_filter(&filter)); let filter = Filter::new("origin_asns", "!67890,!99999").unwrap();
1828 assert!(elem.match_filter(&filter)); let filter = Filter::new("origin_asns", "!12345,!67890").unwrap();
1831 assert!(!elem.match_filter(&filter)); }
1833}