<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>BGPKIT Blog</title><description>Updates, releases, and technical notes from the BGPKIT team.</description><link>https://bgpkit.com/</link><language>en-us</language><atom:link href="https://bgpkit.com/blog/rss.xml" rel="self" type="application/rss+xml"/><item><title>bgpkit-parser 0.18.0 Release</title><link>https://bgpkit.com/blog/bgpkit-parser-018-release/</link><guid isPermaLink="true">https://bgpkit.com/blog/bgpkit-parser-018-release/</guid><description>bgpkit-parser 0.18.0 preserves more raw BGP wire data, parses RIS Live raw messages, and adds typed parsers for AIGP, SFP, BFD Discriminator, BGP Prefix-SID, and BIER attributes.</description><pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser/releases/tag/v0.18.0&quot;&gt;&lt;code&gt;bgpkit-parser&lt;/code&gt; 0.18.0&lt;/a&gt; preserves more of the original BGP wire data. Earlier versions parsed many common attributes into typed Rust structures, but element conversion could drop known attributes that did not yet have a semantic parser. That worked for many analysis workflows, but it was not enough for users who need to inspect, serialize, or re-encode less common BGP path attributes.&lt;/p&gt;
&lt;p&gt;This release changes that behavior: the parser now retains known-but-unsupported, malformed, deprecated, and unknown BGP path attributes in &lt;code&gt;AttrRaw&lt;/code&gt;-backed variants instead of silently dropping them.&lt;/p&gt;
&lt;h2&gt;Install&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cargo add bgpkit-parser@0.18.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the WebAssembly package:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install @bgpkit/parser@0.18.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;RIS Live raw message parsing&lt;/h2&gt;
&lt;p&gt;RIS Live&apos;s JSON messages expose a convenient projection of UPDATE data, but that projection does not include every BGP path attribute. For example, attributes such as Only to Customer (OTC, code 35), BGP Prefix-SID, BFD Discriminator, and other less common path attributes may appear in the raw UPDATE while remaining absent from the projected JSON fields. Version 0.18.0 adds helpers that parse RIS Live messages containing the original raw BGP UPDATE bytes.&lt;/p&gt;
&lt;p&gt;To request raw BGP messages from RIS Live, set &lt;code&gt;socketOptions.includeRaw&lt;/code&gt; in the subscription message. The crate now re-exports &lt;code&gt;RisSubscribe&lt;/code&gt;, &lt;code&gt;RisSubscribeType&lt;/code&gt;, and &lt;code&gt;RisLiveClientMessage&lt;/code&gt; from the crate root, so examples and downstream applications can build subscription messages directly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use bgpkit_parser::{RisLiveClientMessage, RisSubscribe};

let subscribe = RisSubscribe::new()
    .with_host(&quot;rrc00&quot;)
    .with_include_raw(true);

let msg = RisLiveClientMessage::RisSubscribe(subscribe).to_json_string().unwrap();
// send `msg` over a WebSocket connection to RIS Live
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a RIS Live message arrives, parse the full &lt;code&gt;ris_message&lt;/code&gt; envelope with the raw parser:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use bgpkit_parser::parse_ris_live_message_raw;

let elems = parse_ris_live_message_raw(message_text).expect(&quot;parse RIS Live message&quot;);
for elem in elems {
    println!(&quot;{} {} {:?}&quot;, elem.elem_type, elem.prefix, elem.as_path);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Users who prefer RIS Live&apos;s JSON-projected fields can call &lt;code&gt;parse_ris_live_message_json()&lt;/code&gt; for the explicit JSON-path parser, or keep using the older &lt;code&gt;parse_ris_live_message()&lt;/code&gt; function for the same JSON-projected path.&lt;/p&gt;
&lt;h2&gt;Raw BGP attribute retention&lt;/h2&gt;
&lt;p&gt;The main parser now keeps raw bytes for attributes it cannot or should not decode semantically. The new &lt;code&gt;AttributeValue::Raw(AttrRaw)&lt;/code&gt; variant stores the wire attribute code and value bytes for known-but-unsupported attributes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use bgpkit_parser::models::bgp::attributes::AttributeValue;

for attr in attributes.inner.iter() {
    match attr {
        AttributeValue::Raw(raw) =&gt; {
            println!(&quot;raw attribute code={} len={}&quot;, raw.code, raw.bytes.len());
        }
        other =&gt; {
            println!(&quot;typed attribute code={}&quot;, other.attr_code());
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This matters for round-trip workflows. If a route contains a known attribute that bgpkit-parser does not yet decode into a typed model, 0.18.0 preserves and re-encodes the original attribute bytes.&lt;/p&gt;
&lt;p&gt;Malformed attributes that target typed parsers now fall back to &lt;code&gt;Raw&lt;/code&gt; after recording a validation warning, rather than disappearing from the parsed attribute list.&lt;/p&gt;
&lt;h2&gt;Deprecated attribute code handling&lt;/h2&gt;
&lt;p&gt;The IANA registry lists attribute code 13 as deprecated: &lt;code&gt;RCID_PATH / CLUSTER_ID&lt;/code&gt;. Version 0.18.0 removes &lt;code&gt;AttrType::CLUSTER_ID&lt;/code&gt; and retains code 13 as deprecated raw data instead.&lt;/p&gt;
&lt;p&gt;If downstream code exhaustively matches &lt;code&gt;AttrType&lt;/code&gt; or &lt;code&gt;AttributeValue&lt;/code&gt;, this is one of the release&apos;s breaking changes. Use &lt;code&gt;AttributeValue::attr_code()&lt;/code&gt; when the raw code point matters, and &lt;code&gt;AttrRaw::attr_type()&lt;/code&gt; when you want to map the raw wire code back to an &lt;code&gt;AttrType&lt;/code&gt; value.&lt;/p&gt;
&lt;h2&gt;New typed BGP path attribute parsers&lt;/h2&gt;
&lt;p&gt;This release adds typed parsers and round-trip encoding support for several RFC-defined path attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc7311&quot;&gt;RFC 7311&lt;/a&gt; — AIGP, attribute code 26&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9015&quot;&gt;RFC 9015&lt;/a&gt; — SFP, attribute code 37&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9026&quot;&gt;RFC 9026&lt;/a&gt; — BFD Discriminator, attribute code 38&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc8669&quot;&gt;RFC 8669&lt;/a&gt; — BGP Prefix-SID, attribute code 40&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9793&quot;&gt;RFC 9793&lt;/a&gt; — BIER, attribute code 41&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each parser preserves unknown TLVs, so applications can inspect known fields without losing the rest of the attribute payload.&lt;/p&gt;
&lt;p&gt;For example, AIGP exposes an &lt;code&gt;accumulated_metric()&lt;/code&gt; helper:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use bgpkit_parser::models::bgp::attributes::AttributeValue;

for attr in attributes.inner.iter() {
    if let AttributeValue::Aigp(aigp) = attr {
        if let Some(metric) = aigp.accumulated_metric() {
            println!(&quot;AIGP accumulated metric: {metric}&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;API changes to note&lt;/h2&gt;
&lt;p&gt;The main breaking changes affect raw attribute representation and exhaustive matches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AttrRaw&lt;/code&gt; now stores &lt;code&gt;code: u8&lt;/code&gt; instead of &lt;code&gt;attr_type: AttrType&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AttrRaw.bytes&lt;/code&gt; now uses &lt;code&gt;bytes::Bytes&lt;/code&gt; instead of &lt;code&gt;Vec&amp;#x3C;u8&gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AigpTlv.value&lt;/code&gt; now uses &lt;code&gt;Bytes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AttributeValue&lt;/code&gt; has new variants: &lt;code&gt;Raw&lt;/code&gt;, &lt;code&gt;BfdDiscriminator&lt;/code&gt;, &lt;code&gt;BgpPrefixSid&lt;/code&gt;, &lt;code&gt;Bier&lt;/code&gt;, and &lt;code&gt;Sfp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The release removes &lt;code&gt;AttrType::CLUSTER_ID&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For most users who iterate over &lt;code&gt;BgpElem&lt;/code&gt;, the visible behavior should stay the same except that the parser preserves more attributes internally. Code that destructures attributes or performs exhaustive matching will need updates.&lt;/p&gt;
&lt;h2&gt;WebAssembly package&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;@bgpkit/parser&lt;/code&gt; npm package also ships 0.18.0. The WASM package continues to expose parsers for BGP UPDATE, BMP/OpenBMP, and MRT data in Node.js, bundler, browser, and Worker environments.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import { parseBgpUpdate } from &apos;@bgpkit/parser&apos;;

const elems = parseBgpUpdate(updateBytes);
for (const elem of elems) {
  console.log(elem.type, elem.prefix, elem.as_path);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Rust crate includes RIS Live parsing in this release. We track WASM-level RIS Live parser exports and WebSocket helpers separately.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser/releases/tag/v0.18.0&quot;&gt;GitHub release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Crate: &lt;a href=&quot;https://crates.io/crates/bgpkit-parser/0.18.0&quot;&gt;https://crates.io/crates/bgpkit-parser/0.18.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;npm package: &lt;a href=&quot;https://www.npmjs.com/package/@bgpkit/parser/v/0.18.0&quot;&gt;https://www.npmjs.com/package/@bgpkit/parser/v/0.18.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href=&quot;https://docs.rs/bgpkit-parser/0.18.0/bgpkit_parser/&quot;&gt;https://docs.rs/bgpkit-parser/0.18.0/bgpkit_parser/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>Finding Well-Known BGP Communities with monocle</title><link>https://bgpkit.com/blog/well-known-bgp-communities-monocle/</link><guid isPermaLink="true">https://bgpkit.com/blog/well-known-bgp-communities-monocle/</guid><description>An ad-hoc, reproducible analysis for scanning public MRT updates for BGP communities, joining them with the IANA registry, and checking behavior against RFC text.</description><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;BGP communities are easy to parse and hard to interpret. A route can carry a list of 32-bit values, but the meaning of each value depends on a mix of RFCs, IANA registries, operator conventions, and local routing policy.&lt;/p&gt;
&lt;p&gt;This post walks through a small ad-hoc &lt;a href=&quot;https://bgpkit.com/tools/monocle/&quot;&gt;&lt;code&gt;monocle&lt;/code&gt;&lt;/a&gt; analysis for turning raw MRT updates into a protocol-aware summary:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;search public BGP updates for communities in the &lt;code&gt;65535:*&lt;/code&gt; range,&lt;/li&gt;
&lt;li&gt;summarize where each value appears,&lt;/li&gt;
&lt;li&gt;join observed values with the IANA BGP Well-known Communities registry,&lt;/li&gt;
&lt;li&gt;check specific values, such as &lt;code&gt;BLACKHOLE&lt;/code&gt; and &lt;code&gt;NOPEER&lt;/code&gt;, against the relevant RFC text.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The goal is not to infer operator intent from route collector data. This is a one-off research note, not production automation. The goal is narrower: show how BGPKIT tooling can make this kind of evidence gathering easier to repeat and audit.&lt;/p&gt;
&lt;h2&gt;Why &lt;code&gt;65535:*&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc1997&quot;&gt;RFC 1997&lt;/a&gt; introduced the BGP COMMUNITIES path attribute. In the &quot;Well-known Communities&quot; part of the document, it defines several globally significant values and says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The following communities have global significance and their operations shall be implemented in any community-attribute-aware BGP speaker.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The current IANA registry is the &lt;a href=&quot;https://www.iana.org/assignments/bgp-well-known-communities/bgp-well-known-communities.xhtml&quot;&gt;BGP Well-known Communities registry&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The registry divides the community space into ranges, including:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Range&lt;/th&gt;&lt;th&gt;Registration rule&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x00000000-0x0000FFFF&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Reserved&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x00010000-0xFFFEFFFF&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Private Use&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0xFFFF0000-0xFFFF8000&lt;/code&gt;&lt;/td&gt;&lt;td&gt;First Come First Served&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0xFFFF8001-0xFFFFFFFF&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Standards Action&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;That makes values with high-order 16 bits set to &lt;code&gt;65535&lt;/code&gt; interesting: they sit in the part of the 32-bit community space where many globally significant values live.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;On macOS, install &lt;code&gt;monocle&lt;/code&gt; with Homebrew:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install monocle
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you prefer building from Rust crates, or are not using Homebrew:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cargo install monocle
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For reproducibility, record the version before running the scan:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This run used:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;monocle 1.3.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This analysis used the public MRT index available to &lt;code&gt;monocle&lt;/code&gt; at run time. To make the collector/file set auditable, first write the matching MRT file list:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;START=1782324000   # 2026-06-24T18:00:00Z
END=$((START+900)) # 15 minutes
OUT=updates-20260624T1800Z-15m-allcollectors-wk

monocle search \
  --dump-type updates \
  --start-ts $START \
  --end-ts $END \
  --community &apos;65535:*&apos; \
  --broker-files \
  &gt; &quot;$OUT-files.txt&quot;

wc -l &quot;$OUT-files.txt&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For this run, &lt;code&gt;monocle&lt;/code&gt; found 125 matching MRT update files.&lt;/p&gt;
&lt;p&gt;The exact file count can vary if the public collector index changes or if late files appear. If you need a fixed rerun, save this file list with the analysis artifacts and use the same &lt;code&gt;monocle&lt;/code&gt; version.&lt;/p&gt;
&lt;h2&gt;Searching MRT updates with &lt;code&gt;monocle&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;For this run, I scanned the same 15-minute update window:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;START=1782324000   # 2026-06-24T18:00:00Z
END=$((START+900)) # 15 minutes
OUT=updates-20260624T1800Z-15m-allcollectors-wk

monocle search \
  --dump-type updates \
  --start-ts $START \
  --end-ts $END \
  --community &apos;65535:*&apos; \
  --fields timestamp,collector,prefix,peer_asn,origin_asns,as_path,communities \
  --format json-line \
  &gt; &quot;$OUT.jsonl&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A representative JSONL row looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;timestamp&quot;: 1782324014.12,
  &quot;collector&quot;: &quot;rrc07&quot;,
  &quot;prefix&quot;: &quot;83.171.244.0/24&quot;,
  &quot;peer_asn&quot;: 3399,
  &quot;origin_asns&quot;: [&quot;197761&quot;],
  &quot;as_path&quot;: &quot;3399 5511 6204 51202 51202 197761&quot;,
  &quot;communities&quot;: [&quot;3399:100&quot;, &quot;3399:107&quot;, &quot;5511:500&quot;, &quot;6204:1000&quot;, &quot;65535:65284&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important part is the community filter:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;--community &apos;65535:*&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;monocle&lt;/code&gt; handles the collector/file discovery and MRT parsing. The output is JSON Lines, so the next step can be a short post-processing script rather than a custom MRT parser.&lt;/p&gt;
&lt;p&gt;For this post, I used small Python scripts rather than building a reusable tool. The main script, &lt;a href=&quot;https://bgpkit.com/files/well-known-bgp-communities-monocle/summarize_existing_well_known.py&quot;&gt;&lt;code&gt;summarize_existing_well_known.py&lt;/code&gt;&lt;/a&gt;, groups rows by observed &lt;code&gt;65535:*&lt;/code&gt; value and counts rows, distinct prefixes, collectors, peer ASNs, origin ASNs, address families, and prefix lengths. It also normalizes symbolic community names that &lt;code&gt;monocle&lt;/code&gt; may emit, such as &lt;code&gt;no-export&lt;/code&gt;, back to their numeric well-known-community values before doing the &lt;code&gt;65535:*&lt;/code&gt; grouping.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 summarize_existing_well_known.py &quot;$OUT.jsonl&quot; &quot;$OUT&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second script, &lt;a href=&quot;https://bgpkit.com/files/well-known-bgp-communities-monocle/enrich_iana_status.py&quot;&gt;&lt;code&gt;enrich_iana_status.py&lt;/code&gt;&lt;/a&gt;, joins the per-community CSV against a local transcription of the IANA BGP Well-known Communities registry, so each observed value can be labeled as &lt;code&gt;BLACKHOLE&lt;/code&gt;, &lt;code&gt;NOPEER&lt;/code&gt;, &lt;code&gt;GRACEFUL_SHUTDOWN&lt;/code&gt;, &lt;code&gt;Unassigned&lt;/code&gt;, and so on. The registry source is the IANA &lt;a href=&quot;https://www.iana.org/assignments/bgp-well-known-communities/bgp-well-known-communities.xhtml&quot;&gt;BGP Well-known Communities registry&lt;/a&gt;, checked for this write-up on 2026-06-25.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 enrich_iana_status.py \
  &quot;$OUT-by-community.csv&quot; \
  &quot;$OUT-by-community-iana.csv&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;What the scan found&lt;/h2&gt;
&lt;p&gt;In this 15-minute window:&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Matching update rows&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;769,537&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Distinct numeric &lt;code&gt;65535:*&lt;/code&gt; values&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;109&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Values not assigned in the IANA registry snapshot&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;102&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The highest-volume values were:&lt;/p&gt;




































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Community&lt;/th&gt;&lt;th&gt;IANA status&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Rows&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Distinct prefixes&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Collectors&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;65535:6939&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Unassigned&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;553,352&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;20&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;27&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;65535:1200&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Unassigned&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;63,194&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;10,455&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;65535:34&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Unassigned&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;41,584&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;6,022&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;58&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;65535:30&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Unassigned&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;37,436&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;2,896&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;54&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;65535:65284&lt;/code&gt;&lt;/td&gt;&lt;td&gt;NOPEER, &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc3765&quot;&gt;RFC 3765&lt;/a&gt;&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;27,766&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1,033&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;51&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;65535:9957&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Unassigned&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;13,245&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1,125&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;65535:0&lt;/code&gt;&lt;/td&gt;&lt;td&gt;GRACEFUL_SHUTDOWN, &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc8326&quot;&gt;RFC 8326&lt;/a&gt;&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;2,176&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;210&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;51&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;65535:666&lt;/code&gt;&lt;/td&gt;&lt;td&gt;BLACKHOLE, &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999&quot;&gt;RFC 7999&lt;/a&gt;&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1,270&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;499&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;18&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;This table is a good example of why registry-aware scripting helps. A raw list of communities is just numbers. Once joined with IANA, the same data separates into named RFC-defined values and values that need local/operator-specific interpretation.&lt;/p&gt;
&lt;p&gt;Importantly, &quot;unassigned&quot; does not mean &quot;bad&quot;. Public BGP data alone does not tell us whether a value is a local convention, a route-server policy signal, a legacy convention, an implementation artifact, or something else. It only tells us where the value appeared and gives us concrete rows to inspect next.&lt;/p&gt;
&lt;h2&gt;Checking BLACKHOLE behavior against &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999&quot;&gt;RFC 7999&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;BLACKHOLE&lt;/code&gt; is assigned as &lt;code&gt;65535:666&lt;/code&gt; by &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999&quot;&gt;RFC 7999&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Two parts of the RFC are useful for a mechanical check.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999#section-3.2&quot;&gt;RFC 7999 Section 3.2&lt;/a&gt; says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A BGP speaker receiving an announcement tagged with the BLACKHOLE community SHOULD add the NO_ADVERTISE or NO_EXPORT community as defined in &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc1997&quot;&gt;RFC 1997&lt;/a&gt;, or a similar community, to prevent propagation of the prefix outside the local AS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999#section-3.3&quot;&gt;RFC 7999 Section 3.3&lt;/a&gt; says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;However, blackhole prefix length should be as long as possible in order to limit the impact of discarding traffic for adjacent IP space that is not under DDoS duress. The blackhole prefix length is typically as specific as possible, /32 for IPv4 or /128 for IPv6.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Those sentences translate into simple checks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;is the prefix &lt;code&gt;/32&lt;/code&gt; for IPv4 or &lt;code&gt;/128&lt;/code&gt; for IPv6?&lt;/li&gt;
&lt;li&gt;does the route also carry &lt;code&gt;NO_EXPORT&lt;/code&gt; or &lt;code&gt;NO_ADVERTISE&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the 15-minute scan, &lt;code&gt;65535:666&lt;/code&gt; appeared in 1,270 rows:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Observed property&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Rows with property&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Rows without property&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/32&lt;/code&gt; or &lt;code&gt;/128&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;474 (37.3%)&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;796 (62.7%)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Carries &lt;code&gt;NO_EXPORT&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;460 (36.2%)&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;810 (63.8%)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Carries &lt;code&gt;NO_ADVERTISE&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;24 (1.9%)&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1,246 (98.1%)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Address-family split: 463 IPv4 rows (36.5%) and 807 IPv6 rows (63.5%).&lt;/p&gt;
&lt;p&gt;The resulting rows include examples that match the &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999&quot;&gt;RFC 7999&lt;/a&gt; profile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;collector&quot;: &quot;decix.fra&quot;,
  &quot;peer_asn&quot;: 6695,
  &quot;prefix&quot;: &quot;193.56.63.113/32&quot;,
  &quot;origin_asns&quot;: [&quot;215514&quot;],
  &quot;as_path&quot;: &quot;201053 50607 215514&quot;,
  &quot;communities&quot;: [&quot;65535:666&quot;, &quot;no-export&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It also found examples that lack those observed properties. This is not a compliance judgment: &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999#section-3.2&quot;&gt;RFC 7999 Section 3.2&lt;/a&gt; describes behavior for a receiving BGP speaker, and collector data may show the route before or after other policy actions. Still, rows like this are useful starting points for follow-up:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;collector&quot;: &quot;iix.cgk&quot;,
  &quot;peer_asn&quot;: 7597,
  &quot;prefix&quot;: &quot;2001:4:112::/48&quot;,
  &quot;origin_asns&quot;: [&quot;112&quot;],
  &quot;as_path&quot;: &quot;136106 35280 50628 44097 112&quot;,
  &quot;communities&quot;: [&quot;65535:666&quot;, &quot;7597:1000:1&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is as far as the data should be taken without more context. The collector peer may be a route server. The community may have been attached earlier in the AS path. The value may reflect a local convention. The useful part is that this ad-hoc &lt;code&gt;monocle&lt;/code&gt; analysis can separate these cases quickly and produce concrete rows for follow-up.&lt;/p&gt;
&lt;h2&gt;Checking NOPEER visibility against &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc3765&quot;&gt;RFC 3765&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;NOPEER&lt;/code&gt; is assigned as &lt;code&gt;65535:65284&lt;/code&gt; by &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc3765&quot;&gt;RFC 3765&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc3765#section-2&quot;&gt;RFC 3765 Section 2&lt;/a&gt; describes it as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;an advisory qualification to readvertisement of a route prefix, permitting an AS not to readvertise the route prefix to all external bilateral peer neighbour AS&apos;s.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the same 15-minute update window, &lt;code&gt;NOPEER&lt;/code&gt; was broadly visible:&lt;/p&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Rows&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;27,766&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Distinct prefixes&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1,033&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Collectors&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;51&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Peer ASNs&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;103&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Origin ASNs&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;146&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IPv4 rows&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;25,387&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IPv6 rows&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;2,379&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Example row:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;collector&quot;: &quot;rrc07&quot;,
  &quot;peer_asn&quot;: 3399,
  &quot;prefix&quot;: &quot;83.171.244.0/24&quot;,
  &quot;origin_asns&quot;: [&quot;197761&quot;],
  &quot;as_path&quot;: &quot;3399 5511 6204 51202 51202 197761&quot;,
  &quot;communities&quot;: [
    &quot;3399:100&quot;,
    &quot;3399:107&quot;,
    &quot;5511:500&quot;,
    &quot;6204:1000&quot;,
    &quot;65535:65284&quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Route data alone does not prove whether any downstream AS enforces the advisory signal. But it does show that the value is operationally present across many collectors, peer ASNs, and origin ASNs in this window.&lt;/p&gt;
&lt;h2&gt;A note on policy behavior&lt;/h2&gt;
&lt;p&gt;It is worth being careful when interpreting well-known communities. &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc8642&quot;&gt;RFC 8642&lt;/a&gt; exists because well-known community handling has not always been consistent across implementations and policies. Its introduction says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In hindsight, &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc1997&quot;&gt;RFC 1997&lt;/a&gt; did not prescribe as fully as it should have how well-known communities may be manipulated by policies applied by operators. Currently, implementations differ in this regard, and these differences can result in inconsistent behaviors that operators find difficult to identify and resolve.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is another reason to keep the analysis evidence-focused. The data can say which routes carried which values at which collectors; it should not assign intent by itself.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;A small ad-hoc &lt;a href=&quot;https://bgpkit.com/tools/monocle/&quot;&gt;&lt;code&gt;monocle&lt;/code&gt;&lt;/a&gt; analysis can turn public MRT updates into useful, RFC-aware evidence. The first step is the MRT query; the second step is ordinary JSONL processing that groups rows, counts distinct routing dimensions, and annotates observed values with registry metadata.&lt;/p&gt;
&lt;p&gt;The interesting parts are not hidden inside the MRT files anymore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;which &lt;code&gt;65535:*&lt;/code&gt; values are visible,&lt;/li&gt;
&lt;li&gt;whether those values are assigned in the IANA registry,&lt;/li&gt;
&lt;li&gt;where the values appear,&lt;/li&gt;
&lt;li&gt;whether a route matches a simple RFC-derived profile,&lt;/li&gt;
&lt;li&gt;which rows are worth deeper operator-policy investigation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For this particular window, the main observations were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;many observed &lt;code&gt;65535:*&lt;/code&gt; values were not assigned in the current IANA well-known-community registry snapshot;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BLACKHOLE&lt;/code&gt; appeared both on highly specific prefixes with &lt;code&gt;NO_EXPORT&lt;/code&gt; and on broader prefixes without &lt;code&gt;NO_EXPORT&lt;/code&gt;/&lt;code&gt;NO_ADVERTISE&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NOPEER&lt;/code&gt; was broadly visible across collectors, peers, origins, IPv4, and IPv6.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The broader lesson is the &lt;a href=&quot;https://bgpkit.com/tools/monocle/&quot;&gt;&lt;code&gt;monocle&lt;/code&gt;&lt;/a&gt; research pattern: query a narrow slice of MRT data, summarize it with simple scripts, join with source-of-truth registries, then inspect specific rows with the RFC text next to the data.&lt;/p&gt;
&lt;p&gt;The scripts used for this note are available here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://bgpkit.com/files/well-known-bgp-communities-monocle/summarize_existing_well_known.py&quot;&gt;&lt;code&gt;summarize_existing_well_known.py&lt;/code&gt;&lt;/a&gt; — summarize an existing JSONL file;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bgpkit.com/files/well-known-bgp-communities-monocle/enrich_iana_status.py&quot;&gt;&lt;code&gt;enrich_iana_status.py&lt;/code&gt;&lt;/a&gt; — add IANA status labels to the per-community CSV;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bgpkit.com/files/well-known-bgp-communities-monocle/filter_well_known.py&quot;&gt;&lt;code&gt;filter_well_known.py&lt;/code&gt;&lt;/a&gt; — filter generic JSONL output to rows carrying well-known communities;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bgpkit.com/files/well-known-bgp-communities-monocle/summarize_well_known.py&quot;&gt;&lt;code&gt;summarize_well_known.py&lt;/code&gt;&lt;/a&gt; — stream-oriented variant that reads JSONL from stdin.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>Investigating the India–Telegram BGP Hijack with monocle</title><link>https://bgpkit.com/blog/investigating-the-india-telegram-bgp-hijack-with-monocle/</link><guid isPermaLink="true">https://bgpkit.com/blog/investigating-the-india-telegram-bgp-hijack-with-monocle/</guid><description>A step-by-step forensic investigation of the June 2026 Telegram BGP hijack using monocle and the BGPKIT API - public data, real commands, real output.</description><pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On June 16, 2026, Indian carrier Reliance Communications (AS18101) began originating Telegram&apos;s IP
prefixes in BGP, disrupting access for users inside and briefly outside India. The
event has been covered from a policy angle (&lt;a href=&quot;https://anuragbhatia.com/post/2026/06/telegram-prefix-hijack-by-rcom/&quot;&gt;Anurag Bhatia&apos;s analysis&lt;/a&gt;,
&lt;a href=&quot;https://anuragbhatia.com/post/2026/06/telegram-bgp-hijack-and-blackholing/&quot;&gt;the IPv6 blackholing follow-up&lt;/a&gt;) and a traffic-impact angle (&lt;a href=&quot;https://www.kentik.com/blog/when-local-blocks-go-global-the-india-telegram-bgp-incident/&quot;&gt;Kentik&apos;s
post&lt;/a&gt;). This post does something different: it shows how to &lt;strong&gt;investigate the
forensic evidence yourself&lt;/strong&gt; using &lt;a href=&quot;https://github.com/bgpkit/monocle&quot;&gt;&lt;code&gt;monocle&lt;/code&gt;&lt;/a&gt; and the &lt;a href=&quot;https://api.bgpkit.com&quot;&gt;BGPKIT API&lt;/a&gt; - open-source tools,
real commands, real output. No screenshots of a vendor portal, no proprietary
telemetry. Everything here you can run on your own machine.&lt;/p&gt;
&lt;p&gt;The goal is forensics methodology, not opinion. We won&apos;t speculate about intent or
mitigation policy. We&apos;ll establish ground truth, find the hijack in the data, measure
how it propagated, and check the RPKI timeline - the same steps you&apos;d follow for any
BGP origin-conflict incident.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install monocle  # monocle 1.3.0 used in this post
# cargo install monocle          # also works, but takes longer to build from source
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All timestamps below are UTC. &lt;a href=&quot;https://github.com/bgpkit/monocle&quot;&gt;&lt;code&gt;monocle&lt;/code&gt;&lt;/a&gt; accepts &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc3339&quot;&gt;RFC 3339&lt;/a&gt; strings and converts them
internally:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ monocle time &quot;2026-06-16T16:14:19Z&quot; -s
2026-06-16T16:14:19+00:00
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 1: Establish ground truth - who should originate this prefix?&lt;/h2&gt;
&lt;p&gt;Before touching any MRT data, establish the &lt;em&gt;expected&lt;/em&gt; state of the prefix. This is
free and instant, and it gives you the baseline to compare against.&lt;/p&gt;
&lt;p&gt;Telegram operates five ASNs: AS62041, AS62014, AS59930, AS44907, and AS211157. A
single &lt;code&gt;monocle inspect&lt;/code&gt; call pulls all of them:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle inspect 62041 62014 59930 44907 211157
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;ASN | Name | Country | Org
--- | --- | --- | ---
AS62041 | Telegram Messenger EUR | VG | Telegram Messenger Inc
AS62014 | Telegram Messenger APAC | VG | Telegram Messenger Inc
AS59930 | Telegram Messenger NA  | VG | Telegram Messenger Inc
AS44907 | Telegram Messenger Inc | VG | Telegram Messenger Inc
AS211157| Telegram Messenger Inc | VG | Telegram Messenger Inc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now check who currently announces the prefix that was reported hijacked,
&lt;code&gt;91.105.192.0/23&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle pfx2as 91.105.192.0/23 --show-name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The legitimate origin is &lt;strong&gt;AS211157&lt;/strong&gt; (Telegram). The suspect, &lt;strong&gt;AS18101&lt;/strong&gt; (Reliance Communications),
should not appear here in a clean table.&lt;/p&gt;
&lt;h3&gt;The single most important check: RPKI validation&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ monocle rpki validate 91.105.192.0/23 18101 --format json
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;prefix&quot;: &quot;91.105.192.0/23&quot;,
  &quot;asn&quot;: 18101,
  &quot;state&quot;: &quot;invalid&quot;,
  &quot;reason&quot;: &quot;ASN 18101 not authorized; authorized ASNs: 211157&quot;,
  &quot;covering_roas&quot;: [
    {&quot;prefix&quot;: &quot;91.105.192.0/23&quot;, &quot;max_length&quot;: 23, &quot;origin_asn&quot;: 211157, &quot;ta&quot;: &quot;RIPENCC&quot;}
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The prefix is covered by a ROA authorizing &lt;strong&gt;AS211157&lt;/strong&gt; only. AS18101 originating
it is &lt;strong&gt;RPKI-invalid&lt;/strong&gt; by definition. Any network enforcing &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc6811&quot;&gt;Route Origin Validation
(ROV)&lt;/a&gt; should have rejected this announcement. Confirm the legitimate
origin validates clean:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ monocle rpki validate 91.105.192.0/23 211157 --format json
{&quot;prefix&quot;:&quot;91.105.192.0/23&quot;,&quot;asn&quot;:211157,&quot;state&quot;:&quot;valid&quot;,...}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now know: the prefix is signed, the legitimate origin is valid, and the suspect
origin is invalid - &lt;em&gt;before downloading a single MRT file&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;List Telegram&apos;s signed prefixes&lt;/h3&gt;
&lt;p&gt;To understand what address space was protected by RPKI, pull every Telegram ROA
across all five ASNs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;for asn in 62041 62014 59930 44907 211157; do
  monocle rpki roas $asn --format json-line
done | grep &apos;^{&apos; \
  | jq -r &apos;&quot;\(.prefix) AS\(.origin_asn)&quot;&apos; \
  | sort -u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returns 55 ROAs - Telegram&apos;s signed prefix set, from &lt;code&gt;91.105.192.0/23&lt;/code&gt; up
through &lt;code&gt;149.154.160.0/22&lt;/code&gt; and the &lt;code&gt;91.108.x.0/22&lt;/code&gt; blocks. Every one of these
prefixes, if originated by AS18101, would be RPKI-invalid.&lt;/p&gt;
&lt;h3&gt;Understand the suspect&apos;s connectivity&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle as2rel 18101 --is-downstream --show-name  # upstreams
monocle as2rel 18101 15412 --format json | jq &apos;.results[0]&apos;  # relationship with FLAG
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reliance&apos;s primary upstreams are &lt;strong&gt;AS15412 (FLAG Telecom)&lt;/strong&gt; and &lt;strong&gt;AS4755 (Tata
Communications)&lt;/strong&gt;. Keep these in mind - they&apos;re the transits that would carry (or
filter) any announcement Reliance originates.&lt;/p&gt;
&lt;h2&gt;Step 2: Find the hijack in the data&lt;/h2&gt;
&lt;p&gt;Now look for the actual BGP announcements. The reported time is 16:14:19 UTC on June
16. Start with a &lt;strong&gt;narrow 5-minute window&lt;/strong&gt; and a &lt;strong&gt;single small collector&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Why a small collector?&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rrc01&lt;/code&gt; (London) and &lt;code&gt;route-views2&lt;/code&gt; are large collectors with hundreds of peers.
Searching them downloads and parses a lot of data. For a first confirmatory search, a
small collector is much faster - and if the event is real, a small collector that has
a peer seeing the path will confirm it just as well.&lt;/p&gt;
&lt;p&gt;Use the &lt;a href=&quot;https://api.bgpkit.com&quot;&gt;BGPKIT API&lt;/a&gt; to find collector peer counts:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ curl -s &quot;https://api.bgpkit.com/v3/broker/peers?collector=rrc14&quot; | jq &apos;.data | length&apos;
21
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rrc14&lt;/code&gt; (Palo Alto, 21 peers) is small. Let&apos;s search there:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle search -p 91.105.192.0/23 -s -S -o 18101 \
  -t 1781626459 -d 5m -c rrc14 \
  --cache-dir ~/.cache/monocle \
  --format json-line \
  | jq -c &apos;.timestamp |= todateiso8601&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;{&quot;timestamp&quot;:&quot;2026-06-16T16:14:49Z&quot;,&quot;type&quot;:&quot;ANNOUNCE&quot;,&quot;prefix&quot;:&quot;91.105.192.0/23&quot;,
 &quot;as_path&quot;:&quot;19151 15412 18101&quot;,&quot;peer_asn&quot;:19151,&quot;collector&quot;:&quot;rrc14&quot;,...}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Confirmed. At 16:14:49 UTC, &lt;code&gt;rrc14&lt;/code&gt;&apos;s peer AS19151 received &lt;code&gt;91.105.192.0/23&lt;/code&gt;
originated by AS18101, via the path &lt;code&gt;19151 15412 18101&lt;/code&gt; - meaning &lt;strong&gt;FLAG (AS15412)&lt;/strong&gt;
was the transit carrying the hijack to the global table.&lt;/p&gt;
&lt;h3&gt;Why &lt;code&gt;-s -S&lt;/code&gt;?&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;-p&lt;/code&gt; prefix filter is &lt;strong&gt;exact-only&lt;/strong&gt; by default. Hijacks routinely involve
more-specifics - the victim announces &lt;code&gt;/24&lt;/code&gt;s to fight back, the hijacker follows with
even more specific routes. A bare &lt;code&gt;-p 91.105.192.0/23&lt;/code&gt; misses both the covering
aggregate and any more-specifics. &lt;code&gt;-s&lt;/code&gt; (include-super) and &lt;code&gt;-S&lt;/code&gt; (include-sub) expand
the match. Get in the habit of adding both to any hijack search.&lt;/p&gt;
&lt;h3&gt;The prefix arms race&lt;/h3&gt;
&lt;p&gt;Widen the search slightly (still one collector, still cached) to see the full set of
prefixes Reliance originated in the escalation wave:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle search -p &quot;91.108.4.0/22,149.154.160.0/22,91.105.192.0/23,95.161.64.0/20&quot; \
  -s -S -o 18101 -t 1781594400 -T 1781630400 -c rrc14 \
  --cache-dir ~/.cache/monocle --format json-line \
  | jq -r &apos;&quot;\(.timestamp | todateiso8601) \(.prefix)&quot;&apos; | sort -u
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;2026-06-16T16:14:49Z  149.154.160.0/24
2026-06-16T16:14:49Z  149.154.161.0/24
2026-06-16T16:14:49Z  149.154.162.0/24
2026-06-16T16:14:49Z  149.154.163.0/24
2026-06-16T16:14:49Z  91.105.192.0/23
2026-06-16T16:14:49Z  91.108.4.0/23
2026-06-16T16:14:49Z  91.108.6.0/23
2026-06-16T16:14:49Z  95.161.64.0/21
2026-06-16T16:14:49Z  95.161.72.0/21
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nine prefixes, all announced simultaneously at 16:14:49. Note the specificity: Reliance
originated &lt;code&gt;/23&lt;/code&gt;s &lt;em&gt;under&lt;/em&gt; Telegram&apos;s legitimate &lt;code&gt;/22&lt;/code&gt;s, and &lt;code&gt;/24&lt;/code&gt;s &lt;em&gt;under&lt;/em&gt; the
legitimate &lt;code&gt;/23&lt;/code&gt;s. In BGP, a more-specific route wins. This is the prefix arms race
&lt;a href=&quot;https://www.kentik.com/blog/when-local-blocks-go-global-the-india-telegram-bgp-incident/&quot;&gt;Kentik described visually&lt;/a&gt; - and here it is in the raw update stream, with exact
prefixes and timestamps.&lt;/p&gt;
&lt;h3&gt;Verify every hijacked prefix was RPKI-invalid&lt;/h3&gt;
&lt;p&gt;Validate each against AS18101:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;for pf in 91.108.4.0/23 149.154.160.0/24 91.105.192.0/23; do
  monocle rpki validate $pf 18101 --format json-line 2&gt;/dev/null \
    | grep &apos;^{&apos; \
    | jq -r --arg pf &quot;$pf&quot; &apos;&quot;\($pf) → \(.state): \(.reason)&quot;&apos;
done
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;91.108.4.0/23 → invalid: ASN 18101 not authorized; authorized ASNs: 62041
149.154.160.0/24 → invalid: ASN 18101 not authorized; authorized ASNs: 62041
91.105.192.0/23 → invalid: ASN 18101 not authorized; authorized ASNs: 211157
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every hijacked prefix was RPKI-invalid. This is machine-verified evidence, not an
inference. Any transit that carried these was, by definition, not enforcing ROV.&lt;/p&gt;
&lt;h2&gt;Step 3: Was the hijacked route actually selected in the routing table?&lt;/h2&gt;
&lt;p&gt;Announcements in BGP updates tell you what was &lt;em&gt;advertised&lt;/em&gt;. To see what was actually
&lt;em&gt;installed&lt;/em&gt; in a router&apos;s RIB at a given moment, use &lt;code&gt;monocle rib&lt;/code&gt;. This is more
expensive than &lt;code&gt;search&lt;/code&gt; - always pass a collector filter.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ monocle rib 1781627400 -p &quot;91.105.192.0/23,91.108.4.0/22,149.154.160.0/22&quot; \
    -s -S -c rrc14 --format json-line \
    | jq -r &apos;&quot;\(.prefix) origin=AS\(.origin_asns[0]) via \(.as_path)&quot;&apos; | sort -u
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;149.154.160.0/24 origin=AS18101 via 19151 15412 18101
149.154.161.0/24 origin=AS18101 via 19151 15412 18101
149.154.162.0/24 origin=AS18101 via 19151 15412 18101
149.154.163.0/24 origin=AS18101 via 19151 15412 18101
91.105.192.0/23  origin=AS18101 via 19151 15412 18101
91.108.4.0/23   origin=AS18101 via 19151 15412 18101
91.108.6.0/23   origin=AS18101 via 19151 15412 18101
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At 16:30 UTC - sixteen minutes after the hijack began - &lt;code&gt;rrc14&lt;/code&gt;&apos;s peer AS19151 had
&lt;strong&gt;AS18101 installed as the origin&lt;/strong&gt; for all seven prefixes, all via FLAG. The
hijacked route wasn&apos;t just announced; it was the selected route in the RIB.&lt;/p&gt;
&lt;h3&gt;When did it end?&lt;/h3&gt;
&lt;p&gt;Check a later RIB snapshot:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ monocle rib 1781683200 -p &quot;91.105.192.0/23,91.108.4.0/22,149.154.160.0/22&quot; \
    -s -S -c rrc14 --format json-line \
    | jq -r &apos;&quot;\(.prefix) origin=AS\(.origin_asns[0]) via \(.as_path)&quot;&apos; | sort -u
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;91.105.192.0/23 origin=AS211157 via 19151 9002 211157 211157
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By 2026-06-17 08:00 UTC, the prefix is back to origin &lt;strong&gt;AS211157 (Telegram)&lt;/strong&gt; - and
notice the path changed from &lt;code&gt;... 15412 18101&lt;/code&gt; to &lt;code&gt;... 9002 211157&lt;/code&gt;. The prefix is
now reaching this vantage point via AS9002, not FLAG. The hijack was withdrawn
sometime between 20:00 UTC on June 16 and 08:00 UTC on June 17. (&lt;a href=&quot;https://anuragbhatia.com/post/2026/06/telegram-prefix-hijack-by-rcom/&quot;&gt;Anurag&apos;s update&lt;/a&gt;
noted the prefix fading behind FLAG around 01:47 IST / 20:17 UTC on June 16.)&lt;/p&gt;
&lt;h2&gt;Step 4: Who propagated the invalid routes?&lt;/h2&gt;
&lt;p&gt;From the RIB and update data, the propagation path at &lt;code&gt;rrc14&lt;/code&gt; is consistently
&lt;code&gt;19151 15412 18101&lt;/code&gt; - &lt;strong&gt;FLAG (AS15412)&lt;/strong&gt; was the transit. Confirm the relationship:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ monocle as2rel 18101 15412 --format json | jq &apos;.results[0]&apos;
{&quot;asn1&quot;:18101,&quot;asn2&quot;:15412,&quot;connected&quot;:&quot;86.5%&quot;,&quot;peer&quot;:&quot;61.6%&quot;,&quot;as2_upstream&quot;:&quot;24.9%&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;FLAG is both a peer and an upstream of Reliance at high visibility. Since the
announcement was RPKI-invalid (Step 1) yet FLAG carried it into the global table,
FLAG was not enforcing ROV on this session. The same logic applies to Tata
(AS4755), Reliance&apos;s other transit, which appeared in paths at other collectors.&lt;/p&gt;
&lt;p&gt;Hurricane Electric (AS6939) also appeared briefly in observed AS paths. In the
UPDATE stream, HE propagated the invalid route for about 40 seconds and then
withdrew it, consistent with an RPKI-invalid rejection path that was slower than
the initial propagation. That is useful timing evidence, but it should not be read
as a statement about traffic volume or broad customer impact.&lt;/p&gt;
&lt;h2&gt;Step 5: The RPKI timeline - did Telegram have ROAs before the incident?&lt;/h2&gt;
&lt;p&gt;A common pattern in BGP hijack history is &lt;em&gt;reactive&lt;/em&gt; ROA creation: &lt;a href=&quot;https://manrs.org/2021/02/did-someone-try-to-hijack-twitter-yes/&quot;&gt;Twitter created
ROAs for its routes after the 2021 Myanmar hijack&lt;/a&gt;, and that protection limited
damage during the &lt;a href=&quot;https://manrs.org/2022/03/lesson-learned-twitter-shored-up-its-routing-security/&quot;&gt;2022 Russia incident&lt;/a&gt;. Was Telegram in the same reactive position,
or did it already have ROAs?&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://alpha.api.bgpkit.com&quot;&gt;BGPKIT Alpha API&lt;/a&gt; provides historical ROA data with &lt;code&gt;date_ranges&lt;/code&gt; arrays showing
when each ROA was active:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ curl -s &quot;https://alpha.api.bgpkit.com/roas?asn=211157&amp;#x26;date=2026-06-16&quot; \
    | jq &apos;.data[] | {prefix, date_ranges}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;prefix&quot;: &quot;91.105.192.0/23&quot;,
  &quot;date_ranges&quot;: [
    [&quot;2023-09-23&quot;, &quot;2025-05-04&quot;],
    [&quot;2025-05-06&quot;, &quot;2026-06-12&quot;],
    [&quot;2026-06-14&quot;, &quot;2026-06-20&quot;]
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Telegram&apos;s ROA for &lt;code&gt;91.105.192.0/23&lt;/code&gt; dates back to &lt;strong&gt;September 2023&lt;/strong&gt; - nearly three
years before this incident. Unlike &lt;a href=&quot;https://manrs.org/2021/02/did-someone-try-to-hijack-twitter-yes/&quot;&gt;Twitter&apos;s reactive adoption&lt;/a&gt;, Telegram had
protection in place well ahead of time. This is why RPKI-RoV was able to limit the
hijack&apos;s global impact (&lt;a href=&quot;https://www.kentik.com/blog/when-local-blocks-go-global-the-india-telegram-bgp-incident/&quot;&gt;Kentik reported&lt;/a&gt; only ~1.6% of their BGP sources saw the
hijacked route). The protection was already there; the question was whether
transits enforced it.&lt;/p&gt;
&lt;h2&gt;Step 6: What about the IPv6 blackholing story?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://anuragbhatia.com/post/2026/06/telegram-bgp-hijack-and-blackholing/&quot;&gt;Anurag&apos;s second post&lt;/a&gt; describes a separate phenomenon: Indian ISPs (TTSL AS45820,
Lightstorm AS152144/AS135709) originating Telegram&apos;s IPv6 prefix &lt;code&gt;2a0a:f280::/48&lt;/code&gt;
and its aggregate &lt;code&gt;2a0a:f280::/32&lt;/code&gt;, which he hypothesizes is a misapplied
remote-triggered blackhole configuration.&lt;/p&gt;
&lt;p&gt;For this post, the important question is not whether the &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999&quot;&gt;RFC 7999 BLACKHOLE
community&lt;/a&gt; (&lt;code&gt;65535:666&lt;/code&gt;) appears. A standards-compliant blackhole route
should be tightly scoped: &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999#section-3.2&quot;&gt;section 3.2&lt;/a&gt; says receivers should
add NO_ADVERTISE or NO_EXPORT, and &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999#section-3.3&quot;&gt;section 3.3&lt;/a&gt; says the
neighboring network must be authorized to advertise the covering prefix. TTSL and
Lightstorm are not authorized for Telegram&apos;s IPv6 space, so the observable BGP
fact remains the same either way: these are unauthorized origins.&lt;/p&gt;
&lt;p&gt;Bryton Herdes&apos; &lt;a href=&quot;https://www.youtube.com/watch?v=XDt9mx9z3A4&quot;&gt;False immunity: long prefixes that bypass ROV&lt;/a&gt;
talk is useful background on how TE and RTBH exceptions can create loose handling
for long prefixes. But the public BGP evidence here cannot prove the operators&apos;
internal mechanism; it can only show the invalid origins.&lt;/p&gt;
&lt;p&gt;What is observable is that these ISPs are originating prefixes they do not own -
RPKI-invalid for those origins. This is the same routing anomaly pattern as the
Reliance Communications hijack, regardless of the mechanism. You can verify it the same way:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle pfx2as 2a0a:f280::/48 --show-name          # who announces it?
monocle rpki validate 2a0a:f280::/48 45820 --format json  # is TTSL authorized?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A secondary puzzle remains: if this is not standard &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999&quot;&gt;RFC 7999&lt;/a&gt; RTBH, what mechanism
caused these ISPs to originate Telegram&apos;s IPv6 prefixes? That question is worth
investigating, but it is not something BGP UPDATE data alone can answer - it
requires off-band information about the operators&apos; configuration practices.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Here is the complete evidence chain, reproducible from the commands above:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Ground truth:&lt;/strong&gt; &lt;code&gt;91.105.192.0/23&lt;/code&gt; is RPKI-signed for AS211157 (Telegram).
AS18101 (Reliance Communications) originating it is RPKI-invalid. (&lt;code&gt;monocle rpki validate&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The hijack:&lt;/strong&gt; At 16:14:49 UTC on June 16, Reliance originated 9 Telegram prefixes
(&lt;code&gt;/23&lt;/code&gt;s and &lt;code&gt;/24&lt;/code&gt;s under the legitimate aggregates), all via FLAG (AS15412).
(&lt;code&gt;monocle search -s -S -c rrc14&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RIB confirmation:&lt;/strong&gt; At 16:30 UTC, the hijacked routes were installed in
&lt;code&gt;rrc14&lt;/code&gt;&apos;s RIB with AS18101 as origin. By 08:00 UTC on June 17, the prefix was
back to AS211157. (&lt;code&gt;monocle rib -c rrc14&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ROV failure:&lt;/strong&gt; FLAG (AS15412) and Tata (AS4755) carried RPKI-invalid
announcements, so neither enforced ROV on those sessions. (&lt;code&gt;monocle as2rel&lt;/code&gt; +
propagation data)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ROA timeline:&lt;/strong&gt; Telegram&apos;s ROAs date to September 2023 - protection was
proactive, not reactive. (&lt;a href=&quot;https://alpha.api.bgpkit.com&quot;&gt;BGPKIT Alpha API&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IPv6 anomaly:&lt;/strong&gt; TTSL and Lightstorm originated Telegram&apos;s IPv6 prefixes
(&lt;code&gt;2a0a:f280::/48&lt;/code&gt; and aggregate &lt;code&gt;2a0a:f280::/32&lt;/code&gt;), RPKI-invalid for those
origins. &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999#section-3.3&quot;&gt;RFC 7999 section 3.3&lt;/a&gt; describes blackhole prefixes as &quot;typically as
specific as possible, /32 for IPv4 or /128 for IPv6,&quot; and &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999#section-3.3&quot;&gt;section 3.3&lt;/a&gt;&apos;s MUST
conditions require the origin to be authorized - neither fits aggregates
originated by an unauthorized AS.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Every command in this post has been run against live public data. That&apos;s the point:
BGP forensics doesn&apos;t require a proprietary platform. &lt;a href=&quot;https://github.com/bgpkit/monocle&quot;&gt;&lt;code&gt;monocle&lt;/code&gt;&lt;/a&gt;, the &lt;a href=&quot;https://api.bgpkit.com&quot;&gt;BGPKIT API&lt;/a&gt;, and
public MRT archives are sufficient to investigate - and verify - any routing
incident.&lt;/p&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://anuragbhatia.com/post/2026/06/telegram-prefix-hijack-by-rcom/&quot;&gt;Anurag Bhatia - Telegram prefixes hijack by Rcom AS18101&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://anuragbhatia.com/post/2026/06/telegram-bgp-hijack-and-blackholing/&quot;&gt;Anurag Bhatia - Telegram BGP hijack due to weird blackholing config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kentik.com/blog/when-local-blocks-go-global-the-india-telegram-bgp-incident/&quot;&gt;Kentik - When Local Blocks Go Global: The India Telegram BGP Incident&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7999&quot;&gt;RFC 7999 - BLACKHOLE Community&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc6811&quot;&gt;RFC 6811 - BGP Prefix Origin Validation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc3339&quot;&gt;RFC 3339 - Date and Time on the Internet: Timestamps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bgpkit/monocle&quot;&gt;monocle on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api.bgpkit.com&quot;&gt;BGPKIT API&lt;/a&gt; · &lt;a href=&quot;https://alpha.api.bgpkit.com&quot;&gt;BGPKIT Alpha API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://manrs.org/2021/02/did-someone-try-to-hijack-twitter-yes/&quot;&gt;MANRS - Did someone try to hijack Twitter? Yes!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://manrs.org/2022/03/lesson-learned-twitter-shored-up-its-routing-security/&quot;&gt;MANRS - Lesson Learned: Twitter Shored Up Its Routing Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=XDt9mx9z3A4&quot;&gt;Bryton Herdes - False immunity: long prefixes that bypass ROV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>monocle v1.3.0: Reconstruct BGP RIB State at Any Timestamp</title><link>https://bgpkit.com/blog/monocle-v1-3-0/</link><guid isPermaLink="true">https://bgpkit.com/blog/monocle-v1-3-0/</guid><description>monocle v1.3.0 introduces the rib command for reconstructing BGP RIB state at arbitrary timestamps.</description><pubDate>Wed, 27 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are happy to announce the release of &lt;strong&gt;monocle v1.3.0&lt;/strong&gt;, now available on &lt;a href=&quot;https://github.com/bgpkit/monocle/releases/tag/v1.3.0&quot;&gt;GitHub&lt;/a&gt; and &lt;a href=&quot;https://crates.io/crates/monocle/1.3.0&quot;&gt;crates.io&lt;/a&gt;, also available on Homebrew via &lt;code&gt;brew install monocle&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This release introduces a major new command, &lt;code&gt;monocle rib&lt;/code&gt;, which reconstructs BGP Routing Information Base (RIB) state at arbitrary timestamps.&lt;/p&gt;
&lt;h2&gt;New Feature: &lt;code&gt;monocle rib&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Most BGP tools show you what happened &lt;em&gt;during&lt;/em&gt; an update file. &lt;code&gt;monocle rib&lt;/code&gt; shows you the routing table &lt;em&gt;as it existed&lt;/em&gt; at any moment in time.&lt;/p&gt;
&lt;p&gt;Given a target timestamp, the command:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Finds the latest RIB dump at or before that time&lt;/li&gt;
&lt;li&gt;Downloads and replays all overlapping BGP update files up to the exact target timestamp&lt;/li&gt;
&lt;li&gt;Materializes the final route state per peer and prefix&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The result is the reconstructed RIB — not raw BGP messages, but the actual route state.&lt;/p&gt;
&lt;h3&gt;Basic Usage&lt;/h3&gt;
&lt;p&gt;Reconstruct the RIB for a single timestamp and print to stdout:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle rib 2025-09-01T12:00:00Z -c route-views6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Write multiple snapshots to a single SQLite file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle rib \
  2025-09-01T08:00:00Z \
  2025-09-01T12:00:00Z \
  --sqlite-path /tmp/rib-snapshots.sqlite3 \
  -c route-views6
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Filtering&lt;/h3&gt;
&lt;p&gt;Apply filters at reconstruction time to narrow the result set:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Only routes originated by AS13335 (Cloudflare)
monocle rib 2025-09-01T12:00:00Z -c route-views6 -o 13335

# Routes for a specific prefix, including more-specifics
monocle rib 2025-09-01T12:00:00Z -c route-views6 -p 2606:4700::/32 --include-sub

# Routes from full-feed peers only
monocle rib 2025-09-01T12:00:00Z -c route-views6 --full-feed-only

# Routes where AS path contains a specific ASN
monocle rib 2025-09-01T12:00:00Z -c route-views6 -a &quot;13335&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Performance Notes&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;rib&lt;/code&gt; command keeps the working RIB state in memory during reconstruction, avoiding SQLite lookups and writes on the hot path. The updates query window was tightened from +2 hours lookahead to the exact target timestamp, reducing update file downloads by roughly one third for typical requests.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cargo install monocle
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or download pre-built binaries for Linux (x86_64, aarch64) and macOS (universal) from the &lt;a href=&quot;https://github.com/bgpkit/monocle/releases/tag/v1.3.0&quot;&gt;GitHub release page&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>monocle v1.1.0</title><link>https://bgpkit.com/blog/monocle-v110/</link><guid isPermaLink="true">https://bgpkit.com/blog/monocle-v110/</guid><description>monocle v1.1.0 focuses on interface consistency and day-to-day usability. This release simplifies feature gates, standardizes data refresh APIs, and adds qualit...</description><pubDate>Sat, 14 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;monocle &lt;code&gt;v1.1.0&lt;/code&gt; focuses on interface consistency and day-to-day usability. This release simplifies feature gates, standardizes data refresh APIs, and adds quality-of-life improvements across parsing, search, and configuration workflows.&lt;/p&gt;
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Feature flags are now simplified to &lt;code&gt;lib&lt;/code&gt;, &lt;code&gt;server&lt;/code&gt;, and &lt;code&gt;cli&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Config and update flows are more consistent (&lt;code&gt;config update&lt;/code&gt;, &lt;code&gt;config backup&lt;/code&gt;, &lt;code&gt;config sources&lt;/code&gt;, and &lt;code&gt;--no-update&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Data refresh APIs are standardized across ASInfo, AS2Rel, RPKI, and Pfx2as.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Cache TTL defaults are unified at 7 days, with clearer staleness reporting.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Parse/search workflows gain multi-value filters, negation filters, field selection, ordering, timestamp format control, and local cache support.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What&apos;s New&lt;/h2&gt;
&lt;h3&gt;Simpler feature flags&lt;/h3&gt;
&lt;p&gt;The crate feature model is now reduced to three options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;lib&lt;/code&gt;: complete library functionality (database + lenses + display)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;server&lt;/code&gt;: WebSocket server support (implies &lt;code&gt;lib&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cli&lt;/code&gt;: full command-line binary (implies &lt;code&gt;lib&lt;/code&gt; and &lt;code&gt;server&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This replaces the previous multi-tier setup and makes dependency selection easier for downstream users.&lt;/p&gt;
&lt;h3&gt;Standardized data refresh APIs&lt;/h3&gt;
&lt;p&gt;Database refresh behavior is now more uniform across ASInfo, AS2Rel, RPKI, and Pfx2as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Consistent &lt;code&gt;needs_*_refresh(ttl)&lt;/code&gt; checks&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A shared &lt;code&gt;RefreshResult&lt;/code&gt; shape with source and load details&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Standardized naming (&lt;code&gt;refresh_*&lt;/code&gt;) with compatibility aliases where needed&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;URL and local-path loading paths available across repositories&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This update reduces API drift and makes maintenance code paths more predictable.&lt;/p&gt;
&lt;h3&gt;Config command updates&lt;/h3&gt;
&lt;p&gt;Configuration and maintenance commands now use clearer naming:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;monocle config db-refresh&lt;/code&gt; -&gt; &lt;code&gt;monocle config update&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;monocle config db-backup&lt;/code&gt; -&gt; &lt;code&gt;monocle config backup&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;monocle config db-sources&lt;/code&gt; -&gt; &lt;code&gt;monocle config sources&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The global no-refresh toggle was also renamed for consistency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--no-refresh&lt;/code&gt; -&gt; &lt;code&gt;--no-update&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following command shows the active configuration, cache TTL settings, database status, and server defaults:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;monocle Configuration
=====================

General:
  Config file:    /home/user/.monocle/monocle.toml
  Data dir:       /home/user/.monocle/

Cache TTL:
  ASInfo:         7 days
  AS2Rel:         7 days
  RPKI:           7 days
  Pfx2as:         7 days

Database:
  Path:           /home/user/.monocle/monocle-data.sqlite3
  Status:         exists
  Size:           512.47 MB
  Schema:         initialized (v3)
  ASInfo:         120953 records (updated: 2026-02-02 19:54:01 UTC)
  AS2Rel:         877937 records (updated: 2026-02-02 14:25:34 UTC)
  RPKI:           796899 ROAs, 962 ASPAs (updated: 2026-02-10 19:53:50 UTC)
  Pfx2as:         1580626 records (updated: 2026-02-02 20:02:10 UTC)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Better cache control defaults&lt;/h3&gt;
&lt;p&gt;All major data sources now support configurable cache TTL with a 7-day default. This applies to ASInfo, AS2Rel, RPKI, and Pfx2as.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;monocle config sources&lt;/code&gt; now reports staleness based on TTL, so it is easier to see what needs updating.&lt;/p&gt;
&lt;p&gt;The following command shows per-source status, staleness, and last update recency:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle config sources
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Data Sources:

  Name         Status          Stale      Last Updated
  ------------------------------------------------------------
  asinfo       120953 records  yes        a week ago
  as2rel       877937 records  yes        a week ago
  rpki         797861 records  no         2 hours ago
  pfx2as       1580626 records yes        a week ago

Configuration:
  ASInfo cache TTL: 7 days
  AS2Rel cache TTL: 7 days
  RPKI cache TTL:   7 days
  Pfx2as cache TTL: 7 days
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;RPKI improvements&lt;/h3&gt;
&lt;p&gt;monocle now supports fetching ROAs via RTR (RPKI-to-Router), including endpoint override support and fallback behavior.&lt;/p&gt;
&lt;p&gt;The following command refreshes only RPKI data and uses the provided RTR endpoint for this run instead of the default configured source.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle config update --rpki --rtr-endpoint rtr.rpki.cloudflare.com:8282
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Parse and search enhancements&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;parse&lt;/code&gt; and &lt;code&gt;search&lt;/code&gt; gained several output and filtering improvements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Multi-value filters with OR semantics&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Negation filters using &lt;code&gt;!&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Validation for ASN/prefix filter inputs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--fields&lt;/code&gt; for column selection&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--order-by&lt;/code&gt; and &lt;code&gt;--order&lt;/code&gt; for sorted output&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--time-format&lt;/code&gt; for unix or RFC3339 display&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;search --cache-dir&lt;/code&gt; local file + broker query caching&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following command searches one hour of updates starting at &lt;code&gt;2024-01-01&lt;/code&gt;, filters for prefix &lt;code&gt;1.1.1.0/24&lt;/code&gt;, and caches downloaded MRT files plus broker query results under &lt;code&gt;/tmp/mrt-cache&lt;/code&gt; for faster repeat runs.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle search -t 2024-01-01 -d 1h -p 1.1.1.0/24 --cache-dir /tmp/mrt-cache
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following command uses multi-value filters with negation to exclude two origin ASNs while also matching either of two peer ASNs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;monocle search -t 2024-01-01 -d 1h -o &apos;!13335,!15169&apos; -J 174,2914
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Negation and positive values cannot be mixed within the same filter field.&lt;/p&gt;
&lt;h2&gt;Breaking Changes and Migration Notes&lt;/h2&gt;
&lt;h3&gt;1) Feature flag migration&lt;/h3&gt;
&lt;p&gt;If you previously used feature tiers like &lt;code&gt;database&lt;/code&gt;, &lt;code&gt;lens-core&lt;/code&gt;, &lt;code&gt;lens-bgpkit&lt;/code&gt;, &lt;code&gt;lens-full&lt;/code&gt;, or &lt;code&gt;display&lt;/code&gt;, switch to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;lib&lt;/code&gt; for library use&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;server&lt;/code&gt; for WebSocket API use&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cli&lt;/code&gt; for full command-line use&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) CLI and subcommand renames&lt;/h3&gt;
&lt;p&gt;Update scripts and automation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--no-refresh&lt;/code&gt; -&gt; &lt;code&gt;--no-update&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;config db-refresh&lt;/code&gt; -&gt; &lt;code&gt;config update&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;config db-backup&lt;/code&gt; -&gt; &lt;code&gt;config backup&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;config db-sources&lt;/code&gt; -&gt; &lt;code&gt;config sources&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3) Parse/search filter type updates (library API)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ParseFilters&lt;/code&gt; moved from scalar optional fields to vector-based values for multi-value and negation support. Library consumers should update filter construction accordingly.&lt;/p&gt;
&lt;h2&gt;Additional Improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;AS name rendering now prefers PeeringDB naming fields before falling back to AS2Org/core names&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Data refresh logging now shows specific reasons (empty vs outdated)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Example layout was reorganized to one example per lens&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Full Change List&lt;/h2&gt;
&lt;p&gt;See the v1.1.0 section in &lt;a href=&quot;https://github.com/bgpkit/monocle/blob/main/CHANGELOG.md&quot;&gt;&lt;code&gt;CHANGELOG.md&lt;/code&gt;&lt;/a&gt; for the complete list of changes.&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>BGPKIT Parser v0.14.0 Release and v0.13.0 Highlights</title><link>https://bgpkit.com/blog/bgpkit-parser-v0140-release/</link><guid isPermaLink="true">https://bgpkit.com/blog/bgpkit-parser-v0140-release/</guid><description>We are pleased to announce the release of BGPKIT Parser v0.14.0. This update introduces support for negative filters and the RPKI-to-Router (RTR) protocol. We a...</description><pubDate>Thu, 25 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are pleased to announce the release of &lt;strong&gt;BGPKIT Parser v0.14.0&lt;/strong&gt;. This update introduces support for negative filters and the RPKI-to-Router (RTR) protocol. We also want to highlight key features from the recent v0.13.0 release, including enhanced debugging tools.&lt;/p&gt;
&lt;h2&gt;v0.14.0 Features&lt;/h2&gt;
&lt;h3&gt;Negative Filter Support&lt;/h3&gt;
&lt;p&gt;A frequent request has been the ability to filter &lt;em&gt;out&lt;/em&gt; specific data points. We have added support for negative filters across most filter types, allowing exclusion of specific origins, prefixes, peers, or communities.&lt;/p&gt;
&lt;p&gt;In the CLI, use the &lt;code&gt;!=&lt;/code&gt; operator. For example, to process all records &lt;em&gt;except&lt;/em&gt; those originating from AS 13335:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bgpkit-parser https://spaces.bgpkit.org/parser/update-example.gz --filter &quot;origin_asn!=13335&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/negative-filter.C1GKM1D4.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;In Rust code, use the &lt;code&gt;!&lt;/code&gt; prefix:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let parser = BgpkitParser::new(&quot;...&quot;)
    .add_filter(&quot;!origin_asn&quot;, &quot;13335&quot;)
    .add_filter(&quot;!peer_ip&quot;, &quot;192.168.1.1&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Supported negative filters include &lt;code&gt;!origin_asn&lt;/code&gt;, &lt;code&gt;!prefix&lt;/code&gt;, &lt;code&gt;!peer_ip&lt;/code&gt;, &lt;code&gt;!peer_asn&lt;/code&gt;, &lt;code&gt;!type&lt;/code&gt;, &lt;code&gt;!as_path&lt;/code&gt;, &lt;code&gt;!community&lt;/code&gt;, and &lt;code&gt;!ip_version&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;RPKI RTR Protocol Support&lt;/h3&gt;
&lt;p&gt;We have added support for the RPKI-to-Router (RTR) protocol, covering both version 0 (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6810&quot;&gt;RFC 6810&lt;/a&gt;) and version 1 (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc8210&quot;&gt;RFC 8210&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The new &lt;code&gt;models::rpki::rtr&lt;/code&gt; and &lt;code&gt;parser::rpki::rtr&lt;/code&gt; modules allow developers to build custom RTR clients or servers.&lt;/p&gt;
&lt;p&gt;Here is how you can use the library to connect to an RTR server and request data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use bgpkit_parser::models::rpki::rtr::*;
use bgpkit_parser::parser::rpki::rtr::{read_rtr_pdu, RtrEncode, RtrError};
use std::net::TcpStream;
use std::io::Write;

// 1. Connect to the RTR server
let mut stream = TcpStream::connect(&quot;rtr.rpki.cloudflare.com:8282&quot;)?;

// 2. Send a Reset Query to request the full database
let reset_query = RtrResetQuery::new_v1();
stream.write_all(&amp;#x26;reset_query.encode())?;

// 3. Read the response PDUs
loop {
    match read_rtr_pdu(&amp;#x26;mut stream)? {
        RtrPdu::IPv4Prefix(p) =&gt; {
            println!(&quot;Received IPv4 ROA: {}/{}-{} -&gt; AS{}&quot;, 
                p.prefix, p.prefix_length, p.max_length, p.asn);
        }
        RtrPdu::EndOfData(_) =&gt; break,
        _ =&gt; {}
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have included a fully functional RTR client example that connects to a server, fetches ROAs, and performs route validation.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/rtr-client.D_ecek_F.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;You can run the example yourself:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cargo run --example rtr_client -- rtr.rpki.cloudflare.com 8282
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;In Case You Missed It: v0.13.0&lt;/h2&gt;
&lt;p&gt;The v0.13.0 release introduced several improvements for debugging and analyzing MRT data.&lt;/p&gt;
&lt;h3&gt;Record-Level Output&lt;/h3&gt;
&lt;p&gt;The CLI supports inspecting individual MRT records rather than just parsed BGP elements. This aids in debugging parser issues or analyzing raw MRT files.&lt;/p&gt;
&lt;p&gt;Switch to record-level output with &lt;code&gt;--level records&lt;/code&gt; and choose a format (e.g., JSON):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bgpkit-parser https://spaces.bgpkit.org/parser/update-example.gz --level records --format json
&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/record-level-output.BbWoaz9O.png&quot; alt=&quot;&quot;&gt;
&lt;h3&gt;Raw Bytes Access&lt;/h3&gt;
&lt;p&gt;For developers, &lt;code&gt;RawMrtRecord&lt;/code&gt; now includes a &lt;code&gt;header_bytes&lt;/code&gt; field, and the &lt;code&gt;raw_bytes&lt;/code&gt; field has been renamed to &lt;code&gt;message_bytes&lt;/code&gt;. This provides access to the exact bytes of the MRT header and the message body as they appeared on the wire, enabling byte-for-byte export and debugging without re-encoding.&lt;/p&gt;
&lt;h3&gt;Other Improvements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Testing &amp;#x26; Fuzzing&lt;/strong&gt;: Added a &lt;code&gt;cargo-fuzz&lt;/code&gt; harness and initial fuzz targets.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Continued optimizations for faster processing.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check out the full &lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser/blob/main/CHANGELOG.md&quot;&gt;CHANGELOG&lt;/a&gt; for more details.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Happy Parsing!&lt;/em&gt;&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>BGPKIT Broker v0.9.0: Better Pagination and New Collectors</title><link>https://bgpkit.com/blog/bgpkit-broker-v090/</link><guid isPermaLink="true">https://bgpkit.com/blog/bgpkit-broker-v090/</guid><description>We are excited to announce the release of BGPKIT Broker v0.9.0. This release introduces total count support for efficient pagination, making it easier to build ...</description><pubDate>Sat, 01 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are excited to announce the release of BGPKIT Broker v0.9.0. This release introduces total count support for efficient pagination, making it easier to build data exploration interfaces and manage large result sets. We also expand our collector coverage with two new Internet Exchange points.&lt;/p&gt;
&lt;h2&gt;Previously on BGPKIT Broker&lt;/h2&gt;
&lt;p&gt;Since the major v0.7 release that unified the architecture into a single SQLite-backed CLI application, BGPKIT Broker has been serving the community with stable uptime and continuous data coverage. The v0.8 series brought several enhancements including automated backup systems, configuration validation, and convenient query shortcuts.&lt;/p&gt;
&lt;p&gt;However, one common challenge remained for developers building interfaces on top of Broker: pagination. When querying large time ranges, users needed to fetch all results to know the total count, making it inefficient to build proper pagination controls or display result statistics.&lt;/p&gt;
&lt;h2&gt;V0.9: Efficient Pagination Support&lt;/h2&gt;
&lt;p&gt;Version 0.9.0 addresses pagination needs with total count support at both the SDK and API levels. This allows applications to fetch result counts independently from the data itself, enabling responsive user interfaces without over-fetching data.&lt;/p&gt;
&lt;h3&gt;SDK: &lt;code&gt;query_total_count()&lt;/code&gt; Method&lt;/h3&gt;
&lt;p&gt;The SDK now provides a dedicated &lt;code&gt;query_total_count()&lt;/code&gt; method that returns the total number of matching items without retrieving the actual data. This is useful when you need to know how many results exist before deciding how to paginate through them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use bgpkit_broker::BgpkitBroker;

#[tokio::main]
async fn main() {
    let broker = BgpkitBroker::new()
        .ts_start(&quot;2024-10-01&quot;)
        .ts_end(&quot;2024-10-02&quot;)
        .project(&quot;route-views&quot;);

    // Get total count without fetching items
    let total = broker.query_total_count().await.unwrap();
    println!(&quot;Total matching items: {}&quot;, total);

    // Now fetch paginated results
    let result = broker
        .page_size(100)
        .page(1)
        .query()
        .await
        .unwrap();

    println!(&quot;Retrieved {} items out of {} total&quot;, 
             result.data.len(), 
             result.total.unwrap_or(0));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method executes a &lt;code&gt;COUNT(*)&lt;/code&gt; query against the database with the same filters as your main query, returning just the number rather than all the data. For large time ranges with thousands of results, this can save significant bandwidth and processing time.&lt;/p&gt;
&lt;h3&gt;API: Total Field in Search Results&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;/v3/search&lt;/code&gt; endpoint now includes a &lt;code&gt;total&lt;/code&gt; field in every response, providing the total count of matching items alongside the paginated results. This enhancement makes it straightforward to build pagination controls in web interfaces or CLI tools.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl &quot;https://api.bgpkit.com/v3/broker/search?ts_start=2024-10-01&amp;#x26;ts_end=2024-10-02&amp;#x26;page_size=10&amp;#x26;page=1&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Response:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;total&quot;: 1234,
  &quot;data&quot;: [
    {
      &quot;ts_start&quot;: 1727740800,
      &quot;ts_end&quot;: 1727740800,
      &quot;collector&quot;: &quot;route-views2&quot;,
      &quot;project&quot;: &quot;route-views&quot;,
      &quot;url&quot;: &quot;...&quot;,
      ...
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the &lt;code&gt;total&lt;/code&gt; field, client applications can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Display &quot;showing X of Y results&quot; to users&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Calculate the number of pages needed for pagination&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Decide whether to fetch all results or paginate&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Provide accurate progress indicators for data processing&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;New RouteViews Collectors&lt;/h3&gt;
&lt;p&gt;This release adds support for two new RouteViews collectors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;hkix.hkg&lt;/strong&gt;: Hong Kong Internet Exchange (HKIX) collector in Hong Kong&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ix-br.gru&lt;/strong&gt;: &lt;a href=&quot;https://IX.br&quot;&gt;IX.br&lt;/a&gt; (&lt;a href=&quot;https://PTT.br&quot;&gt;PTT.br&lt;/a&gt;) in São Paulo&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These additions enhance BGPKIT Broker&apos;s global coverage, providing more diverse vantage points into internet routing behavior. Users who update to v0.9.0 can run &lt;code&gt;bgpkit-broker update&lt;/code&gt; to automatically bootstrap data for these new collectors.&lt;/p&gt;
&lt;h2&gt;Behind the Scenes Improvements&lt;/h2&gt;
&lt;p&gt;Beyond the user-facing features, we also made several code improvements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Refactored bootstrap download logging to centralize progress reporting and eliminate redundant code paths&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Updated the oneio dependency to v0.20.0 with optimized feature flags for better handling rustls providers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enhanced test coverage overall&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Upgrading to v0.9.0&lt;/h2&gt;
&lt;h3&gt;For SDK Users&lt;/h3&gt;
&lt;p&gt;Update your &lt;code&gt;Cargo.toml&lt;/code&gt; to use the latest v0.9 release:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;[dependencies]
bgpkit-broker = &quot;0.9&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cargo update
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new &lt;code&gt;query_total_count()&lt;/code&gt; method is immediately available for use. The &lt;code&gt;total&lt;/code&gt; field in &lt;code&gt;BrokerQueryResult&lt;/code&gt; is backward compatible as an optional field.&lt;/p&gt;
&lt;h3&gt;For Self-Hosted Instances&lt;/h3&gt;
&lt;p&gt;If you run your own BGPKIT Broker instance, update to the latest version:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cargo install --force bgpkit-broker --version 0.9.0 --features cli
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or pull the latest Docker image:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker pull bgpkit/bgpkit-broker:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After upgrading, run the update command to add the new collectors:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bgpkit-broker update --db-path your-database.sqlite3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The update process will automatically detect and bootstrap historical data for the two new collectors.&lt;/p&gt;
&lt;h2&gt;Looking Forward&lt;/h2&gt;
&lt;p&gt;BGPKIT Broker continues to serve as a fundamental component for BGP data processing pipelines. With v0.9.0, we focused on making the developer experience better for building applications that need pagination and result statistics. We continue to maintain the public Broker instance at &lt;a href=&quot;https://api.bgpkit.com/v3/broker&quot;&gt;&lt;code&gt;https://api.bgpkit.com/v3/broker&lt;/code&gt;&lt;/a&gt; with 99.996% uptime, and encourage users to deploy on-premise instances for production pipelines to reduce external dependencies.&lt;/p&gt;
&lt;p&gt;For the full v0.9.0 release notes, please check out our &lt;a href=&quot;https://github.com/bgpkit/bgpkit-broker/releases/tag/v0.9.0&quot;&gt;GitHub release page&lt;/a&gt;. If you have any comments, please drop us a message on &lt;a href=&quot;https://twitter.com/bgpkit&quot;&gt;Twitter&lt;/a&gt;, &lt;a href=&quot;https://infosec.exchange/@bgpkit&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://bsky.app/profile/bgpkit.com&quot;&gt;Bluesky&lt;/a&gt; or &lt;a href=&quot;mailto:contact@bgpkit.com&quot;&gt;email&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Get Started&lt;/h2&gt;
&lt;p&gt;Ready to try out the new features? Here are some resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href=&quot;https://github.com/bgpkit/bgpkit-broker&quot;&gt;github.com/bgpkit/bgpkit-broker&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;: Browse &lt;a href=&quot;https://github.com/bgpkit/bgpkit-broker/tree/main/examples&quot;&gt;example code&lt;/a&gt; demonstrating common usage patterns&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Public API&lt;/strong&gt;: Try queries at &lt;a href=&quot;https://api.bgpkit.com&quot;&gt;https://api.bgpkit.com&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Have questions or feedback? Open an issue on &lt;a href=&quot;https://github.com/bgpkit/bgpkit-broker/issues&quot;&gt;GitHub&lt;/a&gt; or join discussions in our &lt;a href=&quot;https://discord.gg/XDaAtZsz6b&quot;&gt;Discord Channel&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;💖&lt;/h2&gt;
&lt;p&gt;If you find our libraries and services useful, we would highly appreciate if you consider sponsoring us on &lt;a href=&quot;https://github.com/sponsors/bgpkit&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>Run BGPKIT on Cloudflare Containers</title><link>https://bgpkit.com/blog/run-bgpkit-on-cloudflare-containers/</link><guid isPermaLink="true">https://bgpkit.com/blog/run-bgpkit-on-cloudflare-containers/</guid><description>For the longest time, I’ve been using Cloudflare exclusively for web/API hosting and relatively lightweight tasks. For the most of my work on BGP, there is real...</description><pubDate>Mon, 30 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For the longest time, I’ve been using &lt;a href=&quot;https://developers.cloudflare.com/&quot;&gt;Cloudflare&lt;/a&gt; exclusively for web/API hosting and relatively lightweight tasks. For the most of my work on BGP, there is really not much that I can accomplish with just JavaScript/TypeScript (except maybe working with &lt;a href=&quot;https://ris-live.ripe.net/&quot;&gt;RIS Live&lt;/a&gt; WebSocket). The computationally intensive nature of the most BGP data processing doesn&apos;t naturally fit within the typical Cloudflare developer platform.&lt;/p&gt;
&lt;p&gt;This changes with the recent &lt;a href=&quot;https://blog.cloudflare.com/containers-are-available-in-public-beta-for-simple-global-and-programmable/&quot;&gt;announcement&lt;/a&gt; of &lt;a href=&quot;https://developers.cloudflare.com/containers/&quot;&gt;Cloudflare Containers&lt;/a&gt;. In short, it allows developers to build and run custom containers on the Cloudflare’s platform, enabling the heavy workload to mix with other primitives and unify the deployment.&lt;/p&gt;
&lt;p&gt;In this blog post, I will show you how to build a BGP data search API with BGPKIT and deploy on Cloudflare Containers. The source code is available &lt;a href=&quot;https://github.com/bgpkit/bgpkit-cf-containers&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;BGP Data Search API&lt;/h2&gt;
&lt;p&gt;For this example, I will show you how to build a very straightforward HTTP API to allow accepting search parameters and let BGPKIT to fetch and parse BGP archives and return the parsed messages.&lt;/p&gt;
&lt;p&gt;The API accepts four parameters: &lt;code&gt;collector&lt;/code&gt;, &lt;code&gt;prefix&lt;/code&gt;, &lt;code&gt;ts_start&lt;/code&gt;, and &lt;code&gt;ts_end&lt;/code&gt;, to filter and parse BGP archives efficiently.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;collector&lt;/code&gt;: the BGP route collector ID to use (e.g. &lt;code&gt;rrc00&lt;/code&gt; or &lt;code&gt;route-views2&lt;/code&gt;). We want to limit the search to a single collector.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;prefix&lt;/code&gt;: the prefix of the BGP relevant updates. Open-ended search will burn up resources quick, but you can opt-out this requirement.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ts_start&lt;/code&gt; and &lt;code&gt;ts_end&lt;/code&gt;: the starting and ending timestamps. The goal is to limit the search to end up parsing a very small amount of MRT files (e.g. give them the same value will do). We should probably leave large-scale data crunching to a environment with more CPU powers.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The parameters are defined in a struct to be able to passed in to a &lt;code&gt;axum&lt;/code&gt; “GET” route:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[derive(Deserialize, Serialize)]
struct Params {
    collector: String,
    prefix: String,
    ts_start: String,
    ts_end: String,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the given parameters, we will first find the relevant MRT files by setting the filters of timestamps and collector to &lt;code&gt;BgpkitBroker&lt;/code&gt; instance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;        let files = match bgpkit_broker::BgpkitBroker::new()
            .ts_end(ts_end.clone())
            .ts_start(ts_start.clone())
            .collector_id(collector.clone())
            .query(){
            Ok(items) =&gt; items,
            Err(e) =&gt; {
                return Json(Result {
                    error: Some(e.to_string()),
                    data: vec![],
                    meta: None,
                });
            }
        };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For each file, we will parse the whole MRT file and collect BGP updates that is relevant to the target prefix:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;        for file in files {
            let mut parser = match bgpkit_parser::BgpkitParser::new(file.url.as_str()){
                Ok(parser) =&gt; parser,
                Err(e) =&gt; {
                    return Json(Result {
                        error: Some(e.to_string()),
                        data: vec![],
                        meta: None,
                    });
                }
            };

            parser = match parser.add_filter(&quot;prefix&quot;, prefix.as_str()){
                Ok(parser) =&gt; parser,
                Err(e) =&gt; {
                    return Json(Result {
                        error: Some(e.to_string()),
                        data: vec![],
                        meta: None,
                    });
                }
            };
            items.extend(parser.into_elem_iter());
        }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the BGPKIT parser and broker code are implemented in the sync environment, we will need to wrap the code above in a blocking thread in order to use it in async web frameworks like &lt;code&gt;axum&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let result = tokio::task::spawn_blocking(move || {
   // THE BLOCKING CODE PIECES
}).await.unwrap();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please see the full source code here for more details:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-cf-containers/blob/main/container-src/src/main.rs&quot;&gt;https://github.com/bgpkit/bgpkit-cf-containers/blob/main/container-src/src/main.rs&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Cloudflare Containers Deployment&lt;/h2&gt;
&lt;p&gt;Now that we have an Rust-based API code working, we will need to 1. containerize the code, 2. put a Cloudflare Container wrapper around it for deployment.&lt;/p&gt;
&lt;p&gt;The container definition is a typical two-stage build definition, with a builder stage to build the binary application, and a minimum deployment stage to call the executable. The two-stage build is almost necessary as Cloudflare Containers &lt;a href=&quot;https://developers.cloudflare.com/containers/platform-details/#limits&quot;&gt;has limits&lt;/a&gt; on the size of each image and the total storage per account. The smaller the image the better.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;# ---- Build Stage ----
FROM rust:1.86 AS builder

WORKDIR /app

# Install build dependencies
RUN apt-get update &amp;#x26;&amp;#x26; apt-get install -y pkg-config libssl-dev

# Build application
COPY container-src/Cargo.lock container-src/Cargo.toml ./
COPY container-src/src ./src
RUN cargo build --release

# ---- Runtime Stage ----
FROM debian:bookworm-slim

# Install minimal runtime dependencies
RUN apt-get update &amp;#x26;&amp;#x26; apt-get install -y ca-certificates &amp;#x26;&amp;#x26; rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy the statically linked binary from the builder
COPY --from=builder /app/target/release/bgpkit-cf-container /app/bgpkit-cf-container

EXPOSE 3000

CMD [&quot;/app/bgpkit-cf-container&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rest of the task is to build a API app for Workers and configure Containers. The following config is pretty much all it needs to configure the Workers script to build and push the container image, and create a &lt;a href=&quot;https://developers.cloudflare.com/durable-objects/&quot;&gt;Durable Object&lt;/a&gt; to coordinate and run Containers.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;	&quot;containers&quot;: [
		{
			&quot;class_name&quot;: &quot;BgpkitContainer&quot;,
			&quot;image&quot;: &quot;./Dockerfile&quot;,
			&quot;max_instances&quot;: 5
		}
	],
	&quot;durable_objects&quot;: {
		&quot;bindings&quot;: [
			{
				&quot;class_name&quot;: &quot;BgpkitContainer&quot;,
				&quot;name&quot;: &quot;BGPKIT_CONTAINER&quot;
			}
		]
	},
	&quot;migrations&quot;: [
		{
			&quot;new_sqlite_classes&quot;: [
				&quot;BgpkitContainer&quot;
			],
			&quot;tag&quot;: &quot;v1&quot;
		}
	]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main Workers script is only 22 lines long:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { Container, getContainer } from &apos;@cloudflare/containers&apos;;
import { Hono } from &quot;hono&quot;;

export class BgpkitContainer extends Container {
	defaultPort = 3000;
	sleepAfter = &apos;5m&apos;;
}

// Create Hono app with proper typing for Cloudflare Workers
const app = new Hono&amp;#x3C;{
	Bindings: { BGPKIT_CONTAINER: DurableObjectNamespace&amp;#x3C;BgpkitContainer&gt; };
}&gt;();

app.get(&quot;/search&quot;, async (c) =&gt; {
	if (!c.req.query(&apos;collector&apos;) || !c.req.query(&apos;prefix&apos;) || !c.req.query(&apos;ts_start&apos;) || !c.req.query(&apos;ts_end&apos;)) {
		return c.json({ error: &quot;Missing required query parameters: collector, prefix, ts_start, ts_end&quot; }, 400);
	}
	const container = getContainer(c.env.BGPKIT_CONTAINER);
	return await container.fetch(c.req.raw);
});

export default app;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important pieces are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;class BgpkitContainer extends Container&lt;/code&gt; block defines the port to use and configures how long the container should run after the last query. In this example, containers will be killed after 5 minutes of inactivity. It is crucial to realize that Cloudflare Containers are not a drop-in replacement of some other container deployment platforms like fly.io or Railway, as the workload for Containers are intended to be short-lived (ping me if this changes) and scales horizontally depending on the requests amount.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the &lt;code&gt;getContainer&lt;/code&gt; function here tries to reach a container. If the intended container is overloaded, it may create a new container on demand. You may choose to use the &lt;code&gt;getRandom&lt;/code&gt; function to round-robin containers. See &lt;a href=&quot;https://developers.cloudflare.com/containers/scaling-and-routing/&quot;&gt;the docs&lt;/a&gt; for more.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the &lt;code&gt;container.fetch(c.req.raw)&lt;/code&gt; forwards the query to the container, including the query parameters, which will then be handled by the running container.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Example Queries&lt;/h2&gt;
&lt;p&gt;The following example will reach the Workers script (handled by Hono), and then reach the container to run the actual BGP data crunching task. (This URL won’t actually work as we don’t have budget to provide such service openly. Feel free to deploy it on your own account to try it out.)&lt;br&gt;
&lt;a href=&quot;https://bgpkit-cf-containers.bgpkit.workers.dev/search?collector=rrc00&amp;#x26;prefix=103.228.200.0/24&amp;#x26;ts_start=1751231488&amp;#x26;ts_end=1751231488&quot;&gt;https://EXAMPLE.bgpkit.workers.dev/search?collector=rrc00&amp;#x26;prefix=1.1.1.0/24&amp;#x26;ts_start=1751231488&amp;#x26;ts_end=1751231488&lt;/a&gt;&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/api-query-1.Cgh5JWEo.png&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/api-query-2.BvDty7sk.png&quot; alt=&quot;&quot;&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>BGPKIT Broker 0.7 Release</title><link>https://bgpkit.com/blog/bgpkit-broker-07-release/</link><guid isPermaLink="true">https://bgpkit.com/blog/bgpkit-broker-07-release/</guid><description>BGPKIT Broker is a fundamental component to our design of a all-purpose BGP data processing pipeline. In short, it is a BGP data file meta information broker...</description><pubDate>Sat, 22 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-broker&quot;&gt;BGPKIT Broker&lt;/a&gt; is a fundamental component to our design of a all-purpose BGP data processing pipeline. In short, it is a BGP data file meta information &quot;broker&quot; that tells the data consumers what MRT files from RouteViews and RIPE RIS are available for any given time range in question. It commonly serves as a data input entry point for data pipelines.&lt;/p&gt;
&lt;p&gt;For instance, here is a simple diagram for a system that creates a semi-real-time BGP data stream with BGPKIT Broker and Parser (a very common use case for these two libraries).&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/broker-parser-workflow.Dqqya9Is.png&quot; alt=&quot;Sample workflow diagram where BGPKIT Broker indexes meta information from MRT archives and BGPKIT parser parses these files to BGP messages.&quot;&gt;
&lt;p&gt;BGPKIT Broker periodically crawls the websites of RIPE RIS and RouteViews MRT data pages of their collectors and index meta information into a database. Downstream consumers can ask and retrieve new files and process the files into BGP messages.&lt;/p&gt;
&lt;h1&gt;Previously on BGPKIT Broker&lt;/h1&gt;
&lt;p&gt;In the BGPKIT Broker version 0.1 to 0.6, a working broker instance consists of three individual components: a crawler, a Postgres database, and an API.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/old-architecture.BBsyc0qO.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Each of the three component runs independently, and requires independent configuration, cronjobs, deployment, and all these goodies. For example, to run BGPKIT Broker v0.6, a user will need to configure and run&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;a PostgreSQL database with proper credentials and schema set up;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a cronjob instance that periodically crawls the data sources, with optional locks to prevent overlapping executions in case some crawl became slow;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a API application likely sitting behind a configured reverse proxy like Caddy to serve the data.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&apos;s fun and exciting to set up all these for the first time, but quickly became tiring and too complex for repeated set up or bootstrapping for new users.&lt;/p&gt;
&lt;h1&gt;V0.7: one CLI app that does everything&lt;/h1&gt;
&lt;p&gt;We completely revamped the architecture for BGPKIT Broker in V0.7 to merge every functionality needed for a running Broker instance into one single command-line application: &lt;code&gt;bgpkit-broker&lt;/code&gt;. V0.7 provides a single application to configure, run, debug, query everything on BGPKIT Broker.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/v07-single-cli.Bb1TfS4H.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;To achieve this redesign, we made some major changes to our architecture.&lt;/p&gt;
&lt;h2&gt;SQLite instead of PostgreSQL&lt;/h2&gt;
&lt;p&gt;There are two major topics to concern when choosing a backend database for BGPKIT Broker: performance and portability.&lt;/p&gt;
&lt;h3&gt;SQLite is more than fast enough&lt;/h3&gt;
&lt;p&gt;BGPKIT Broker indexes metadata for &lt;strong&gt;all collectors&lt;/strong&gt; from RouteViews and RIPE RIS, which includes time, URL, type, size of every RIB dump and updates MRT files from these two public archives. Dating all the way back to 1999, we have indexed roughly 48 million MRT files&apos; metadata.&lt;/p&gt;
&lt;p&gt;With a single index on the timestamp of files, we are able to search data files in less than 0.5s for any queries, which is more than fast enough for our use cases. We admit that we have spent our time for &quot;early optimization&quot; and in the end, the simple schema out-weighs the small performance gains.&lt;/p&gt;
&lt;h3&gt;Backup and bootstrap with just one file&lt;/h3&gt;
&lt;p&gt;Now in terms of portability, we can appreciate enough the beauty of single-file database like SQLite. In our current production setup, we periodically backup the database, and it literally involves just copying a single file to another directory (well, we also upload it to Cloudflare R2 for safekeeping).&lt;/p&gt;
&lt;p&gt;Portability also means users can move their instance anywhere they want with ease. This is definitely the case for V0.7 where new users can bootstrap by simply download a SQLite file (our CLI provides all that functionality), and move to new locations by scp it to anywhere they desire.&lt;/p&gt;
&lt;p&gt;Here is a video demonstrating bootstrapping a local BGPKIT Broker SQLite database with the new &lt;code&gt;bgpkit-broker bootstrap&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;%[&lt;a href=&quot;https://youtu.be/SsCyfQ5q0G0&quot;&gt;https://youtu.be/SsCyfQ5q0G0&lt;/a&gt;]&lt;/p&gt;
&lt;h2&gt;New file notification via NATS&lt;/h2&gt;
&lt;p&gt;Before V0.7, pipelines that needs to continuously processing new MRT files will need to &quot;pull&quot; data from BGPKIT Broker instance periodically and keep track of the latest files processed. We consider this a hassle that developers should not be dealing with and thus introduced a new &lt;a href=&quot;https://nats.io/&quot;&gt;&lt;code&gt;NATS&lt;/code&gt;&lt;/a&gt;-based message channel allowing data consumers to subscribe to the public/private NATS channel where a Broker instance may publish new file notification to.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/nats-notification.BRitxG0Y.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;We dedicated &lt;code&gt;nats.broker.bgpkit.com&lt;/code&gt; as the public endpoint for any NATS consumers to connect to. Whenever a new file becomes available in Broker, it will publish a new file notification with all metadata as in the database entry to the public channel. Consumers (e.g. data pipelines) can use the &lt;code&gt;NatsNotifier::new(None).start_subscription()&lt;/code&gt; to start waiting for new files. The following snippet below shows how a simple pipeline can use this feature in a loop.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let mut notifier = match NatsNotifier::new(url).await {
    Ok(n) =&gt; n,
    Err(e) =&gt; {
        error!(&quot;{}&quot;, e);
        return;
    }
};
if let Err(e) = notifier.start_subscription(subject).await {
    error!(&quot;{}&quot;, e);
    return;
}
while let Some(item) = notifier.next().await {
    if pretty {
        println!(&quot;{}&quot;, serde_json::to_string_pretty(&amp;#x26;item).unwrap());
    } else {
        println!(&quot;{}&quot;, item);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also implemented a simple new file watcher in the app as &lt;code&gt;bgpkit-broker live&lt;/code&gt; subcommand. It will start a subscription to the public BGPKIT NATS endpoint and print out new file data as they come to the channel.&lt;/p&gt;
&lt;h2&gt;One command to serve and update&lt;/h2&gt;
&lt;p&gt;As mentioned previously, the new &lt;code&gt;bgpkit-broker&lt;/code&gt; application includes everything one needs to start a instance. Once one bootstrapped the database to a local sqlite file (via &lt;code&gt;bgpkit-broker bootstrap &amp;#x3C;FILENAME&gt;&lt;/code&gt; command), all they need to start a auto-updating API is to run &lt;code&gt;bgpkit-broker serve &amp;#x3C;FILENAME&gt;&lt;/code&gt; .&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bgpkit-broker serve --help
Serve the Broker content via RESTful API

Usage: bgpkit-broker serve [OPTIONS] &amp;#x3C;DB_PATH&gt;

Arguments:
  &amp;#x3C;DB_PATH&gt;  broker db file location

Options:
  -i, --update-interval &amp;#x3C;UPDATE_INTERVAL&gt;  update interval in seconds [default: 300]
      --no-log                             disable logging
  -b, --bootstrap                          bootstrap the database if it does not exist
      --env &amp;#x3C;ENV&gt;                          
  -s, --silent                             disable bootstrap progress bar
  -h, --host &amp;#x3C;HOST&gt;                        host address [default: 0.0.0.0]
  -p, --port &amp;#x3C;PORT&gt;                        port number [default: 40064]
  -r, --root &amp;#x3C;ROOT&gt;                        root path, useful for configuring docs UI [default: /]
      --no-update                          disable updater service
      --no-api                             disable API service
  -h, --help                               Print help
  -V, --version                            Print version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;serve&lt;/code&gt; subcommand will also start a thread that periodically crawl and update the SQLite database to make sure the API always serve the up-to-date data.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/nats-error.BR87Fj68.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Noticed that error message? It&apos;s by design as it tries to connect to notification channel for new files as a default behavior for a service, but not NATS URL is configured. We use the &lt;code&gt;BGPKIT_BROKER_NATS_URL&lt;/code&gt; environment variable to configure the NATS channel to use.&lt;/p&gt;
&lt;p&gt;We also allow users to optionally configure a heartbeat URL to monitor the data updating status. After every success data crawling run, Broker will try to execute a HTTP GET to a URL if &lt;code&gt;BGPKIT_BROKER_HEARTBEAT_URL&lt;/code&gt; is set in the environment. This is useful to monitor the running status of the Broker instance without the need of setting up a cronjob.&lt;/p&gt;
&lt;p&gt;We use &lt;a href=&quot;https://betterstack.com/&quot;&gt;Better Stack&apos;s Uptime&lt;/a&gt; monitoring service for page and heartbeat monitoring, and the public Broker instance is running V0.7 with the heartbeat URL set to this service. All status information can be found at &lt;a href=&quot;https://status.bgpkit.com/&quot;&gt;https://status.bgpkit.com/&lt;/a&gt;&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/uptime-status.COEXwns7.png&quot; alt=&quot;&quot;&gt;
&lt;h1&gt;Production-ready, on-prem deployment&lt;/h1&gt;
&lt;p&gt;Although BGPKIT Broker has not yet reached V1.0, we consider it to be feature-complete and production-ready. Ever since V0.2, we have made our better efforts on not introducing any breaking changes and the service has been serving the community with a stable uptime ever since. We believe all libraries running in production should at least be 1.0, and thus &lt;strong&gt;we will release V1.0 soon this summer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We also made significant efforts in V0.7 release to make sure BGPKIT Broker is as portable as possible. New users can spin up a fully functioning Broker instance with just two commands: &lt;code&gt;bgpkit-broker bootstrap&lt;/code&gt; and &lt;code&gt;bgpkit-broker serve&lt;/code&gt;, all within 5 minutes. With V0.7 released, we &lt;strong&gt;encourage all data pipeline designers to deploy a Broker instance on-premise&lt;/strong&gt;, ensuring data pipelines are self-containing and reduce external dependencies as much as possible. We also will continue maintain our public instance to our best efforts (we are currently at &lt;strong&gt;99.996% uptime&lt;/strong&gt;). Thanks to &lt;a href=&quot;https://github.com/sponsors/bgpkit&quot;&gt;our sponsors&lt;/a&gt;, we are able to keep the services up as we do, and we plan to continue serving the community the same way in the foreseeable future.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/broker-website.D64RUVmg.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;For the full V0.7 release notes, please check out our &lt;a href=&quot;https://github.com/bgpkit/bgpkit-broker/releases/tag/v0.7.0&quot;&gt;GitHub release page&lt;/a&gt;. If you have any comments, please drop us a message at &lt;a href=&quot;https://twitter.com/bgpkit&quot;&gt;Twitter&lt;/a&gt;, &lt;a href=&quot;https://infosec.exchange/@bgpkit&quot;&gt;Mastodon&lt;/a&gt;, or &lt;a href=&quot;mailto:contact@bgpkit.com&quot;&gt;email&lt;/a&gt;.&lt;/p&gt;
&lt;div data-node-type=&quot;callout&quot;&gt;
&lt;div data-node-type=&quot;callout-emoji&quot;&gt;💖&lt;/div&gt;
&lt;div data-node-type=&quot;callout-text&quot;&gt;If you find our libraries and services useful, we would highly appreciate if you consider &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer nofollow&quot; href=&quot;https://github.com/sponsors/bgpkit&quot; style=&quot;pointer-events: none&quot;&gt;sponsor us on GitHub&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>Command-line Routing Stats with monocle and Cloudflare Radar API</title><link>https://bgpkit.com/blog/monocle-cloudflare-radar/</link><guid isPermaLink="true">https://bgpkit.com/blog/monocle-cloudflare-radar/</guid><description>BGPKIT monocle is a command-line utility program that helps users quickly pull Internet routing-related information from publicly available sources.</description><pubDate>Sun, 21 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;BGPKIT &lt;code&gt;monocle&lt;/code&gt; is a command-line utility program that helps users quickly pull Internet routing-related information from publicly available sources.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/monocle&quot;&gt;https://github.com/bgpkit/monocle&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In BGPKIT &lt;code&gt;monocle&lt;/code&gt; version V0.5, we add support for querying &lt;a href=&quot;https://radar.cloudflare.com/&quot;&gt;Cloudflare Radar&lt;/a&gt;&apos;s new BGP &lt;a href=&quot;https://developers.cloudflare.com/api/operations/radar-get-bgp-routes-stats&quot;&gt;routing statistics&lt;/a&gt; and &lt;a href=&quot;https://developers.cloudflare.com/api/operations/radar-get-bgp-pfx2as&quot;&gt;prefix-to-origin mapping&lt;/a&gt; APIs, the same APIs that power the &lt;a href=&quot;https://radar.cloudflare.com/routing&quot;&gt;Cloudflare Radar routing section&lt;/a&gt;. &lt;code&gt;monocle&lt;/code&gt; users can now quickly glance overview of routing stats for any given ASN, country, or the whole Internet. Users can also quickly look up prefix origins and examine their RPKI validation status as well as prefix visibility on the global routing tables.&lt;/p&gt;
&lt;h2&gt;Using &lt;code&gt;monocle radar&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;We added a new &lt;code&gt;monocle radar&lt;/code&gt; command group in V0.5, which contains the following to subcommands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;monocle radar stats [QUERY]&lt;/code&gt;: get routing stats (like prefix count, rpki invalid count) for a given country or ASN.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;monocle radar pfx2as [QUERY] [--rpki-status valid|invalid|unknown]&lt;/code&gt;: get prefix to origin mapping for a given prefix or ASN&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;mingwei@terrier ~ % monocle radar
Cloudflare Radar API lookup (set CF_API_TOKEN to enable)

Usage: monocle radar &amp;#x3C;COMMAND&gt;

Commands:
  stats   get routing stats
  pfx2as  look up prefix to origin mapping on the most recent global routing table snapshot
  help    Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Cloudflare API token needed&lt;/h3&gt;
&lt;p&gt;Since the &lt;code&gt;monocle radar&lt;/code&gt; command relies on querying data using Cloudflare Radar public API, we also need to specify a user API token as the environment variable &lt;code&gt;CF_API_TOKEN&lt;/code&gt;. Obtaining an API token is free and only needs a Cloudflare account. Interested users can follow their &lt;a href=&quot;https://developers.cloudflare.com/radar/get-started/first-request/&quot;&gt;official tutorial&lt;/a&gt; to obtain a token. The environment variable can be set in a &lt;code&gt;.env&lt;/code&gt; file in the current directory, or set in ~&lt;code&gt;/.bashrc&lt;/code&gt; or &lt;code&gt;~/.profile&lt;/code&gt; etc.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;monocle radar stats&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Users can query the routing statistics for a given country or ASN. For example, &lt;code&gt;monocle radar stats us&lt;/code&gt; returns the routing stats for the United States, while &lt;code&gt;monocle radar stats 174&lt;/code&gt; returns the stats for Cogent (&lt;code&gt;AS174&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The displayed table is further divided into three rows, one for overall counting, and one for IPv4 and IPv6-specific counting. For each row, we show the following fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;origins&lt;/code&gt;: the number of origins ASes registered in the given country&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;prefixes&lt;/code&gt;: the number of prefixes originated by the given ASN or ASes registered in the given country&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;rpki_valid/invalid/unknown&lt;/code&gt;: the number of RPKI valid/invalid/unknown prefix routes (prefix-origin mapping) on the global routing table and their percentage of the overall routes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/radar-stats-output.DtnTcW2F.png&quot; alt=&quot;&quot;&gt;
&lt;h3&gt;&lt;code&gt;monocle radar pfx2as&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Users can query the prefix-to-origin API to get the mapping of origin ASes and their originated prefixes on the global routing table.&lt;/p&gt;
&lt;p&gt;In the following example, &lt;code&gt;monocle radar pfx2as 174 --rpki-status invalid&lt;/code&gt;, we ask for all the prefixes originated by &lt;code&gt;AS174&lt;/code&gt; with the RPKI validation status to be invalid. This command returns us the list of RPKI invalid prefixes originated by &lt;code&gt;AS174&lt;/code&gt; at the time of generating the dataset.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/radar-pfx2as-output.C8DHRa_F.png&quot; alt=&quot;&quot;&gt;
&lt;h3&gt;Questions it can answer now (more in the future)&lt;/h3&gt;
&lt;p&gt;Here is a selected list of questions that &lt;code&gt;monocle radar&lt;/code&gt; command can answer you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;How many ASes are there on the Internet that announce at least one prefix? (81,770)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How many of these ASes announce only IPv6 prefixes? (6,853)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How many prefixes are there on the global routing table? (1,205,218)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How many prefixes do &lt;code&gt;AS400644&lt;/code&gt; announce? (1)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Which AS(es) originates &lt;code&gt;1.1.1.0/24&lt;/code&gt;? (AS13335)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How many prefixes originated by &lt;code&gt;AS174&lt;/code&gt; are NOT covered by some RPKI ROA? (a lot, 94%+)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How about the RPKI valid ratio for the Philippines? (77%, nice!)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Powered by Cloudflare Radar free API&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Cloudflare Radar is a hub that showcases global Internet traffic, attack, and technology trends and insights.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What &lt;a href=&quot;https://radar.cloudflare.com/&quot;&gt;Cloudflare Radar&lt;/a&gt; shines is its data openness. Everything you see on the Cloudflare Radar website is powered by their free &lt;a href=&quot;https://developers.cloudflare.com/api/operations/radar-get-bgp-pfx2as-moas&quot;&gt;publicly available APIs&lt;/a&gt;. It&apos;s a treasure trove there, and all users need is a &lt;a href=&quot;https://developers.cloudflare.com/radar/get-started/first-request/&quot;&gt;free API token&lt;/a&gt; to access everything.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/cloudflare-radar-site.DxotvYqB.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;At BGPKIT, we think we can further improve the usability of the API by exposing them as a proper Rust SDK: &lt;a href=&quot;https://github.com/bgpkit/radar-rs&quot;&gt;&lt;code&gt;radar-rs&lt;/code&gt;&lt;/a&gt;. This is our (unofficial) effort on bringing the Cloudflare Radar&apos;s rich data to Rust developers. For example, &lt;code&gt;monocle radar&lt;/code&gt; is powered by this SDK.&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>ASPA Path Verification Explained</title><link>https://bgpkit.com/blog/aspa-path-verification-explained/</link><guid isPermaLink="true">https://bgpkit.com/blog/aspa-path-verification-explained/</guid><description>An explanation of Autonomous System Provider Authorization (ASPA) path verification and how it detects route leaks and improbable AS paths.</description><pubDate>Sun, 25 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href=&quot;https://rfc.hashnode.dev/aspa-path-verification-explained&quot;&gt;Hashnode&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Autonomous System Provider Authorization (ASPA) is a recently proposed
solution designed to detect and mitigate route leaks and improbable AS
paths. ASPA utilizes the existing RPKI infrastructure to allow storing
and verifying cryptographically protected AS-level relationship
information. This post focuses on the AS path verification process used
by ASPA.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This post is based on &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-sidrops-aspa-verification/&quot;&gt;this IETF draft&lt;/a&gt; from June 2023. The draft text, including address-family handling, may differ from the final RFC.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Route leak&lt;/h2&gt;
&lt;p&gt;In overview, the definition of route leak considered in ASPA is based on
&lt;a href=&quot;https://datatracker.ietf.org/doc/rfc7908/&quot; target=&quot;_blank&quot;&gt;RFC
7908: Problem Definition and Classification of BGP Route Leaks&lt;/a&gt;. The
route leaks defined in this RFC is mostly based on the
&lt;a href=&quot;https://ieeexplore.ieee.org/document/974527&quot; target=&quot;_blank&quot;&gt;&quot;valley-free&quot; principle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A previous Cloudflare Radar post on &lt;a href=&quot;https://blog.cloudflare.com/route-leak-detection-with-cloudflare-radar/#route-leak-definition-and-types&quot; target=&quot;_blank&quot;&gt;route-leak detection&lt;/a&gt; summarized four common route
leak types as follows:&lt;/p&gt;
&lt;div class=&quot;hn-table&quot;&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Routes from&lt;/th&gt;&lt;th&gt;To provider&lt;/th&gt;&lt;th&gt;To peer&lt;/th&gt;&lt;th&gt;To customer&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;From provider&lt;/td&gt;&lt;td&gt;Type 1&lt;/td&gt;&lt;td&gt;Type 3&lt;/td&gt;&lt;td&gt;Normal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;From peer&lt;/td&gt;&lt;td&gt;Type 4&lt;/td&gt;&lt;td&gt;Type 2&lt;/td&gt;&lt;td&gt;Normal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;From customer&lt;/td&gt;&lt;td&gt;Normal&lt;/td&gt;&lt;td&gt;Normal&lt;/td&gt;&lt;td&gt;Normal&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;In essence, route leaks defined in RFC 7908 can be summarized into one
principle: &lt;strong&gt;routes obtained from a non-customer (peer, customer,
route-server) AS can only be propagated to customers.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In ASPA, the authors inherited the definition of BGP roles (e.g.
customer, provider) from &lt;a href=&quot;https://bgpkit.com/blog/rfc9234-observed-in-the-wild/#heading-rfc9234&quot; target=&quot;_blank&quot;&gt;RFC 9234&lt;/a&gt;. See the &lt;a href=&quot;https://bgpkit.com/blog/rfc9234-observed-in-the-wild/#heading-rfc9234&quot; target=&quot;_blank&quot;&gt;RFC9234 post&lt;/a&gt; for more detail on RFC9234 and the OTC
BGP attribute.&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Local AS Role&lt;/th&gt;&lt;th&gt;Remote AS Role&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Provider&lt;/td&gt;&lt;td&gt;Customer&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Customer&lt;/td&gt;&lt;td&gt;Provider&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;RS&lt;/td&gt;&lt;td&gt;RS-Client&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;RS-Client&lt;/td&gt;&lt;td&gt;RS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Peer&lt;/td&gt;&lt;td&gt;Peer&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Table: allowed pairs of BGP role capabilities.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;ASPA TL;DR&lt;/h2&gt;
&lt;p&gt;At a high level, the ASPA mechanism has the following properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ASPA stores &lt;code&gt;customer-to-provider&lt;/code&gt; information in RPKI&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One customer AS (CAS) associated with a set of provider ASes (SPAS)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ASPA is useful for verifying BGP &lt;code&gt;AS_PATH&lt;/code&gt; attribute content&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AS_PATH&lt;/code&gt; that violates the &quot;valley-free&quot; routing principle can be
detected with ASPA objects information&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ASPA is incrementally deployable, benefiting early adopters&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ASPA Verification Explained&lt;/h2&gt;
&lt;p&gt;Before describing how ASPA detects route leaks, it is useful to review
the shape of a typical valley-free route.&lt;/p&gt;
&lt;p&gt;A typical long BGP route that travels from one edge network to a
different edge network would look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.cloudflare.com/route-leak-detection-with-cloudflare-radar/#route-leak-definition-and-types&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://bgpkit.com/blog-assets/rfc-hashnode/aspa-path-verification-explained/image-2.png&quot; alt=&quot;Typical valley-free AS path with customer-provider, peer-peer, and provider-customer phases&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The route travels three different phases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;upward: a consecutive &lt;code&gt;customer-to-provider&lt;/code&gt; segments&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;peering: a single &lt;code&gt;peer-to-peer&lt;/code&gt; segment&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;downward: a consecutive &lt;code&gt;provider-to-customer&lt;/code&gt; segments&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note that in many cases, the routes may only have one or two phases, but
they must be in this order. For example, one legit route could have an
&lt;code&gt;upward&lt;/code&gt; then &lt;code&gt;downward&lt;/code&gt; phase, without a &lt;code&gt;peering&lt;/code&gt; phase. A route leak
route could be any route with reverse order of the phases, e.g.
&lt;code&gt;downward&lt;/code&gt; then &lt;code&gt;upward&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In ASPA, the phases are defined into two categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;upstream&lt;/code&gt;: path received from a customer or a peer or a route server&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;downstream&lt;/code&gt;: path received from a provider or sibling&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ASPA verification uses different procedures for &lt;code&gt;upstream&lt;/code&gt; and
&lt;code&gt;downstream&lt;/code&gt; paths. Each category is described below.&lt;/p&gt;
&lt;h3&gt;Upstream paths&lt;/h3&gt;
&lt;p&gt;ASPA verification for upstream paths (received from a customer or peer
or route server) is relatively simple. The key principle is that &lt;strong&gt;a
normal upstream path should only contain&lt;/strong&gt; &lt;code&gt;customer-to-provider&lt;/code&gt;
&lt;strong&gt;relationships&lt;/strong&gt; for the consecutive AS path hops.&lt;/p&gt;
&lt;p&gt;A path is&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Valid&lt;/code&gt;: if all hops in the AS paths are verifiable
&lt;code&gt;customer-to-provider&lt;/code&gt;, i.e. every hop on the path registers their
providers on ASPA and the providers match the next hop on the path;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Unknown&lt;/code&gt;: one of the hops is missing ASPA information;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Invalid&lt;/code&gt;: one of AS has a set of provider AS (SPAS) but the next hop
is not in it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Downstream paths&lt;/h3&gt;
&lt;p&gt;The downstream paths are considerably more complex as it may have legit
paths segments that travel all three phases (up, peer, down) before
reaching the receiving AS. Interested readers should refer to the
original IETF draft document&apos;s &lt;a href=&quot;https://datatracker.ietf.org/doc/html/draft-ietf-sidrops-aspa-verification-14#name-algorithm-for-downstream-pa&quot; target=&quot;_blank&quot;&gt;section on downstream path verification&lt;/a&gt; for a more
definitive definition. The summary below simplifies the wording to give
a high-level view of the verification process.&lt;/p&gt;
&lt;p&gt;Based on the original documentation, a &lt;code&gt;downstream&lt;/code&gt; path is &lt;code&gt;Invalid&lt;/code&gt; if
there exist hops indexed as &lt;code&gt;u&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt; such that&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;u&amp;#x3C;=v&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;hop(AS(u-1), AS(u))=&quot;Not Provider+&quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;hop(AS(v+1),AS(v))=&quot;Not Provider+&quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In practical terms, this condition can be read as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;two hops at the location &lt;code&gt;u&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt; and &lt;code&gt;u&lt;/code&gt; is before &lt;code&gt;v&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;hop &lt;code&gt;u&lt;/code&gt; is verified to be NOT a customer-to-provider hop, i.e.
&lt;code&gt;peer-to-peer&lt;/code&gt; or &lt;code&gt;provider-to-customer&lt;/code&gt;, i.e. a &lt;code&gt;peering&lt;/code&gt; or
&lt;code&gt;downward&lt;/code&gt; phase&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;hop v is verified to be NOT a provider-to-customer hop, i.e.
&lt;code&gt;peer-to-peer&lt;/code&gt; or &lt;code&gt;customer-to-provider&lt;/code&gt;, i.e. a &lt;code&gt;peering&lt;/code&gt; or
&lt;code&gt;upward&lt;/code&gt; phase&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In summary, the path would be ASPA &lt;code&gt;Invalid&lt;/code&gt;, if it contains a
&lt;code&gt;downward&lt;/code&gt; or &lt;code&gt;peering&lt;/code&gt; then later a &lt;code&gt;upward&lt;/code&gt; or &lt;code&gt;peering&lt;/code&gt;. The AS
relationship would look like the following figure, where &lt;code&gt;u&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt;
could be the same ASN. As long as this pattern appears somewhere in the
path, it violates the &quot;valley-free&quot; routing and thus is considered as
route leak.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;graph LR
  UProvider[&quot;u-1&quot;] --&gt;|&quot;provider-to-customer&quot;| U[&quot;u&quot;]
  UPeer[&quot;u-1&quot;] --&gt;|&quot;peer-to-peer&quot;| U
  U -.-&gt; V[&quot;v&quot;]
  V --&gt;|&quot;customer-to-provider&quot;| VProvider[&quot;v+1&quot;]
  V --&gt;|&quot;peer-to-peer&quot;| VPeer[&quot;v+1&quot;]
&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Unknown&lt;/code&gt; and &lt;code&gt;Valid&lt;/code&gt; definitions are then straightforward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Unknown&lt;/code&gt;: the path is NOT &lt;code&gt;Invalid&lt;/code&gt; and there are hops that miss ASPA
information&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Valid&lt;/code&gt;: the path is NOT &lt;code&gt;Invalid&lt;/code&gt; and NOT &lt;code&gt;Unknown&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;a href=&quot;https://datatracker.ietf.org/doc/html/draft-ietf-sidrops-aspa-verification-14#section-7&quot;&gt;ASPA document&lt;/a&gt; allows different verification algorithms, as long as they produce results equivalent to the specified process.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;ASPA vs RFC9234&lt;/h2&gt;
&lt;p&gt;ASPA and RFC 9234 both aim to prevent or mitigate route leaks, but they
use different mechanisms. The table below summarizes the main
differences.&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;ASPA&lt;/th&gt;&lt;th&gt;RFC9234&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;By whom&lt;/td&gt;&lt;td&gt;Customer AS (owner) only&lt;/td&gt;&lt;td&gt;BGP sessions, providers&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Content security&lt;/td&gt;&lt;td&gt;Cryptographically signed&lt;/td&gt;&lt;td&gt;Secured by BGP session; OTC attributes can be forged&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Where to detect/filter&lt;/td&gt;&lt;td&gt;Any AS&lt;/td&gt;&lt;td&gt;Neighbors or ASes that receive OTC attribute&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Prevent forged path?&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Deployment timing&lt;/td&gt;&lt;td&gt;Initial objects had already appeared; general deployment was expected in 2025 or later (&lt;a href=&quot;https://www.manrs.org/2023/05/estimating-the-timeline-for-aspa-deployment/&quot;&gt;Job Snijders / MANRS&lt;/a&gt;)&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://bgpkit.com/blog/rfc9234-observed-in-the-wild/#heading-only-to-customer-attribute-in-the-wild&quot;&gt;Initial deployment had appeared&lt;/a&gt;; general deployment remained unclear&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;How to view ASPA objects on RPKI today?&lt;/h2&gt;
&lt;p&gt;Although still in the draft phase, ASPA support has already been added
to popular routing software like
&lt;a href=&quot;https://github.com/NLnetLabs/routinator/pull/847&quot; target=&quot;_blank&quot;&gt;routinator&lt;/a&gt; and
&lt;a href=&quot;https://twitter.com/openbsd/status/1636785866596286465&quot; target=&quot;_blank&quot;&gt;OpenBGPd&lt;/a&gt;. BGPKIT monocle also supports reading ASPA
objects from RPKI repositories through the
&lt;a href=&quot;https://github.com/bgpkit/monocle#monocle-rpki-read-aspa&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;monocle rpki read-aspa&lt;/code&gt;&lt;/a&gt; command. The following
steps show how to inspect currently published ASPA objects.&lt;/p&gt;
&lt;h3&gt;Install BGPKIT monocle&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/monocle&quot; target=&quot;_blank&quot;&gt;BGPKIT
monocle&lt;/a&gt; is a Rust-based BGP data processing command-line tool.
Install the Rust toolchain using the instructions at
&lt;a href=&quot;https://rustup.rs/&quot; target=&quot;_blank&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://rustup.rs/&quot;&gt;https://rustup.rs/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then install &lt;code&gt;monocle&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cargo install monocle
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Download the recent RPKI archive&lt;/h3&gt;
&lt;p&gt;The RPKI repository archive used here comes from Job Snijders&apos;
&lt;a href=&quot;http://www.rpkiviews.org/&quot; target=&quot;_blank&quot;&gt;RPKIViews&lt;/a&gt;
project. The example below downloads one archive from
&lt;a href=&quot;http://josephine.sobornost.net/josephine.sobornost.net/&quot; target=&quot;_blank&quot;&gt;josephine.sobornost.net&lt;/a&gt; (about 260 MB).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd /tmp
wget http://josephine.sobornost.net/josephine.sobornost.net/rpkidata/2023/06/25/rpki-20230625T173535Z.tgz
tar xzf rpki-20230625T173535Z.tgz
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Find and view ASPA objects&lt;/h3&gt;
&lt;p&gt;The ASPA objects are stored as files with the suffix &lt;code&gt;.asa&lt;/code&gt; in the RPKI
repository. You can use &lt;code&gt;find&lt;/code&gt; command to search across the untarred
repo file from the previous step:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;➜  rpki-20230625T173535Z find . -name &quot;*.asa&quot;
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148808.asa
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148809.asa
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148810.asa
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148804.asa
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148805.asa
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148807.asa
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148806.asa
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148802.asa
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148803.asa
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148801.asa
./data/rpki.sub.apnic.net/repository/A914BC7A0000/0/AS148800.asa
./data/rpki.cc/repo/MythicalKitten/9/AS60310.asa
./data/rpki.cc/repo/MythicalKitten/0/AS203635.asa
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;monocle rpki read-aspa&lt;/code&gt; to inspect an ASPA object. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;➜  rpki-20230625T173535Z monocle rpki read-aspa ./data/rpki.cc/repo/MythicalKitten/9/AS60310.asa

| asn   | afi_limit | allowed_upstream |
|-------|-----------|------------------|
| 60310 | none      | 924              |
|       |           | 6939             |
|       |           | 48581            |
|       |           | 50224            |
|       |           | 52210            |
|       |           | 204857           |
➜  rpki-20230625T173535Z monocle rpki read-aspa ./data/rpki.co/repo/Mlgt/0/AS204508.asa

| asn    | afi_limit | allowed_upstream |
|--------|-----------|------------------|
| 204508 | none      | 6939             |
|        |           | 7721             |
|        |           | 29632            |
|        |           | 34872            |
|        |           | 34927            |
|        |           | 38230            |
|        |           | 41051            |
|        |           | 50058            |
|        |           | 60326            |
|        |           | 200105           |
|        |           | 208753           |
|        |           | 210633           |
|        |           | 211411           |
|        |           | 212271           |
|        |           | 212895           |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit&quot; target=&quot;_blank&quot;&gt;BGPKIT&lt;/a&gt; is
experimenting with library support for ASPA to support measurement and
research use cases. Feedback and feature requests are welcome at
&lt;a href=&quot;mailto:data@bgpkit.com&quot;&gt;data@bgpkit.com&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>RFCs for IP Address Usage in Network Documentation and Examples</title><link>https://bgpkit.com/blog/rfcs-for-ip-address-usage-in-documentation/</link><guid isPermaLink="true">https://bgpkit.com/blog/rfcs-for-ip-address-usage-in-documentation/</guid><description>A short guide to the RFC-defined IPv4 and IPv6 address ranges reserved for documentation and examples.</description><pubDate>Wed, 22 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href=&quot;https://rfc.hashnode.dev/rfcs-for-ip-address-usage-in-documentation&quot;&gt;Hashnode&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Network documentation and topology examples should use address ranges
reserved for documentation, rather than globally routed addresses such as
&lt;code&gt;1.1.1.1&lt;/code&gt; or &lt;code&gt;1.2.3.4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Two RFCs define standardized IP address ranges reserved specifically for
documentation and examples: &lt;a href=&quot;https://tools.ietf.org/html/rfc3849&quot; target=&quot;_blank&quot;&gt;RFC3849&lt;/a&gt; and
&lt;a href=&quot;https://tools.ietf.org/html/rfc5737&quot; target=&quot;_blank&quot;&gt;RFC5737&lt;/a&gt;. RFC3849 outlines the use of the IPv6
address prefix &lt;code&gt;2001:DB8::/32&lt;/code&gt;, while RFC5737 outlines the use of
several IPv4 address blocks, including &lt;code&gt;192.0.2.0/24&lt;/code&gt;,
&lt;code&gt;198.51.100.0/24&lt;/code&gt;, and &lt;code&gt;203.0.113.0/24&lt;/code&gt;. These addresses are not
intended for use in a production network but instead serve as
placeholders to ensure accurate and clear documentation.&lt;/p&gt;
&lt;h3&gt;RFC3849: IPv6 Address Prefix Reserved for Documentation&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc3849&quot; target=&quot;_blank&quot;&gt;RFC3849&lt;/a&gt; specifies the IPv6 address prefix
&lt;code&gt;2001:DB8::/32&lt;/code&gt; for use in documentation and example code, not in a
production network. For example, a network administrator might use the
IPv6 address &lt;code&gt;2001:DB8::1&lt;/code&gt; in their documentation or example code to
represent a specific device or network location. This address would not
be used for actual network traffic, but rather as a reference point for
documentation purposes.&lt;/p&gt;
&lt;h3&gt;RFC5737: IPv4 Address Blocks Reserved for Documentation&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc5737&quot; target=&quot;_blank&quot;&gt;RFC5737&lt;/a&gt; outlines the use of several IPv4 address
blocks for use in documentation and example code. These address blocks
are similar to the IPv4 addresses outlined in
&lt;a href=&quot;https://tools.ietf.org/html/rfc5735&quot; target=&quot;_blank&quot;&gt;RFC5735&lt;/a&gt; (which contains more special blocks such as
&lt;code&gt;0.0.0.0/8&lt;/code&gt;), but are specifically for use in documentation and
examples.&lt;/p&gt;
&lt;p&gt;The address blocks outlined in RFC5737 include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;192.0.2.0/24&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;198.51.100.0/24&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;203.0.113.0/24&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Similarly, these address blocks are not intended for use in a production
network, but rather as placeholders for documentation and examples.&lt;/p&gt;
&lt;h2&gt;Proper Use of Reserved IP Addresses&lt;/h2&gt;
&lt;p&gt;Using reserved IP addresses for documentation and examples is essential
for a number of reasons. First, it ensures that these addresses are not
used for general Internet routing, which can help to prevent routing
issues and other network problems. Second, it helps to prevent confusion
and technical issues that can arise when non-reserved addresses are used
in documentation or example code.&lt;/p&gt;
&lt;p&gt;In recent years, there have been cases where non-reserved IP addresses
have been used in documentation and examples, leading to confusion and
technical issues. In a recent YouTube video, Cloudflare&apos;s Tom Strickx
highlighted this problem and encouraged operators to avoid addresses such
as &lt;code&gt;1.1.1.1&lt;/code&gt; and &lt;code&gt;1.2.3.4&lt;/code&gt;, using the ranges specified in RFC5735,
RFC5737, and RFC3849 instead.&lt;/p&gt;
&lt;div class=&quot;embed-wrapper&quot;&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=SIb_wxO1wt0&quot; class=&quot;embed-card&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=SIb_wxO1wt0&quot;&gt;https://www.youtube.com/watch?v=SIb_wxO1wt0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Using reserved IP addresses in documentation and examples avoids
unintended traffic to real networks and improves clarity for readers.
RFC3849 and RFC5737 provide the standard address ranges for these use
cases, and network operators and researchers should use them consistently
in examples and documentation.&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>RFC9234 Observed in the Wild</title><link>https://bgpkit.com/blog/rfc9234-observed-in-the-wild/</link><guid isPermaLink="true">https://bgpkit.com/blog/rfc9234-observed-in-the-wild/</guid><description>A look at RFC 9234 BGP roles and Only-to-Customer route-leak prevention observed in public BGP data.</description><pubDate>Thu, 16 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href=&quot;https://rfc.hashnode.dev/rfc9234-observed-in-the-wild&quot;&gt;Hashnode&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;BGP Route Leaks&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Route leaks&lt;/strong&gt; occur when BGP prefixes are propagated in a way that
goes against the expected topology relationships of BGP. For example,
this can happen when a route learned from one transit provider is
announced to another transit provider or a lateral peer
(peer-peer-peer), or when a route learned from one lateral peer is
announced to another lateral peer or a transit provider (see
&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7908&quot; target=&quot;_blank&quot;&gt;RFC7908&lt;/a&gt;). These leaks often result from
misconfiguration or the absence of BGP route filtering, or from
inadequate coordination between autonomous systems (ASes).&lt;/p&gt;
&lt;p&gt;Cloudflare Radar includes a public &lt;a href=&quot;https://blog.cloudflare.com/route-leak-detection-with-cloudflare-radar/&quot; target=&quot;_blank&quot;&gt;route-leak detection system&lt;/a&gt;. The system detects
potential route leaks by first inferring inter-AS relationships on a
per-prefix basis, then examining each announced AS path for valley-free
violations. Detection is useful for visibility, but prevention is the
stronger operational goal.&lt;/p&gt;
&lt;h2&gt;RFC9234&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/rfc9234/&quot; target=&quot;_blank&quot;&gt;RFC9234&lt;/a&gt; documents an active route-leak prevention
approach where it defines new BGP capacities (&lt;code&gt;BGP Roles&lt;/code&gt;) exchanged
during the eBGP session open time and allows the BGP routers to
understand AS relationships between local and remote ASes, and thus
prevent the propagation of route leaks. RFC9234 also defines a new BGP
attributes type, &lt;code&gt;only-to-customer&lt;/code&gt;, which tells the receiving BGP
routers whether some routes should never be announced to another
provider.&lt;/p&gt;
&lt;p&gt;When a pair of eBGP routers both implement RFC9234, they will first
confirm the BGP role of each other.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9234.html#section-4.2&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://bgpkit.com/blog-assets/rfc-hashnode/rfc9234-observed-in-the-wild/image-1.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With roles defined, it handles the only-to-customer attributes as
follows.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9234.html#section-5&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://bgpkit.com/blog-assets/rfc-hashnode/rfc9234-observed-in-the-wild/image-2.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In essence, RFC9234 uses BGP roles and OTC attributes to make sure
&lt;strong&gt;routes received from a provider or a peer can only be propagated to
customers&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Only-to-customer Attribute in the Wild&lt;/h2&gt;
&lt;p&gt;RFC9234 was published in May 2022. Public discussion of RFC9234
deployment has been limited, and public measurements of deployment on
the Internet remain relatively scarce.&lt;/p&gt;
&lt;p&gt;To measure RFC9234 deployment, &lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser/pull/65&quot; target=&quot;_blank&quot;&gt;RFC9234 support&lt;/a&gt; was added to
&lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser&quot; target=&quot;_blank&quot;&gt;BGPKIT
Parser&lt;/a&gt;. The repository also includes &lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser/blob/main/bgpkit-parser/examples/only-to-customer.rs&quot; target=&quot;_blank&quot;&gt;example code&lt;/a&gt; demonstrating how to parse a RIB file
and identify messages that contain Only-to-Customer attributes.&lt;/p&gt;
&lt;p&gt;The following output comes from parsing a single &lt;code&gt;route-views2&lt;/code&gt; RIB dump (&lt;a href=&quot;http://archive.routeviews.org/bgpdata/2023.03/RIBS/rib.20230316.0200.bz2&quot; target=&quot;_blank&quot;&gt;file link&lt;/a&gt;).&lt;/p&gt;
&lt;div class=&quot;gist-block embed-wrapper&quot; gist-show-loading=&quot;false&quot; data-id=&quot;756bd6b4d6627bf1e2a0a4f87a1c8290&quot;&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://gist.github.com/digizeph/756bd6b4d6627bf1e2a0a4f87a1c8290&quot; class=&quot;embed-card&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://gist.github.com/digizeph/756bd6b4d6627bf1e2a0a4f87a1c8290&quot;&gt;https://gist.github.com/digizeph/756bd6b4d6627bf1e2a0a4f87a1c8290&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This output shows that at least four ASes (&lt;code&gt;AS6939&lt;/code&gt;, &lt;code&gt;AS15562&lt;/code&gt;,
&lt;code&gt;AS20555&lt;/code&gt;, &lt;code&gt;AS212068&lt;/code&gt;) were associated with RFC9234 OTC attributes in
messages that propagated to the route collector and were preserved in MRT
files.&lt;/p&gt;
&lt;p&gt;The code that generates this result iterates over parsed BGP messages
and checks whether the &lt;code&gt;only_to_customer&lt;/code&gt; attribute is present.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use bgpkit_parser::BgpkitParser;

fn main() {
    for elem in BgpkitParser::new(
        &quot;http://archive.routeviews.org/bgpdata/2023.03/RIBS/rib.20230316.0200.bz2&quot;,
    )
    .unwrap()
    {
        if let Some(otc) = elem.only_to_customer {
            println!(
                &quot;OTC found: {} for path {}\n{}\n&quot;,
                &amp;#x26;otc,
                &amp;#x26;elem.as_path.as_ref().unwrap(),
                &amp;#x26;elem
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To reproduce this example, install the
&lt;a href=&quot;https://rustup.rs/&quot; target=&quot;_blank&quot;&gt;Rust toolchain&lt;/a&gt; and run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/bgpkit/bgpkit-parser
cd bgpkit-parser
cargo run --release --example only-to-customer
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;Update, Mar. 16, 2023: Job Snijders provided additional context on the
source of these OTC attributes: &lt;a href=&quot;https://yycix.ca/&quot; target=&quot;_blank&quot;&gt;YYCIX&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;embed-wrapper&quot;&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/JobSnijders/status/1636291640519282688&quot; class=&quot;embed-card&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://twitter.com/JobSnijders/status/1636291640519282688&quot;&gt;https://twitter.com/JobSnijders/status/1636291640519282688&lt;/a&gt;&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>2022 Year in Review</title><link>https://bgpkit.com/blog/2022-year-in-review/</link><guid isPermaLink="true">https://bgpkit.com/blog/2022-year-in-review/</guid><description>In 2022, BGPKIT as an open-source organization made significant progresses. As the founder, I am grateful for all the opportunities would like to take this time...</description><pubDate>Tue, 31 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In 2022, BGPKIT as an open-source organization made significant progresses. As the founder, I am grateful for all the opportunities would like to take this time to appreciate all the milestones we achieved. In this post, I will go through some notable changes we made in 2022, and take a look at what we are excited about for the year of 2023.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;BGPKIT Parser&lt;/h2&gt;
&lt;p&gt;There were a number of major features added to BGPKIT Parser in 2022:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser/releases/tag/v0.7.0&quot;&gt;v0.7.0&lt;/a&gt; added support for filtering messages by many fields, and allowing reading from uncompressed files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser/releases/tag/v0.7.1&quot;&gt;v0.7.1&lt;/a&gt; added better examples of parallel MRT files processing with &lt;code&gt;rayon&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser/releases/tag/v0.7.2&quot;&gt;v0.7.2&lt;/a&gt; added filtering by multiple &lt;code&gt;peer_ip&lt;/code&gt;s.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser/releases/tag/v0.7.1&quot;&gt;v0.8.0&lt;/a&gt; includes many internal refactoring and brought in the new &lt;a href=&quot;https://github.com/bgpkit/oneio&quot;&gt;oneio&lt;/a&gt; library to improve developer experience.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;BGPKIT Broker&lt;/h2&gt;
&lt;p&gt;In 2022, we have revised the BGPKIT Broker backend to support crawling for estimated file sizes in addition to other fields like timestamps and URLs. This allows us to keep track of MRT file size changes and help users to pick suitable collectors to use, especially at the age where RIB dumps from a single collector could reach over 1 GB in size.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/rib-sizes-chart.Dc17U3jx.png&quot; alt=&quot;2022 Year in Review&quot;&gt;
&lt;p&gt;Figure of RIB sizes over time. Blue line is for rrc00, while yellow line is for route-views2.&lt;/p&gt;
&lt;p&gt;We also made major revisions to our Rust SDK for more features like &lt;code&gt;.latest()&lt;/code&gt; to get the lastest MRT files from each collector.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-broker/releases/tag/v0.5.0&quot;&gt;https://github.com/bgpkit/bgpkit-broker/releases/tag/v0.5.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Plus, if you would like to deploy a broker instance yourself, we also have made some significant efforts to improve our documentation on self-hosting guide.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-broker-backend/blob/main/deployment/README.md&quot;&gt;https://github.com/bgpkit/bgpkit-broker-backend/blob/main/deployment/README.md&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Python Bindings&lt;/h2&gt;
&lt;p&gt;In addition to core Rust code base for parser and broker, we have also added support for Python bindings for various Rust SDKs. Users can easily parse MRT files directly using &lt;code&gt;pybgpkit&lt;/code&gt; Python library. It is also proven to be usable on cloud-based Jupyter notebooks like Google Colab (&lt;a href=&quot;https://colab.research.google.com/drive/1AuNnzT43LYAZNnp1muhJTy0rvv3qbCX6&quot;&gt;examples&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/pybgpkit&quot;&gt;https://github.com/bgpkit/pybgpkit&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;monocle&lt;/h2&gt;
&lt;p&gt;To ties things together, we have also developed our first investigative tool, &lt;code&gt;monocle&lt;/code&gt; , to help users to quick find relevant BGP announcements with a suite of easy-to-use utilities. Users with Rust toolchain installed can run &lt;code&gt;cargo install monocle&lt;/code&gt; to install the tool.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/monocle&quot;&gt;https://github.com/bgpkit/monocle&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Users can use the following subcommands&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;parse&lt;/code&gt;: parse single MRT files, remotely or locally&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;search&lt;/code&gt;: find and filter BGP messages accross multiple public collectors&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;time&lt;/code&gt;: convert between local time string and Unix timestamps&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;whois&lt;/code&gt;: find out AS names, ASN, registration countries, and organizations.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Web/API and Infrastructure&lt;/h2&gt;
&lt;p&gt;In 2022, we started to experiment new cloud-based infrastructure, especially cloud-based databases for better API stability and developer experiences. We ended up selecting &lt;a href=&quot;https://supabase.com/&quot;&gt;Supabase&lt;/a&gt; as our PostgreSQL production host and a self-hosted instance for backup. We are happy with the performance and cost provided by Supabase and more exicted about the potential capability it brings such as user authentication, cloud storage, local dev schema changes, etc.&lt;/p&gt;
&lt;p&gt;Based on the new infrastructure, we have started to test a new integrated API system (&lt;a href=&quot;https://alpha.api.bgpkit.com/docs/&quot;&gt;still in alpha&lt;/a&gt;). This allows us to put all of our data access and processing end-points into one location. Based on the new API, we have also developed a newer version of the BGPKIT Broker statistics page: &lt;a href=&quot;https://alpha.stats.bgpkit.com/&quot;&gt;https://alpha.stats.bgpkit.com/&lt;/a&gt;&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/alpha-api-stats.B5n3591r.png&quot; alt=&quot;2022 Year in Review&quot;&gt;
&lt;h2&gt;New Datasets&lt;/h2&gt;
&lt;p&gt;Apart from provide SDKs and data APIs, we have also started providing free access to historical archives of some data that we find interesting. We blogged about one dataset previous, the peer-stats dataset:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bgpkit.com/blog/introducing-peer-stats-dataset&quot;&gt;/blog/introducing-peer-stats-dataset&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here is a list of our currently available datasets at &lt;a href=&quot;https://data.bgpkit.com/&quot;&gt;https://data.bgpkit.com/&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[peer-stats](https://data.bgpkit.com/peer-stats/)&lt;/code&gt;: route collector peers statistics (IP, ASN, v4/v6 prefixes counts)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[as2rel](https://data.bgpkit.com/as2rel/)&lt;/code&gt;: AS-level relationship, using all available collectors&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[pfx2as](https://data.bgpkit.com/pfx2as/)&lt;/code&gt;: prefix-to-AS mapping, using all available collectors&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[ihr-hegemony](https://data.bgpkit.com/ihr/hegemony/ipv4/global/)&lt;/code&gt;: mirror of &lt;a href=&quot;https://ihr.iijlab.net/ihr/en-us&quot;&gt;IIJ-IHR&lt;/a&gt;&apos;s global hegemony score dataset (big shout out to &lt;a href=&quot;https://twitter.com/romain_fontugne&quot;&gt;Romain&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/ihr_alerts&quot;&gt;Internet Health Report&lt;/a&gt; for producing this data)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All the above datasets are free to use for research or commercial usages, and here is the &lt;a href=&quot;https://bgpkit.com/aua&quot;&gt;acceptable usage agreement&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;More Public Repositories&lt;/h2&gt;
&lt;p&gt;There are more open code repositories set available, some experimental and some for data analysis. You can check out the full list here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/orgs/bgpkit/repositories&quot;&gt;https://github.com/orgs/bgpkit/repositories&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Founder&apos;s Notes&lt;/h2&gt;
&lt;p&gt;In later 2022, I have joined Cloudflare to continue working on routing security for public benefits. During the first few months, we have built and shipped our &lt;a href=&quot;https://blog.cloudflare.com/route-leak-detection-with-cloudflare-radar/&quot;&gt;new route-leak detection system&lt;/a&gt; under the public &lt;a href=&quot;https://blog.cloudflare.com/route-leak-detection-with-cloudflare-radar/&quot;&gt;Radar&lt;/a&gt; platform. BGPKIT suite is now used in production for the BGP data anaysis pipeline at Cloudflare. While working full-time now, I am still committed to maintain the software suite and bringing new features to BGPKIT. For example, this year, I ported back our Kafka support used in Cloudflare to BGPKIT Broker backend. Folks at Cloudflare are doing great things in the open-source realm, and BGPKIT software suite will continue to become more useful to BGP enthuesastics and remain completely open-source.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/cloudflare-route-leak.B-cDL-17.png&quot; alt=&quot;2022 Year in Review&quot;&gt;
&lt;p&gt;Quote from Cloudflare&apos;s &lt;a href=&quot;https://blog.cloudflare.com/route-leak-detection-with-cloudflare-radar&quot;&gt;route-leak detection system blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Looking at 2023, here are a few things that I am really excited to work on for BGPKIT suite:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;continue improve the infrastructure of the system and adding new data processing pipelines&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;continue improve the parser&apos;s performance and reliability&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;adding new RFC supports to parser (e.g. &lt;a href=&quot;https://datatracker.ietf.org/doc/rfc9234/&quot;&gt;RFC9234&lt;/a&gt; for route-leak prevention)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;productionizing the new API and stats website&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;write more examples and documentations (don&apos;t we all love that)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And yeah, we will continue to be open-source first!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bgpkit.com&quot;&gt;&lt;img src=&quot;https://bgpkit.com/_astro/bgpkit-website.Btyt9Ekl.png&quot; alt=&quot;2022 Year in Review&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you like what we do here, please consider subscribe to our blog. For all code repositories, check out our GitHub page.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit&quot;&gt;https://github.com/bgpkit&lt;/a&gt;&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>Introducing Peer-Stats Dataset</title><link>https://bgpkit.com/blog/introducing-peer-stats-dataset/</link><guid isPermaLink="true">https://bgpkit.com/blog/introducing-peer-stats-dataset/</guid><description>Public BGP data collector projects like RouteViews and RIPE RIS provide valuable research and operational information for understanding BGP and detecting Intern...</description><pubDate>Mon, 16 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Public BGP data collector projects like RouteViews and RIPE RIS provide valuable research and operational information for understanding BGP and detecting Internet routing anomalies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;There are many BGP routers involved in BGP collection projects.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A project includes many &quot;collectors,&quot; and each serves as a collection of messages from several active BGP peers from different networks. Some bigger collectors collect BGP data from more than one hundred BGP routers. For example, RIPE RIS &lt;code&gt;rrc00&lt;/code&gt;   has 112 active BGP peers at writing. &lt;em&gt;(To learn about the complete list of BGP peers from all collectors, try the&lt;/em&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-labs/tree/main/collector-peers&quot;&gt;&lt;em&gt;experimental tools&lt;/em&gt;&lt;/a&gt;&lt;em&gt;we developed.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sometimes, too many BGP peers may become problematic.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Not all peers present the same amount of data. Some peers are so-called &quot;full-feed&quot; peers, which are the ones that provide the full routing tables to the collector. In a routing table dump file from the collectors, we can observe the full table of these peers. Some peers, however, only provide a limited number of routing entries to the collectors, not representing the whole routing status from these peers. In a project that tries to rebuild full routing tables, e.g., some BGP hijack detection or anomaly detectors, people prefer to use the full-feed peers as their data source.&lt;/p&gt;
&lt;p&gt;At times, we are only interested in data from certain peers. For example, when studying the routing data from a particular network, if the network connects to BGP data collectors, we can directly pull data from the collectors&apos; data. However, it can be troublesome to learn about what collectors have data from certain peers. RIPE RIS provides a &lt;a href=&quot;https://stat.ripe.net/data/ris-peers/data.json&quot;&gt;nice API&lt;/a&gt; for querying such info, but we couldn&apos;t find one for RouteViews.&lt;/p&gt;
&lt;p&gt;Historical data for such information is also missing. Unfortunately, for the researchers who want to study the evolution of the data collectors, even RIPE RIS&apos;s peers API could not help with that.&lt;/p&gt;
&lt;h2&gt;Introducing BGPKIT Peer-Stats Dataset&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Peer-Stats&lt;/code&gt; dataset is a publicly available, free-to-use dataset that aims to provide daily collector peer information for all RouteViews and RIPE RIS collectors for ten years.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://data.bgpkit.com/peer-stats/&quot;&gt;https://data.bgpkit.com/peer-stats/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The data includes the following fields for each peer of a BGP collector:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;asn&lt;/code&gt;: Autonomous System Number of the collector peer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ip&lt;/code&gt;: the IP address of the collector peer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;num_v4_pfxs&lt;/code&gt;: the number of IPv4 prefixes propagated from the collector peer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;num_v6_pfx&lt;/code&gt;s: the number of IPv6 prefixes propagated from the collector peer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;num_connected_asns&lt;/code&gt;: the number of connected (immediate next hop) ASes from the collector peer&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The dataset is organized by the following structure.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;- collector
	- year
		- month
			- data files
&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/dataset-listing-1.Cr1ixJRg.png&quot; alt=&quot;Introducing Peer-Stats Dataset&quot;&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/dataset-listing-2.DUTCb5_q.png&quot; alt=&quot;Introducing Peer-Stats Dataset&quot;&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/dataset-listing-3.DP6D80fo.png&quot; alt=&quot;Introducing Peer-Stats Dataset&quot;&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/dataset-listing-4.C76KweMC.png&quot; alt=&quot;Introducing Peer-Stats Dataset&quot;&gt;
&lt;p&gt;Screenshots of the dataset file listing site.&lt;/p&gt;
&lt;p&gt;Each data file is in JSON format (see the section below) and compressed with bzip2. Users can easily use tools like &lt;code&gt;bzcat&lt;/code&gt; and &lt;code&gt;jq&lt;/code&gt; to view the data files. For example, you can run the following command to quickly view any of the peer-stats data for the collector &lt;code&gt;rrc00&lt;/code&gt; on 2022-05-01.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl &quot;https://data.bgpkit.com/peer-stats/rrc00/2022/05/rrc00-2022-05-01-1651363200.bz2&quot; --silent | bzcat | jq
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;collector&quot;: &quot;rrc00&quot;,
  &quot;peers&quot;: {
    &quot;102.67.56.1&quot;: {
      &quot;asn&quot;: 328474,
      &quot;ip&quot;: &quot;102.67.56.1&quot;,
      &quot;num_connected_asns&quot;: 330,
      &quot;num_v4_pfxs&quot;: 919443,
      &quot;num_v6_pfxs&quot;: 0
    },
    &quot;103.102.5.1&quot;: {
      &quot;asn&quot;: 131477,
      &quot;ip&quot;: &quot;103.102.5.1&quot;,
      &quot;num_connected_asns&quot;: 184,
      &quot;num_v4_pfxs&quot;: 895482,
      &quot;num_v6_pfxs&quot;: 0
    },
...
&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/dataset-listing-5.D-gdUTTB.png&quot; alt=&quot;Introducing Peer-Stats Dataset&quot;&gt;
&lt;p&gt;Because all the data files are generated against the midnight UTC RIB dump of the day, you can also easily construct a URL to a data file for any particular date using the following template.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://data.bgpkit.com/peer-stats/{COLLECTOR}/{YEAR}/{MONTH}/{COLLECTOR}-{YEAR}-{MONTH}-{DAY}-{MIDNIGHT_TIMESTAMP}.bz2&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Open-source&lt;/h2&gt;
&lt;p&gt;We also open-sourced the data collection command-line tool source code on GitHub. Feel free to check it out and run it on your infrastructure if needed.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/peer-stats&quot;&gt;https://github.com/bgpkit/peer-stats&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Credits and Sponsorship&lt;/h2&gt;
&lt;p&gt;The original idea for this work came from our extensive discussion with Romain Fontugne (follow him on Twitter at &lt;a href=&quot;https://twitter.com/romain_fontugne&quot;&gt;@romain_fontugne&lt;/a&gt;) from IIJ. This work is made possible by IIJ&apos;s generous sponsorship.&lt;/p&gt;
&lt;p&gt;Please consider sponsoring us on GitHub if you find our work valuable and would like to see more open-source code and datasets on BGP.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sponsors/bgpkit&quot;&gt;https://github.com/sponsors/bgpkit&lt;/a&gt;&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>KhersonTelecom Outage and Connectivity Change</title><link>https://bgpkit.com/blog/2022-05-01-khersontelecom-connectivity-change/</link><guid isPermaLink="true">https://bgpkit.com/blog/2022-05-01-khersontelecom-connectivity-change/</guid><description>KhersonTelecom service outage and upstream provider change during the Ukraine-Russia conflict in April 2022.</description><pubDate>Tue, 03 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/tweet-outage-announcement.BvZddyRq.png&quot; alt=&quot;KhersonTelecom Outage and Connectivity Change&quot;&gt;
&lt;p&gt;Internet service in Russian-occupied Kherson, Ukraine was disabled at 16:12 UTC (6:12pm local) on Saturday, 30 April. &lt;a href=&quot;https://twitter.com/hashtag/UkraineRussiaWar?src=hash&amp;#x26;ref_src=twsrc%5Etfw&quot;&gt;#UkraineRussiaWar&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Khersontelecom service was restored ~24hrs later via Russian transit from nearby Crimea. &lt;a href=&quot;https://t.co/uN31jLrzEc&quot;&gt;pic.twitter.com/uN31jLrzEc&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;— Doug Madory (@DougMadory) &lt;a href=&quot;https://twitter.com/DougMadory/status/1521102562509873152?ref_src=twsrc%5Etfw&quot;&gt;May 2, 2022&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;AS47598&lt;/code&gt; experienced an outage shortly after &lt;code&gt;2022-04-30T16:10:00&lt;/code&gt; and then resumed connectivity &lt;code&gt;2022-05-01T16:15:00&lt;/code&gt; (both UTC time). After the outage, the &lt;code&gt;AS47598&lt;/code&gt; is then connected via a different upstream provider, &lt;code&gt;AS201776&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The upstream provide change can also be seen on IIJ&apos;s &lt;a href=&quot;https://ihr.iijlab.net/ihr/en-us/networks/AS47598?af=4&amp;#x26;last=3&amp;#x26;date=2022-05-01&amp;#x26;rov_tb=routes&quot;&gt;Internet Health Report&lt;/a&gt;.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/iij-internet-health-report.BU8PF1Sb.png&quot; alt=&quot;KhersonTelecom Outage and Connectivity Change&quot;&gt;
&lt;h2&gt;Prefixes&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;AS47598&lt;/code&gt; announces only one prefix &lt;code&gt;91.206.110.0/23&lt;/code&gt; (data from &lt;a href=&quot;https://bgp.he.net/AS47598#_prefixes&quot;&gt;Hurricane Electric&lt;/a&gt;). Most of the BGP announcements are for this prefix. However, after the provider change happened, there were also a IPv6 prefix announcements for this V6 prefix as well &lt;code&gt;5bce:6e00::/23&lt;/code&gt;. It is possible that this prefix is a V4-translated prefix propagated to a V6 collector peer, but we do not know for sure.&lt;/p&gt;
&lt;p&gt;We can also confirm the outage of the prefix with RIPEstat’s &lt;a href=&quot;https://stat.ripe.net/widget/routing-history#w.resource=91.206.110.0/23&amp;#x26;w.starttime=2022-04-25T00:00:00&amp;#x26;w.endtime=2022-05-02T00:00:00&quot;&gt;Routing History data widget&lt;/a&gt;. (uncheck the &lt;code&gt;No low visibility&lt;/code&gt; box to reveal the outage).&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/ripestat-routing-history.BNkMrZur.png&quot; alt=&quot;KhersonTelecom Outage and Connectivity Change&quot;&gt;
&lt;h2&gt;BGP Messages&lt;/h2&gt;
&lt;p&gt;We can visualize the overall BGP announcements volume with &lt;a href=&quot;https://radar.cloudflare.com/asn/47598?date_filter=last_7_days&quot;&gt;Cloudflare Radar’s AS-level page&lt;/a&gt;.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/cloudflare-radar-bgp.C8p3hu1S.png&quot; alt=&quot;KhersonTelecom Outage and Connectivity Change&quot;&gt;
&lt;p&gt;The following are the UTC timestamps for the corresponding BGP message spikes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;2022-04-30T16:10:00&lt;/code&gt;: announcements with old provider &lt;code&gt;12883&lt;/code&gt; in the paths.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2022-05-01T16:00:00&lt;/code&gt;: announcements with the new provider &lt;code&gt;201776&lt;/code&gt; in the paths&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2022-05-03T10:45:00&lt;/code&gt;: similar announcements with a new provider in the paths.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;During the first gap time (2022-04-30T16:15:00 to 2022-05-01T16:00:00), there were 0 BGP updates for the prefix or from the ASN.&lt;/p&gt;
&lt;p&gt;The old provider paths look like this where &lt;code&gt;AS12883&lt;/code&gt; is the next hop for &lt;code&gt;AS47598&lt;/code&gt;. See the full list of messages from &lt;code&gt;rrc00&lt;/code&gt; here: &lt;a href=&quot;https://gist.github.com/digizeph/c58b77f755d7fec8a7969807fb17d5ba&quot;&gt;https://gist.github.com/digizeph/c58b77f755d7fec8a7969807fb17d5ba&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;207564 56655 3257 12883 47598
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new provider paths look like this where &lt;code&gt;AS12389&lt;/code&gt; and &lt;code&gt;AS201776&lt;/code&gt; are the next hops for &lt;code&gt;AS47598&lt;/code&gt;. See the full list of messages from &lt;code&gt;rrc00&lt;/code&gt; here: &lt;a href=&quot;https://gist.github.com/digizeph/896a4a7e4de23082b496b92ab5bdab5b&quot;&gt;https://gist.github.com/digizeph/896a4a7e4de23082b496b92ab5bdab5b&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;207564 28824 28824 1299 12389 201776 47598
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;BGP Data Tooling&lt;/h2&gt;
&lt;p&gt;The analysis is done using a privately hosted open-source BGPKIT parser web API. You can host it on your infrastructure, and the source code is freely available at &lt;a href=&quot;https://github.com/bgpkit/pybgpkit-api&quot;&gt;https://github.com/bgpkit/pybgpkit-api&lt;/a&gt;. Comments and feedback are welcome!&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Update on 2022-05-04T09:20:00 Pacific&lt;/h3&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/tweet-connectivity-update.Nvge0DRJ.png&quot; alt=&quot;KhersonTelecom Outage and Connectivity Change&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/hashtag/Kherson?src=hash&amp;#x26;ref_src=twsrc%5Etfw&quot;&gt;#Kherson&lt;/a&gt; — Internet connectivity is returning to the occupied city in South of &lt;a href=&quot;https://twitter.com/hashtag/Ukraine?src=hash&amp;#x26;ref_src=twsrc%5Etfw&quot;&gt;#Ukraine&lt;/a&gt;, after an outage since Saturday. &lt;a href=&quot;https://twitter.com/Cloudflare?ref_src=twsrc%5Etfw&quot;&gt;@Cloudflare&lt;/a&gt; data shows growth in requests since 04:15 UTC, and telecom connection was confirmed by the Ukrainian Vice PM &lt;a href=&quot;https://twitter.com/FedorovMykhailo?ref_src=twsrc%5Etfw&quot;&gt;@FedorovMykhailo&lt;/a&gt;. &lt;a href=&quot;https://t.co/JxT3kcM234&quot;&gt;pic.twitter.com/JxT3kcM234&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;— Cloudflare Radar (@CloudflareRadar) &lt;a href=&quot;https://twitter.com/CloudflareRadar/status/1521812037055176705?ref_src=twsrc%5Etfw&quot;&gt;May 4, 2022&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The upstreams for &lt;code&gt;AS47598&lt;/code&gt; has reverted back to the original ASes, and the traffic has started coming back to normal.&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>Parallel MRT Files Parsing with BGPKIT</title><link>https://bgpkit.com/blog/parallel-mrt-files-parsing-with-bgpkit/</link><guid isPermaLink="true">https://bgpkit.com/blog/parallel-mrt-files-parsing-with-bgpkit/</guid><description>In this post, we will talk about how to implement a Rust workflow that can process a large number of BGP data files as fast as we can. We will use BGPKIT Parser...</description><pubDate>Wed, 16 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this post, we will talk about how to implement a Rust workflow that can process a &lt;strong&gt;large number&lt;/strong&gt; of BGP data files &lt;strong&gt;as fast as we can&lt;/strong&gt;. We will use BGPKIT &lt;a href=&quot;https://bgpkit.com/parser&quot;&gt;Parser&lt;/a&gt; and &lt;a href=&quot;https://bgpkit.com/broker&quot;&gt;Broker&lt;/a&gt; for data collection and parsing, and &lt;a href=&quot;https://github.com/rayon-rs/rayon&quot;&gt;Rayon&lt;/a&gt; crate for parallelization of the code.&lt;/p&gt;
&lt;h2&gt;Task Overview&lt;/h2&gt;
&lt;p&gt;Before we begin to talk about the code design, we first need to introduce the data we are dealing with. We want to process the BGP data collected by various collectors, saved in compressed binary MRT format, and archived to files with a fixed interval. In this post, we are using &lt;a href=&quot;https://archive.routeviews.org/&quot;&gt;RouteViews&lt;/a&gt; archive data as an example. The average data file size ranges from 2MB to 10MB by different collectors (&lt;a href=&quot;https://archive.routeviews.org/route-views.amsix/bgpdata/2021.10/UPDATES/&quot;&gt;AMSIX collector&lt;/a&gt; for example has pretty large files).&lt;/p&gt;
&lt;p&gt;For processing, we can use the simplest task possible to do: &lt;em&gt;sum the number of MRT records in all the files&lt;/em&gt;. We want to download and process all the updates files for one hour from all the collectors in RouteViews project. Here is the estimated amount of data we are dealing with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;35 collectors&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;5-minute interval — 12 files per collector&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;420 total number of files&lt;/strong&gt; to download and process&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;840MB to 4.2GB total download size&lt;/strong&gt; (it’s somewhere in between)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ok! Now that we know what we want to do and have a sense of the estimated workload of the overall task, let’s coding!&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/header-parallel.CKcRAIk8.jpeg&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Photo by &lt;a href=&quot;https://unsplash.com/@glenncarstenspeters?utm_source=medium&amp;#x26;utm_medium=referral&quot;&gt;Glenn Carstens-Peters&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=medium&amp;#x26;utm_medium=referral&quot;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. Sequential Parsing&lt;/h2&gt;
&lt;p&gt;Our first attempt to achieve the goal is to design and implement a naive sequential workflow as described below&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;find all BGP updates files within the hour of interest&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iterate through each file, parse the MRT data and count the number of records&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;sum all record counts and print out the result&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For this sequential workflow, we will need to pull in two dependencies into &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[dependencies]
bgpkit-parser = &quot;0.7.2&quot;
bgpkit-broker = &quot;0.3.2&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;bgpkit-broker&lt;/code&gt; handles looking for updates files within the hour, while &lt;code&gt;bgpkit-parser&lt;/code&gt; handles parsing each individual file.&lt;/p&gt;
&lt;h3&gt;Finding files&lt;/h3&gt;
&lt;p&gt;BGPKIT Broker indexes all available BGP MRT data archive files from both RouteViews and RIPE RIS in close-to-real-time. For each data file, it saves the following information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;project&lt;/code&gt;: &lt;code&gt;route-views&lt;/code&gt; or &lt;code&gt;riperis&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;collector&lt;/code&gt;: the collector ID, e.g. &lt;code&gt;rrc00&lt;/code&gt; or &lt;code&gt;route-views2&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;url&lt;/code&gt;: the URL to the corresponding MRT file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;timestamp&lt;/code&gt;: the UNIX time of the &lt;em&gt;start time&lt;/em&gt; of the MRT data file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With all this information indexed, we can then query the backend and retrieve files information as we want. BGPKIT Broker provides both &lt;a href=&quot;https://docs.broker.bgpkit.com&quot;&gt;RESTful API&lt;/a&gt;, &lt;a href=&quot;https://github.com/bgpkit/bgpkit-broker&quot;&gt;Rust API&lt;/a&gt;, as well as a &lt;a href=&quot;https://pypi.org/project/pybgpkit/&quot;&gt;Python API&lt;/a&gt;. Here we use the Rust API to pull in the information we need:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let broker = BgpkitBroker::new_with_params(
    &quot;https://api.broker.bgpkit.com/v1&quot;,
    QueryParams {
        start_ts: Some(1640995200),
        end_ts: Some(1640998799),
        project: Some(&quot;route-views&quot;.to_string()),
        data_type: Some(&quot;update&quot;.to_string()),
        ..Default::default()
    });

for item in &amp;#x26;broker {
    println!(&quot;processing {:?}...&quot;, &amp;#x26;item);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above block queries the broker and prints out all information of the retrieved files&apos; metadata. The &lt;code&gt;BgpkitBroker::new_with_params&lt;/code&gt; call accepts two parameters, one for the endpoint of the broker instance, and the other specifies the filtering criteria. In this example, we search for all BGP updates files from RouteViews with timestamps between &lt;code&gt;2022-01-01T00:00:00&lt;/code&gt; and &lt;code&gt;2022-01-01T00:59:59&lt;/code&gt; UTC. It prints out the output as the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;processing BrokerItem { collector_id: &quot;route-views.telxatl&quot;, timestamp: 1640997000, data_type: &quot;update&quot;, url: &quot;https://archive.routeviews.org/route-views.telxatl/bgpdata/2022.01/UPDATES/updates.20220101.0030.bz2&quot; }...
processing BrokerItem { collector_id: &quot;route-views.uaeix&quot;, timestamp: 1640997000, data_type: &quot;update&quot;, url: &quot;https://archive.routeviews.org/route-views.uaeix/bgpdata/2022.01/UPDATES/updates.20220101.0030.bz2&quot; }...
processing BrokerItem { collector_id: &quot;route-views.wide&quot;, timestamp: 1640997000, data_type: &quot;update&quot;, url: &quot;https://archive.routeviews.org/route-views.wide/bgpdata/2022.01/UPDATES/updates.20220101.0030.bz2&quot; }...
processing BrokerItem { collector_id: &quot;route-views2&quot;, timestamp: 1640997900, data_type: &quot;update&quot;, url: &quot;https://archive.routeviews.org/bgpdata/2022.01/UPDATES/updates.20220101.0045.bz2&quot; }...
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Parse each MRT file&lt;/h3&gt;
&lt;p&gt;Previously, in the for loop, we only print out the retrieved meta information of the MRT files. Now let&apos;s add the actual parsing of the files into the loop. The code is very simple, as designed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let mut sum: usize = 0;
for item in &amp;#x26;broker {
    println!(&quot;processing {}...&quot;, &amp;#x26;item.url);
    let parser = BgpkitParser::new(&amp;#x26;item.url).unwrap();
    let count = parser.into_record_iter().count();
    sum += count;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first define a mutable variable &lt;code&gt;sum&lt;/code&gt; outside the loop. Then for each file, we create a new Parser instance by &lt;code&gt;BgpkitParser::_new_(&amp;#x26;item.url)&lt;/code&gt;. Here, as our goal is to count the number of records, we call the parser&apos;s &lt;code&gt;.into_record_iter()&lt;/code&gt; function to create a iterator over the records of the file, and then &lt;code&gt;.count()&lt;/code&gt; to get the count of the records. Lastly, we add the count to the overall sum variable.&lt;/p&gt;
&lt;h3&gt;Run and timing&lt;/h3&gt;
&lt;p&gt;For testing, I use a fairly powerful VM on a host with AMD 3950x CPU (32 threads), then build the release build and time the release run. The runtime includes downloading the MRT files to my machine with 1Gpbs down link in Southern California.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cargo build --release
time cargo run --release --bin sequential
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It ended up taking about &lt;strong&gt;1 minute and 23 seconds&lt;/strong&gt; to sequentially parse 144 MRT files from RouteViews for all available ones within the first hour of 2022 (UTC).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;total number of records for 144 files is 10554212

real    1m23.081s
user    0m39.535s
sys     0m1.006s
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. Parallel Parsing&lt;/h2&gt;
&lt;p&gt;Since the parsing of each file is completely independent of each other, we can parse the files in parallel and then sum up the count for each thread at the end. In Rust with Rayon, this conversion is very simple.&lt;/p&gt;
&lt;p&gt;Let&apos;s first add the dependency of Rayon first:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[dependencies]
bgpkit-parser = &quot;0.7.2&quot;
bgpkit-broker = &quot;0.3.2&quot;
rayon = &quot;1.5.1&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we change the broker code one tiny bit to collect all meta information for files into a vector first.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let items = broker.into_iter().collect::&amp;#x3C;Vec&amp;#x3C;BrokerItem&gt;&gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would enable us to fully utilize &lt;code&gt;rayon&lt;/code&gt;&apos;s great syntax sugar to turn our sequential code into a parallel one.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let sum: usize = items.par_iter().map(|item| {
    println!(&quot;processing {}...&quot;, &amp;#x26;item.url);
    let parser = BgpkitParser::new(&amp;#x26;item.url).unwrap();
    let count = parser.into_record_iter().count();
    count
}).sum();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key difference here is the calling of &lt;code&gt;.par_iter()&lt;/code&gt;. It turns a sequential iterator into a parallel iterator, and by default going to utilize all available cores on the host machine for scheduling. Then we call &lt;code&gt;.map()&lt;/code&gt; to define the parsing steps for each file, and then call &lt;code&gt;.sum()&lt;/code&gt; at the end to add all results up.&lt;/p&gt;
&lt;p&gt;The final result is approximately &lt;strong&gt;10x faster&lt;/strong&gt; than the sequential version, and it took only &lt;strong&gt;8 seconds&lt;/strong&gt; to parse all MRT files and get the record counts.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;total number of records is 10554212

real    0m8.086s
user    0m42.569s
sys     0m1.068s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The full code for this example is as follows:&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The source code of the two examples is available on GitHub. Feel free to poke around and tweak it as you wish.&lt;/p&gt;
&lt;p&gt;%[&lt;a href=&quot;https://gist.github.com/digizeph/c23ba39968c6cb4e1ad323520540010f#file-parallel-parsing-rs&quot;&gt;https://gist.github.com/digizeph/c23ba39968c6cb4e1ad323520540010f#file-parallel-parsing-rs&lt;/a&gt;]&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-tutorials/tree/main/parallel-parsing&quot;&gt;https://github.com/bgpkit/bgpkit-tutorials/tree/main/parallel-parsing&lt;/a&gt;&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>Real-time RIS Live Data with BGPKIT Parser</title><link>https://bgpkit.com/blog/real-time-bgp-data-processing-2-ris-live/</link><guid isPermaLink="true">https://bgpkit.com/blog/real-time-bgp-data-processing-2-ris-live/</guid><description>In terms of real-time BGP data processing, RIPE NCC provides a great data source: Routing Information Service Live (RIS Live).</description><pubDate>Fri, 12 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In terms of real-time BGP data processing, &lt;a href=&quot;https://www.ripe.net/&quot;&gt;RIPE NCC&lt;/a&gt; provides a great data source: &lt;a href=&quot;https://ris-live.ripe.net/&quot;&gt;Routing Information Service Live (RIS Live)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To begin with, here is what &lt;a href=&quot;https://ris-live.ripe.net/&quot;&gt;RIS Live&lt;/a&gt; by the creators:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;RIS Live is a feed that offers BGP messages in real-time. It collects information from the RIS Route Collectors (RRCs) and uses a WebSocket JSON API to monitor and detect routing events around the world. A non-interactive full stream (“firehose”) is also available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In essence, RIS Live provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;a WebSocket interface to stream BGP messages in real-time&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ability to subscribe to “sub-streams” with custom filtering messages&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;JSON-encoded BGP messages as the stream payload&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;“firehose” HTTPS stream interface as well, without needing to work with websocket.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this post, we will discuss how to use the RIS Live stream in practice.&lt;/p&gt;
&lt;h2&gt;RIS Live Message Format&lt;/h2&gt;
&lt;p&gt;RIS Live has &lt;em&gt;client messages&lt;/em&gt; and &lt;em&gt;server messages&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The client messages is used to setup or dismantle “subscriptions”, which essentially tell the server what kind of BGP messages a client would like to receive, and allow the server to send only the interested messages to the client.&lt;/p&gt;
&lt;p&gt;A server acknowledges the requests from the client and afterwards start streaming requested data back to the client. At a high-level, a server sends either &lt;code&gt;ris_message&lt;/code&gt; or &lt;code&gt;ris_error&lt;/code&gt; messages. The &lt;code&gt;ris_message&lt;/code&gt; is the main payload that we are interested in, while the &lt;code&gt;ris_error&lt;/code&gt; message provides debugging messages for the scenarios where stream or subscription fails.&lt;/p&gt;
&lt;h2&gt;Subscribe to a WebSocket Stream&lt;/h2&gt;
&lt;p&gt;RIS Live provides great flexibility for the clients to specify/narrowdown the interested messages, allowing both the server and client process less messages during a streaming session.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;host&lt;/code&gt;: only messages collected from a particular RRC (e.g. &lt;code&gt;rrc21&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;type&lt;/code&gt;: only messages of a given type, e.g. &lt;code&gt;UPDATE&lt;/code&gt; , &lt;code&gt;OPEN&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;require&lt;/code&gt; : only messages containing a given key, e.g. &lt;code&gt;withdrawals&lt;/code&gt; will return only message that contains any withdrawn prefixes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;peer&lt;/code&gt;: messages from a particular BGP peer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;path&lt;/code&gt; : ASN or pattern to match the AS Path attribute in BGP update messages&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;prefix&lt;/code&gt;: only messages containing information for a given prefix&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;moreSpecific&lt;/code&gt; and &lt;code&gt;lessSpecific&lt;/code&gt;: only messages that are the subprefix or super-prefix of the specified prefix&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;includeRaw&lt;/code&gt;: whether to include the Base64-encoded RAW BGP messages&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As an example, let’s take a look at the following message from the official manual:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;host&quot;: &quot;rrc01&quot;,
  &quot;type&quot;: &quot;UPDATE&quot;,
  &quot;require&quot;: &quot;announcements&quot;,
  &quot;path&quot;: &quot;64496,64497$&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example subscription message composer on &lt;a href=&quot;https://ris-live.ripe.net/&quot;&gt;RIS Live official site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As an example, let’s take a look at the following message from the official manual:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;host&quot;: &quot;rrc01&quot;,
  &quot;type&quot;: &quot;UPDATE&quot;,
  &quot;require&quot;: &quot;announcements&quot;,
  &quot;path&quot;: &quot;64496,64497$&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;collected by &lt;code&gt;rrc01&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;BGP UPDATE messages&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;have at least one announced prefix&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the last two hops of the AS Path is 66496 and 64497 (the origin)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;ris_message&lt;/code&gt; consists of “common header” fields and “data” fields (although they’re on the same level).&lt;/p&gt;
&lt;p&gt;The “common header” fields are present for all types of sub-type messages, including &lt;code&gt;timestamp&lt;/code&gt;, &lt;code&gt;peer&lt;/code&gt;, &lt;code&gt;peer_asn&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;host&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt; . The rest of the fields are the data fields that are dependent on the type of the messages. For most people, the &lt;code&gt;UPDATE&lt;/code&gt; message is what they need. The following JSON block is an example message pulled directly from the demo site.&lt;/p&gt;
&lt;p&gt;Example JSON formatted RIS message:&lt;/p&gt;
&lt;p&gt;This example shows a BGP announcement of AS132354 originating two prefixes &lt;code&gt;103.249.208.0/23&lt;/code&gt; and &lt;code&gt;103.14.184.0/24&lt;/code&gt; , with the next hop to be &lt;code&gt;37.49.237.228&lt;/code&gt;. At this point, the information we see here is pretty similar to what we can see from other BGP MRT reader’s output (e.g. from &lt;code&gt;bgpdump&lt;/code&gt; or &lt;code&gt;bgpreader&lt;/code&gt;), just in JSON format.&lt;/p&gt;
&lt;h2&gt;WebSocket or Firehose?&lt;/h2&gt;
&lt;p&gt;Provided that RIS Live provides both WebSocket and HTTP Firehose, one would naturally wonder which one is the right choice for their application. Here we have a brief comparison between the two in the context of RIS Live.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WebSocket&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Good:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;easy to customize stream by composing a simple JSON subscribe message&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;work with various toolings in languages like Python and JavaScript&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bad:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;requires extra library dependencies to work with WebSocket&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;need to write somewhat lengthy to get started (comparing to firehose)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Firehose&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Good:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;easy to consume by simply calling GET request on the URL&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;simple single-liner commandline program can start the stream (e.g. a simple &lt;code&gt;curl&lt;/code&gt; call), no need complex script&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bad:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;customizing stream is doable with &lt;code&gt;XRIS-SUBSCRIBE&lt;/code&gt; HTTP request header, but feels clunky and limited&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;in my personal tests, the stream get disconnected often due to the stream cannot keep up with the data producer. this did not happen with websocket tests.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;If your application could afford additional dependencies or writing extra scripts, WebSocket is the better choice. RIS Live official manual also makes implication that the WebSocket format is the current formally-supported streaming method.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;RIS Live Coding Example with BGPKIT Parser&lt;/h2&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/header-coding.DYyAPyqA.png&quot; alt=&quot;Person coding with MacBook Pro&quot;&gt;
&lt;p&gt;Now that we have a basic idea of what is RIS Live and the basic message format, we can get started working on some code that will actually use RIS Live to do something useful.&lt;/p&gt;
&lt;p&gt;In the following example, we will build a short monitoring service that alerts us when Facebook operators announces their DNS IP prefix (see what happened before &lt;a href=&quot;https://blog.cloudflare.com/october-2021-facebook-outage/&quot;&gt;here&lt;/a&gt;). We are going to build the service in Rust with &lt;a href=&quot;https://bgpkit.com/parser&quot;&gt;BGPKIT Parser&lt;/a&gt; , WebSocket library &lt;a href=&quot;https://github.com/snapview/tungstenite-rs&quot;&gt;Tungstenite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, lets collect some basic information about what we are going to monitor here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Facebook’s autonomous system number is 32934. So we will watch for all messages that was originated from AS32934.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Facebook’s DNS server IP prefixes involved in the previous incidence are &lt;code&gt;129.134.30.0/23&lt;/code&gt; and &lt;code&gt;185.89.218.0/23&lt;/code&gt; . So we want to carefully watch these two prefixes in our monitoring system.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We want to use one of the RIPE RIS collectors’ data for monitoring, &lt;code&gt;rrc21&lt;/code&gt; is a good choice since it’s being used by RIS Live’s demonstration. You can easily extend this service by tweaking the subscription message later.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;OK, we are good to go. Let’s do it!&lt;/p&gt;
&lt;h2&gt;Setting up the stream&lt;/h2&gt;
&lt;p&gt;We picked the Tungstenite library as our WebSocket library of choice, partly because it has a very straightforward API design.&lt;/p&gt;
&lt;p&gt;Let’s first connect to the websocket server by calling &lt;code&gt;connect&lt;/code&gt; function given a websocket URL. One thing to notice is that the URL protocol section here is &lt;code&gt;ws&lt;/code&gt; as opposed to the &lt;code&gt;wss&lt;/code&gt; mentioned in the RIS Live documentation. For some reason, Tungstenite does not work with &lt;code&gt;wss&lt;/code&gt; protocol (with SSL).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use tungstenite::{connect, Message}; 
const RIS_LIVE_URL: &amp;#x26;str = &quot;ws://ris-live.ripe.net/v1/ws/?client=rust-bgpkit-parser&quot;;
let (mut socket, _response) =
    connect(Url::parse(RIS_LIVE_URL).unwrap())
    .expect(&quot;Can&apos;t connect to RIS Live websocket server&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, with a socket ready, we will first send a subscription message to let server know that we want some messages and we are ready to receive.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let msg = json!({&quot;type&quot;: &quot;ris_subscribe&quot;, &quot;data&quot;: {&quot;host&quot;: &quot;rrc21&quot;}}).to_string();
socket.write_message(Message::Text(msg)).unwrap();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we composed a simple subscription message that limits the stream to have messages only from &lt;code&gt;rrc21&lt;/code&gt; collector.&lt;/p&gt;
&lt;h2&gt;Parsing JSON messages&lt;/h2&gt;
&lt;p&gt;At this point, we have a WebSocket connection to RIS Live server, and have sent out a subscription message to the server. The server should be sending back messages anytime now, and we are ready to consume the stream.&lt;/p&gt;
&lt;p&gt;We would like to code the following behavior:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;continuously reading the websocket messages;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;parse JSON string into internal BGP structs;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;check each message if it contains origins (withdraw-only messages does not contain AS paths, and thus no origins either;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;if the origin AS is AS32934, and the announced prefix is &lt;code&gt;129.134.30.0/23&lt;/code&gt; or &lt;code&gt;185.89.218.0/23&lt;/code&gt; , then we print out the message to output.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;loop {
    let msg = socket.read_message().expect(&quot;Error reading message&quot;).to_string();
    if letOk(elems) = parse_ris_live_message(msg.as_str()) {
        for elem in elems {
            if letSome(origins) = elem.origin_asns.as_ref() {
                if origins.contains(&amp;#x26;32934) &amp;#x26;&amp;#x26;
                    ( elem.prefix.to_string() ==  &quot;129.134.30.0/23&quot;.to_string() ||
                        elem.prefix.to_string() ==  &quot;185.89.218.0/23&quot;.to_string() )
                {
                    println!(&quot;{}&quot;, elem);
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The full example code can be found here:&lt;/p&gt;
&lt;p&gt;%[&lt;a href=&quot;https://gist.github.com/digizeph/fcac3027555c0b744ea0b3a11197b694#file-bgpkit-parser-kafka-bmp-example-rs&quot;&gt;https://gist.github.com/digizeph/fcac3027555c0b744ea0b3a11197b694#file-bgpkit-parser-kafka-bmp-example-rs&lt;/a&gt;]&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Building More with BGPKIT Tools&lt;/h2&gt;
&lt;p&gt;As introduced in our &lt;a href=&quot;https://bgpkit.com/blog/real-time-bgp-data-processing-1-bmp-and-openbmp&quot;&gt;previous blog post&lt;/a&gt;, we added support of real-time BMP stream to BGPKIT Parser as well. Combining with RIPE RIS Live, and RouteViews BMP stream, we can build a powerful real-time BGP monitoring service directly within BGPKIT Parser. We also offer indexing and processing of historical BGP data as well with &lt;a href=&quot;https://bgpkit.com/broker&quot;&gt;BGPKIT Broker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our goal at BGPKIT is to design, develop, and deploy the most developer-friendly BGP data processing toolkit. To learn more about our offerings, please check out our website and official &lt;a href=&quot;https://twitter.com/bgpkit&quot;&gt;Twitter account&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>Real-time BMP with BGPKIT Parser</title><link>https://bgpkit.com/blog/real-time-bgp-data-processing-1-bmp-and-openbmp/</link><guid isPermaLink="true">https://bgpkit.com/blog/real-time-bgp-data-processing-1-bmp-and-openbmp/</guid><description>Real-time BGP data processing is very critical on building monitoring services that can detect BGP issues quickly with minimum delay and react to anomalies quic...</description><pubDate>Wed, 10 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Real-time BGP data processing is very critical on building monitoring services that can detect BGP issues quickly with minimum delay and react to anomalies quickly and mitigate potential issues.&lt;/p&gt;
&lt;p&gt;We are creating a new series of posts describing how we design our software to work with real-time BGP data streams. As an opening, we will describe how we handle data streams with BMP protocol and OpenBMP messages.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;BMP&lt;/h1&gt;
&lt;p&gt;The BGP Monitoring Protocol (BMP) is a protocol that allows monitoring of BGP devices.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc7854&quot;&gt;RFC7854&lt;/a&gt; describes the purpose of BMP as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many researchers and network operators wish to have access to the contents of routers’ BGP Routing Information Bases (RIBs) as well as a view of protocol updates the router is receiving. This monitoring task cannot be realized by standard protocol mechanisms. Prior to the introduction of BMP, this data could only be obtained through screen scraping.&lt;/p&gt;
&lt;p&gt;BMP provides access to the Adj-RIB-In of a peer on an ongoing basis and a periodic dump of certain statistics the monitoring station can use for further analysis. From a high level, BMP can be thought of as the result of multiplexing together the messages received on the various monitored BGP sessions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are multiple types of BMP messages, each serving different purposes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Peer up and down notification: notification about the status of peering sessions to a monitored router;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Initiation message: inform the monitoring station of the routers vendor, software version, and so on;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Termination message: provides information on why a monitored router is terminating a session;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Route monitoring&lt;/strong&gt;:initial synchronization of the routing table;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Route mirroring&lt;/strong&gt;: verbatim duplication of messages as received.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For real-time BGP data processing, we are specifically interested in the route monitoring and route-mirroring messages, as we provide the routing information encoded as actual BGP messages.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;OpenBMP&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.openbmp.org/&quot;&gt;OpenBMP&lt;/a&gt; is a software implementation of the BMP protocol. It is an open-source project created by Cisco and currently maintained by nice folks from &lt;a href=&quot;https://www.caida.org/&quot;&gt;CAIDA/UCSD&lt;/a&gt; and &lt;a href=&quot;https://routeviews.org/&quot;&gt;RouteViews&lt;/a&gt;. It is implemented in C++, can be used with any compliant BMP sender (e.g., router).&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/openbmp-architecture.B0H8ukJV.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Architecture graph of OpenBMP&lt;/p&gt;
&lt;p&gt;OpenBMP provides multiple formats for outputting the BMP messages collected from the connected routers, one of which is the &lt;code&gt;raw_bmp&lt;/code&gt; format, which is a thin wrapper of the raw BMP messages. The &lt;code&gt;raw_bmp&lt;/code&gt; format provides the best performance and allows use to handle the BMP messages directly without having to write a different parser for the plaintext messages.&lt;/p&gt;
&lt;p&gt;RouteViews currently provides a OpenBMP Kafka stream that streams BMP messages from their collectors.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;BGPKIT Parser with BMP/OpenBMP Support&lt;/h1&gt;
&lt;p&gt;We develop BGPKIT Parser to provide a one-stop solution for handling all parsing tasks regarding BGP data. Supporting real-time data like BMP is a very important milestone for us.&lt;/p&gt;
&lt;p&gt;We have recently developed the full support for BMP messages, and partial support for OpenBMP messages (for &lt;code&gt;raw_bmp&lt;/code&gt; type only). This enables us to start working with real-time BMP streams like RouteViews’ Kafka stream.&lt;/p&gt;
&lt;p&gt;Below is an example code that takes RouteViews’ Kafka OpenBMP stream and parse the messages into internal data structures:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;let mut reader = Cursor::new(Vec::from(kafka_payload));
let header = parse_openbmp_header(&amp;#x26;mut reader).unwrap();
if let Ok(msg) = parse_bmp_msg(&amp;#x26;mut reader) {
    info!(&quot;Parsing OK: {:?}&quot;, msg.common_header.msg_type);
    match msg.message_body {
        MessageBody::RouteMonitoring(m) =&gt; {
            dbg!(m.bgp_update);
        }
        _ =&gt; {}
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is a break down of what it does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;it first creates a bytes reader from the raw Kafka message payload;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;then parse OpenBMP message header, which contains some basic information about the BMP session;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;then it calls the &lt;code&gt;parse_bmp_msg&lt;/code&gt; function to parse the embedded raw BMP messages and print out the BGP update messages if the parsing is successful.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is a full code example:&lt;/p&gt;
&lt;p&gt;%[&lt;a href=&quot;https://gist.github.com/digizeph/fcac3027555c0b744ea0b3a11197b694#file-bgpkit-parser-kafka-bmp-example-rs&quot;&gt;https://gist.github.com/digizeph/fcac3027555c0b744ea0b3a11197b694#file-bgpkit-parser-kafka-bmp-example-rs&lt;/a&gt;]&lt;/p&gt;
&lt;p&gt;We have published the SDK on &lt;a href=&quot;https://crates.io/crates/bgpkit-parser&quot;&gt;crates.io&lt;/a&gt; and &lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser&quot;&gt;GitHub&lt;/a&gt;. Feel free the check out the example code at &lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser/blob/main/examples/real-time-routeviews-kafka-openbmp.rs&quot;&gt;examples/routeviews-kafka.rs&lt;/a&gt; if you are interested.&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>Introducing BGPKIT Parser</title><link>https://bgpkit.com/blog/introducing-bgpkit-parser/</link><guid isPermaLink="true">https://bgpkit.com/blog/introducing-bgpkit-parser/</guid><description>BGPKIT Parser is an open-source Rust-based MRT/BGP data parser that takes a MRT formatted binary file and turns it into BGP messages. It is one of the most impo...</description><pubDate>Mon, 01 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://bgpkit.com/_astro/parser-architecture.CTkI0CeB.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;BGPKIT Parser is an open-source Rust-based MRT/BGP data parser that takes a &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6396&quot;&gt;MRT&lt;/a&gt; formatted binary file and turns it into BGP messages. It is one of the most important building block software that enables BGP data processing and analysis tasks.&lt;/p&gt;
&lt;h2&gt;Design and Features&lt;/h2&gt;
&lt;p&gt;As mentioned in our previous post &lt;a href=&quot;https://medium.com/bgpkit/introducing-bgpkit-broker-b734dac4661e&quot;&gt;introducing BGPKIT Broker&lt;/a&gt;, the most used BGP data collection projects, RouteViews and RIPE RIS, both publish their collected BGP data in MRT format on their data platform. The BGPKIT Parser is designed to handle parsing tasks for these data sources.&lt;/p&gt;
&lt;p&gt;We design our parser to strictly follow the industry standard, e.g. &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc4271&quot;&gt;RFC4271&lt;/a&gt; and &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6396&quot;&gt;RFC6396&lt;/a&gt;. BGPKIT Parser is also designed with the following goals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;performant&lt;/strong&gt;: comparable to C-based implementations like &lt;code&gt;bgpdump&lt;/code&gt; or &lt;code&gt;bgpreader&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;actively maintained&lt;/strong&gt;: we consistently introduce feature updates and bug fixes, and support most of the relevant BGP RFCs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ergonomic API&lt;/strong&gt;: a three-line for loop can already get you started.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;battery-included&lt;/strong&gt;: ready to handle remote or local, &lt;code&gt;bzip2&lt;/code&gt; or &lt;code&gt;gz&lt;/code&gt; data files out of the box.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;open-source&lt;/strong&gt;: we want people to use our parser freely and we can continue develop and improve it based on community feedbacks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To demonstrate how easy it is to get started using BGPKIT Parser, check out the example below where we print out all BGP messages from a remote MRT file on RouteViews:&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/parser-remote-example.BSEtLIra.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Example of reading a remote MRT file from RouteViews and print out BGP messages. Code available at &lt;a href=&quot;https://gist.github.com/digizeph/9977371653f39a459ff3ae507dc3636c&quot;&gt;https://gist.github.com/digizeph/9977371653f39a459ff3ae507dc3636c&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are a number of things happens when we call &lt;code&gt;for elem in BgpkitParser::new(url)&lt;/code&gt; :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;it creates a new BgpkitParser struct instance with the provided URL to the data file;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;it tries to retrieve the content of the remote file and download the raw compressed bytes into memory;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;it determines the compression type by file suffix and calls corresponding decompression library to create a buffered reader;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;it then creates an iterator (used by the for loop) that continuous return new parsed items until it reaches the end of the data stream.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We determined that it is worth the extra binary file size to bring in the network and compression libraries into the project so that the library users will never have to worry about handling data downloading and decompression by themselves again.&lt;/p&gt;
&lt;h2&gt;The Future&lt;/h2&gt;
&lt;p&gt;The future of the BGPKIT Parser lies on continuous performance and stability improvements, as well as some exciting features that we are currently planning. Some of the coming features include&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;adding capability of handling &lt;strong&gt;real-time data streams&lt;/strong&gt; coming from RIPE RIS Live and RotueViews’s Kafka BMP stream;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;supporting &lt;strong&gt;data serialization&lt;/strong&gt; back to MRT files (reverse-parsing), which allows users to produce customized MRT files after data processing;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;adding &lt;strong&gt;WASM support&lt;/strong&gt; to allow BGP data parsing directly on the web with JavaScript.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Because we are building our software in Rust, we can effortlessly tapping into Rust’s great software ecosystem and continue introducing new features and improvements. The future of BGPKIT Parser is exciting and we can’t wait to bring more features for you to try out!&lt;/p&gt;
&lt;p&gt;For more details about the BGPKIT Parser, check out our GitHub repo and our website. Feedback is highly appreciated!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-parser&quot;&gt;https://github.com/bgpkit/bgpkit-parser&lt;/a&gt;&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>Introducing BGPKIT Broker</title><link>https://bgpkit.com/blog/introducing-bgpkit-broker/</link><guid isPermaLink="true">https://bgpkit.com/blog/introducing-bgpkit-broker/</guid><description>BGPKIT Broker is a data API service that focuses on building a BGP data file index to enable searching for public/private BGP data files with custom filters. It...</description><pubDate>Sun, 31 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;BGPKIT Broker is a data API service that focuses on building a BGP data file index to enable searching for public/private BGP data files with custom filters. It is one of the building block components designed by BGPKIT to facilitate BGP data processing with ease.&lt;/p&gt;
&lt;h2&gt;The first step to investigate a BGP event.&lt;/h2&gt;
&lt;p&gt;Imagine this scenario: a malicious player just attempted to hijack a IP prefix using BGP announcements, and you are interested in learning what exactly happened during that half-hour down time of the victim network, how would you start investigating?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Collecting evidences&lt;/strong&gt;. Luckily for us, the actors on the Internet, good and bad, always leave traces behind if they use BGP as their method. There are a number of &lt;strong&gt;reputable public BGP route collectors&lt;/strong&gt; operating for years collecting all BGP messages received from their connected router peers and dump them into regular dump files. The most used projects are &lt;a href=&quot;https://routeviews.org/&quot;&gt;RouteViews&lt;/a&gt; and &lt;a href=&quot;https://www.ripe.net/analyse/internet-measurements/routing-information-service-ris/ris-raw-data&quot;&gt;RIPE RIS Data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Files are all over the places.&lt;/strong&gt; There are more than 60 different data collectors from the two projects alone, and each publishing their data under separate sites. The two projects also have different data file structure and compression algorithms for their data dump files. It is not hard to go find one data file during the interested event time from one collector, but it will be a real hassle to gather URLs toward &lt;strong&gt;all data files&lt;/strong&gt; that include information within a specified time range.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/collector-mrt-files.NhsyDmjV.png&quot; alt=&quot;Collector’s data is published as compressed MRT files at regular frequency.&quot;&gt;
&lt;p&gt;Collector’s data is published as compressed MRT files at regular frequency.&lt;/p&gt;
&lt;h2&gt;BGPKIT Broker — A BGP Data File Index API Service&lt;/h2&gt;
&lt;p&gt;We designed the BGPKIT Broker to resolve one and only one problem: quickly collect links to the BGP data files that matches a filtering criteria.&lt;/p&gt;
&lt;p&gt;You can filter BGP data using multiple criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;start_ts&lt;/code&gt; : UNIX timestamp that all files must be dumped after&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;end_ts&lt;/code&gt; : UNIX timestamp that all files must be dumped before&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;data_type&lt;/code&gt; : the type of the data file, can be &lt;code&gt;update&lt;/code&gt; or &lt;code&gt;rib&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;collector&lt;/code&gt;: the collector ID that the files are generated from&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;project&lt;/code&gt;: the data collection project, can be &lt;code&gt;route-views&lt;/code&gt; or &lt;code&gt;riperis&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;page&lt;/code&gt; and &lt;code&gt;page_size&lt;/code&gt;: the pagination control for collecting a large number of files&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is an example REST API call: &lt;a href=&quot;https://api.broker.bgpkit.com/v1/search?data_type=update&amp;#x26;start_ts=1633046400&amp;#x26;end_ts=1633132800&amp;#x26;collector=rrc00&amp;#x26;project=riperis&amp;#x26;page%20=2&amp;#x26;page_size=3&quot;&gt;https://api.broker.bgpkit.com/v1/search?data_type=update&amp;#x26;start_ts=1633046400&amp;#x26;end_ts=1633132800&amp;#x26;collector=rrc00&amp;#x26;project=riperis&amp;#x26;page%20=2&amp;#x26;page_size=3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It asks for all data &lt;strong&gt;updates&lt;/strong&gt; files dumped between &lt;strong&gt;1633046400&lt;/strong&gt; and &lt;strong&gt;1633132800&lt;/strong&gt;, from &lt;strong&gt;RIPE RIS&lt;/strong&gt;’s collector &lt;strong&gt;rrc00&lt;/strong&gt;. It also requested for the &lt;strong&gt;second page&lt;/strong&gt; of the results and &lt;strong&gt;each page contains 3 items&lt;/strong&gt;.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/broker-api.D4KMyvpf.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;&lt;strong&gt;DEPRECATED&lt;/strong&gt;: BGPKIT Broker API service is freely available to use, hosted at &lt;a href=&quot;https://api.broker.bgpkit.com/v1/&quot;&gt;https://api.broker.bgpkit.com/v1/&lt;/a&gt;. The documentation is available at &lt;a href=&quot;https://docs.broker.bgpkit.com/&quot;&gt;https://docs.broker.bgpkit.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BGPKIT Broker API and SDK has been upgraded to V2 now. The V1 examples are left up for legacy services that depends on it. Please check out the current API documentation at for more:&lt;/strong&gt;&lt;a href=&quot;https://api.broker.bgpkit.com/v2/&quot;&gt;&lt;strong&gt;https://api.broker.bgpkit.com/v2/&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;BGPKIT Broker Rust API&lt;/h2&gt;
&lt;p&gt;The BGPKIT Broker API service is built entirely using Rust, and of course we also developed native Rust API to access the broker data with ease using Rust.&lt;/p&gt;
&lt;img src=&quot;https://bgpkit.com/_astro/broker-rust-api.DMXU0OSc.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Example BGPKIT Broker Rust API call&lt;/p&gt;
&lt;p&gt;The Rust API is open source under MIT license, and with the free API, you can already build your own workflow today!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bgpkit/bgpkit-broker&quot;&gt;https://github.com/bgpkit/bgpkit-broker&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Easy On-premise Deployment&lt;/h2&gt;
&lt;p&gt;For API service like this, especially that it also collects and indexes data of over 10 years span, one might imagine the deployment could be complex and slow.&lt;/p&gt;
&lt;p&gt;For BGPKIT Broker, we spent extra efforts to make the API deployment process as quickly as possible, and also efficient on resource consumption. For references, we bootstrapped the entirety of the database in &lt;strong&gt;under 5 minutes&lt;/strong&gt;, and cost &lt;strong&gt;less than 1 GB storage&lt;/strong&gt; to store the information in PostgreSQL database. The whole database and API run fluently with &lt;strong&gt;less than 500MB of RAM&lt;/strong&gt; usage. This allows use to deployment extra instances when under heavy load without costing a fortune.&lt;/p&gt;
&lt;p&gt;Users who need dedicated resource allocation for query performance can contact us for private API hosting that are not shared by others. Enterprise option is also available for on-premise deployment with customization consultation available. If you are interested in testing it out, feel free to shoot us an email at &lt;a href=&quot;mailto:contact@bgpkit.com&quot;&gt;contact@bgpkit.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For more information, checkout our website!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bgpkit.com/broker&quot;&gt;https://bgpkit.com/broker&lt;/a&gt;&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item><item><title>BGPKIT Journey Started</title><link>https://bgpkit.com/blog/bgpkit-journey-started/</link><guid isPermaLink="true">https://bgpkit.com/blog/bgpkit-journey-started/</guid><description>BGPKIT is a small-team start-up that aims to provide comprehensive tool suite to facilitate companies building on-premise BGP data monitoring services. We start...</description><pubDate>Tue, 26 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://bgpkit.com/&quot;&gt;BGPKIT&lt;/a&gt; is a small-team start-up that aims to provide comprehensive tool suite to facilitate companies building on-premise BGP data monitoring services. We started our journey of building the best BGP data toolkit for developers in October, 2021. Here is a brief glance on what we are working on and what values we strive to provide.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bgpkit.com&quot;&gt;&lt;img src=&quot;https://bgpkit.com/_astro/bgpkit-logo.Dt47iy_S.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;BGP&lt;/h1&gt;
&lt;p&gt;Border Gateway Protocol (BGP) is the de facto inter-domain routing protocols being used by every major companies on the Internet. &lt;strong&gt;The main purpose of BGP is to allow companies exchange IP prefix reachability information.&lt;/strong&gt; In other words, companies tell other companies what IP blocks they have, and how to reach these IP blocks. This the key functionality that enables the Internet.&lt;/p&gt;
&lt;p&gt;Because the purpose of BGP is to exchange information and allow everyone to know how to reach certain IP blocks, the BGP messages must be propagated globally and publicly. There are a number of data sources available out there that provides BGP data archives (e.g. &lt;a href=&quot;https://www.routeviews.org/routeviews/&quot;&gt;RouteViews&lt;/a&gt; and &lt;a href=&quot;https://www.ripe.net/analyse/internet-measurements/routing-information-service-ris/ris-raw-data&quot;&gt;RIPE RIS&lt;/a&gt;), or real-time data like BGP &lt;a href=&quot;https://lg.he.net/&quot;&gt;looking glass&lt;/a&gt;es or &lt;a href=&quot;https://ris-live.ripe.net/&quot;&gt;live BGP data stream&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The aforementioned data sources contains a wealth of information if you know what to look for. For example, by looking at BGP information, researchers can &lt;a href=&quot;https://www.caida.org/catalog/datasets/as-relationships/&quot;&gt;infer companies relationships&lt;/a&gt;, monitor &lt;a href=&quot;https://www.bgpmon.net/&quot;&gt;security&lt;/a&gt; &lt;a href=&quot;https://radar.qrator.net/&quot;&gt;incidences&lt;/a&gt;. Having handy toolkit in hand enables people to build successful businesses around BGP data.&lt;/p&gt;
&lt;h1&gt;BGPKIT&lt;/h1&gt;
&lt;p&gt;At BGPKIT, we build software tools to process BGP data and reveal insights from BGP messages. We aims to provide the best developer experience, and enable customers to build their own BGP data processing pipeline and monitoring services on-premise.&lt;/p&gt;
&lt;h2&gt;Complete Tool Suite&lt;/h2&gt;
&lt;p&gt;Our goal is to build complete tool suite for BGP data processing: data collection, parsing, analysis, programmable and visual interface, and data warehousing. Everything you need to handle BGP data.&lt;/p&gt;
&lt;h2&gt;Rust Implementation&lt;/h2&gt;
&lt;p&gt;To achieve the best performance and security, we choose to focus on building our tools using the &lt;a href=&quot;https://www.rust-lang.org/&quot;&gt;Rust programming language&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We believe that Rust’s ecosystem is now mature enough that building modern features like data streaming, async data workflow, parallel processing, web API, or even porting the entire codebase onto WASM and running it on browsers is a archivable task with reasonable efforts.&lt;/p&gt;
&lt;h2&gt;Powerful Extensibility&lt;/h2&gt;
&lt;p&gt;At BGPKIT, we design our libraries to provide powerful API and assist customers to further customize workflow to meet individual needs. We strive to provide the most ergonomic interfaces that allow library consumers to easily integrate our library into theirs.&lt;/p&gt;
&lt;h2&gt;Embrace Open-Source&lt;/h2&gt;
&lt;p&gt;We also believe that good tools empowers people, so we open-sourced our building block libraries to the public with very permissive license so that any interested parties can explore and build their own ideas free of charge and limitation.&lt;/p&gt;
&lt;p&gt;We are excited to start this journey of building, and we hope to have the chance to work with more people and building more dreams together! Follow us here, on &lt;a href=&quot;https://twitter.com/bgpkit&quot;&gt;Twitter&lt;/a&gt;, or visit our website to learn more!&lt;/p&gt;</content:encoded><dc:creator>Mingwei Zhang</dc:creator></item></channel></rss>