1use crate::models::*;
126use crate::parser::ComparableRegex;
127use crate::ParserError;
128use crate::ParserError::FilterError;
129use ipnet::IpNet;
130use std::net::{IpAddr, Ipv4Addr};
131use std::str::FromStr;
132
133#[derive(Debug, Clone, PartialEq)]
168pub enum Filter {
169 OriginAsn(u32),
170 OriginAsns(Vec<u32>),
171 Prefix(IpNet, PrefixMatchType),
172 Prefixes(Vec<IpNet>, PrefixMatchType),
173 PeerIp(IpAddr),
174 PeerIps(Vec<IpAddr>),
175 PeerAsn(u32),
176 PeerAsns(Vec<u32>),
177 Type(ElemType),
178 IpVersion(IpVersion),
179 TsStart(f64),
180 TsEnd(f64),
181 AsPath(ComparableRegex),
182 Community(ComparableRegex),
183 OnlyToCustomer(Asn),
184 NextHop(IpAddr),
185 Origin(Origin),
186 LocalPref(u32),
187 Med(u32),
188 Atomic(bool),
189 AggrAsn(Asn),
190 AggrIp(Ipv4Addr),
191 PeerBgpId(Ipv4Addr),
192 FieldPresent(PresentField),
195 Negated(Box<Filter>),
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq)]
204pub enum PresentField {
205 OnlyToCustomer,
206 NextHop,
207 Origin,
208 LocalPref,
209 Med,
210 AggrAsn,
211 AggrIp,
212 PeerBgpId,
213}
214
215#[derive(Debug, Clone, PartialEq, Eq)]
216pub enum IpVersion {
217 Ipv4,
218 Ipv6,
219}
220
221#[derive(Debug, Clone, PartialEq, Eq)]
222pub enum PrefixMatchType {
223 Exact,
224 IncludeSuper,
225 IncludeSub,
226 IncludeSuperSub,
227}
228
229fn parse_time_str(time_str: &str) -> Option<chrono::NaiveDateTime> {
230 if let Ok(t) = time_str.parse::<f64>() {
231 return chrono::DateTime::from_timestamp(t as i64, 0).map(|t| t.naive_utc());
232 }
233 if let Ok(t) = chrono::DateTime::parse_from_rfc3339(time_str) {
234 return Some(t.naive_utc());
235 }
236 None
237}
238
239fn parse_asn_list(filter_value: &str) -> Result<(Vec<u32>, bool), ParserError> {
240 let mut asns = vec![];
241 let mut all_negated: Option<bool> = None;
242
243 for asn_str in filter_value.replace(' ', "").split(',') {
244 if asn_str.is_empty() {
246 continue;
247 }
248
249 let (is_negated, actual_value) = if let Some(stripped) = asn_str.strip_prefix('!') {
250 (true, stripped)
251 } else {
252 (false, asn_str)
253 };
254
255 match all_negated {
257 None => all_negated = Some(is_negated),
258 Some(prev) if prev != is_negated => {
259 return Err(FilterError(
260 "cannot mix positive and negative values in the same filter".to_string(),
261 ));
262 }
263 _ => {}
264 }
265
266 match u32::from_str(actual_value) {
267 Ok(v) => asns.push(v),
268 Err(_) => return Err(FilterError(format!("cannot parse ASN from {actual_value}"))),
269 }
270 }
271 if asns.is_empty() {
273 return Err(FilterError(
274 "ASN list filter requires at least one ASN".to_string(),
275 ));
276 }
277 Ok((asns, all_negated.unwrap_or(false)))
278}
279
280fn parse_prefix_list(filter_value: &str) -> Result<(Vec<IpNet>, bool), ParserError> {
281 let mut prefixes = vec![];
282 let mut all_negated: Option<bool> = None;
283
284 for prefix_str in filter_value.replace(' ', "").split(',') {
285 if prefix_str.is_empty() {
287 continue;
288 }
289
290 let (is_negated, actual_value) = if let Some(stripped) = prefix_str.strip_prefix('!') {
291 (true, stripped)
292 } else {
293 (false, prefix_str)
294 };
295
296 match all_negated {
298 None => all_negated = Some(is_negated),
299 Some(prev) if prev != is_negated => {
300 return Err(FilterError(
301 "cannot mix positive and negative values in the same filter".to_string(),
302 ));
303 }
304 _ => {}
305 }
306
307 match IpNet::from_str(actual_value) {
308 Ok(v) => prefixes.push(v),
309 Err(_) => {
310 return Err(FilterError(format!(
311 "cannot parse prefix from {actual_value}"
312 )))
313 }
314 }
315 }
316 if prefixes.is_empty() {
318 return Err(FilterError(
319 "prefix list filter requires at least one prefix".to_string(),
320 ));
321 }
322 Ok((prefixes, all_negated.unwrap_or(false)))
323}
324
325fn parse_ip_list(filter_value: &str) -> Result<(Vec<IpAddr>, bool), ParserError> {
326 let mut ips = vec![];
327 let mut all_negated: Option<bool> = None;
328
329 for ip_str in filter_value.replace(' ', "").split(',') {
330 if ip_str.is_empty() {
332 continue;
333 }
334
335 let (is_negated, actual_value) = if let Some(stripped) = ip_str.strip_prefix('!') {
336 (true, stripped)
337 } else {
338 (false, ip_str)
339 };
340
341 match all_negated {
343 None => all_negated = Some(is_negated),
344 Some(prev) if prev != is_negated => {
345 return Err(FilterError(
346 "cannot mix positive and negative values in the same filter".to_string(),
347 ));
348 }
349 _ => {}
350 }
351
352 match IpAddr::from_str(actual_value) {
353 Ok(v) => ips.push(v),
354 Err(_) => {
355 return Err(FilterError(format!(
356 "cannot parse IP address from {actual_value}"
357 )))
358 }
359 }
360 }
361 if ips.is_empty() {
363 return Err(FilterError(
364 "IP list filter requires at least one IP address".to_string(),
365 ));
366 }
367 Ok((ips, all_negated.unwrap_or(false)))
368}
369
370impl Filter {
371 pub fn new(filter_type: &str, filter_value: &str) -> Result<Filter, ParserError> {
372 let multi_value_filters = [
375 "origin_asns",
376 "prefixes",
377 "prefixes_super",
378 "prefixes_sub",
379 "prefixes_super_sub",
380 "peer_ips",
381 "peer_asns",
382 ];
383
384 if multi_value_filters.contains(&filter_type) {
385 return Self::new_base(filter_type, filter_value);
387 }
388
389 let (negated, actual_value) = if let Some(stripped) = filter_value.strip_prefix('!') {
391 if stripped.starts_with('!') {
393 return Err(FilterError(format!(
394 "invalid filter value '{}': double negation is not allowed",
395 filter_value
396 )));
397 }
398 (true, stripped)
399 } else {
400 (false, filter_value)
401 };
402
403 if negated
405 && (filter_type == "ts_start"
406 || filter_type == "start_ts"
407 || filter_type == "ts_end"
408 || filter_type == "end_ts")
409 {
410 return Err(FilterError(format!(
411 "timestamp filter '{}' does not support negation",
412 filter_type
413 )));
414 }
415
416 if actual_value == "*" {
418 if let Some(field) = present_field_for(filter_type) {
419 let present = Filter::FieldPresent(field);
420 return if negated {
421 Ok(Filter::Negated(Box::new(present)))
422 } else {
423 Ok(present)
424 };
425 }
426 }
427
428 let base_filter = Self::new_base(filter_type, actual_value)?;
429
430 if negated {
431 Ok(Filter::Negated(Box::new(base_filter)))
432 } else {
433 Ok(base_filter)
434 }
435 }
436
437 fn new_base(filter_type: &str, filter_value: &str) -> Result<Filter, ParserError> {
438 match filter_type {
439 "origin_asn" => match u32::from_str(filter_value) {
440 Ok(v) => Ok(Filter::OriginAsn(v)),
441 Err(_) => Err(FilterError(format!(
442 "cannot parse origin asn from {filter_value}"
443 ))),
444 },
445 "origin_asns" => {
446 let (asns, negated) = parse_asn_list(filter_value)?;
447 let filter = Filter::OriginAsns(asns);
448 if negated {
449 Ok(Filter::Negated(Box::new(filter)))
450 } else {
451 Ok(filter)
452 }
453 }
454 "prefix" => match IpNet::from_str(filter_value) {
455 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::Exact)),
456 Err(_) => Err(FilterError(format!(
457 "cannot parse prefix from {filter_value}"
458 ))),
459 },
460 "prefix_super" => match IpNet::from_str(filter_value) {
461 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::IncludeSuper)),
462 Err(_) => Err(FilterError(format!(
463 "cannot parse prefix from {filter_value}"
464 ))),
465 },
466 "prefix_sub" => match IpNet::from_str(filter_value) {
467 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::IncludeSub)),
468 Err(_) => Err(FilterError(format!(
469 "cannot parse prefix from {filter_value}"
470 ))),
471 },
472 "prefix_super_sub" => match IpNet::from_str(filter_value) {
473 Ok(v) => Ok(Filter::Prefix(v, PrefixMatchType::IncludeSuperSub)),
474 Err(_) => Err(FilterError(format!(
475 "cannot parse prefix from {filter_value}"
476 ))),
477 },
478 "prefixes" => {
479 let (prefixes, negated) = parse_prefix_list(filter_value)?;
480 let filter = Filter::Prefixes(prefixes, PrefixMatchType::Exact);
481 if negated {
482 Ok(Filter::Negated(Box::new(filter)))
483 } else {
484 Ok(filter)
485 }
486 }
487 "prefixes_super" => {
488 let (prefixes, negated) = parse_prefix_list(filter_value)?;
489 let filter = Filter::Prefixes(prefixes, PrefixMatchType::IncludeSuper);
490 if negated {
491 Ok(Filter::Negated(Box::new(filter)))
492 } else {
493 Ok(filter)
494 }
495 }
496 "prefixes_sub" => {
497 let (prefixes, negated) = parse_prefix_list(filter_value)?;
498 let filter = Filter::Prefixes(prefixes, PrefixMatchType::IncludeSub);
499 if negated {
500 Ok(Filter::Negated(Box::new(filter)))
501 } else {
502 Ok(filter)
503 }
504 }
505 "prefixes_super_sub" => {
506 let (prefixes, negated) = parse_prefix_list(filter_value)?;
507 let filter = Filter::Prefixes(prefixes, PrefixMatchType::IncludeSuperSub);
508 if negated {
509 Ok(Filter::Negated(Box::new(filter)))
510 } else {
511 Ok(filter)
512 }
513 }
514 "peer_ip" => match IpAddr::from_str(filter_value) {
515 Ok(v) => Ok(Filter::PeerIp(v)),
516 Err(_) => Err(FilterError(format!(
517 "cannot parse peer IP from {filter_value}"
518 ))),
519 },
520 "peer_ips" => {
521 let (ips, negated) = parse_ip_list(filter_value)?;
522 let filter = Filter::PeerIps(ips);
523 if negated {
524 Ok(Filter::Negated(Box::new(filter)))
525 } else {
526 Ok(filter)
527 }
528 }
529 "peer_asn" => match u32::from_str(filter_value) {
530 Ok(v) => Ok(Filter::PeerAsn(v)),
531 Err(_) => Err(FilterError(format!(
532 "cannot parse peer asn from {filter_value}"
533 ))),
534 },
535 "peer_asns" => {
536 let (asns, negated) = parse_asn_list(filter_value)?;
537 let filter = Filter::PeerAsns(asns);
538 if negated {
539 Ok(Filter::Negated(Box::new(filter)))
540 } else {
541 Ok(filter)
542 }
543 }
544 "type" => match filter_value {
545 "w" | "withdraw" | "withdrawal" => Ok(Filter::Type(ElemType::WITHDRAW)),
546 "a" | "announce" | "announcement" => Ok(Filter::Type(ElemType::ANNOUNCE)),
547 _ => Err(FilterError(format!(
548 "cannot parse elem type from {filter_value}"
549 ))),
550 },
551 "ts_start" | "start_ts" => match parse_time_str(filter_value) {
552 Some(t) => Ok(Filter::TsStart(t.and_utc().timestamp() as f64)),
553 None => Err(FilterError(format!(
554 "cannot parse TsStart filter from {filter_value}"
555 ))),
556 },
557 "ts_end" | "end_ts" => match parse_time_str(filter_value) {
558 Some(t) => Ok(Filter::TsEnd(t.and_utc().timestamp() as f64)),
559 None => Err(FilterError(format!(
560 "cannot parse TsEnd filter from {filter_value}"
561 ))),
562 },
563 "as_path" => match ComparableRegex::new(filter_value) {
564 Ok(v) => Ok(Filter::AsPath(v)),
565 Err(_) => Err(FilterError(format!(
566 "cannot parse AS path regex from {filter_value}"
567 ))),
568 },
569 "community" => match ComparableRegex::new(filter_value) {
570 Ok(v) => Ok(Filter::Community(v)),
571 Err(_) => Err(FilterError(format!(
572 "cannot parse Community regex from {filter_value}"
573 ))),
574 },
575 "ip_version" | "ip" => match filter_value {
576 "4" | "v4" | "ipv4" => Ok(Filter::IpVersion(IpVersion::Ipv4)),
577 "6" | "v6" | "ipv6" => Ok(Filter::IpVersion(IpVersion::Ipv6)),
578 _ => Err(FilterError(format!(
579 "cannot parse IP version from {filter_value}"
580 ))),
581 },
582 "otc" => match u32::from_str(filter_value) {
583 Ok(v) => Ok(Filter::OnlyToCustomer(Asn::from(v))),
584 Err(_) => Err(FilterError(format!(
585 "cannot parse OTC ASN from {filter_value}"
586 ))),
587 },
588 "next_hop" => match IpAddr::from_str(filter_value) {
589 Ok(v) => Ok(Filter::NextHop(v)),
590 Err(_) => Err(FilterError(format!(
591 "cannot parse next hop IP from {filter_value}"
592 ))),
593 },
594 "origin" => match filter_value.to_lowercase().as_str() {
595 "igp" => Ok(Filter::Origin(Origin::IGP)),
596 "egp" => Ok(Filter::Origin(Origin::EGP)),
597 "incomplete" => Ok(Filter::Origin(Origin::INCOMPLETE)),
598 _ => Err(FilterError(format!(
599 "cannot parse origin from {filter_value} (expected: igp, egp, or incomplete)"
600 ))),
601 },
602 "local_pref" => match u32::from_str(filter_value) {
603 Ok(v) => Ok(Filter::LocalPref(v)),
604 Err(_) => Err(FilterError(format!(
605 "cannot parse local_pref from {filter_value}"
606 ))),
607 },
608 "med" => match u32::from_str(filter_value) {
609 Ok(v) => Ok(Filter::Med(v)),
610 Err(_) => Err(FilterError(format!("cannot parse med from {filter_value}"))),
611 },
612 "atomic" => match filter_value {
613 "true" | "t" | "1" => Ok(Filter::Atomic(true)),
614 "false" | "f" | "0" => Ok(Filter::Atomic(false)),
615 _ => Err(FilterError(format!(
616 "cannot parse atomic from {filter_value} (expected: true or false)"
617 ))),
618 },
619 "aggr_asn" => match u32::from_str(filter_value) {
620 Ok(v) => Ok(Filter::AggrAsn(Asn::from(v))),
621 Err(_) => Err(FilterError(format!(
622 "cannot parse aggregator ASN from {filter_value}"
623 ))),
624 },
625 "aggr_ip" => match Ipv4Addr::from_str(filter_value) {
626 Ok(v) => Ok(Filter::AggrIp(v)),
627 Err(_) => Err(FilterError(format!(
628 "cannot parse aggregator IP from {filter_value}"
629 ))),
630 },
631 "peer_bgp_id" => match Ipv4Addr::from_str(filter_value) {
632 Ok(v) => Ok(Filter::PeerBgpId(v)),
633 Err(_) => Err(FilterError(format!(
634 "cannot parse peer BGP ID from {filter_value}"
635 ))),
636 },
637 _ => Err(FilterError(format!("unknown filter type: {filter_type}"))),
638 }
639 }
640}
641
642fn present_field_for(filter_type: &str) -> Option<PresentField> {
645 match filter_type {
646 "otc" => Some(PresentField::OnlyToCustomer),
647 "next_hop" => Some(PresentField::NextHop),
648 "origin" => Some(PresentField::Origin),
649 "local_pref" => Some(PresentField::LocalPref),
650 "med" => Some(PresentField::Med),
651 "aggr_asn" => Some(PresentField::AggrAsn),
652 "aggr_ip" => Some(PresentField::AggrIp),
653 "peer_bgp_id" => Some(PresentField::PeerBgpId),
654 _ => None,
655 }
656}
657
658fn is_elem_only_filter(filter: &Filter) -> bool {
662 matches!(
663 filter,
664 Filter::Community(_)
665 | Filter::OnlyToCustomer(_)
666 | Filter::NextHop(_)
667 | Filter::Origin(_)
668 | Filter::LocalPref(_)
669 | Filter::Med(_)
670 | Filter::Atomic(_)
671 | Filter::AggrAsn(_)
672 | Filter::AggrIp(_)
673 | Filter::PeerBgpId(_)
674 | Filter::FieldPresent(_)
675 )
676}
677
678fn is_full_elem_view<T: RouteFilterView>(view: &T) -> bool {
682 view.supports_community_filter()
683}
684
685pub trait Filterable {
686 fn match_filter(&self, filter: &Filter) -> bool;
687 fn match_filters(&self, filters: &[Filter]) -> bool {
688 filters.iter().all(|f| self.match_filter(f))
689 }
690}
691
692trait RouteFilterView {
693 fn timestamp(&self) -> f64;
694 fn elem_type(&self) -> ElemType;
695 fn peer_ip(&self) -> IpAddr;
696 fn peer_asn(&self) -> Asn;
697 fn prefix(&self) -> &NetworkPrefix;
698 fn as_path(&self) -> Option<&AsPath>;
699 fn matches_origin_asn(&self, asn: Asn) -> bool;
700
701 fn next_hop(&self) -> Option<IpAddr> {
704 None
705 }
706 fn origin(&self) -> Option<Origin> {
707 None
708 }
709 fn local_pref(&self) -> Option<u32> {
710 None
711 }
712 fn med(&self) -> Option<u32> {
713 None
714 }
715 fn atomic(&self) -> bool {
716 false
717 }
718 fn aggr_asn(&self) -> Option<Asn> {
719 None
720 }
721 fn aggr_ip(&self) -> Option<Ipv4Addr> {
722 None
723 }
724 fn peer_bgp_id(&self) -> Option<Ipv4Addr> {
725 None
726 }
727 fn only_to_customer(&self) -> Option<Asn> {
728 None
729 }
730
731 fn matches_community(&self, _regex: &ComparableRegex) -> bool {
732 false
733 }
734
735 fn supports_community_filter(&self) -> bool {
736 false
737 }
738}
739
740const fn same_family(prefix_1: &IpNet, prefix_2: &IpNet) -> bool {
741 matches!(
742 (prefix_1, prefix_2),
743 (IpNet::V4(_), IpNet::V4(_)) | (IpNet::V6(_), IpNet::V6(_))
744 )
745}
746
747fn prefix_match(match_prefix: &IpNet, input_prefix: &IpNet, t: &PrefixMatchType) -> bool {
748 let exact = input_prefix.eq(match_prefix);
749 match t {
750 PrefixMatchType::Exact => exact,
751 PrefixMatchType::IncludeSuper => {
752 if exact {
753 exact
754 } else if !same_family(match_prefix, input_prefix) {
755 false
757 } else {
758 match_prefix.addr() >= input_prefix.addr()
760 && match_prefix.broadcast() <= input_prefix.broadcast()
761 }
762 }
763 PrefixMatchType::IncludeSub => {
764 if exact {
765 exact
766 } else if !same_family(match_prefix, input_prefix) {
767 false
769 } else {
770 match_prefix.addr() <= input_prefix.addr()
772 && match_prefix.broadcast() >= input_prefix.broadcast()
773 }
774 }
775 PrefixMatchType::IncludeSuperSub => {
776 if exact {
777 exact
778 } else if !same_family(match_prefix, input_prefix) {
779 false
781 } else {
782 (match_prefix.addr() >= input_prefix.addr()
784 && match_prefix.broadcast() <= input_prefix.broadcast())
785 || (match_prefix.addr() <= input_prefix.addr()
786 && match_prefix.broadcast() >= input_prefix.broadcast())
787 }
788 }
789 }
790}
791
792fn match_route_view_filter<T: RouteFilterView>(view: &T, filter: &Filter) -> bool {
793 if !is_full_elem_view(view) {
797 let inner = match filter {
798 Filter::Negated(inner) => inner.as_ref(),
799 f => f,
800 };
801 if is_elem_only_filter(inner) {
802 return false;
803 }
804 }
805 match filter {
806 Filter::Negated(inner) => !match_route_view_filter(view, inner),
807 Filter::OriginAsn(v) => view.matches_origin_asn((*v).into()),
808 Filter::OriginAsns(v) => v.iter().any(|asn| view.matches_origin_asn((*asn).into())),
809 Filter::Prefix(v, t) => prefix_match(v, &view.prefix().prefix, t),
810 Filter::Prefixes(v, t) => v
811 .iter()
812 .any(|prefix| prefix_match(prefix, &view.prefix().prefix, t)),
813 Filter::PeerIp(v) => view.peer_ip() == *v,
814 Filter::PeerIps(v) => v.contains(&view.peer_ip()),
815 Filter::PeerAsn(v) => view.peer_asn().eq(v),
816 Filter::PeerAsns(v) => v.iter().any(|asn| view.peer_asn().eq(asn)),
817 Filter::Type(v) => view.elem_type().eq(v),
818 Filter::TsStart(v) => view.timestamp() >= *v,
819 Filter::TsEnd(v) => view.timestamp() <= *v,
820 Filter::AsPath(v) => view
821 .as_path()
822 .map(|path| v.is_match(path.to_string().as_str()))
823 .unwrap_or(false),
824 Filter::Community(r) => view.supports_community_filter() && view.matches_community(r),
825 Filter::IpVersion(version) => match version {
826 IpVersion::Ipv4 => view.prefix().prefix.addr().is_ipv4(),
827 IpVersion::Ipv6 => view.prefix().prefix.addr().is_ipv6(),
828 },
829 Filter::OnlyToCustomer(v) => view.only_to_customer() == Some(*v),
830 Filter::NextHop(v) => view.next_hop() == Some(*v),
831 Filter::Origin(v) => view.origin() == Some(*v),
832 Filter::LocalPref(v) => view.local_pref() == Some(*v),
833 Filter::Med(v) => view.med() == Some(*v),
834 Filter::Atomic(v) => view.atomic() == *v,
835 Filter::AggrAsn(v) => view.aggr_asn() == Some(*v),
836 Filter::AggrIp(v) => view.aggr_ip() == Some(*v),
837 Filter::PeerBgpId(v) => view.peer_bgp_id() == Some(*v),
838 Filter::FieldPresent(field) => match field {
839 PresentField::OnlyToCustomer => view.only_to_customer().is_some(),
840 PresentField::NextHop => view.next_hop().is_some(),
841 PresentField::Origin => view.origin().is_some(),
842 PresentField::LocalPref => view.local_pref().is_some(),
843 PresentField::Med => view.med().is_some(),
844 PresentField::AggrAsn => view.aggr_asn().is_some(),
845 PresentField::AggrIp => view.aggr_ip().is_some(),
846 PresentField::PeerBgpId => view.peer_bgp_id().is_some(),
847 },
848 }
849}
850
851impl RouteFilterView for BgpElem {
852 fn timestamp(&self) -> f64 {
853 self.timestamp
854 }
855
856 fn elem_type(&self) -> ElemType {
857 self.elem_type
858 }
859
860 fn peer_ip(&self) -> IpAddr {
861 self.peer_ip
862 }
863
864 fn peer_asn(&self) -> Asn {
865 self.peer_asn
866 }
867
868 fn prefix(&self) -> &NetworkPrefix {
869 &self.prefix
870 }
871
872 fn as_path(&self) -> Option<&AsPath> {
873 self.as_path.as_ref()
874 }
875
876 fn matches_origin_asn(&self, asn: Asn) -> bool {
877 self.origin_asns
878 .as_ref()
879 .map(|origins| origins.contains(&asn))
880 .unwrap_or(false)
881 }
882
883 fn next_hop(&self) -> Option<IpAddr> {
884 self.next_hop
885 }
886 fn origin(&self) -> Option<Origin> {
887 self.origin
888 }
889 fn local_pref(&self) -> Option<u32> {
890 self.local_pref
891 }
892 fn med(&self) -> Option<u32> {
893 self.med
894 }
895 fn atomic(&self) -> bool {
896 self.atomic
897 }
898 fn aggr_asn(&self) -> Option<Asn> {
899 self.aggr_asn
900 }
901 fn aggr_ip(&self) -> Option<Ipv4Addr> {
902 self.aggr_ip
903 }
904 fn peer_bgp_id(&self) -> Option<Ipv4Addr> {
905 self.peer_bgp_id
906 }
907 fn only_to_customer(&self) -> Option<Asn> {
908 self.only_to_customer
909 }
910
911 fn matches_community(&self, regex: &ComparableRegex) -> bool {
912 self.communities
913 .as_ref()
914 .map(|communities| communities.iter().any(|c| regex.is_match(c.to_string())))
915 .unwrap_or(false)
916 }
917
918 fn supports_community_filter(&self) -> bool {
919 true
920 }
921}
922
923impl RouteFilterView for BgpRouteElem {
924 fn timestamp(&self) -> f64 {
925 self.timestamp
926 }
927
928 fn elem_type(&self) -> ElemType {
929 self.elem_type
930 }
931
932 fn peer_ip(&self) -> IpAddr {
933 self.peer_ip
934 }
935
936 fn peer_asn(&self) -> Asn {
937 self.peer_asn
938 }
939
940 fn prefix(&self) -> &NetworkPrefix {
941 &self.prefix
942 }
943
944 fn as_path(&self) -> Option<&AsPath> {
945 self.as_path.as_deref()
946 }
947
948 fn matches_origin_asn(&self, asn: Asn) -> bool {
949 self.as_path
950 .as_ref()
951 .map(|path| path.iter_origins().any(|origin| origin == asn))
952 .unwrap_or(false)
953 }
954}
955
956impl Filterable for BgpElem {
957 fn match_filter(&self, filter: &Filter) -> bool {
958 match_route_view_filter(self, filter)
959 }
960}
961
962impl Filterable for BgpRouteElem {
963 fn match_filter(&self, filter: &Filter) -> bool {
964 match_route_view_filter(self, filter)
965 }
966}
967
968#[cfg(test)]
969mod tests {
970 use super::*;
971 use crate::BgpkitParser;
972 use anyhow::Result;
973 use std::str::FromStr;
974 use std::sync::Arc;
975
976 fn filter_test_elem() -> BgpElem {
977 BgpElem {
978 timestamp: 1637437798_f64,
979 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
980 peer_asn: Asn::new_32bit(12345),
981 peer_bgp_id: None,
982 prefix: NetworkPrefix::new(IpNet::from_str("192.168.1.0/24").unwrap(), None),
983 next_hop: None,
984 as_path: Some(AsPath::from_sequence(vec![174, 1916, 52888])),
985 origin_asns: Some(vec![Asn::new_32bit(52888)]),
986 origin: None,
987 local_pref: None,
988 med: None,
989 communities: Some(vec![MetaCommunity::Large(LargeCommunity::new(
990 12345,
991 [678910, 111213],
992 ))]),
993 atomic: false,
994 aggr_asn: None,
995 aggr_ip: None,
996 only_to_customer: None,
997 unknown: None,
998 elem_type: ElemType::ANNOUNCE,
999 deprecated: None,
1000 }
1001 }
1002
1003 fn route_projection(elem: &BgpElem) -> BgpRouteElem {
1004 BgpRouteElem {
1005 timestamp: elem.timestamp,
1006 elem_type: elem.elem_type,
1007 peer_ip: elem.peer_ip,
1008 peer_asn: elem.peer_asn,
1009 prefix: elem.prefix,
1010 as_path: elem.as_path.clone().map(Arc::new),
1011 }
1012 }
1013
1014 #[test]
1015 fn test_route_community_filters_fail_closed() {
1016 let elem = filter_test_elem();
1017 let route = route_projection(&elem);
1018 let community = Filter::new("community", r"12345:.*").unwrap();
1019 let negated_community = Filter::new("community", r"!12345:.*").unwrap();
1020
1021 assert!(elem.match_filter(&community));
1022 assert!(!elem.match_filter(&negated_community));
1023 assert!(!route.match_filter(&community));
1024 assert!(!route.match_filter(&negated_community));
1025 }
1026
1027 #[test]
1028 fn test_filters_on_mrt_file() {
1029 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
1030 let parser = BgpkitParser::new(url).unwrap();
1031 let elems = parser.into_elem_iter().collect::<Vec<BgpElem>>();
1032
1033 let filters = vec![Filter::PeerIp(IpAddr::from_str("185.1.8.65").unwrap())];
1034 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1035 assert_eq!(count, 3393);
1036
1037 let filters = vec![
1038 Filter::PeerIp(IpAddr::from_str("185.1.8.65").unwrap()),
1039 Filter::Prefix(
1040 IpNet::from_str("190.115.192.0/22").unwrap(),
1041 PrefixMatchType::Exact,
1042 ),
1043 ];
1044 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1045 assert_eq!(count, 5);
1046
1047 let filters = vec![Filter::Prefix(
1048 IpNet::from_str("190.115.192.0/24").unwrap(),
1049 PrefixMatchType::IncludeSuper,
1050 )];
1051 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1052 assert_eq!(count, 18);
1053
1054 let filters = vec![Filter::Prefix(
1055 IpNet::from_str("190.115.192.0/22").unwrap(),
1056 PrefixMatchType::IncludeSub,
1057 )];
1058 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1059 assert_eq!(count, 42);
1060
1061 let filters = vec![Filter::Prefix(
1062 IpNet::from_str("190.115.192.0/23").unwrap(),
1063 PrefixMatchType::IncludeSuperSub,
1064 )];
1065 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1066 assert_eq!(count, 24);
1067
1068 let filters = vec![Filter::new("as_path", r" ?174 1916 52888$").unwrap()];
1069 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1070 assert_eq!(count, 12);
1071
1072 let filters = vec![Filter::new("community", r"60924:.*").unwrap()];
1074 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1075 assert_eq!(count, 4243);
1076
1077 let filters = vec![Filter::new("community", r".+:784$").unwrap()];
1079 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1080 assert_eq!(count, 107);
1081
1082 let filters = vec![Filter::new("community", r"\d+:\d+:\d+$").unwrap()];
1084 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1085 assert_eq!(count, 4397);
1086
1087 let filters = vec![
1088 Filter::TsStart(1637437798_f64),
1089 Filter::TsEnd(1637437798_f64),
1090 ];
1091 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1092 assert_eq!(count, 13);
1093
1094 let filters = vec![Filter::Type(ElemType::WITHDRAW)];
1095 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1096 assert_eq!(count, 379);
1097
1098 let filters = vec![
1099 Filter::Type(ElemType::WITHDRAW),
1100 Filter::Prefix(
1101 IpNet::from_str("2804:100::/32").unwrap(),
1102 PrefixMatchType::Exact,
1103 ),
1104 ];
1105 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1106 assert_eq!(count, 1);
1107
1108 let filters = vec![Filter::PeerIps(vec![
1119 IpAddr::from_str("185.1.8.65").unwrap(),
1120 IpAddr::from_str("2001:7f8:73:0:3:fa4:0:1").unwrap(),
1121 ])];
1122 let count = elems.iter().filter(|e| e.match_filters(&filters)).count();
1123 assert_eq!(count, 3393 + 834);
1124 }
1125
1126 #[test]
1127 fn test_filter_incorrect_filters() {
1128 let incorrect_filters = [
1130 Filter::new("community", r"[abc"),
1131 Filter::new("as_path", r"[0-9"),
1132 Filter::new("prefix_super_sub", "-192.-168.-1.1/24"),
1133 ];
1134 assert!(incorrect_filters
1135 .iter()
1136 .all(|f| matches!(f, Err(FilterError(_)))));
1137 }
1138
1139 #[test]
1140 fn test_parsing_time_str() {
1141 let ts = chrono::NaiveDateTime::from_str("2021-11-20T19:49:58").unwrap();
1142 assert_eq!(parse_time_str("1637437798"), Some(ts));
1143 assert_eq!(parse_time_str("2021-11-20T19:49:58Z"), Some(ts));
1144 assert_eq!(parse_time_str("2021-11-20T19:49:58+00:00"), Some(ts));
1145
1146 assert_eq!(parse_time_str("2021-11-20T19:49:58"), None);
1147 assert_eq!(parse_time_str("2021-11-20T19:49:58ZDXV"), None);
1148 assert_eq!(parse_time_str("2021-11-20 19:49:58"), None);
1149 assert_eq!(parse_time_str("2021-11-20"), None);
1150 }
1151
1152 #[test]
1153 fn test_filter_iter() -> Result<()> {
1154 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
1155 let parser = BgpkitParser::new(url)?
1156 .add_filter("peer_ip", "185.1.8.50")?
1157 .add_filter("type", "w")?;
1158 let count = parser.into_elem_iter().count();
1159 assert_eq!(count, 39);
1160
1161 let parser = BgpkitParser::new(url)?
1162 .add_filter("ts_start", "1637437798")?
1163 .add_filter("ts_end", "2021-11-20T19:49:58Z")?;
1164 let count = parser.into_elem_iter().count();
1165 assert_eq!(count, 13);
1166 Ok(())
1167 }
1168
1169 #[test]
1170 fn test_filter_iter_with_negation() -> Result<()> {
1171 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
1172
1173 let parser = BgpkitParser::new(url)?.add_filter("peer_ip", "!185.1.8.65")?;
1176 let count = parser.into_elem_iter().count();
1177 assert_eq!(count, 8160 - 3393);
1178
1179 let parser = BgpkitParser::new(url)?.add_filter("type", "!w")?;
1182 let count = parser.into_elem_iter().count();
1183 assert_eq!(count, 8160 - 379);
1184
1185 let parser = BgpkitParser::new(url)?
1188 .add_filter("peer_ip", "185.1.8.50")?
1189 .add_filter("type", "!w")?;
1190 let count = parser.into_elem_iter().count();
1191 assert_eq!(count, 1563 - 39);
1193
1194 Ok(())
1195 }
1196
1197 #[test]
1198 fn test_filter_iter_multi_peers() {
1199 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
1200 let parser = BgpkitParser::new(url)
1201 .unwrap()
1202 .add_filter("peer_ips", "185.1.8.65, 2001:7f8:73:0:3:fa4:0:1")
1203 .unwrap();
1204 let count = parser.into_elem_iter().count();
1205 assert_eq!(count, 3393 + 834);
1206 }
1207
1208 #[test]
1209 fn test_prefix_match() {
1210 let p1 = IpNet::from_str("10.1.1.0/24").unwrap();
1212 let p1_exact = IpNet::from_str("10.1.1.0/24").unwrap();
1213 let p1_super = IpNet::from_str("10.1.0.0/16").unwrap();
1214 let p1_sub = IpNet::from_str("10.1.1.0/25").unwrap();
1215
1216 let p2 = IpNet::from_str("2001:0DB8:0000:000b::/64").unwrap();
1217
1218 assert!(prefix_match(&p1, &p1_exact, &PrefixMatchType::Exact));
1220 assert!(!prefix_match(&p1, &p1_sub, &PrefixMatchType::Exact));
1221 assert!(!prefix_match(&p1, &p1_super, &PrefixMatchType::Exact));
1222 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::Exact));
1223
1224 assert!(prefix_match(&p1, &p1_exact, &PrefixMatchType::IncludeSuper));
1226 assert!(!prefix_match(&p1, &p1_sub, &PrefixMatchType::IncludeSuper));
1227 assert!(prefix_match(&p1, &p1_super, &PrefixMatchType::IncludeSuper));
1228 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::IncludeSuper));
1229
1230 assert!(prefix_match(&p1, &p1_exact, &PrefixMatchType::IncludeSub));
1232 assert!(prefix_match(&p1, &p1_sub, &PrefixMatchType::IncludeSub));
1233 assert!(!prefix_match(&p1, &p1_super, &PrefixMatchType::IncludeSub));
1234 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::IncludeSub));
1235
1236 assert!(prefix_match(
1238 &p1,
1239 &p1_exact,
1240 &PrefixMatchType::IncludeSuperSub
1241 ));
1242 assert!(prefix_match(
1243 &p1,
1244 &p1_sub,
1245 &PrefixMatchType::IncludeSuperSub
1246 ));
1247 assert!(prefix_match(
1248 &p1,
1249 &p1_super,
1250 &PrefixMatchType::IncludeSuperSub
1251 ));
1252 assert!(!prefix_match(&p1, &p2, &PrefixMatchType::IncludeSuperSub));
1253 }
1254
1255 #[test]
1256 fn test_filter_new() {
1257 let filter = Filter::new("origin_asn", "12345").unwrap();
1258 assert_eq!(filter, Filter::OriginAsn(12345));
1259
1260 let filter = Filter::new("origin_asn", "!12345").unwrap();
1262 assert_eq!(filter, Filter::Negated(Box::new(Filter::OriginAsn(12345))));
1263
1264 let filter = Filter::new("prefix", "!192.168.1.0/24").unwrap();
1265 assert_eq!(
1266 filter,
1267 Filter::Negated(Box::new(Filter::Prefix(
1268 IpNet::from_str("192.168.1.0/24").unwrap(),
1269 PrefixMatchType::Exact
1270 )))
1271 );
1272
1273 let filter = Filter::new("peer_ip", "!192.168.1.1").unwrap();
1274 assert_eq!(
1275 filter,
1276 Filter::Negated(Box::new(Filter::PeerIp(
1277 IpAddr::from_str("192.168.1.1").unwrap()
1278 )))
1279 );
1280
1281 let filter = Filter::new("peer_asn", "!12345").unwrap();
1282 assert_eq!(filter, Filter::Negated(Box::new(Filter::PeerAsn(12345))));
1283
1284 let filter = Filter::new("type", "!w").unwrap();
1285 assert_eq!(
1286 filter,
1287 Filter::Negated(Box::new(Filter::Type(ElemType::WITHDRAW)))
1288 );
1289
1290 let filter = Filter::new("ip_version", "!4").unwrap();
1291 assert_eq!(
1292 filter,
1293 Filter::Negated(Box::new(Filter::IpVersion(IpVersion::Ipv4)))
1294 );
1295
1296 let filter = Filter::new("prefix", "192.168.1.0/24").unwrap();
1297 assert_eq!(
1298 filter,
1299 Filter::Prefix(
1300 IpNet::from_str("192.168.1.0/24").unwrap(),
1301 PrefixMatchType::Exact
1302 )
1303 );
1304 let filter = Filter::new("prefix_super", "192.168.1.0/24").unwrap();
1305 assert_eq!(
1306 filter,
1307 Filter::Prefix(
1308 IpNet::from_str("192.168.1.0/24").unwrap(),
1309 PrefixMatchType::IncludeSuper
1310 )
1311 );
1312 let filter = Filter::new("prefix_sub", "192.168.1.0/24").unwrap();
1313 assert_eq!(
1314 filter,
1315 Filter::Prefix(
1316 IpNet::from_str("192.168.1.0/24").unwrap(),
1317 PrefixMatchType::IncludeSub
1318 )
1319 );
1320 let filter = Filter::new("prefix_super_sub", "192.168.1.0/24").unwrap();
1321 assert_eq!(
1322 filter,
1323 Filter::Prefix(
1324 IpNet::from_str("192.168.1.0/24").unwrap(),
1325 PrefixMatchType::IncludeSuperSub
1326 )
1327 );
1328
1329 let filter = Filter::new("peer_ip", "192.168.1.1").unwrap();
1330 assert_eq!(
1331 filter,
1332 Filter::PeerIp(IpAddr::from_str("192.168.1.1").unwrap())
1333 );
1334
1335 let filter = Filter::new("peer_asn", "12345").unwrap();
1336 assert_eq!(filter, Filter::PeerAsn(12345));
1337
1338 let filter = Filter::new("type", "w").unwrap();
1339 assert_eq!(filter, Filter::Type(ElemType::WITHDRAW));
1340
1341 let filter = Filter::new("ts_start", "1637437798").unwrap();
1342 assert_eq!(filter, Filter::TsStart(1637437798_f64));
1343
1344 let filter = Filter::new("ts_end", "1637437798").unwrap();
1345 assert_eq!(filter, Filter::TsEnd(1637437798_f64));
1346
1347 let filter = Filter::new("as_path", r" ?174 1916 52888$").unwrap();
1348 assert_eq!(
1349 filter,
1350 Filter::AsPath(ComparableRegex::new(r" ?174 1916 52888$").unwrap())
1351 );
1352
1353 assert!(Filter::new("origin_asn", "not a number").is_err());
1354 assert!(Filter::new("peer_asn", "not a number").is_err());
1355 assert!(Filter::new("ts_start", "not a number").is_err());
1356 assert!(Filter::new("ts_end", "not a number").is_err());
1357 assert!(Filter::new("prefix", "not a prefix").is_err());
1358 assert!(Filter::new("prefix_super", "not a prefix").is_err());
1359 assert!(Filter::new("prefix_sub", "not a prefix").is_err());
1360 assert!(Filter::new("peer_ip", "not a IP").is_err());
1361 assert!(Filter::new("peer_ips", "not,a,IP").is_err());
1362 assert!(Filter::new("type", "not a type").is_err());
1363 assert!(Filter::new("as_path", "[abc").is_err());
1364 assert!(Filter::new("ip_version", "5").is_err());
1365 assert!(Filter::new("unknown_filter", "some_value").is_err());
1366 }
1367
1368 #[test]
1369 fn test_filterable_match_filter() {
1370 let elem = filter_test_elem();
1371
1372 let mut filters = vec![];
1373
1374 let filter = Filter::new("origin_asn", "52888").unwrap();
1375 filters.push(filter.clone());
1376 assert!(elem.match_filter(&filter));
1377
1378 let filter = Filter::new("origin_asn", "678910").unwrap();
1379 assert!(!elem.match_filter(&filter));
1380
1381 let filter = Filter::new("prefix", "192.168.1.0/24").unwrap();
1382 filters.push(filter.clone());
1383 assert!(elem.match_filter(&filter));
1384
1385 let filter = Filter::new("peer_ip", "192.168.1.1").unwrap();
1386 filters.push(filter.clone());
1387 assert!(elem.match_filter(&filter));
1388
1389 let filter = Filter::new("peer_asn", "12345").unwrap();
1390 filters.push(filter.clone());
1391 assert!(elem.match_filter(&filter));
1392
1393 let filter = Filter::new("type", "a").unwrap();
1394 filters.push(filter.clone());
1395 assert!(elem.match_filter(&filter));
1396
1397 let filter = Filter::new("ts_start", "1637437798").unwrap();
1398 filters.push(filter.clone());
1399 assert!(elem.match_filter(&filter));
1400
1401 let filter = Filter::new("ts_end", "1637437798").unwrap();
1402 filters.push(filter.clone());
1403 assert!(elem.match_filter(&filter));
1404
1405 let filter = Filter::new("as_path", r" ?174 1916 52888$").unwrap();
1406 filters.push(filter.clone());
1407 assert!(elem.match_filter(&filter));
1408
1409 let filter = Filter::new("ip_version", "4").unwrap();
1410 filters.push(filter.clone());
1411 assert!(elem.match_filter(&filter));
1412
1413 let filter = Filter::new("ip", "ipv6").unwrap();
1414 assert!(!elem.match_filter(&filter));
1415
1416 let filter = Filter::new("community", r"12345:678910:111213$").unwrap();
1417 filters.push(filter.clone());
1418 assert!(elem.match_filter(&filter));
1419
1420 assert!(elem.match_filters(&filters));
1421 }
1422
1423 #[test]
1424 fn test_route_filterable_matches_elem_for_route_level_filters() {
1425 let elem = filter_test_elem();
1426 let route = route_projection(&elem);
1427
1428 let cases = vec![
1429 (
1430 "origin_asn matches",
1431 Filter::new("origin_asn", "52888").unwrap(),
1432 true,
1433 ),
1434 (
1435 "origin_asn misses",
1436 Filter::new("origin_asn", "64496").unwrap(),
1437 false,
1438 ),
1439 (
1440 "origin_asns matches",
1441 Filter::new("origin_asns", "64496,52888").unwrap(),
1442 true,
1443 ),
1444 (
1445 "origin_asns misses",
1446 Filter::new("origin_asns", "64496,64497").unwrap(),
1447 false,
1448 ),
1449 (
1450 "prefix exact matches",
1451 Filter::new("prefix", "192.168.1.0/24").unwrap(),
1452 true,
1453 ),
1454 (
1455 "prefix exact misses",
1456 Filter::new("prefix", "192.168.2.0/24").unwrap(),
1457 false,
1458 ),
1459 (
1460 "prefix_super matches",
1461 Filter::new("prefix_super", "192.168.1.128/25").unwrap(),
1462 true,
1463 ),
1464 (
1465 "prefix_sub matches",
1466 Filter::new("prefix_sub", "192.168.0.0/23").unwrap(),
1467 true,
1468 ),
1469 (
1470 "prefix_super_sub matches",
1471 Filter::new("prefix_super_sub", "192.168.1.128/25").unwrap(),
1472 true,
1473 ),
1474 (
1475 "prefixes matches",
1476 Filter::new("prefixes", "10.0.0.0/8,192.168.1.0/24").unwrap(),
1477 true,
1478 ),
1479 (
1480 "peer_ip matches",
1481 Filter::new("peer_ip", "192.168.1.1").unwrap(),
1482 true,
1483 ),
1484 (
1485 "peer_ips matches",
1486 Filter::new("peer_ips", "192.168.1.2,192.168.1.1").unwrap(),
1487 true,
1488 ),
1489 (
1490 "peer_asn matches",
1491 Filter::new("peer_asn", "12345").unwrap(),
1492 true,
1493 ),
1494 (
1495 "peer_asns matches",
1496 Filter::new("peer_asns", "12346,12345").unwrap(),
1497 true,
1498 ),
1499 ("type matches", Filter::new("type", "a").unwrap(), true),
1500 ("type misses", Filter::new("type", "w").unwrap(), false),
1501 (
1502 "ts_start matches",
1503 Filter::new("ts_start", "1637437797").unwrap(),
1504 true,
1505 ),
1506 (
1507 "ts_end matches",
1508 Filter::new("ts_end", "1637437799").unwrap(),
1509 true,
1510 ),
1511 (
1512 "as_path matches",
1513 Filter::new("as_path", r"174 1916 52888$").unwrap(),
1514 true,
1515 ),
1516 (
1517 "as_path misses",
1518 Filter::new("as_path", r"64496$").unwrap(),
1519 false,
1520 ),
1521 (
1522 "ip_version matches",
1523 Filter::new("ip_version", "4").unwrap(),
1524 true,
1525 ),
1526 (
1527 "ip_version misses",
1528 Filter::new("ip_version", "6").unwrap(),
1529 false,
1530 ),
1531 (
1532 "negated origin_asn matches",
1533 Filter::new("origin_asn", "!64496").unwrap(),
1534 true,
1535 ),
1536 (
1537 "negated origin_asn misses",
1538 Filter::new("origin_asn", "!52888").unwrap(),
1539 false,
1540 ),
1541 ];
1542
1543 for (name, filter, expected) in cases {
1544 assert_eq!(elem.match_filter(&filter), expected, "{name} BgpElem");
1545 assert_eq!(route.match_filter(&filter), expected, "{name} BgpRouteElem");
1546 assert_eq!(
1547 elem.match_filter(&filter),
1548 route.match_filter(&filter),
1549 "{name} parity"
1550 );
1551 }
1552
1553 let filters = vec![
1554 Filter::new("origin_asn", "52888").unwrap(),
1555 Filter::new("peer_asn", "!64496").unwrap(),
1556 Filter::new("prefix_super", "192.168.1.128/25").unwrap(),
1557 ];
1558 assert!(elem.match_filters(&filters));
1559 assert_eq!(elem.match_filters(&filters), route.match_filters(&filters));
1560 }
1561
1562 #[test]
1563 fn test_route_filterable_does_not_match_community_filters() {
1564 let elem = filter_test_elem();
1565 let route = route_projection(&elem);
1566 let filter = Filter::new("community", r"12345:678910:111213$").unwrap();
1567
1568 assert!(elem.match_filter(&filter));
1569 assert!(!route.match_filter(&filter));
1570 }
1571
1572 #[test]
1573 fn test_negated_filters() {
1574 let elem = BgpElem {
1575 timestamp: 1637437798_f64,
1576 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
1577 peer_asn: Asn::new_32bit(12345),
1578 peer_bgp_id: None,
1579 prefix: NetworkPrefix::new(IpNet::from_str("192.168.1.0/24").unwrap(), None),
1580 next_hop: None,
1581 as_path: Some(AsPath::from_sequence(vec![174, 1916, 52888])),
1582 origin_asns: Some(vec![Asn::new_16bit(12345)]),
1583 origin: None,
1584 local_pref: None,
1585 med: None,
1586 communities: Some(vec![MetaCommunity::Large(LargeCommunity::new(
1587 12345,
1588 [678910, 111213],
1589 ))]),
1590 atomic: false,
1591 aggr_asn: None,
1592 aggr_ip: None,
1593 only_to_customer: None,
1594 unknown: None,
1595 elem_type: ElemType::ANNOUNCE,
1596 deprecated: None,
1597 };
1598
1599 let filter = Filter::new("origin_asn", "!12345").unwrap();
1602 assert!(!elem.match_filter(&filter));
1603
1604 let filter = Filter::new("origin_asn", "!99999").unwrap();
1606 assert!(elem.match_filter(&filter));
1607
1608 let filter = Filter::new("prefix", "!192.168.1.0/24").unwrap();
1611 assert!(!elem.match_filter(&filter));
1612
1613 let filter = Filter::new("prefix", "!10.0.0.0/8").unwrap();
1615 assert!(elem.match_filter(&filter));
1616
1617 let filter = Filter::new("peer_ip", "!192.168.1.1").unwrap();
1620 assert!(!elem.match_filter(&filter));
1621
1622 let filter = Filter::new("peer_ip", "!10.0.0.1").unwrap();
1624 assert!(elem.match_filter(&filter));
1625
1626 let filter = Filter::new("peer_asn", "!12345").unwrap();
1629 assert!(!elem.match_filter(&filter));
1630
1631 let filter = Filter::new("peer_asn", "!99999").unwrap();
1633 assert!(elem.match_filter(&filter));
1634
1635 let filter = Filter::new("type", "!a").unwrap();
1638 assert!(!elem.match_filter(&filter));
1639
1640 let filter = Filter::new("type", "!w").unwrap();
1642 assert!(elem.match_filter(&filter));
1643
1644 let filter = Filter::new("ip_version", "!4").unwrap();
1647 assert!(!elem.match_filter(&filter));
1648
1649 let filter = Filter::new("ip_version", "!6").unwrap();
1651 assert!(elem.match_filter(&filter));
1652
1653 let filter = Filter::new("as_path", r"!174 1916 52888$").unwrap();
1656 assert!(!elem.match_filter(&filter));
1657
1658 let filter = Filter::new("as_path", r"!99999$").unwrap();
1660 assert!(elem.match_filter(&filter));
1661
1662 let filter = Filter::new("community", r"!12345:678910:111213$").unwrap();
1664 assert!(!elem.match_filter(&filter));
1665
1666 let filter = Filter::new("community", r"!99999:99999$").unwrap();
1667 assert!(elem.match_filter(&filter));
1668
1669 let filter = Filter::new("peer_ips", "!192.168.1.1, !10.0.0.1").unwrap();
1671 assert!(!elem.match_filter(&filter)); let filter = Filter::new("peer_ips", "!10.0.0.1, !10.0.0.2").unwrap();
1674 assert!(elem.match_filter(&filter)); let filters = vec![
1678 Filter::new("origin_asn", "12345").unwrap(), Filter::new("peer_asn", "!99999").unwrap(), Filter::new("prefix", "!10.0.0.0/8").unwrap(), ];
1682 assert!(elem.match_filters(&filters));
1683
1684 let filters = vec![
1686 Filter::new("origin_asn", "12345").unwrap(), Filter::new("origin_asn", "!12345").unwrap(), ];
1689 assert!(!elem.match_filters(&filters));
1690 }
1691
1692 #[test]
1693 fn test_negated_filters_on_mrt_file() {
1694 let url = "https://spaces.bgpkit.org/parser/update-example.gz";
1695 let parser = BgpkitParser::new(url).unwrap();
1696 let elems = parser.into_elem_iter().collect::<Vec<BgpElem>>();
1697
1698 let filters = vec![Filter::PeerIp(IpAddr::from_str("185.1.8.65").unwrap())];
1700 let count_with_peer = elems.iter().filter(|e| e.match_filters(&filters)).count();
1701 assert_eq!(count_with_peer, 3393);
1702
1703 let filters = vec![Filter::new("peer_ip", "!185.1.8.65").unwrap()];
1705 let count_without_peer = elems.iter().filter(|e| e.match_filters(&filters)).count();
1706 assert_eq!(count_without_peer, elems.len() - 3393);
1707
1708 assert_eq!(count_with_peer + count_without_peer, elems.len());
1710
1711 let filters = vec![Filter::Type(ElemType::WITHDRAW)];
1713 let count_withdrawals = elems.iter().filter(|e| e.match_filters(&filters)).count();
1714 assert_eq!(count_withdrawals, 379);
1715
1716 let filters = vec![Filter::new("type", "!w").unwrap()];
1717 let count_not_withdrawals = elems.iter().filter(|e| e.match_filters(&filters)).count();
1718 assert_eq!(count_not_withdrawals, elems.len() - 379);
1719
1720 let filters = vec![Filter::Prefix(
1722 IpNet::from_str("190.115.192.0/22").unwrap(),
1723 PrefixMatchType::Exact,
1724 )];
1725 let count_with_prefix = elems.iter().filter(|e| e.match_filters(&filters)).count();
1726
1727 let filters = vec![Filter::new("prefix", "!190.115.192.0/22").unwrap()];
1728 let count_without_prefix = elems.iter().filter(|e| e.match_filters(&filters)).count();
1729 assert_eq!(count_with_prefix + count_without_prefix, elems.len());
1730
1731 let filters = vec![Filter::Prefix(
1733 IpNet::from_str("190.115.192.0/24").unwrap(),
1734 PrefixMatchType::IncludeSuper,
1735 )];
1736 let count_with_super = elems.iter().filter(|e| e.match_filters(&filters)).count();
1737
1738 let filters = vec![Filter::new("prefix_super", "!190.115.192.0/24").unwrap()];
1739 let count_without_super = elems.iter().filter(|e| e.match_filters(&filters)).count();
1740 assert_eq!(count_with_super + count_without_super, elems.len());
1741
1742 let filters = vec![Filter::Prefix(
1744 IpNet::from_str("190.115.192.0/22").unwrap(),
1745 PrefixMatchType::IncludeSub,
1746 )];
1747 let count_with_sub = elems.iter().filter(|e| e.match_filters(&filters)).count();
1748
1749 let filters = vec![Filter::new("prefix_sub", "!190.115.192.0/22").unwrap()];
1750 let count_without_sub = elems.iter().filter(|e| e.match_filters(&filters)).count();
1751 assert_eq!(count_with_sub + count_without_sub, elems.len());
1752
1753 let filters = vec![Filter::Prefix(
1755 IpNet::from_str("190.115.192.0/23").unwrap(),
1756 PrefixMatchType::IncludeSuperSub,
1757 )];
1758 let count_with_super_sub = elems.iter().filter(|e| e.match_filters(&filters)).count();
1759
1760 let filters = vec![Filter::new("prefix_super_sub", "!190.115.192.0/23").unwrap()];
1761 let count_without_super_sub = elems.iter().filter(|e| e.match_filters(&filters)).count();
1762 assert_eq!(count_with_super_sub + count_without_super_sub, elems.len());
1763 }
1764
1765 #[test]
1766 fn test_double_negation_rejected() {
1767 let result = Filter::new("origin_asn", "!!13335");
1770 assert!(result.is_err());
1771 let err = result.unwrap_err();
1772 assert!(err.to_string().contains("double negation"));
1773
1774 let result = Filter::new("prefix", "!!!10.0.0.0/8");
1775 assert!(result.is_err());
1776 let err = result.unwrap_err();
1777 assert!(err.to_string().contains("double negation"));
1778 }
1779
1780 #[test]
1781 fn test_timestamp_negation_rejected() {
1782 let result = Filter::new("ts_start", "!1637437798");
1784 assert!(result.is_err());
1785 let err = result.unwrap_err();
1786 assert!(err
1787 .to_string()
1788 .contains("timestamp filter 'ts_start' does not support negation"));
1789
1790 let result = Filter::new("ts_end", "!1637437798");
1791 assert!(result.is_err());
1792 let err = result.unwrap_err();
1793 assert!(err
1794 .to_string()
1795 .contains("timestamp filter 'ts_end' does not support negation"));
1796
1797 let result = Filter::new("start_ts", "!1637437798");
1798 assert!(result.is_err());
1799
1800 let result = Filter::new("end_ts", "!1637437798");
1801 assert!(result.is_err());
1802 }
1803
1804 #[test]
1805 fn test_multiple_origin_asns() -> Result<()> {
1806 let filter = Filter::new("origin_asns", "12345,67890,13335").unwrap();
1808 match filter {
1809 Filter::OriginAsns(asns) => {
1810 assert_eq!(asns.len(), 3);
1811 assert!(asns.contains(&12345));
1812 assert!(asns.contains(&67890));
1813 assert!(asns.contains(&13335));
1814 }
1815 _ => panic!("Expected OriginAsns filter"),
1816 }
1817
1818 let filter = Filter::new("origin_asns", "12345, 67890, 13335").unwrap();
1820 match filter {
1821 Filter::OriginAsns(asns) => {
1822 assert_eq!(asns.len(), 3);
1823 }
1824 _ => panic!("Expected OriginAsns filter"),
1825 }
1826
1827 Ok(())
1828 }
1829
1830 #[test]
1831 fn test_multiple_prefixes() -> Result<()> {
1832 let prefix1 = IpNet::from_str("190.115.192.0/22").unwrap();
1834 let prefix2 = IpNet::from_str("2804:100::/32").unwrap();
1835
1836 let filter = Filter::new("prefixes", "190.115.192.0/22,2804:100::/32").unwrap();
1837 match filter {
1838 Filter::Prefixes(prefixes, match_type) => {
1839 assert_eq!(prefixes.len(), 2);
1840 assert!(prefixes.contains(&prefix1));
1841 assert!(prefixes.contains(&prefix2));
1842 assert_eq!(match_type, PrefixMatchType::Exact);
1843 }
1844 _ => panic!("Expected Prefixes filter"),
1845 }
1846
1847 let filter = Filter::new("prefixes", "190.115.192.0/22, 2804:100::/32").unwrap();
1849 match filter {
1850 Filter::Prefixes(prefixes, _) => {
1851 assert_eq!(prefixes.len(), 2);
1852 }
1853 _ => panic!("Expected Prefixes filter"),
1854 }
1855
1856 Ok(())
1857 }
1858
1859 #[test]
1860 fn test_multiple_prefixes_with_match_types() -> Result<()> {
1861 let filter = Filter::new("prefixes_super", "190.115.192.0/24,2804:100::/32").unwrap();
1863 match filter {
1864 Filter::Prefixes(prefixes, match_type) => {
1865 assert_eq!(prefixes.len(), 2);
1866 assert_eq!(match_type, PrefixMatchType::IncludeSuper);
1867 }
1868 _ => panic!("Expected Prefixes filter with IncludeSuper"),
1869 }
1870
1871 let filter = Filter::new("prefixes_sub", "190.115.192.0/22,2804:100::/32").unwrap();
1873 match filter {
1874 Filter::Prefixes(prefixes, match_type) => {
1875 assert_eq!(prefixes.len(), 2);
1876 assert_eq!(match_type, PrefixMatchType::IncludeSub);
1877 }
1878 _ => panic!("Expected Prefixes filter with IncludeSub"),
1879 }
1880
1881 let filter = Filter::new("prefixes_super_sub", "190.115.192.0/23,2804:100::/32").unwrap();
1883 match filter {
1884 Filter::Prefixes(prefixes, match_type) => {
1885 assert_eq!(prefixes.len(), 2);
1886 assert_eq!(match_type, PrefixMatchType::IncludeSuperSub);
1887 }
1888 _ => panic!("Expected Prefixes filter with IncludeSuperSub"),
1889 }
1890
1891 Ok(())
1892 }
1893
1894 #[test]
1895 fn test_multiple_peer_asns() -> Result<()> {
1896 let filter = Filter::new("peer_asns", "12345,67890,13335").unwrap();
1898 match filter {
1899 Filter::PeerAsns(asns) => {
1900 assert_eq!(asns.len(), 3);
1901 assert!(asns.contains(&12345));
1902 assert!(asns.contains(&67890));
1903 assert!(asns.contains(&13335));
1904 }
1905 _ => panic!("Expected PeerAsns filter"),
1906 }
1907
1908 Ok(())
1909 }
1910
1911 #[test]
1912 fn test_negated_multiple_filters() -> Result<()> {
1913 let filter = Filter::new("origin_asns", "!13335,!15169").unwrap();
1915 assert!(matches!(filter, Filter::Negated(_)));
1916
1917 let filter = Filter::new("prefixes", "!1.1.1.0/24,!8.8.8.0/24").unwrap();
1919 assert!(matches!(filter, Filter::Negated(_)));
1920
1921 let filter = Filter::new("peer_asns", "!12345,!67890").unwrap();
1923 assert!(matches!(filter, Filter::Negated(_)));
1924
1925 Ok(())
1926 }
1927
1928 #[test]
1929 fn test_invalid_multiple_filters() {
1930 let result = Filter::new("origin_asns", "12345,not_a_number,67890");
1932 assert!(result.is_err());
1933
1934 let result = Filter::new("prefixes", "1.1.1.0/24,invalid_prefix");
1936 assert!(result.is_err());
1937
1938 let result = Filter::new("peer_asns", "12345,invalid,67890");
1940 assert!(result.is_err());
1941
1942 let result = Filter::new("peer_ips", "192.168.1.1,invalid_ip");
1944 assert!(result.is_err());
1945
1946 let result = Filter::new("origin_asns", "12345,!67890");
1948 assert!(result.is_err());
1949 assert!(result
1950 .unwrap_err()
1951 .to_string()
1952 .contains("cannot mix positive and negative values"));
1953
1954 let result = Filter::new("prefixes", "1.1.1.0/24,!8.8.8.0/24");
1955 assert!(result.is_err());
1956 assert!(result
1957 .unwrap_err()
1958 .to_string()
1959 .contains("cannot mix positive and negative values"));
1960
1961 let result = Filter::new("peer_ips", "192.168.1.1,!10.0.0.1");
1962 assert!(result.is_err());
1963 assert!(result
1964 .unwrap_err()
1965 .to_string()
1966 .contains("cannot mix positive and negative values"));
1967
1968 let result = Filter::new("peer_asns", "!12345,67890");
1969 assert!(result.is_err());
1970 assert!(result
1971 .unwrap_err()
1972 .to_string()
1973 .contains("cannot mix positive and negative values"));
1974
1975 let result = Filter::new("origin_asns", "");
1977 assert!(result.is_err());
1978 assert!(result.unwrap_err().to_string().contains("at least one ASN"));
1979
1980 let result = Filter::new("prefixes", "");
1982 assert!(result.is_err());
1983 assert!(result
1984 .unwrap_err()
1985 .to_string()
1986 .contains("at least one prefix"));
1987
1988 let result = Filter::new("peer_ips", "");
1990 assert!(result.is_err());
1991 assert!(result.unwrap_err().to_string().contains("at least one IP"));
1992
1993 let result = Filter::new("origin_asns", ",,,");
1995 assert!(result.is_err());
1996 assert!(result.unwrap_err().to_string().contains("at least one ASN"));
1997
1998 let result = Filter::new("prefixes", ",,,");
2000 assert!(result.is_err());
2001 assert!(result
2002 .unwrap_err()
2003 .to_string()
2004 .contains("at least one prefix"));
2005
2006 let result = Filter::new("peer_ips", ",,,");
2008 assert!(result.is_err());
2009 assert!(result.unwrap_err().to_string().contains("at least one IP"));
2010
2011 let result = Filter::new("origin_asns", "12345,67890,");
2013 assert!(result.is_ok());
2014
2015 let result = Filter::new("origin_asns", "12345,,67890");
2017 assert!(result.is_ok());
2018
2019 let result = Filter::new("peer_ips", "192.168.1.1,192.168.1.2,");
2021 assert!(result.is_ok());
2022 }
2023
2024 #[test]
2025 fn test_multiple_filters_or_logic_behavior() {
2026 let elem = BgpElem {
2028 timestamp: 1637437798_f64,
2029 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
2030 peer_asn: Asn::new_32bit(12345),
2031 peer_bgp_id: None,
2032 prefix: NetworkPrefix::new(IpNet::from_str("192.168.1.0/24").unwrap(), None),
2033 next_hop: None,
2034 as_path: Some(AsPath::from_sequence(vec![174, 1916, 52888])),
2035 origin_asns: Some(vec![Asn::new_16bit(12345)]),
2036 origin: None,
2037 local_pref: None,
2038 med: None,
2039 communities: None,
2040 atomic: false,
2041 aggr_asn: None,
2042 aggr_ip: None,
2043 only_to_customer: None,
2044 unknown: None,
2045 elem_type: ElemType::ANNOUNCE,
2046 deprecated: None,
2047 };
2048
2049 let filter = Filter::new("origin_asns", "12345,67890,99999").unwrap();
2051 assert!(elem.match_filter(&filter)); let filter = Filter::new("origin_asns", "67890,99999").unwrap();
2054 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();
2058 assert!(elem.match_filter(&filter)); let filter = Filter::new("prefixes", "10.0.0.0/8,172.16.0.0/12").unwrap();
2061 assert!(!elem.match_filter(&filter)); let filter = Filter::new("peer_asns", "12345,67890").unwrap();
2065 assert!(elem.match_filter(&filter)); let filter = Filter::new("peer_asns", "67890,99999").unwrap();
2068 assert!(!elem.match_filter(&filter)); let filter = Filter::new("origin_asns", "!67890,!99999").unwrap();
2072 assert!(elem.match_filter(&filter)); let filter = Filter::new("origin_asns", "!12345,!67890").unwrap();
2075 assert!(!elem.match_filter(&filter)); }
2077
2078 fn elem_with_attrs() -> BgpElem {
2079 BgpElem {
2080 timestamp: 1637437798_f64,
2081 peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
2082 peer_asn: Asn::new_32bit(12345),
2083 peer_bgp_id: Some(Ipv4Addr::from_str("10.0.0.1").unwrap()),
2084 prefix: NetworkPrefix::new(IpNet::from_str("192.168.1.0/24").unwrap(), None),
2085 next_hop: Some(IpAddr::from_str("10.0.0.2").unwrap()),
2086 as_path: Some(AsPath::from_sequence(vec![174, 1916, 52888])),
2087 origin_asns: Some(vec![Asn::new_32bit(52888)]),
2088 origin: Some(Origin::IGP),
2089 local_pref: Some(100),
2090 med: Some(50),
2091 communities: None,
2092 atomic: true,
2093 aggr_asn: Some(Asn::new_32bit(65100)),
2094 aggr_ip: Some(Ipv4Addr::from_str("10.0.0.3").unwrap()),
2095 only_to_customer: Some(Asn::new_32bit(65200)),
2096 unknown: None,
2097 elem_type: ElemType::ANNOUNCE,
2098 deprecated: None,
2099 }
2100 }
2101
2102 #[test]
2103 fn test_extended_filters_value_match() {
2104 let elem = elem_with_attrs();
2105
2106 assert!(elem.match_filter(&Filter::new("otc", "65200").unwrap()));
2108 assert!(!elem.match_filter(&Filter::new("otc", "99999").unwrap()));
2109
2110 assert!(elem.match_filter(&Filter::new("next_hop", "10.0.0.2").unwrap()));
2112 assert!(!elem.match_filter(&Filter::new("next_hop", "10.0.0.9").unwrap()));
2113
2114 assert!(elem.match_filter(&Filter::new("origin", "igp").unwrap()));
2116 assert!(elem.match_filter(&Filter::new("origin", "IGP").unwrap())); assert!(!elem.match_filter(&Filter::new("origin", "egp").unwrap()));
2118
2119 assert!(elem.match_filter(&Filter::new("local_pref", "100").unwrap()));
2121 assert!(!elem.match_filter(&Filter::new("local_pref", "200").unwrap()));
2122
2123 assert!(elem.match_filter(&Filter::new("med", "50").unwrap()));
2125 assert!(!elem.match_filter(&Filter::new("med", "99").unwrap()));
2126
2127 assert!(elem.match_filter(&Filter::new("atomic", "true").unwrap()));
2129 assert!(!elem.match_filter(&Filter::new("atomic", "false").unwrap()));
2130
2131 assert!(elem.match_filter(&Filter::new("aggr_asn", "65100").unwrap()));
2133 assert!(!elem.match_filter(&Filter::new("aggr_asn", "99999").unwrap()));
2134
2135 assert!(elem.match_filter(&Filter::new("aggr_ip", "10.0.0.3").unwrap()));
2137 assert!(!elem.match_filter(&Filter::new("aggr_ip", "10.0.0.9").unwrap()));
2138
2139 assert!(elem.match_filter(&Filter::new("peer_bgp_id", "10.0.0.1").unwrap()));
2141 assert!(!elem.match_filter(&Filter::new("peer_bgp_id", "10.0.0.9").unwrap()));
2142 }
2143
2144 #[test]
2145 fn test_extended_filters_negation() {
2146 let elem = elem_with_attrs();
2147
2148 assert!(elem.match_filter(&Filter::new("otc", "!99999").unwrap()));
2150 assert!(!elem.match_filter(&Filter::new("otc", "!65200").unwrap()));
2151 assert!(elem.match_filter(&Filter::new("next_hop", "!10.0.0.9").unwrap()));
2152 assert!(!elem.match_filter(&Filter::new("next_hop", "!10.0.0.2").unwrap()));
2153 assert!(elem.match_filter(&Filter::new("origin", "!egp").unwrap()));
2154 assert!(elem.match_filter(&Filter::new("local_pref", "!200").unwrap()));
2155 assert!(elem.match_filter(&Filter::new("med", "!99").unwrap()));
2156 assert!(elem.match_filter(&Filter::new("atomic", "!false").unwrap()));
2157 assert!(elem.match_filter(&Filter::new("aggr_asn", "!99999").unwrap()));
2158 assert!(elem.match_filter(&Filter::new("aggr_ip", "!10.0.0.9").unwrap()));
2159 assert!(elem.match_filter(&Filter::new("peer_bgp_id", "!10.0.0.9").unwrap()));
2160 }
2161
2162 #[test]
2163 fn test_extended_filters_presence() {
2164 let elem_full = elem_with_attrs();
2165 let elem_empty = filter_test_elem(); assert!(elem_full.match_filter(&Filter::new("otc", "*").unwrap()));
2169 assert!(elem_full.match_filter(&Filter::new("next_hop", "*").unwrap()));
2170 assert!(elem_full.match_filter(&Filter::new("origin", "*").unwrap()));
2171 assert!(elem_full.match_filter(&Filter::new("local_pref", "*").unwrap()));
2172 assert!(elem_full.match_filter(&Filter::new("med", "*").unwrap()));
2173 assert!(elem_full.match_filter(&Filter::new("aggr_asn", "*").unwrap()));
2174 assert!(elem_full.match_filter(&Filter::new("aggr_ip", "*").unwrap()));
2175 assert!(elem_full.match_filter(&Filter::new("peer_bgp_id", "*").unwrap()));
2176
2177 assert!(!elem_empty.match_filter(&Filter::new("otc", "*").unwrap()));
2179 assert!(!elem_empty.match_filter(&Filter::new("next_hop", "*").unwrap()));
2180 assert!(!elem_empty.match_filter(&Filter::new("origin", "*").unwrap()));
2181 assert!(!elem_empty.match_filter(&Filter::new("local_pref", "*").unwrap()));
2182 assert!(!elem_empty.match_filter(&Filter::new("med", "*").unwrap()));
2183 assert!(!elem_empty.match_filter(&Filter::new("aggr_asn", "*").unwrap()));
2184 assert!(!elem_empty.match_filter(&Filter::new("aggr_ip", "*").unwrap()));
2185 assert!(!elem_empty.match_filter(&Filter::new("peer_bgp_id", "*").unwrap()));
2186
2187 assert!(elem_empty.match_filter(&Filter::new("otc", "!*").unwrap()));
2189 assert!(elem_empty.match_filter(&Filter::new("next_hop", "!*").unwrap()));
2190 assert!(elem_empty.match_filter(&Filter::new("origin", "!*").unwrap()));
2191 assert!(elem_empty.match_filter(&Filter::new("local_pref", "!*").unwrap()));
2192 assert!(elem_empty.match_filter(&Filter::new("med", "!*").unwrap()));
2193 assert!(elem_empty.match_filter(&Filter::new("aggr_asn", "!*").unwrap()));
2194 assert!(elem_empty.match_filter(&Filter::new("aggr_ip", "!*").unwrap()));
2195 assert!(elem_empty.match_filter(&Filter::new("peer_bgp_id", "!*").unwrap()));
2196
2197 assert!(!elem_full.match_filter(&Filter::new("otc", "!*").unwrap()));
2199 assert!(!elem_full.match_filter(&Filter::new("next_hop", "!*").unwrap()));
2200 }
2201
2202 #[test]
2203 fn test_extended_filters_on_route_elem() {
2204 let elem = elem_with_attrs();
2205 let route = route_projection(&elem);
2206
2207 assert!(!route.match_filter(&Filter::new("otc", "65200").unwrap()));
2209 assert!(!route.match_filter(&Filter::new("next_hop", "10.0.0.2").unwrap()));
2210 assert!(!route.match_filter(&Filter::new("otc", "*").unwrap()));
2211 assert!(!route.match_filter(&Filter::new("atomic", "false").unwrap()));
2213 assert!(!route.match_filter(&Filter::new("atomic", "true").unwrap()));
2214
2215 assert!(!route.match_filter(&Filter::new("otc", "!65200").unwrap()));
2217 assert!(!route.match_filter(&Filter::new("otc", "!*").unwrap()));
2218
2219 assert!(route.match_filter(&Filter::new("origin_asn", "52888").unwrap()));
2221 assert!(route.match_filter(&Filter::new("prefix", "192.168.1.0/24").unwrap()));
2222 }
2223
2224 #[test]
2225 fn test_extended_filter_parsing_errors() {
2226 assert!(Filter::new("otc", "not_a_number").is_err());
2227 assert!(Filter::new("next_hop", "not_an_ip").is_err());
2228 assert!(Filter::new("origin", "invalid").is_err());
2229 assert!(Filter::new("local_pref", "not_a_number").is_err());
2230 assert!(Filter::new("med", "not_a_number").is_err());
2231 assert!(Filter::new("atomic", "not_a_bool").is_err());
2232 assert!(Filter::new("aggr_asn", "not_a_number").is_err());
2233 assert!(Filter::new("aggr_ip", "not_an_ip").is_err());
2234 assert!(Filter::new("peer_bgp_id", "not_an_ip").is_err());
2235 }
2236}