One message, a thousand withdrawals

· Mingwei Zhang
bgp mrt measurement

Public route collectors record every BGP message they receive, and those records are the raw material for most routing research. This post looks at one simple ratio in that data, prefixes per UPDATE message, across one full week and the entire collector fleet. The average is 2.87. The interesting part is everything the average hides.

A thousand prefixes in one message

On 21 October 2026, at 04:05 UTC, the RIPE RIS collector rrc24 recorded an unusual five minutes from AS46997. One of its UPDATE messages withdrew 1,133 network prefixes. Over the whole file the session withdrew 1,079,048 prefixes in 1,069 messages, and the median message carried 1,008 of them.

For context: across the entire public collector fleet that week, the average UPDATE message carried 2.87 prefixes.

The gap is not a bug. A BGP UPDATE can be up to 4 KB. A withdrawal only needs to list prefixes, roughly four bytes each for IPv4, so a single message can hold about a thousand of them. An announcement must also carry the path attributes: origin, AS path, next hop, often communities. The same 4 KB then holds a handful of prefixes.

The ceiling is real. 4 KB divided by four bytes per IPv4 prefix is about 1,200, and the largest single withdrawal message in the whole week, 1,197 prefixes from AS29691 at rrc20, sits just under it.

That asymmetry is the seed of this whole story. The internet’s update stream is not one behavior. It is at least two, and the average of 2.87 hides both of them.

What we counted

Public route collectors write their data to MRT files: RIPE RIS every five minutes, RouteViews every fifteen. Each BGP UPDATE message can carry several route entries, called NLRI (network layer reachability information). One entry is one prefix being announced or withdrawn. If a message announces ten prefixes, that is one message and ten NLRI.

Define packing as NLRI per message. A router that batches aggressively has high packing; a router that sends one prefix per message has packing near 1. The denominator is the record count for that session, which is worth remembering later: RouteViews update files contain only UPDATE records, while RIPE RIS files also record keepalives and state changes, so RIS numbers are understated by roughly 2.5%.

We processed every update file published between 2026-10-16 20:00 and 2026-10-23 20:00 UTC:

  • 79 collectors: 23 from RIPE RIS, 56 from RouteViews
  • 83,999 update files, 98.9% of which we could attribute to individual sessions
  • 20.82 billion NLRI carried in 7.25 billion records
  • 2,696,994 per-file session records, covering 2,846 peer addresses

Packing is not traffic and it is not convergence time. It is a property of how a speaker chooses to serialize its updates, and that is exactly why it is interesting: it is the one part of BGP behavior that a remote observer can measure cleanly, without seeing inside anyone’s network.

The shape of the fleet

Over the week, the fleet-wide weighted packing was 2.872 NLRI per message. Cross-checking at file level instead of session level gives 2.865, and the per-session mean is 2.711 with a median of 2.229. Three ways of counting, all within 0.3% of each other.

The distribution is where it gets interesting.

Figure 1. Distribution of packing across 2.7 million file-sessions, 2026-10-16 to 10-23 UTC.

Distribution of NLRI per UPDATE message across 2.7 million file-sessions

Four out of ten sessions sit between 1.5 and 2.5 NLRI per message. A further 13% sit between 1.0 and 1.5. Then there is a long tail: 1.05% of sessions exceed 10 NLRI per message, and 169 sessions (0.006%) exceed 100. The tail is tiny in count and large in consequence, as a later section shows.

Same week, same internet, threefold difference

Figure 2. Networks with more than 50 million NLRI over the week, ranked by packing.

Networks with more than 50 million NLRI over the week, ranked by NLRI per UPDATE message

The busiest packer among large peers was AS16276 (OVH), at 6.05 NLRI per message, and it was stable: between 5.81 and 6.40 every single day. AS142289 (Roxonic Systems) followed at 5.95, AS38880 (Micron21) at 5.22, AS24115 at 4.86.

At the other end, AS212635 sat at 1.62, AS51185 at 1.92, AS31027 at 1.98, and AS32590 (Valve Corporation) at 2.20.

A threefold spread between networks that are all sending a full BGP feed is a real difference in behavior, not a measurement artifact. But we cannot attribute it from this data. Peer role (transit versus exchange member), router vendor, address family mix, and local policy all plausibly contribute, and MRT files do not say which. What the data does support is narrower and still useful: packing is a stable property of a session, not noise. OVH’s number barely moved for seven days.

Trickle and bulk: one session, two modes

The most instructive case in the whole dataset is AS6939 (Hurricane Electric), because the same network appears at many collectors and behaves differently at each.

Figure 3. AS6939 across four sessions on 2026-10-22, by message size. Left: share of messages. Right: share of NLRI.

AS6939 message size distribution across four sessions on 2026-10-22

We took one file per hour for a full day and looked at AS6939’s sessions at two RIPE RIS collectors and two RouteViews collectors:

  • rrc16 (IPv6 session): 14,396 messages, 89,825 NLRI, packing 6.24
  • rrc16 (IPv4 session): 2,594 messages, 10,378 NLRI, packing 4.00
  • route-views2 (IPv4): 147,882 messages, 425,444 NLRI, packing 2.88
  • route-views6 (IPv6): 146,780 messages, 281,911 NLRI, packing 1.92

The first thing to notice is that in all four sessions, between 60% and 71% of messages carried exactly one prefix. Single-prefix messaging is the default everywhere. The sessions differ in the tail: at rrc16’s IPv6 session, 149 messages carried 100 or more prefixes and those messages accounted for 67% of all NLRI. At route-views6, only 8 messages in the entire day carried 100 or more, together 0.5% of NLRI.

The tail is not a smoothing artifact of large messages carrying more. Inside one five-minute file at rrc16, five messages carried 1,957 prefixes, about 391 each, which is close to what a 4 KB message can hold for IPv6. The other 575 prefixes in the same file arrived one per message. The two sets had zero prefixes in common.

So the same network was running both modes simultaneously: a bulk path for one set of prefixes and a trickle path for another.

Three alternative explanations do not survive the data:

  • Window length. RIS files are five minutes and RouteViews files are fifteen. Combining three consecutive rrc16 files gives a fifteen-minute window with packing 6.67, still 3.5 times the RouteViews session.
  • Add-path. Both sides record BGP4MP_MESSAGE_AS4 (subtype 4), not the add-path subtype 9.
  • Collector-triggered refresh. The file contains no locally originated records and no route refresh, so the bulk messages came from the peer unprompted.

What remains unexplained is why AS6939’s sessions differ. That is a question about outbound policy or implementation on specific sessions, and MRT files cannot answer it. The honest conclusion is the weaker one: cross-collector packing differences are driven by content shape, so they should not be read as a uniform batching policy.

Withdrawals are the exception

Withdrawals are a minority of the stream: 1.49 billion withdrawal NLRI against 20.82 billion total, or 7.13%. But they behave nothing like announcements.

Figure 4. Largest single withdrawal message observed per network in the week, against the fleet average of 2.87 NLRI per message.

Largest single withdrawal message per network in the week

Sessions that were mostly withdrawing (80% or more of their NLRI were withdrawals) averaged 37.84 NLRI per message. Sessions with no withdrawals at all averaged 4.50. The bulk examples are stark: AS1257 (Tele2) withdrew 426,062 prefixes at rrc25 on 19 October using 422 messages, the largest carrying 1,089, followed by two more bursts of 347,427 and 148,759 in the next ten minutes. AS328840 withdrew 636,782 prefixes in 787 messages at rrc00 on 18 October, largest 1,078. AS8359 and AS51519 also crossed 600 prefixes in a single message.

These events are rare. Of 2.7 million session records, 169 exceeded 100 NLRI per message. But when a network decides to drop a large part of its table, it does so in the densest messages BGP can produce, because there are no attributes to carry.

When the shape breaks

On 23 October 2026 the shape broke, at 12:17 UTC. Six RouteViews collectors lost their BGP sessions within seconds of each other. What followed is visible in the numbers even though the session events themselves are not.

Figure 5. Packing and withdrawal share per 15-minute slot for three RouteViews collectors during the 23 October reset.

Packing and withdrawal share per 15-minute slot during the 23 October RouteViews reset

In the 12:15 slot, route-views2 jumped from 2.47 to 6.18 NLRI per message while its withdrawal share collapsed from 7.99% to 0.03%. route-views5 went from 2.08 to 5.75 with withdrawals falling from 9.69% to 0.15%. route-views8 went from 3.00 to 6.77, withdrawals from 13.28% to 0.30%. Each collector re-advertised its full table in the densest possible form, then settled back within 45 minutes.

The RIPE RIS collectors were unaffected: their session state-change counts stayed flat through the same window.

One caveat matters here. RouteViews update files contain only BGP4MP UPDATE records, with no OPEN, KEEPALIVE, or STATE_CHANGE entries. The session reset is therefore inferred from the shape of the update stream, not observed directly. The inference is strong, since a synchronized silence followed by a full re-advertisement with almost no withdrawals has no other ordinary explanation, but it is still an inference.

What this is good for

Three practical consequences.

First, do not compare file sizes across archives. RIPE RIS writes gzip and RouteViews writes bzip2, so byte counts differ by roughly a factor of two for the same traffic. The packing numbers, 2.804 for RIS and 2.922 for RouteViews, are nearly identical, and once the RIS denominator is corrected for keepalive and state-change records the two are effectively the same. Content shape is the same; the compressor is not.

Second, packing is a cheap detector. Two numbers per file, NLRI per message and withdrawal share, identify a full-table replay without any session state. That is useful precisely where the session records are missing, which is every RouteViews update file.

Third, capacity planning should count NLRI, not messages. During a replay, elements per second rise about 2.5 times while message counts rise far less. A pipeline that sizes itself on messages per second will under-provision exactly when a collector resets.

What this does not say

  • Only update files are covered, not RIB dumps. RIB packing is a different measurement.
  • The per-session breakdown covers the seven days studied here; earlier periods are not included.
  • The AS6939 case is one day and one sample per hour. It is enough to establish that the two modes coexist, not to characterize how often the bulk mode fires.
  • Packing is not BGP message rate, convergence time, or traffic volume. It says nothing about how fast a network converges.

Reproduce it

Everything here comes from public archives, and the toolchain is open source. Download any update file from the RIPE RIS or RouteViews archives, then expand it into elements with bgpkit-parser:

bgpkit-parser updates.20261022.1200.gz

Each output line is one NLRI with its peer, timestamp, and path attributes, so counting lines per peer gives the NLRI total, and counting MRT records gives the message total. BGPKIT Broker locates the files; Monocle does the same slicing from the command line.

Figures were generated with matplotlib from the same aggregates.