bgpkit_parser/parser/
filter.rs

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