<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Aggregation · XB Field Notes</title><link>https://hk.xbcatm.com/tags/aggregation/</link><description>Illustrated field notes on HTTP, DNS, TLS, Caddy, SSH, releases, and recovery.</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://hk.xbcatm.com/tags/aggregation/index.xml" rel="self" type="application/rss+xml"/><item><title>BGP aggregation shrinks the routing table</title><link>https://hk.xbcatm.com/posts/bgp-aggregation-shrinks-the-table/</link><pubDate>Fri, 08 Oct 2027 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/posts/bgp-aggregation-shrinks-the-table/</guid><description>Aggregation reduces downstream entries when specifics are suppressed, but the covering route needs clear ownership, origin policy, and handling for missing components.</description><content:encoded>&lt;p&gt;Aggregation can replace several more-specific advertisements with one covering prefix. It reduces downstream table entries only where the specifics are actually suppressed and no alternative peer supplies them. The components need not fill the whole summary: advertising a sparse block also attracts traffic for its holes. Aggregation therefore combines a scaling decision with a reachability and ownership decision.&lt;/p&gt;
&lt;h2 id="what-aggregation-actually-buys"&gt;What aggregation actually buys&lt;/h2&gt;
&lt;p&gt;A peer receiving three component prefixes plus their summary has four routes, not one. In FRR, &lt;code&gt;aggregate-address 10.1.0.0/16 summary-only&lt;/code&gt; suppresses advertisements of the covered more-specifics. It does not delete those routes from the aggregator&amp;rsquo;s own BGP table. FRR requires at least one contributing more-specific in that BGP table; having only an unrelated routing-table entry is insufficient.&lt;/p&gt;
&lt;p&gt;A stable summary can hide changes to individual contributors from downstream peers. That benefit is conditional: losing the last contributor, changing aggregate attributes, or changing export policy can still generate updates. Suppression does not repair the failed customer path. It changes what other networks learn about the failure, while the aggregator must continue diagnosing its internal components.&lt;/p&gt;
&lt;p&gt;The covering route is also broader than the sum of a few listed subnets. Before announcing it, verify authority for the whole range and the intended aggregate origin. Address alignment and a short table are insufficient evidence of permission. A provider may legitimately aggregate customer space under its allocation; different component origins do not automatically make aggregation unauthorized.&lt;/p&gt;
&lt;h2 id="preserve-a-clear-origin"&gt;Preserve a clear origin&lt;/h2&gt;
&lt;p&gt;FRR&amp;rsquo;s &lt;code&gt;suppress-map&lt;/code&gt; selects which more-specifics to suppress. Its &lt;code&gt;as-set&lt;/code&gt; option concerns a different dimension: representing AS information from component paths. An AS_SET is an unordered set, not a list of only the customers&amp;rsquo; origin numbers. Under the historical RFC 4271 path-length calculation, a set counts as one, regardless of its members.&lt;/p&gt;
&lt;p&gt;RFC 9774 now deprecates AS_SET and AS_CONFED_SET. Unless explicitly configured otherwise during transition, speakers must not advertise them and must treat received routes containing them as withdrawn. Choose an unambiguous authorized aggregate origin and appropriate ROAs instead of treating AS_SET as a routine aggregation fix. An origin filter does not automatically accept an aggregate merely because a router generated it.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;For ordinary destination-based lookup in a selected forwarding table, the longest installed matching prefix wins. A valid &lt;code&gt;/24&lt;/code&gt; therefore beats its covering &lt;code&gt;/16&lt;/code&gt;, regardless of their relative BGP local preferences. First, however, each route must pass policy, next-hop resolution, and installation. BGP selection among paths for one prefix and forwarding lookup across different prefixes are separate decisions.&lt;/p&gt;
&lt;p&gt;If one component disappears, only destinations that lose their usable route fall back to the summary; healthy components can keep forwarding. One customer&amp;rsquo;s withdrawal does not inherently blackhole every address covered by the aggregate. At the aggregating router, a discard route for unmatched summary destinations prevents traffic from following a default route back upstream and forming a loop.&lt;/p&gt;
&lt;p&gt;Discard behavior and diagnostic messages depend on the platform and configuration. Neither silence nor ICMP delivery is universal. Removing the summary also does not necessarily mean no route: another covering prefix or a default can remain. Check the actual forwarding table and counters rather than declaring the summary dishonest solely because a specific prefix is absent.&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/posts-bgp-aggregation-shrinks-the-table.detail.en.svg" alt="Three specifics plus a summary still make four advertised routes unless specifics are suppressed and not supplied elsewhere. In the article model, 10.1.4.0/24 is withdrawn under the 10.1.0.0/16 discard route, while 10.1.7.0/24 remains usable. The covering range needs full authorization, and removing a summary does not always remove every possible route." width="560" height="1960"&gt;&lt;figcaption&gt;Containment is schematic, not proportional address space. The lookup model preserves the healthy component and sends unmatched summary destinations to discard. It does not validate BGP sessions, origin policy or convergence, and was not rerun.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;p&gt;Run this self-contained Python 3 model with assertions enabled. It creates no sockets and changes no router configuration. Two customer routes share a covering discard entry. The deliberately small model isolates longest-prefix matching and the scope of a component withdrawal; it does not implement BGP sessions, policy, or convergence.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ipaddress&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip_network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ip_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.0.0/16&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;discard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ip_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.4.0/24&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;customer-a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ip_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.7.0/24&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;customer-b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;no-route&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefixlen&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.4.9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;customer-a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.7.9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;customer-b&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ip_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.4.0/24&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.4.9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;discard&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.7.9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;customer-b&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;withdraw A: A=discard, B=customer-b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ip_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.0.0/16&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.4.9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;no-route&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.1.7.9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;customer-b&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;withdraw summary: A=no-route, B=customer-b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first output shows customer A falling into the discard route while customer B remains reachable through its own entry. After deleting the summary, A reports no route because this fixture intentionally has no default. B still resolves independently. Add a default entry as a follow-on exercise and observe how the no-route result changes.&lt;/p&gt;
&lt;p&gt;On an authorized lab router, separately inspect received, selected, advertised, and installed routes before and after withdrawing a contributor. Confirm whether the summary survives and whether its attributes change. Router integration and forwarding behavior require that real exercise; the Python assertions establish only the stated lookup model.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc4271.html"&gt;RFC 4271 BGP-4&lt;/a&gt;, defines ATOMIC_AGGREGATE, AGGREGATOR and AS_SET, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.frrouting.org/en/latest/bgp.html"&gt;FRR BGP route aggregation&lt;/a&gt;, documents aggregate generation, component suppression and AS-set options, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc4632.html"&gt;RFC 4632 CIDR&lt;/a&gt;, the classless prefix and longest-match forwarding model, checked 2026-09-16.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9774.html"&gt;RFC 9774: Deprecation of AS_SET&lt;/a&gt;, updated aggregation and origin requirements, checked 2026-10-15.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>