Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ All notable changes to this project will be documented in this file.

### New features

* **Negative Filter Support**: Most filters now support negation by prefixing the filter type with `!`
- `!origin_asn`: Match elements where origin AS is NOT the specified value
- `!prefix`: Match elements where prefix is NOT the specified value
- `!peer_ip`, `!peer_asn`, `!type`, `!as_path`, `!community`, `!ip_version`: All support negation
- Note: Timestamp filters (`ts_start`, `ts_end`) do not support negation
- Example: `.add_filter("!origin_asn", "13335")` matches all elements NOT from AS 13335
- New `Filter::Negated(Box<Filter>)` variant wraps any filter to invert its match result
- **CLI**: New `--filter` / `-f` option supports both positive and negative filter expressions
- Positive: `--filter "origin_asn=13335"`
- Negative: `--filter "origin_asn!=13335"`
- Can be used multiple times: `--filter "peer_ip!=192.168.1.1" --filter "type!=w"`

* **RPKI RTR Protocol Support**: Add full support for the RPKI-to-Router (RTR) protocol
- New `models::rpki::rtr` module with all PDU types: SerialNotify, SerialQuery, ResetQuery, CacheResponse, IPv4Prefix, IPv6Prefix, EndOfData, CacheReset, RouterKey, ErrorReport
- New `parser::rpki::rtr` module with parsing (`parse_rtr_pdu`, `read_rtr_pdu`) and encoding (`RtrEncode` trait)
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,25 @@ for elem in parser {
- `elem_type`: Filter by announcement (`a`) or withdrawal (`w`)
- `as_path`: Match AS path with regex

**Negative filters**: Most filters support negation by prefixing the filter type with `!`. For example:
- `!origin_asn`: Match elements where origin AS is NOT the specified value
- `!prefix`: Match elements where prefix is NOT the specified value
- `!peer_ip`: Match elements where peer IP is NOT the specified value

**Note**: Timestamp filters (`ts_start`, `ts_end`) do not support negation.

```rust
use bgpkit_parser::BgpkitParser;

// Filter out all elements from AS 13335 (get everything EXCEPT AS 13335)
let parser = BgpkitParser::new("http://archive.routeviews.org/bgpdata/2021.10/UPDATES/updates.20211001.0000.bz2").unwrap()
.add_filter("!origin_asn", "13335").unwrap();

for elem in parser {
println!("{}", elem);
}
```

#### Parsing Multiple MRT Files with BGPKIT Broker

[BGPKIT Broker][broker-repo] library provides search API for all RouteViews and RIPE RIS MRT data files. Using the
Expand Down Expand Up @@ -453,6 +472,7 @@ Options:
-e, --elems-count Count BGP elems
-r, --records-count Count MRT records
-o, --origin-asn <ORIGIN_ASN> Filter by origin AS Number
-f, --filter <FILTERS> Generic filter expression (key=value or key!=value)
-p, --prefix <PREFIX> Filter by network prefix
-4, --ipv4-only Filter by IPv4 only
-6, --ipv6-only Filter by IPv6 only
Expand Down Expand Up @@ -507,6 +527,18 @@ bgpkit-parser -c ~/.bgpkit-cache http://example.com/updates.mrt.bz2
bgpkit-parser -o 13335 -m a -4 updates.bz2
```

#### Negative filters (exclude matching elements)
```bash
# Exclude elements from AS 13335
bgpkit-parser --filter "origin_asn!=13335" updates.bz2

# Exclude a specific peer
bgpkit-parser --filter "peer_ip!=192.168.1.1" updates.bz2

# Combine positive and negative filters
bgpkit-parser -o 13335 --filter "peer_asn!=64496" updates.bz2
```

## Data Representation

BGPKIT Parser provides three ways to access parsed BGP data: [MrtRecord], [MrtUpdate], and [BgpElem]. Choose based on your needs:
Expand Down
58 changes: 58 additions & 0 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ struct Filters {
#[clap(short = 'o', long)]
origin_asn: Option<u32>,

/// Generic filter expression (can be used multiple times)
/// Format: "key=value" for positive match, "key!=value" for negative match
/// Examples: --filter "origin_asn!=13335" --filter "peer_ip!=192.168.1.1"
/// Supported keys: origin_asn, prefix, peer_ip, peer_ips, peer_asn, type, as_path, community, ip_version
#[clap(short = 'f', long = "filter")]
filters: Vec<String>,

/// Filter by network prefix
#[clap(short = 'p', long)]
prefix: Option<IpNet>,
Expand Down Expand Up @@ -194,6 +201,25 @@ fn main() {
parser = parser.add_filter("end_ts", v.to_string().as_str()).unwrap();
}

// Process generic filter expressions
for filter_expr in &opts.filters.filters {
match parse_filter_expression(filter_expr) {
Ok((filter_type, filter_value)) => {
parser = match parser.add_filter(&filter_type, &filter_value) {
Ok(p) => p,
Err(e) => {
eprintln!("Error adding filter '{}': {}", filter_expr, e);
std::process::exit(1);
}
};
}
Err(e) => {
eprintln!("Invalid filter expression '{}': {}", filter_expr, e);
std::process::exit(1);
}
}
}

match (opts.filters.ipv4_only, opts.filters.ipv6_only) {
(true, true) => {
eprintln!("Error: --ipv4-only and --ipv6-only cannot be used together");
Expand Down Expand Up @@ -306,3 +332,35 @@ fn format_record(record: &bgpkit_parser::MrtRecord, format: OutputFormat) -> Str
}
}
}

/// Parse a filter expression in the format "key=value" or "key!=value"
/// Returns (filter_type, filter_value) where filter_type may be prefixed with "!" for negation
fn parse_filter_expression(expr: &str) -> Result<(String, String), String> {
// Check for "!=" (negative filter) first
if let Some(pos) = expr.find("!=") {
let key = expr[..pos].trim();
let value = expr[pos + 2..].trim();
if key.is_empty() {
return Err("filter key cannot be empty".to_string());
}
if value.is_empty() {
return Err("filter value cannot be empty".to_string());
}
// Prefix with "!" to indicate negation
Ok((format!("!{}", key), value.to_string()))
}
// Check for "=" (positive filter)
else if let Some(pos) = expr.find('=') {
let key = expr[..pos].trim();
let value = expr[pos + 1..].trim();
if key.is_empty() {
return Err("filter key cannot be empty".to_string());
}
if value.is_empty() {
return Err("filter value cannot be empty".to_string());
}
Ok((key.to_string(), value.to_string()))
} else {
Err("filter expression must contain '=' or '!=' (e.g., 'origin_asn=13335' or 'origin_asn!=13335')".to_string())
}
}
32 changes: 32 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,25 @@ for elem in parser {
- `elem_type`: Filter by announcement (`a`) or withdrawal (`w`)
- `as_path`: Match AS path with regex

**Negative filters**: Most filters support negation by prefixing the filter type with `!`. For example:
- `!origin_asn`: Match elements where origin AS is NOT the specified value
- `!prefix`: Match elements where prefix is NOT the specified value
- `!peer_ip`: Match elements where peer IP is NOT the specified value

**Note**: Timestamp filters (`ts_start`, `ts_end`) do not support negation.

```no_run
use bgpkit_parser::BgpkitParser;

// Filter out all elements from AS 13335 (get everything EXCEPT AS 13335)
let parser = BgpkitParser::new("http://archive.routeviews.org/bgpdata/2021.10/UPDATES/updates.20211001.0000.bz2").unwrap()
.add_filter("!origin_asn", "13335").unwrap();

for elem in parser {
println!("{}", elem);
}
```

### Parsing Multiple MRT Files with BGPKIT Broker

[BGPKIT Broker][broker-repo] library provides search API for all RouteViews and RIPE RIS MRT data files. Using the
Expand Down Expand Up @@ -449,6 +468,7 @@ Options:
-e, --elems-count Count BGP elems
-r, --records-count Count MRT records
-o, --origin-asn <ORIGIN_ASN> Filter by origin AS Number
-f, --filter <FILTERS> Generic filter expression (key=value or key!=value)
-p, --prefix <PREFIX> Filter by network prefix
-4, --ipv4-only Filter by IPv4 only
-6, --ipv6-only Filter by IPv6 only
Expand Down Expand Up @@ -503,6 +523,18 @@ bgpkit-parser -c ~/.bgpkit-cache http://example.com/updates.mrt.bz2
bgpkit-parser -o 13335 -m a -4 updates.bz2
```

### Negative filters (exclude matching elements)
```bash
# Exclude elements from AS 13335
bgpkit-parser --filter "origin_asn!=13335" updates.bz2

# Exclude a specific peer
bgpkit-parser --filter "peer_ip!=192.168.1.1" updates.bz2

# Combine positive and negative filters
bgpkit-parser -o 13335 --filter "peer_asn!=64496" updates.bz2
```

# Data Representation

BGPKIT Parser provides three ways to access parsed BGP data: [MrtRecord], [MrtUpdate], and [BgpElem]. Choose based on your needs:
Expand Down
Loading